You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
the drift check cannot see a requirement that no longer describes the code, whether it lost its implementation or was contradicted by its successor #728
Found while backfilling the 6.0 changelog, by an agent reading #665's diff rather than its title.
The stale requirement
REQ-change-055 (specs/change/requirements.md:810) is live and unsuperseded. It says:
Change sequence allocation SHALL floor on the highest sequence observed locally (active, archive, local ledger) and, when available, the remote default-branch .specsync/change-sequence.json high-water. Concurrent multi-clone fleets MAY set SPECSYNC_SEQUENCE_BASE to disjoint ranges so agents that cannot see each other do not mint the same numeric CHG prefix.
with the acceptance criterion SPECSYNC_SEQUENCE_BASE=N makes the next allocated sequence at least N.
None of that exists. The ordinal retirement (7cbe820, #665) deleted maximum_observed_sequence and remote_sequence_high_water, and change identity became a slug. Verified rather than assumed:
SPECSYNC_SEQUENCE_BASE appears in zero.rs files. Neither does the fragment SEQUENCE_BASE or sequence_base, so it is not constructed dynamically either.
src/change.rs:2188 says it outright, in an error message: "nothing writes this file any more, so it cannot be repaired by allocating".
The active change workspace on this branch is backfill-the-6-0-changelog-… — a slug. Archived changes since the retirement are date+slug (2026-08-27-a-definition-approval-may-not-withdraw-…). There is no numeric CHG prefix left to collide.
AGENTS.md:49 still tells agents to prefer SPECSYNC_SEQUENCE_BASE=<N> with disjoint ranges. That guidance is inert rather than dangerous — the collision it guards against cannot occur once identity is a slug — but it is instructions to do something that does nothing.
The part that is actually about the product
specsync check --spec change does not flag REQ-change-055, and by the current model it cannot.
Drift detection measures specs against the code that implements them. A requirement whose implementation was partly changed drifts and is caught. A requirement whose implementation was entirely deleted has nothing left to measure against, so it produces no finding at all — it reads as "nothing to check" rather than "orphaned requirement."
That is this release's most-repeated defect shape one level up, in the tool's own model rather than in a single function: a category empty for want of input, read as a verdict.#672 read an unparseable schema's zero tables as "every table missing"; #684 gated on advice the reader could not take; #720 read an unreadable bypass_actors as "no bypass granted"; #689's first design read absent history as absent readiness. Here, zero implementing symbols reads as zero drift.
The requirement most likely to be stale is exactly the one with no code left pointing at it, and that is precisely the one the current check is blind to.
What this needs
Fix REQ-change-055 — supersede or delete it, and remove the AGENTS.md:49 guidance. Small and unambiguous.
Decide whether the drift model should detect an orphaned requirement at all. A requirement with zero attributable implementation is not automatically a defect — aspirational, process, and policy requirements legitimately have none, and this repository has some. So the answer is probably report, never gate: a distinct unattributed requirement outcome, disclosed and not counted as drift, in the same shape bug: deps silently skips Kotlin import analysis #477 used for unresolvable imports — "owned, foreign, or unattributed and disclosed", where silence is never the default.
(2) is the real work and should not be rushed into 6.0. (1) should land before the stable tag, since a released spec that documents a removed environment variable is the drift this tool exists to catch.
Related: #665 (the retirement), #533 (why the ledger is still read), #477 (the disclosure shape), #672 / #684 / #689 / #720 (the same defect shape).
Found while backfilling the 6.0 changelog, by an agent reading #665's diff rather than its title.
The stale requirement
REQ-change-055 (
specs/change/requirements.md:810) is live and unsuperseded. It says:with the acceptance criterion
SPECSYNC_SEQUENCE_BASE=Nmakes the next allocated sequence at leastN.None of that exists. The ordinal retirement (7cbe820, #665) deleted
maximum_observed_sequenceandremote_sequence_high_water, and change identity became a slug. Verified rather than assumed:SPECSYNC_SEQUENCE_BASEappears in zero.rsfiles. Neither does the fragmentSEQUENCE_BASEorsequence_base, so it is not constructed dynamically either.src/change.rs:2188says it outright, in an error message: "nothing writes this file any more, so it cannot be repaired by allocating".backfill-the-6-0-changelog-…— a slug. Archived changes since the retirement are date+slug (2026-08-27-a-definition-approval-may-not-withdraw-…). There is no numeric CHG prefix left to collide..specsync/change-sequence.jsonis still on disk, frozen atsequence: 163from security(6.0): a trust anchor must be where evidence entered history, not any commit that re-introduces it #663, and still read by validation — deliberately, to enforce that nothing rewrites it downwards (bug(change):change check --commitself-commits change-sequence.json backwards, regressing the high-water mark #533). Read-only is the intended end state; the requirement describes the write side that is gone.AGENTS.md:49still tells agents to preferSPECSYNC_SEQUENCE_BASE=<N>with disjoint ranges. That guidance is inert rather than dangerous — the collision it guards against cannot occur once identity is a slug — but it is instructions to do something that does nothing.The part that is actually about the product
specsync check --spec changedoes not flag REQ-change-055, and by the current model it cannot.Drift detection measures specs against the code that implements them. A requirement whose implementation was partly changed drifts and is caught. A requirement whose implementation was entirely deleted has nothing left to measure against, so it produces no finding at all — it reads as "nothing to check" rather than "orphaned requirement."
That is this release's most-repeated defect shape one level up, in the tool's own model rather than in a single function: a category empty for want of input, read as a verdict. #672 read an unparseable schema's zero tables as "every table missing"; #684 gated on advice the reader could not take; #720 read an unreadable
bypass_actorsas "no bypass granted"; #689's first design read absent history as absent readiness. Here, zero implementing symbols reads as zero drift.The requirement most likely to be stale is exactly the one with no code left pointing at it, and that is precisely the one the current check is blind to.
What this needs
AGENTS.md:49guidance. Small and unambiguous.unattributed requirementoutcome, disclosed and not counted as drift, in the same shape bug: deps silently skips Kotlin import analysis #477 used for unresolvable imports — "owned, foreign, or unattributed and disclosed", where silence is never the default.(2) is the real work and should not be rushed into 6.0. (1) should land before the stable tag, since a released spec that documents a removed environment variable is the drift this tool exists to catch.
Related: #665 (the retirement), #533 (why the ledger is still read), #477 (the disclosure shape), #672 / #684 / #689 / #720 (the same defect shape).