ci(sovereign): roadmap-fragment-parity — a PR writes ONE fragment, never the generated aggregate - #73
Merged
Merged
Conversation
…ver the aggregate docs/roadmaps/roadmap.yaml is a GENERATED aggregate of docs/roadmaps/entries/<id>.yaml. A pull request writes one fragment, named for its own Pmat-Ticket trailer, and never touches the aggregate. WHY. Every PR writing the roadmap makes it a shared mutable file on the merge path — Amdahl serial fraction 1, so N pull requests contend on one file however disjoint their code is. Measured in paiml/aprender 2026-09-15: 146 PRs opened over 10 days against 88 merged, and of the last 25 merge_group CI runs 7 succeeded and 15 were CANCELLED. On a nine-PR batch the textual merge placed PMAT-3226 after PMAT-3229 and the sorted-roadmap guard went RED while all nine inputs were individually green. OPT-IN ON entries/, and that is load-bearing, not caution. This workflow runs in EVERY consumer repo, and the ones that have not migrated still write roadmap.yaml legitimately. An unconditional gate here lands everywhere at once — the shape that took every consumer red when continue-on-error was removed upstream. No entries/ dir => NOT-RUN, printed, never a silent pass. Same discipline as the roadmap-valid step it sits beside: a result that was not measured must never read as a pass. Also fail-closed on an unreachable base: a check that cannot read its input does not pass. The logic is the one proved in paiml/aprender's scripts/check_roadmap_fragment_parity.sh — 6 rows, 0 red, including a REGISTERED mutation (removing the aggregate check must make the "PR edits the aggregate" row pass, and does). It is inline here rather than calling that script because a script in the consumer repo is a gate the producer can edit. This is the bespoke form. It becomes `pmat comply` once pmat ships the check (paiml/paiml-mcp-agent-toolkit#1363), which is tracked there, not forgotten. NOT ARMED FOR aprender YET: aprender pins this workflow by SHA (70e51ec). It takes effect there only when that pin is bumped, which is a separate PR whose RED->GREEN evidence is a fragment-less PR failing under the new SHA. Refs paiml/aprender#3296, paiml/paiml-mcp-agent-toolkit#1363. Pmat-Ticket: PMAT-1363
noahgift
added a commit
to paiml/aprender
that referenced
this pull request
Sep 15, 2026
…tream, and this one ran I said this guard was "wired nowhere, dark on purpose". That was WRONG. `guard_tree.sh` discovers every script in `scripts/` and runs it; `skip_reason` exempts a guard only when it is wired WITH ARGS in a workflow, release-time, or an unwired baseline. Mine is none of those, so guard_tree ran it BARE — with its default `--base $(git merge-base origin/main HEAD)` — against the one branch that legitimately rewrites roadmap.yaml, and it failed exactly as designed: FAIL docs/roadmaps/roadmap.yaml is GENERATED — run `make roadmap-aggregate` It also pushed check_no_pipe_into_grep_q.sh over its ceiling: baseline 75, measured 78, all three sites mine (lines 49, 61 and the mutation needle at 157). Deleting it rather than exempting it, on merit: * The real gate SHIPPED UPSTREAM in paiml/.github#73 (merged, f02067e), inline in sovereign-ci.yml where the consumer repo cannot edit it. * Keeping a copy here is precisely the producer-owns-the-gate problem that PR's own body gives as the reason for inlining it. * Its case table did its job: 6 rows 0 red with a registered mutation, and that is what the upstream logic was proved against. It is preserved in this branch's history at 411e718 and cited from #73. Also regenerates the README CONTRACT_COUNT block: this branch adds contracts/apr-roadmap-fragments-v1.yaml, and FALSIFY-README-002 compares the block against the MERGE tree, not the diff — 1840 -> 1841. The block is generated, so it is an equality and not a ratchet. Verified after: check_no_pipe_into_grep_q.sh baseline 75 measured 75 rc=0 check_readme_claims.sh rc=0 (5/5) make roadmap-aggregate-check rc=0 roadmap_fragments --selftest 14 rows, 0 red Pmat-Ticket: PMAT-3296
This was referenced Sep 15, 2026
noahgift
added a commit
that referenced
this pull request
Sep 21, 2026
… untracked at the base (paiml/aprender#3709) (#77) #73's step opts in on `docs/roadmaps/entries/` and assumes that a repo with entries/ has stopped committing `roadmap.yaml`. aprender has entries/ and still commits the aggregate on purpose: its own roadmap-aggregate-check requires the committed file in sync. So bumping aprender's pin past f02067e (needed for `coverage_on: tag`, paiml/aprender#3676) would have refused every aprender PR, and it would also have refused the migration PR that is meant to arm this check. The step is now NOT-ACTIVE while `roadmap.yaml` is TRACKED at the BASE, and says so on one line. It arms by itself once the aggregate is untracked, with no flag to forget. It keys on the BASE tree, not the PR head: a PR that re-adds the aggregate cannot switch its own gate off, and the PR that deletes it is not refused by the gate that deletion arms. Case rows, run from this step's own `run:` text in fixture repos, 7/7 ok: - no entries/ -> NOT-RUN - aggregate tracked at base (aprender today) -> NOT-ACTIVE - untracked, fragment named by its Pmat-Ticket trailer -> ok - untracked, fragment with no trailer -> refused - untracked at base, PR re-adds the aggregate -> refused - the migration PR deleting the tracked aggregate -> NOT-ACTIVE - untracked, no fragment touched -> ok, 0 fragment(s) On 69482db (before this change) the aprender-today row and the migration row are both RED ("is GENERATED"). Mutant: keyed on HEAD instead of the base, the re-add row passes (a hole) and the migration row is refused. Refs paiml/aprender#3709 paiml/aprender#3676 paiml/aprender#3650 Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
PR2 of the roadmap-fragment sequence.
docs/roadmaps/roadmap.yamlbecomes aGENERATED aggregate of
docs/roadmaps/entries/<id>.yaml; a pull request writes onefragment, named for its own
Pmat-Tickettrailer, and never touches the aggregate.Why
Every PR writing the roadmap makes it a shared mutable file on the merge path —
Amdahl serial fraction 1, so N pull requests contend on one file however disjoint their
code is.
Measured in paiml/aprender 2026-09-15: 146 PRs opened over 10 days against 88 merged
(production 14.6/day, drain 8.8/day), and of the last 25
merge_groupCI runs7 succeeded, 15 were CANCELLED. On a nine-PR batch the textual merge placed
PMAT-3226afterPMAT-3229and the sorted-roadmap guard went RED while all nineinputs were individually sorted and green.
.gitattributescould not fix it:merge=roadmapis a custom driver registeredper-invocation locally, so GitHub's merge-queue server never runs it. Only git's
built-in drivers apply server-side, and
unionis correct for a line-delimited ledgerand wrong for a file needing sorted insertion.
Opt-in on
entries/— load-bearing, not cautionThis workflow runs in every consumer repo. rmedia, infra, apex and the rest still
write
roadmap.yamllegitimately, and an unconditional gate here lands in all of themat once — the shape that took every consumer red when
continue-on-errorwas removedupstream.
No
docs/roadmaps/entries/⟹ NOT-RUN, printed, never a silent pass. Same disciplineas the
roadmap-validstep it sits beside. It also fails closed on an unreachable base:a check that cannot read its input does not pass.
Evidence
The logic is the one proved in paiml/aprender's
scripts/check_roadmap_fragment_parity.sh— 6 rows, 0 red, including a registered mutation: removing the aggregate check
must make the "PR edits the aggregate" row pass, and it does.
entries/dir → NOT-RUNPmat-TickettrailerInline here rather than calling that script, because a script in the consumer repo is a
gate the producer can edit.
Not armed for aprender yet
aprender pins this workflow by SHA (
70e51ec045d95983e9a3d02342d13283bf5f9dc1). It takeseffect there only when that pin is bumped — a separate PR (PR3) whose RED→GREEN evidence
is a fragment-less PR failing under the new SHA.
Becomes
pmat complyThis is the bespoke form. paiml/paiml-mcp-agent-toolkit#1363 ships the check in
pmat comply, and this step should call that once 3.41.0 is on the fleet pin. Trackedthere, not forgotten.
no-close: paiml/aprender#3296 and paiml/paiml-mcp-agent-toolkit#1363 are cross-repo
references. This PR is one step of the sequence they describe and closes neither — #3296
needs PR3's pin bump, #1363 needs the pmat implementation and release.
🤖 Generated with Claude Code