Skip to content

roadmap fragments: one file per ticket so the merge path has no shared mutable file — Amdahl serial fraction is currently 1 #3296

Closed
paiml/.github
#73

Description

@noahgift

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)

  1. Trailer ↔ filename parity: a new entries/<id>.yaml must match the commit's
    Pmat-Ticket: <id> trailer.
  2. No hand edits: a PR touching roadmap.yaml directly fails.
  3. check_roadmap_sorted.sh runs against the aggregate only.
  4. A pv contract that the aggregate's ordering is total, ids unique, every fragment
    present. Extends the existing contracts/apr-roadmap-ids-unique-v1.yaml and
    contracts/apr-roadmap-additive-diff-v1.yaml rather than duplicating them.

What breaks, named up front

  • git blame docs/roadmaps/roadmap.yaml stops being chronological; history moves to
    git log docs/roadmaps/entries/.
  • Anything consuming the raw file keeps working only because the aggregate stays
    committed on main
    — 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

  1. noahgift commented on Sep 15, 2026

    @noahgift
    MemberAuthor

    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 set refuses a second ticket per session, and that refusal stands rather than being overridden.

    That session does route=self orchestration 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-implement in a separate session. Neither session claims the other's ticket.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions