Repository navigation
roadmap fragments: one file per ticket so the merge path has no shared mutable file — Amdahl serial fraction is currently 1 #3296
Description
Diagnosis — queueing, not compute
Little's Law: 42 open ÷ 8.8 merges/day ≈ 4.8 days lead time. Arrival exceeds drain,
and drain is capped at 1 because every PR writes docs/roadmaps/roadmap.yaml. That
makes the Amdahl serial fraction on the merge path 1. 27 runners cannot help a queue
whose critical section is a shared file — measured at the time of writing, aprender had
4 runs in progress carrying 5 jobs while the queue drained at 8.8/day.
Why the current mitigation cannot work
.gitattributes declares docs/roadmaps/roadmap.yaml merge=roadmap. roadmap is a
custom driver registered per-invocation by scripts/ci_resolve_dirty.sh
(git -c merge.roadmap.driver=…), deliberately, so a git config write does not leak
into sibling worktrees. GitHub's merge-queue server has no such config, so inside the
queue the driver is inert and git falls back to a plain text merge.
Only git's built-in drivers work server-side. union is built-in — but it is correct
for impl-estimates.jsonl (line-delimited, order-insensitive; #3256) and wrong for
roadmap.yaml, which requires sorted insertion. Measured 2026-09-15 on the nine-PR
batch #3295: the textual merge placed PMAT-3226 after PMAT-3229 and
check_roadmap_sorted.sh went RED — "prefix PMAT must be numerically ascending among
its own peers" — while all nine inputs were individually sorted and green.
Two files, two policies. No single merge driver fixes both.
The change: one fragment per ticket
docs/roadmaps/entries/PMAT-NNNN.yaml <- what a PR writes
docs/roadmaps/roadmap.yaml <- GENERATED on main, never hand-edited
Unique filename by construction ⟹ N PRs pairwise disjoint on the roadmap ⟹ conflict
rate 0% by proof, not by luck. The sort problem stops existing because there is no
shared file to sort — ordering becomes a property of the aggregator, which is total.
Gates (base-owned — the producer is never the gate)
- Trailer ↔ filename parity: a new
entries/<id>.yamlmust match the commit's
Pmat-Ticket: <id>trailer. - No hand edits: a PR touching
roadmap.yamldirectly fails. check_roadmap_sorted.shruns against the aggregate only.- A
pvcontract that the aggregate's ordering is total, ids unique, every fragment
present. Extends the existingcontracts/apr-roadmap-ids-unique-v1.yamland
contracts/apr-roadmap-additive-diff-v1.yamlrather than duplicating them.
What breaks, named up front
git blame docs/roadmaps/roadmap.yamlstops being chronological; history moves to
git log docs/roadmaps/entries/.- Anything consuming the raw file keeps working only because the aggregate stays
committed onmain— that is deliberate, and is why the file is generated
post-merge rather than gitignored. - The open PRs that carry inline roadmap edits need a one-time conversion.
Sequence
This lands before raising max_entries_to_merge (live ruleset 17836320:
max_entries_to_build=3, max_entries_to_merge=1, grouping_strategy=ALLGREEN,
merge_method=SQUASH). Grouping 8 entries that all write roadmap.yaml amplifies the
collision instead of fixing it. Once fragments land, native queue batching gives one
commit per PR on main — which hand-squashing a batch does not.
Falsifier
A merge group containing ≥ 2 roadmap-touching PRs builds green, and each merges as its
own commit. Today that group ejects.
Refs #3294 (five-whys), #3256 (union for the ledger).
Measured 2026-09-15 on main @ e6e13ad.
Activity
noahgift commentedon Sep 15, 2026
Session split (operator, 2026-09-15).
This ticket's orchestration runs in Claude session
b59147ae-201e-4299-9354-6a53738c822b, which is bound to PMAT-1098 —goal.sh setrefuses a second ticket per session, and that refusal stands rather than being overridden.That session does
route=selforchestration only, in this order: #3299 (RED main blocks everything) → #3297 → PR2 (fragment gate in sovereign-ci.yml) → PR3 (pin bump) → clean-room → publish → forjar → drain.The pmat half (PMAT-1363, paiml/paiml-mcp-agent-toolkit#1364) runs under
/paiml-implementin a separate session. Neither session claims the other's ticket.ci(sovereign): roadmap-fragment-parity — a PR writes ONE fragment, ne…
roadmap: PMAT-4433 as an entries/ fragment, aggregate regenerated (#3296
roadmap fragments (§1): one file per ticket, so the merge path has no…
PR3: a roadmap edit without its fragment is refused — the contention p…
ci(paiml#4433): 20 CI jobs → 5 fat jobs + Σ-executed invariant (paiml…
docs(lookahead): KREG fold plan - the roadmap entry is a source; name…