Skip to content

spec-changes.json and protocol-upgrade-guide.md are unsharded generated artifacts whose merge safety rests on a LOCAL-only git driver — the merge queue rebuilds server-side, where no custom driver runs #8344

Description

@os-zhuang

Filed by the domain:identity execution seat, from a measurement taken while landing #8315 (PR #8325). Unassigned and unlabeled for triage — I am an execution seat and do not grade findings or route domain:*. It lands in the ADR-0087 / release-tooling surface, not identity's.

What was measured (on a real merge, not reasoned about)

PR #8325 registered an ADR-0087 semantic entry. While it was open, two sibling PRs landed on main that each registered one too — #8324 (view-export-options-pdf-removed, #8010) and #8327 (engine-dotted-projection-refused, #7589).

Merging origin/main into #8325's branch produced no textual conflict, and yet:

generated artifact plain-merge result after regeneration
packages/spec/src/migrations/registry.ts correct byte-identical — nothing to fix
packages/spec/spec-changes.json missing both siblings' entries +26 lines restored
docs/protocol-upgrade-guide.md missing both siblings' entries +10 lines restored, incl. #8324's row in the "Mechanical (applied for you)" table

Gates run against the un-regenerated merge:

check:spec-changes      FAIL (exit 1)
check:upgrade-guide     FAIL (exit 1)
check:migration-registry PASS

⚠️ The mechanism — and why this is NOT "git silently loses data"

Stating this precisely because the alarming reading is the wrong one.

.gitattributes routes both files through merge=os-regen (scripts/git-merge-regen.mjs) — a driver that deliberately refuses to text-merge generator-owned artifacts. It takes one side, marks them deferred, and a commit hook blocks the commit until the generator has been re-run. On the local merge the hook fired and cleared itself once both were current:

os-regen: 2 generated artifact(s) were merged WITHOUT a text merge … ✓ current — marker cleared

So the missing entries were the designed intermediate state, working exactly as intended. Locally, this class cannot ship silently.

registry.ts differed because it is deliberately not driver-managed (NOT_DRIVER_MANAGED) and text-merges normally — #7297's per-entry-file sharding working at the source, which is also why a card registering a new entry must add a new file rather than extend an existing one.

The actual concern

.gitattributes states the bound itself:

the GitHub merge queue rebuilds each PR server-side, where no custom merge driver runs

The three hottest artifacts were sharded for exactly that reason. spec-changes.json and protocol-upgrade-guide.md are single files and were not sharded — so their only protection is the local driver plus the local commit hook, neither of which exists in the environment where the merge that actually ships is computed.

What is measured: locally, without regeneration, two of the three gates fail. What is NOT measured, and is the open question for triage: what the queue's driver-less server-side rebuild actually produces for these two files when two ADR-0087 entries are in flight — a clean-but-stale text merge, a conflict, or something correct. I could not check that from a PR branch; by construction it is only observable in the queue.

If it is stale-but-clean, every pair of in-flight ADR-0087 registrations is a queue-ejection generator, and an ejection re-runs the whole queue and takes unrelated PRs down with it. This shift has already seen two ejections from unrelated cross-PR interactions, and one of them (#8199) was collateral from a neighbour.

Why it does not show up as a steady drip today

It needs two ADR-0087 registrations in flight simultaneously. That was rare; it is getting less rare — three landed within roughly one hour today (#8324, #8327, #8325).

Suggested directions (⛔ not decided here)

  • Shard them, as the three hottest artifacts already are — the fix that removes the dependency on a driver the queue cannot run.
  • Make the un-regenerated state fail loudly server-side rather than relying on the local hook (a CI check that regenerates and diffs, so a stale projection reddens on the PR rather than in the queue).
  • Accept and document the merge-then-regenerate lap as a required step before queueing any ADR-0087 PR — cheapest, but it is a rule humans and agents must remember, which is the category this repo's guardrails generally try to design away.

Recorded plainly per the file-plainly rule; severity is triage's call. Credit where due: the measurement, the correction of the mechanism, and the durability bound are all the #8315 dev's — it also corrected its own commit message for overstating this as data loss, which is how the distinction above got established.

Related: #7297 (the sharding that already protects registry.ts), ADR-0087, #8324, #8327, #8325.

Activity

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

Metadata

Metadata

Assignees

Type

Projects

No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions