Development record
2026-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.
2026-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.
Frontmatter
| id | dcn-2609230000000016 |
|---|---|
| type | decision |
| date | 2026-09-23 |
.abcd/work/decisions/dcn-2609230000000016-itd-4-ac3-built-as-ruled-b3-the-promote.md
open on GitHub ↗ · commit history ↗
mentions8
- adr-57Grounds accumulate as an append-only section, in one shared form
- iss-327The ship flow permits an unreleasable cut: launch ship writes the dated heading with no check that the receipts protocol can complete, so the receipt gate fires at tag time — the most expensive possible moment, unrecoverable once tagged. Folded into itd-93 by maintainer decision: the emit prints the receipts protocol as a checklist, the ingest refuses a cut it cannot prove releasable, and a launch receipts sub-verb runs the release job's exact check locally before the merge
- iss-2609091642508005The 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.
- iss-2610240932493158The two-way pair that marks an issue as promoted (the issue's `related_intents` naming an intent that names it back in `related_issues`) cannot tell an issue the intent delivers from an issue the intent only cites as evidence, so an evidence-only issue reads as promoted and `capture promote` refuses it.
- itd-4Nothing You Notice Gets Lost
- itd-93abcd Scaffolds a Release Gate That Works on the First Try
- itd-2609111003026787A gate catches the intent whose work is live while its spec is still open
- spc-6issue-capture