Development record
The agent-contract prompt lint ships and is armed as a blocker in the record-lint configuration, enforcing the frontmatter floor on every prompt under the agents directory and gating the diff-armed unbumped-edit check, yet the intent that promised it sits in the planned bucket and its spec sits open. The definition of done says a change that delivers a planned intent closes its spec in the same change, and that the omission is silent by construction: the release cut composes its changelog from terminal folders only, so an intent whose code is on the branch with its spec still open ships with no line and the cut exits zero doing so. That is the state here, and it means the release notes for this cut describe a repository in which this rule does not exist. The condition was found by a cross-check of the design record against the shipped surface rather than by any gate, because no gate asks whether a planned intent's work is already present: the record lint reads what the records say about each other and the release derivation reads only what reached a terminal folder, so the one question that would catch it, whether the code a planned intent describes is already merged, is asked by nothing. Fix: close the spec so the intent ships and the cut sees it, once the acceptance criteria are checked against what actually landed, which is the maintainer's call rather than an automatic move. Detector: a planned intent whose acceptance criteria are satisfied by code already on the branch is reported before a release derives its cut, so the choice to ship it or to keep it planned is made rather than defaulted.
The agent-contract prompt lint ships and is armed as a blocker in the record-lint configuration, enforcing the frontmatter floor on every prompt under the agents directory and gating the diff-armed unbumped-edit check, yet the intent that promised it sits in the planned bucket and its spec sits open. The definition of done says a change that delivers a planned intent closes its spec in the same change, and that the omission is silent by construction: the release cut composes its changelog from terminal folders only, so an intent whose code is on the branch with its spec still open ships with no line and the cut exits zero doing so. That is the state here, and it means the release notes for this cut describe a repository in which this rule does not exist. The condition was found by a cross-check of the design record against the shipped surface rather than by any gate, because no gate asks whether a planned intent's work is already present: the record lint reads what the records say about each other and the release derivation reads only what reached a terminal folder, so the one question that would catch it, whether the code a planned intent describes is already merged, is asked by nothing. Fix: close the spec so the intent ships and the cut sees it, once the acceptance criteria are checked against what actually landed, which is the maintainer's call rather than an automatic move. Detector: a planned intent whose acceptance criteria are satisfied by code already on the branch is reported before a release derives its cut, so the choice to ship it or to keep it planned is made rather than defaulted.
Second instance, and the size of the population, 2026-09-09
itd-93 and spc-14 are the same shape: the verb ships, the intent sits planned, the spec sits open, and the cut derives no line for it. Two confirmed instances make this a pattern rather than an oversight, and the population it could hide in is large: the planned bucket holds sixty-one intents and every one of their specs is open, so nothing in the record distinguishes an intent whose work is still ahead from one whose work is already merged. Neither instance was found by a gate. Both were found by a human-directed cross-check of the design record against the shipped surface, which is not a thing that runs on a schedule.
Grounds
- pursued: we expect a mechanical refusal to close the forgotten-close gap where documentation did not, because the measured population of 61 accumulated under a documented rule that every reader had already been told; what would show this wrong is the gate arming and the population continuing to grow, which would mean the cause is not forgetting but an absent signal for when an intent is done
- pursued: we expect a declared delivery to arrive with its spec closed, because the merge now refuses one that does not; what would show this wrong is planned intents with open specs continuing to accumulate while changes that finish them carry no Delivers: trailer, meaning the cause is reluctance or an absent signal rather than forgetting
Frontmatter
| id | iss-2609091642508005 |
|---|---|
| type | issue |
| lifecycle | resolved |
| severity | major |
.abcd/work/issues/resolved/iss-2609091642508005-the-agent-contract-rule-ships-while-its.md
open on GitHub ↗ · commit history ↗
References1
- itd-2609111003026787A gate catches the intent whose work is live while its spec is still openrelated
mentions3
- dcn-26092300000000162026-09-23 — itd-4 AC3 built as ruled (B3): the promote join's back-links are `related_issues` on the intent and `related_intents` on the ledger record, and `abcd intent audit --issue-drift` checks them. Seven implementation decisions the ruling left open, taken by the implementing lane. (1) No reader tolerates the retired `promoted_to` / `promoted_from`: the ledger reader refuses and skips a record carrying `promoted_to`, the intent reader ignores `promoted_from`, and a promote write meeting either refuses. The refusal, the record gate's finding and the drift finding each name the successor and the remedy, `abcd capture migrate --apply`, a verb that reports by default, writes only with `--apply`, and is idempotent. It is a verb and not a one-off script because managed repositories hold the same retired names and need the same repair. (2) "Promoted" is the PAIR, an intent in the record's `related_intents` that names the record back in its `related_issues`, because an issue's `related_intents` already carried loose relations before the rename and still does; the double-promote refusal and the record page's next move read the pair. So the migration completes a join an older abcd wrote from one end only (iss-327 and itd-93, iss-2609091642508005 and itd-2609111003026787), or those records would stop reading as promoted. (3) Link mode on the issue route writes both halves; before the rename it stamped the issue alone, which left a hand-filed draft with no edge back. (4) `related_issues` holds `rdi-N` as well as `iss-N`: a reading item lives in the issue ledger's tree and graduates the same way, and one key per side keeps the join one shape. The first entry is the record the intent was promoted from; a later link appends, so `back_edge_kept` still reports the first. (5) The drift check reports an intent naming a record that does not name it back, but not an ISSUE naming an intent that does not name it back, since that is a loose relation; a reading item carries no loose relation, so from its end a one-way stamp is drift. A shipped intent naming an issue in `wontfix/` is drift, like one in `open/`, because the itd-4 scope asks whether the issue "actually moved to resolved/". The check is not wired into preflight or CI; `--strict` is there for whoever wires it. (6) The historical passages in shipped intents, closed specs and adr-0057 carry a `(historical)` marker beside the retired names, the precedent `development/activity` set, so the new `retired-promote-back-links` ban can gate every current record. (7) The impact is `breaking`: itd-4 declares none, and the rename removes `promoted_to` from `capture list --json`, `promoted_from` from the intent JSON and both link keys from the record page, and makes an unmigrated ledger unreadable until the migration runs.
- itd-93abcd Scaffolds a Release Gate That Works on the First Try
- spc-14abcd-scaffolds-a-hardened-changelog-driven-release-gate-into