Development record
A native minimal spec layer with the companion harness `ccpm` as the primary deeper backend
Context
abcd's spec and task tracking rode on flow-next: its receipts, its plan/work
skills, its on-disk state. The rebuild drops flow-next as a hard dependency
(ADR-22), which leaves abcd
needing a spec/task store of its own — but abcd already owns a lightweight,
proven pattern for lifecycle state (directory location is the source of truth,
ADR-3), and the companion harness's ccpm is a
capable deeper store abcd can reach at the convention level without linking to
it (ADR-24).
Decision
The spec/task layer is a native minimal store as the MVP, with the companion
harness ccpm as the primary deeper backend, and flow-next is not built.
- Native minimal (MVP). Specs and tasks are directories whose location encodes status (ADR-3), plus a dependency graph over them. This is enough to plan, sequence, and track work with no external tool, and it ships in the first milestone.
- The companion harness
ccpm(primary deeper backend). When an operator wants a richer store, abcd reads and writes theccpmmarkdown layout at the convention level — a shared on-disk shape, no binary or library dependency on the companion harness. - flow-next: not built. Its role is covered by the native store below and ccpm above; abcd carries no flow-next code or contract.
Alternatives Considered
- Port flow-next's model natively. Reuses a known design. Rejected: flow-next's shape assumes its own orchestration and receipts; porting it rebuilds machinery ADR-22 exists to shed, when directory-as-truth already gives abcd a lighter native store.
- Native store only, no deeper backend. Simplest. Rejected: it caps abcd at minimal tracking and forgoes ccpm's depth, which is available almost for free at convention level.
- Depend on ccpm as the only store. Deepest integration. Rejected: it makes a peer tool mandatory, breaking the no-hard-deps rule and leaving abcd unable to track work out of the box.
- Chosen: native minimal MVP + ccpm as convention-level deeper backend, flow-next dropped. Works alone, scales up when a peer store is present.
Consequences
- The MVP spec layer is buildable with only abcd's own code — directories plus a dependency graph — so first-milestone planning does not wait on any adapter.
- ccpm interop is a convention contract, documented as an on-disk shape, wired as an optional backend and absent by default.
- The spec store is one more capability behind the core (ADR-23): every front door sees the same specs and tasks regardless of which backend holds them.
- flow-next references in the design record are reconciled away as the native store and ccpm backend land; nothing re-introduces a flow-next contract.
- itd-49 (the flow-state drift detector) is superseded by this decision: it compared checkpoint blocks against flow-next's runtime state store, and flow-next is not built, so there is nothing left to drift against.
Frontmatter
| id | adr-26 |
|---|---|
| type | adr |
| status | accepted |
| date | 2026-07-06 |
.abcd/development/decisions/adrs/0026-native-spec-layer-ccpm-backend.md
open on GitHub ↗ · commit history ↗
References2
Referenced by11
- adr-1Three-layer mental model (brief / intent / spec)related
- adr-11One canonical word for a specced block of work — specrelated
- adr-12`.work/issues.md` stays the live operational ledger; the structured per-issue store is deferred until the native spec layer schedules the migration (historical)related
- adr-22Bundled dependencies become pluggable adapters over a native defaultrelated
- adr-23A transport-agnostic Go core behind thin front doorsrelated
- adr-24the companion harness is a peer integrated via conventions and MCP, not a code dependencyrelated
- adr-32The issue ledger is working-tier data, not authored recordrelated
- adr-2609151513118583An intent owns one or more specs, and it ships when its last spec closesrelated
- itd-43abcd Speaks One Word for a Specced Block of Work, and That Word Is "Spec"related
- itd-57Every abcd Voyage Ships With A Manual-Hold Sentinel That Blocks A Spec From Autonomous Pickup Until A Human Lifts Itrelated
- itd-80An Intent Ships Itself: `planned → shipped` Follows Its Spec Closing, With A Fidelity Audit Attachedrelated
mentions10
- adr-7`/abcd:intent grill` — One Sub-verb with Two Inseparable Phases
- dcn-26080200000000042026-08-02 — iss-39: record-lint gains `record_schema`, the first rule that reasons ACROSS the record's stores (ADRs, intents, specs, the issue ledger) rather than inside one, and the `superseded/` exemption narrows for the intent tree: both rules that read it (`intent_lifecycle`, `intent_impact_valid`) now run there too, while the spec-store rules (`spec_lifecycle`, `spec_id_unique`) still honour it as before and the cross-store `record_schema` never consulted it at all. Four invariants, one scan: a cross-reference field names a record the corpus has; a supersession is declared from BOTH sides; a filename and the id inside it agree; and every lifecycle directory is enumerated, so an undeclared bucket is a finding rather than a state no rule reads. Scope is deliberate on two axes. (1) Cross-references are checked in the machine-readable frontmatter fields (`supersedes`, `related_adrs`, `related_intents`, `builds_on`, `blocked_by`), not in bare prose: a frontmatter handle is a claim that the record exists and is a live input, whereas prose legitimately narrates ids that do not resolve ("itd-38's id was released, not reserved"; a test fixture's `itd-9999`; adr-29's account of what ADR-6 settled) — arming prose would have cost ten false positives against one true one, the same false-positive budget that scopes GL002. (2) A handle resolves either to a file or to a `supersedes:` declaration elsewhere, because the ADR lifecycle PRUNES a superseded record and keeps the trace in its successor — that is what distinguishes adr-4/adr-8/adr-14..18 (pruned, accounted for) from adr-6 (cited, never accounted for). The exemption narrowing is the root cause of the corpus defect it exposes: `intent_lifecycle` is a schema rule, but the `superseded/` bucket exempted its own records from the one rule that checks a supersession is recorded, so itd-47 and itd-49 sat there for months with a prose blockquote and no `superseded_by` field. Being historical excuses a record from how it is WRITTEN, never from being well-formed. `superseded_by` also widens from `^itd-\d+` to `^(itd|adr)-\d+`: an ADR that redecides the question an intent rested on retires that intent as surely as a successor intent does, and refusing to spell that forced those two records to say nothing at all. Corpus repairs in the same change: adr-6 is dropped from the two `related_adrs` lists and the two brief pages that cited it re-point at adr-29 (which records the two-stage redaction model and states in its own body that adr-6's decision stands — adr-29 does not supersede it, so claiming it does would have written a falsehood to clear a finding); adr-12 and adr-32 gain the bidirectional pair the ledger-location redecision always implied, adr-12's status moving to `superseded` and the index row with it; itd-47/itd-49 gain `superseded_by: adr-22`/`adr-26` with `kind_at_supersession`, and those ADRs name them back; and the pre-existing one-way links itd-32→itd-31 and itd-31→itd-48 gain their reverse halves. CHANGELOG impact `additive`, matching iss-38's disposition on the same axis: the rule ships in the lint engine `abcd docs lint` runs, and any abcd-managed repo gains it by declaring its own `record_stores`.
- dcn-26080200000000062026-08-02 — iss-39 (merge-gate pass): four more `record_schema` corrections, all of the same family as the previous round — a check that was right for one shape and absent for its sibling. (1) The undeclared-directory finding was implemented for store ROOTS only; a directory inside a declared bucket (`intents/shipped/archive/`) was skipped with no finding at all, so the previous round's flat-store fix closed the escape for one store of four. Both cases are now reported from the one place that reads a directory of records, because a flat store and a declared bucket hold records the same way and a directory in either is a lifecycle nobody declared. (2) `spc` was missing from the handle pattern although the spec store is indexed and armed: a bidirectional spec supersession tripped a false blocker whose message named the very prefixes it omitted, while the reverse direction parsed to zero handles and was never checked. The alternation now covers every store the rule indexes, and the message is composed from one constant so it cannot drift from the pattern again. (3) `superseded_by: []` was a false blocker: the empty flow sequence is this record's house spelling for an empty list (`related_rfcs: []` across six ADRs), so it means absence. Fixed with a local `isAbsentValue` rather than by widening the shared `isNull`, which also judges scalar fields (kind, impact, slug) where a list literal is a wrong value rather than an unset one. (4) The filename/id check compared strings, so `id: adr-0012` in `0012-*.md` was reported as a mismatch — contradicting the rule's own stated design that the padded and bare spellings are one handle; it now parses both sides and compares numerically, as every other comparison in the rule already did. Record-side: iss-179 now cites the recoverable evidence for what adr-6 was (`.abcd/work/reviews/2026-07-06-plan-consistency/05-link-structure.md` lines 47-49 name the original file, `03-cross-corpus-consistency.md` line 42 records it as voided by DECISIONS.md lines 8-9 together with adr-8 and adr-17 — the only other two ADRs that decision voids, both since pruned by a successor that names them (adr-25, adr-22), which is what leaves adr-6 alone — and observes that the unused `deprecated` status is the state it fits) so the disposition is a decision with evidence rather than an open question; the two brief re-pointings drop a reflexive attribution that had adr-29 reusing a model from itself; and adr-22, adr-26, and adr-32 gain one body clause each naming what their newly-declared `supersedes` handles retire, so the frontmatter and the prose say the same thing.
- iss-239Dangling pre-rebuild spec ids in the draft corpus: itd-44 (spc-56), itd-51 (spc-33/37), itd-57 (spc-48/60/62), itd-61 (spc-75) cite specs from the numbering adr-21/adr-26 reset (live store is spc-1..spc-22). Worse, intents/README.md and itd-34 assert the itd-44 lineage 'landed as itd-44 (spc-56 thin adoption)' — no such spec or decision-verdict code exists, so the record claims a delivery the tree cannot back.
- iss-2608220150157497principles/sota-per-intent.md links `[adr-22](../decisions)` and `[adr-26](../decisions)` to the decisions directory instead of the ADR files; links_resolve passes because the directory exists; the only two ID-labelled links in the record whose target does not match the id
- iss-2610262032024609The dependencies page says flow-next is not a dependency and is not built; itd-34's lifecycle paths call `/flow-next:plan`.
- iss-2610262041532193In the glossary a spec lives in abcd's native spec store; itd-34 maps each intent to a flow-next spec planned through /flow-next:plan.
- iss-2610262043232891itd-34 routes a standalone intent to a flow-next spec, while itd-37 places the spec template in abcd's native Go binary
- itd-29Domain Experts Run A Spec Autonomously And Recover From Anything Without Touching Git
- sota-per-intentSOTA Per Intent