Skip to content

PMAT-3459: the tag step cannot be reached without a clean milestone — gate inside cut_tag(), 3 mutants killed - #3617

Merged
noahgift merged 6 commits into
mainfrom
PMAT-3459-tag-calls-milestone-cut
Sep 21, 2026
Merged

noahgift merged 6 commits into
mainfrom
PMAT-3459-tag-calls-milestone-cut

Conversation

@noahgift

@noahgift noahgift commented Sep 20, 2026 •

Copy link
Copy Markdown
Contributor

This is the exit criterion #3459 is actually about: the tag step calls scripts/check_milestone_cut.sh and cannot be reached without it.

v0.68.1 was tagged 2026-09-17T15:06:29Z by an autopilot copy living outside the repository — six minutes after #3455 merged the milestone gate. grep -c check_milestone_cut autopilot.sh was 0. The spec required the read; no code path that cuts a tag made it.

The gate is inside the function that tags

cut_tag() calls the gate ahead of git tag. There is no path through it that reaches git tag with the gate unsatisfied, and the two non-zero codes are different failures, named separately:

gate rc what happens
0 MILESTONE-GATE <v> clean at the cut — the tag is cut
1 the milestone holds open item(s) → no tag, no publish
2+ the gate could not judge → no tag. Unknown is not a pass.

The guard is behavioural, not a grep

"The file mentions the gate" is satisfied by a comment. scripts/check_tag_step_gated.sh extracts cut_tag() from the autopilot and runs it against a stubbed gate, once per outcome, asserting on whether a GIT-TAG appears in the transcript.

ok    gate rc=0 -> the tag is cut
ok    gate rc=1 -> no tag, no publish
ok    gate rc=2 (Unknown) -> no tag

Its --self-test then builds mutants and requires each to be RED and the real subject to be GREEN, so a red subject cannot masquerade as a killed mutant:

ok    mutant 1: gate call deleted -> RED (2 failing row(s))
ok    mutant 2: gate verdict discarded -> RED
ok    mutant 3: cut_tag() removed -> ENV rc=2, not a pass
ok    the real subject is GREEN, so the REDs above are the mutants'
SELF-TEST PASSED

Mutant 1 is the #3459 defect restored — it reconstructs the tree that tagged v0.68.1 and requires the guard to refuse it. That is the difference between a guard and a decoration.

This covers the ticket's three required mutations: gate call removed ⇒ RED · gate exit 1 ⇒ no tag, no publish · gate exit 2 ⇒ no tag.

The contract

contracts/tag-step-milestone-gate-v1.yaml, kind: pattern, in the same PR as the ticket asks. pv validate → 0 errors, 0 warnings, contract is valid; pv lint → PASS, 8 armed.

Every one of its six falsification tests was run, not assumed — which is how FALSIFY-TSMG-006 was caught asserting test -x on a file the repo deliberately keeps non-executable (guard_tree.sh invokes its whole universe as bash scripts/..., so the executable bit is not the property). The test was wrong, not the file; corrected to test -f. A falsification test that fails for the wrong reason is worse than none.

The self-test was running nowhere — found while answering criterion 4

The previous commit added a --self-test with three killed mutants. No job executed it. Measured with guard_tree.sh --no-cargo, not read:

before:  PASS  scripts/check_tag_step_gated.sh [run]        <- self-test never ran
after:   PASS  scripts/check_tag_step_gated.sh [self-test]
         PASS  scripts/check_tag_step_gated.sh [run]

guard_tree.sh decides a guard advertises --self-test by looking for that literal substring in the guard's own --help output (guard_tree.sh:47, advertises_self_test). This guard had no --help handler, so it got one row and its three mutants were executed by nothing — a facility with a self-test and no caller, in the commit that added the self-test. Invisible to reading: the guard worked, the self-test worked, and the wiring between them did not exist. Fixed with the tree's own idiom (ci_run_explicit_test_commands.sh:147 and others). guard_tree.sh --no-cargo: 66 checks, 0 failed, both rows present.

Criterion 4 — what this satisfies, and the gap it does not close

The chain is now live and a broken self-test reddens a required check:

self-test runs in guard-tree → gate fails on needs.guard-tree.result != "success" → gate is required by ruleset 13878864 ("Green Main — unified gate enforcement")

Worth recording, because both of us had it wrong: the required set is two mechanisms, not one. Classic branch protection lists ["ci / gate","workspace-test"]; the rulesets additionally require gate. An earlier revision of this body claimed ci / gate reads needs.guard-cargo.result — it does not. ci / gate is job gate: at line 1536 of paiml/.github's sovereign-ci.yml and aggregates only [test, lint, coverage, security, provenance]. The job that reads the guards is aprender's own gate.

This satisfies criterion 4's effect but not its base-definedness, and that distinction is the point of the bullet. ci.yml:66 pins sovereign-ci.yml@70e51ec045d95983e9a3d02342d13283bf5f9dc1 — sha-pinned, in another repo, immutable by the PR under review. Everything in aprender's ci.yml, guard-tree and gate alike, is head-defined: a PR can edit it. That is precisely why the PR-receipt check was moved out of gate (PP-066 C0-5, PRQ-013, #2982):

Receipt presence is NOT read here any more. This job runs under the PR head's definition of ci.yml, so a receipt clause here was a check the PR under review could edit.

So moving this self-test into gate would re-introduce the property that job was deliberately cleaned of, and buy nothing — guard-tree is equally head-defined and already reddens gate. Closing the base-definedness gap needs an input hook in paiml/.github's sovereign-ci.yml, which has none today and is a cross-repo row. Recorded on #3459 rather than left in a PR body.

Nothing under .github/workflows/ is touched by this PR.

Gates — all 12 rc=0

Fresh worktree off origin/main (00e5d2dde):

check_tag_step_gated.sh + --self-test · check_hardcoded_paths.sh --full-if-capable · check_bashrs_gate.sh + --self-test · check_apr_bin_pinned.sh · check_no_pipe_into_grep_q.sh · check_shell_lint_ratchet.sh · check_guards_are_wired.sh · check_contract_enforcement.sh · check_contract_test_binding.sh · check_sourced_libs_option_neutral.sh

SEC011 was real in the new guard and is fixed with one validated rmtree() used by every call site — not suppressed, and not the check copy-pasted per site.

⚠ What is NOT in this PR, and the hazard nothing catches

V/T/MS/EPIC and AP=/mnt/nvme-raid0/agent-wt/rel-0682-autopilot are still 0.68.2's. Parameterising them is its own row — mixing it here would make the gate change unreviewable, which is the lesson this ticket has been teaching all day.

The hardcoded-path ratchet does not match /mnt (#3592, proved with a committed two-path probe scoring +1, not +2). So AP=/mnt/... passes every gate we own, and it has to be changed by hand with nothing to remind anyone. This paragraph is the only guard that exists for it.

keep-open: #3459 — this PR satisfies two of its four stated exit criteria: all three mutations RED, and the kind: pattern contract in the same PR. Criterion 1 cannot be satisfied by any pull request — "The v0.69.0 tag is cut by the in-repo path" happens when the train runs. Criterion 4 (self-test inside ci / gate) is not satisfied either: the issue measured on #3455 that a guard_tree.sh self-test is not in that context, and satisfying it needs a .github/workflows/* edit, which is an operator check-in. Parameterisation is not an exit criterion — that was my own addition and it is withdrawn; it is #3618. #3080 is the parent epic; #3592 is its own row. Refs #3080, #3445, #3455, #3592, #3618.
ont-delta: none — no ontology type, shape or known-red list touched. The changes to contracts/census.json (1799→1800), contracts/contracts.nt (+5 triples) and README CONTRACT_COUNT are the regenerated derivatives of adding one kind: pattern contract, produced with the pinned pv (scripts/pv_bin.sh), not ontology edits.

🤖 Generated with Claude Code

Closes the exit criterion #3459 is actually about. v0.68.1 was tagged 2026-09-17
15:06:29Z by an autopilot copy living OUTSIDE the repository, six minutes after
#3455 merged the milestone gate; `grep -c check_milestone_cut autopilot.sh` was 0.
The spec required the read and no code path that cuts a tag made it.

THE GATE IS INSIDE THE FUNCTION THAT TAGS. cut_tag() calls
scripts/check_milestone_cut.sh ahead of `git tag`, and there is no path through it
that reaches `git tag` with the gate unsatisfied. Both non-zero codes are fail-closed
and they are DIFFERENT failures, named separately:

  rc 0  -> MILESTONE-GATE <v> clean at the cut; the tag is cut
  rc 1  -> the milestone holds open item(s): no tag, no publish
  rc 2+ -> the gate could not judge: no tag. Unknown is not a pass.

THE GUARD IS BEHAVIOURAL, NOT A grep. "The file mentions the gate" is satisfied by a
comment. scripts/check_tag_step_gated.sh EXTRACTS cut_tag() from the autopilot, runs
it against a stubbed gate once per outcome, and asserts on whether a GIT-TAG appears
in the transcript. Its --self-test then builds three mutants and requires each to be
RED, and the real subject to be GREEN so a red subject cannot masquerade as a killed
mutant:

  mutant 1  gate call deleted (the #3459 defect, restored)  -> RED, 2 failing rows
  mutant 2  gate verdict discarded with `|| true`           -> RED
  mutant 3  cut_tag() removed entirely                      -> ENV rc=2, never a pass

That covers #3459's three required mutations: gate call removed => RED, gate exit 1
=> no tag and no publish, gate exit 2 => no tag.

contracts/tag-step-milestone-gate-v1.yaml (kind: pattern) lands in the same PR, per
the ticket. `pv validate` and `pv lint` both PASS, and every one of its six
falsification tests was RUN, not assumed -- which is how FALSIFY-TSMG-006 was caught
asserting `test -x` on a file the repo deliberately keeps non-executable (guard_tree.sh
invokes its whole universe as `bash scripts/...`, so the executable bit is not the
property). The test was wrong, not the file; corrected to `test -f`.

WIRING, with no .github/workflows edit and no operator check-in: guard_tree.sh's
universe is exactly `git ls-files 'scripts/check_*.sh'`, a flat glob, so the guard runs
in the guard-tree job -- and `ci / gate` reads needs.guard-tree.result, so it is gated
by the required check transitively. #3459 notes that wiring a self-test literally inside
the `ci` job would be a workflow edit; this route does not need one.

SEC011 ("missing validation for 'd' before rm -rf") was real in the new guard and is
fixed with ONE validated rmtree() used by every call site, not suppressed and not
repeated per site.

Gates, fresh worktree off origin/main 00e5d2d -- all 12 rc=0:
  check_tag_step_gated.sh and --self-test · check_hardcoded_paths.sh --full-if-capable
  check_bashrs_gate.sh and --self-test · check_apr_bin_pinned.sh
  check_no_pipe_into_grep_q.sh · check_shell_lint_ratchet.sh · check_guards_are_wired.sh
  check_contract_enforcement.sh · check_contract_test_binding.sh
  check_sourced_libs_option_neutral.sh

NOT in this PR, deliberately: V/T/MS/EPIC and AP are still 0.68.2's. Parameterising
them is its own row -- mixing it here would make the gate change unreviewable, which is
the lesson this ticket has been teaching all day. The hazard is stated in the PR body:
the hardcoded-path ratchet does NOT match /mnt (#3592, proved with a committed
two-path probe scoring +1 not +2), so AP=/mnt/... passes every gate we own and must be
changed by hand with nothing to remind anyone.

Refs #3080, #3445, #3455, #3592

Pmat-Ticket: PMAT-3459
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
@github-actions

github-actions Bot commented Sep 20, 2026 •

Copy link
Copy Markdown

§13.11 rung 1 — quorum shadow verdict

S13-SHADOW pr=3617 head=4a62af449128965f734ea9e0014b95d9693c0c37 verdict=REFUSE class=Q1 arm_rc=1

Shadow mode: this records a verdict and merges nothing. A refusal
to arm is not a block (§13 adds zero rows to §7) — the pull request is
exactly as green as it was.

Found while answering #3459's exit criterion 4. The self-test added in the previous
commit was DISCOVERED BY NOTHING and therefore ran in no job at all:

  before:  PASS  scripts/check_tag_step_gated.sh [run]
  after:   PASS  scripts/check_tag_step_gated.sh [self-test]
           PASS  scripts/check_tag_step_gated.sh [run]

guard_tree.sh decides a guard "advertises --self-test" by looking for the literal
substring `self-test` in that guard's OWN `--help` output (guard_tree.sh:47,
advertises_self_test). check_tag_step_gated.sh had no --help handler, so it got one
row instead of two and its three mutants were never executed by CI. The mutation
proof existed and nothing ran it -- a facility with a self-test and no caller, in the
commit that added the self-test. It is the same class this ticket keeps producing,
and it was invisible to reading: the guard works, its self-test works, and the wiring
between them did not exist.

The fix is a --help handler printing the header comment, which is the idiom the rest
of the tree uses (ci_run_explicit_test_commands.sh:147 and others).

Verified by running guard_tree.sh --no-cargo, not by reading it: 66 checks, 0 failed,
with both rows present.

Refs #3080, #3459

Pmat-Ticket: PMAT-3459
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
@noahgift

Copy link
Copy Markdown
Contributor Author

Mutation proof on a real run — #3620 (draft, never merged)

One line deleted from cut_tag(): the check_milestone_cut.sh call. Nothing else.

guard-tree on that head: https://github.com/paiml/aprender/actions/runs/35526944476/job/106120648591

66 checks, 2 failed
FAIL  scripts/check_tag_step_gated.sh [self-test]
FAIL  scripts/check_tag_step_gated.sh [run]
      | FAIL  gate rc=1 (milestone holds open items) -> A TAG WAS CUT ANYWAY
      | FAIL  gate rc=2 (Unknown) -> A TAG WAS CUT ANYWAY

Exactly the two rows of this guard and no other — the mutant broke nothing else in the tree. gate (required, ruleset 13878864) reads needs.guard-tree.result != "success" and exits 1; its run on this head is queued behind the pool and will be linked here when it lands.

The mutant is more instructive than the design argument. With the call deleted, grep -c check_milestone_cut on cut_tag() returns 3, not 0 — the string survives in the function's own die messages, which name the check in order to explain the refusal. So the heuristic that detected the v0.68.1 defect (grep -c … = 0) passes this reconstruction of it: the call is gone, the tag is cut with the milestone unread, and the grep is satisfied by the error text describing the check that no longer runs.

The general form: a substring test is satisfied by the documentation of the thing it looks for. The more carefully a function explains what it enforces, the more strings it leaves behind for a grep to find after the enforcement is deleted. That is why this guard runs cut_tag() instead of reading it.

…README regenerated

Fixes the three REAL reds on this PR's first run (the rest were env: gx10 ENOSPC
and an intel "dep info ... No such file" mid-build, #2822 class).

1. sigma gate (shard 3/3, lint_passes_on_real_contracts): two errors.
   - `proof_obligations[1].formal` carried an em-dash, which Σ does not declare.
   - formal_prose rose 1464 -> 1467: three `formal:` entries with no Σ symbol,
     against a shrink-only baseline. `prose: true` exempts the glyph check but
     NOT the ratchet (sigma_symbols.rs:107 counts before :110 checks the flag),
     so the fix is real formals, not a marker:
       ∀ rc ∈ {1, 2}: cut_tag(v, t, mc) with gate(v) = rc ⇒ ¬ tagged(t) ∧ gate(v) = 0 ⇒ tagged(t)
       ∀ m ∈ {gate_call_deleted, verdict_discarded, cut_tag_removed}: guard(m) ≠ 0 ∧ guard(subject) = 0
       scripts/release/autopilot.sh ∈ tracked(main) ∧ cut_tag ∈ autopilot.sh
     The shell commands that were there belong in falsification_tests, where they
     already are. This is a better contract, not just a passing one.
   My local `pv lint` had reported "not armed: sigma" and I read that as clean.
   CI's test arms it. The CLAUDE.md pre-push checklist names
   `cargo test -p aprender-contracts --lib`, which reproduces CI exactly; I did
   not run it before the first push. Run now: 1666 passed, 0 failed.

2. the_tracked_repo_graph_is_fresh (shard 1/3): contracts/contracts.nt lacked the
   new contract's 5 triples. `pv extract contracts` with the pinned binary.

3. README claims must match measurement (guard-cargo): contracts/census.json said
   1799, HEAD carries 1800. `pv census contracts --format json > contracts/census.json`
   (the Makefile's form -- bare `pv census` prints and writes nothing) and
   `scripts/readme_sync.sh --write`, CONTRACT_COUNT now 1800.

All three derivatives produced with `. scripts/pv_bin.sh` (pv 0.68.2 from this
tree's target dir), never a bare pv. Each of the three failing CI tests reproduced
locally and re-run green.

Refs #3080, #3459, #3569

Pmat-Ticket: PMAT-3459
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
@noahgift

Copy link
Copy Markdown
Contributor Author

Mutation proof — the required context, on the same run

gate (required via ruleset 13878864): RED — https://github.com/paiml/aprender/actions/runs/35526944476/job/106133801818

guard-tree failed: failure
##[error]Process completed with exit code 1.

ci / gate on the same head: GREEN. That is the two-mechanism finding demonstrated rather than argued: the sha-pinned reusable workflow's ci / gate aggregates only its own jobs and cannot see this guard; aprender's own gate reads needs.guard-tree.result != "success" and goes red. The guard blocks the merge through gate, and criterion 4's base-definedness remains the cross-repo gap recorded on #3459.

Chain, all on head 933b7f1bb of #3620 (one line deleted from cut_tag()):
check_tag_step_gated.sh [self-test] + [run] FAIL → guard-tree FAIL (66 checks, 2 failed) → gate FAIL.

#3620 closed and its branch deleted; the run URLs above are the durable record.

noahgift and others added 2 commits September 20, 2026 23:20
…ilestone-cut

# Conflicts:
#	contracts/census.json
#3617 went CONFLICTING after #3614 landed: #3581 and this branch each regenerated
contracts/census.json, contracts/contracts.nt and README.md for their own contract
additions, and git saw two edits to the same generated bytes.

Resolved the only correct way for a derived file: merge origin/main (taking either
side on the conflict, since it is overwritten next), then REGENERATE all three from
the merged tree with the pinned pv (scripts/pv_bin.sh, pv 0.68.2) -- never by hand.
A hand-merged contracts.nt is a derived file no derivation produced.

Only census.json hard-conflicted. contracts.nt and README.md would have auto-merged
line-wise, which is the worse case: combined derived output that happens to apply
cleanly. Regenerated anyway and compared: contracts.nt is byte-identical to what git
produced (15605 lines both ways) -- verified, not assumed. census 1800 -> 1801 (main's
one plus this branch's one); README CONTRACT_COUNT 1801.

  cargo test -p aprender-contracts --lib      1666 passed, 0 failed
  the_tracked_repo_graph_is_fresh             ok
  check_readme_claims.sh                      rc=0
  check_tag_step_gated.sh --self-test         rc=0
  check_pr_ont_delta.sh on the merged file set  PASS

Refs #3080, #3459, #3569

Pmat-Ticket: PMAT-3459
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
@noahgift

Copy link
Copy Markdown
Contributor Author

Quorum routing (AD-04, Phase 1 step 4), stated by the author — Q1 FIRES and no quorum has run. Disarm pending a verdict.

Files: README.md, contracts/{census.json, contracts.nt, tag-step-milestone-gate-v1.yaml}, scripts/check_tag_step_gated.sh, scripts/release/autopilot.sh → |M| = 4 {contracts, docs(README), scripts, scripts/release}; discounting the three derived files as consequences of the contract it is still 3.

  • Q1 |M|≥3: FIRES
  • Q2 spec/plan artifact: fires — lands a kind: pattern contract
  • Q3: no · Q4: no

No receipt at docs/audits/quorum-PMAT-3459.json; auto-merge was armed by arm_pr_automerge.sh, which asserts guard-tree on the head and by its own header says nothing about review. Running quorum-review.sh --base main --ticket PMAT-3459 --pr 3617 now; the receipt will be posted here. Re-arm only on three independent PASS.

@noahgift
noahgift removed this pull request from the merge queue due to a manual request Sep 20, 2026
@noahgift

Copy link
Copy Markdown
Contributor Author

quorum-review (AD-04): three PASS — agreed (auto_merge: checked=true was_armed=false disarmed=false)

{
 "ticket": "PMAT-3459",
 "head": "b0e4be8e8072af119f936cdb903d37e1a78384c8",
 "width": 3,
 "executor": "agy",
 "agreed": true,
 "auto_merge": {
  "checked": true,
  "was_armed": false,
  "disarmed": false,
  "note": "auto-merge not armed"
 },
 "lanes": [
  {
   "lane": 1,
   "verdict": "PASS",
   "findings": 4
  },
  {
   "lane": 2,
   "verdict": "PASS",
   "findings": 0
  },
  {
   "lane": 3,
   "verdict": "PASS",
   "findings": 0
  }
 ]
}

docs/audits/quorum-PMAT-3459.json from quorum-review.sh --base origin/main --pr 3617
--author-model claude-opus-5, executor agy, width 3, judged at head b0e4be8:

  lane 1  gemini-3.1-pro-high    PASS  4 findings (all confirmations, 2 cited / 2 asserted)
  lane 2  gemini-3.8-flash-high  PASS
  lane 3  gemini-3.7-flash-high  PASS
  dissent: none

Independence inspected, not assumed: every lane file (lane-{1,2,3}.{json,err}) has zero
references to a sibling lane, to $WORK or to the .lanes/ dir -- the recorded failure
where a 3/3 was one vote read through $WORK. Three models, none in the author's family.

This receipt was owed before arming and was not there: the PR was armed through
arm_pr_automerge.sh, which asserts guard-tree on the head and by its own header says
nothing about review. Pulled from the merge queue, judged, re-armed on this receipt.

The ticket text the lanes judged against came from #3461's registration (PMAT-3459's
fragment lives only on that unmerged branch); it was placed in the working tree for
`pmat work status` and never staged, so the diff the lanes judged is base...HEAD
unchanged -- the six files this PR carries.

Pmat-Ticket: PMAT-3459
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
@noahgift

Copy link
Copy Markdown
Contributor Author

AD-04 quorum: 3/3 PASS, three independent lanes

docs/audits/quorum-PMAT-3459.json, executor agy, width 3, judged at head b0e4be8e8, author model claude-opus-5:

lane model verdict
1 gemini-3.1-pro-high PASS (4 findings, all confirmations)
2 gemini-3.8-flash-high PASS
3 gemini-3.7-flash-high PASS

dissent: none. Independence inspected: zero references to a sibling lane, $WORK, or the .lanes/ dir in any lane file. None of the three models is in the author's family.

Ticket text for the lanes came from #3461's registration (PMAT-3459 is on that unmerged branch only), placed in the working tree for pmat work status and never staged — the judged diff is base...HEAD unchanged. Re-armed on this receipt.

@noahgift

noahgift commented Sep 20, 2026 •

Copy link
Copy Markdown
Contributor Author

Cross-inspection of docs/audits/quorum-PMAT-3459.json lanes (non-author: aprender-04, 23:09Z)

lane conversation status verdict findings refs to sibling lanes/receipts turns
lane 1 9079b207 SUCCESS PASS 4 (2 cited) 0 1 turns / 243.522966875 s
lane 2 775c7e2e SUCCESS PASS (structured_output) 0 (0 cited) 0 1 turns / 311.915992261 s
lane 3 c81a9fcd SUCCESS PASS 0 (0 cited) 0 1 turns / 204.140183204 s

Distinct agy conversation ids: 3/3. No lane's response references another lane's file or any quorum-PMAT-*.json. Each verdict is grounded in its own findings (file:line). Read from /mnt/nvme-raid0/agent-wt/pmat3459-milestone/docs/audits/quorum-PMAT-3459.json.lanes on this box; the receipt on the branch records agreed: true, three PASS, models gemini-3.1-pro-high / 3.8-flash-high / 3.7-flash-high, author claude-opus-5 (flag).

Verdict line: 3/3 PASS, independent. Arming.

Table corrected 23:12Z: two cells first read "None" because my parser took response instead of structured_output; the receipt's verdict_source is structured_output for all three lanes and all three are PASS there. #3617 lane 2 opens with an interim sentence ("running in the background…") but its structured summary records the guard run (check_tag_step_gated.sh rc=0, --self-test rc=0, 3 mutants killed) — a completed review, not a premature PASS.

@noahgift
noahgift enabled auto-merge September 20, 2026 23:09
@noahgift
noahgift added this pull request to the merge queue Sep 21, 2026
Merged via the queue into main with commit 6a657fc Sep 21, 2026
25 of 26 checks passed
@noahgift
noahgift deleted the PMAT-3459-tag-calls-milestone-cut branch September 21, 2026 03:43
noahgift added a commit that referenced this pull request Sep 21, 2026
…ted, the three contract derivatives regenerated under the pinned pv

Adopted from aprender-d4 (session unreachable; handoff on #3395). Conflicts were
README.md and contracts/census.json (plus roadmap.yaml's aggregate): #3617 added
contracts/tag-step-milestone-gate-v1.yaml to main (census 1800 -> 1801) after this
branch was cut.

Resolution: main's copies, then regenerated together with the pv built from this
tree at HEAD (`. scripts/pv_bin.sh`, pv 0.68.2): `"$PV" census contracts --format
json > contracts/census.json`, `"$PV" extract contracts`, `make readme-sync`;
`make roadmap-aggregate` (75 fragments, aggregate --check idempotent).

Mechanical, measured: the branch's own delta at its merge-base 237fbc3 was
+24 contracts / +96 triples (1800 -> 1824, 15600 -> 15696); the merged tree is
main + exactly that (1801 -> 1825, 15605 -> 15701). README CONTRACT_COUNT = 1825
in both blocks. The judged diff (docs/audits/quorum-PMAT-3401.json at 9f59beb)
is unchanged in intent; the receipt stands.

Gates: four roadmap guards PASS, check_readme_claims PASS,
cargo test -p aprender-contracts --lib 1666 passed.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant