Development record
Three intent kinds (standalone / bundle-member / discipline)
Terminology note. The how layer is named the spec. This ADR's prose was updated by the spec-terminology-rename ADR (adr-11).
Context
Per ADR-1, intents capture user-facing why in press-release shape, mapped 1:1 to specs that build them. The day-zero v5 audit (2026-05-07) surfaced a structural finding that wasn't drift-shaped: roughly 40% of the intent corpus had non-1:1 relationships with at least one other intent. Three patterns recurred:
- Coupled intents — multiple intents that only made sense delivered together; splitting them across separate specs duplicated plumbing.
- Cross-cutting rules — intents whose deliverable was "every other spec inherits this gate" (e.g., itd-1 acceptance-criteria gate, itd-5 prompt-quality additions). Forcing these into the standard
drafts/ → planned/ → shipped/lifecycle produced specs whose only output was a rule applied elsewhere. - Standalone intents (~60%) fit the original 1:1 model cleanly.
The 1:1-only model calcified the wrong fits. Coupled work fragmented; cross-cutting rules became specs that didn't ship anything observable.
Decision
Every intent declares a kind in frontmatter, set at /abcd:intent plan time. Three kinds exist:
standalone— one press-release-shaped user moment, ships as one spec. The default; ~60% of the corpus. Lifecycle:drafts/→planned/→shipped/.bundle-member— multiple intents that share underlying delivery. Each member has its own press release (each captures a distinct user moment), but they ship together as one spec via multi-arg/abcd:intent plan itd-A itd-B. Members declare their bundle in frontmatter (bundle: <bundle-id>).discipline— a cross-cutting rule with no user moment of its own; applies to every other spec as an inherited acceptance gate. Lives indisciplines/(notdrafts/); never gets its own spec; uses a## Ruletemplate instead of a press release. Examples: itd-1, itd-5, itd-37.
The kind is project-agnostic — application projects produce their own disciplines (privacy-impact review, accessibility, code-style). The three kinds are a property of the intent framework, not abcd's particular subject matter.
Classification happens at plan time, with an advisory suggested_kind hint at capture time and continuous reclassification suggestions from the third role on intent-fidelity-reviewer.
Discipline subtypes are deferred — v1 introduces the kind itself; subtype taxonomy waits for empirical evidence (revisit triggers in 04-surfaces/05-intent.md § 1).
Alternatives Considered
- Keep 1:1-only; force coupled intents to share a spec name. Rejected: the spec field is a single ID; representing "two intents, one spec" with naming convention only is ambiguous and breaks
internal/core/lint's reciprocal-link check. - Two kinds: standalone + cross-cutting (no bundle-member). Rejected: bundle-member captures a real pattern (coupled-but-each-has-its-own-press-release) that neither standalone nor discipline fits. Collapsing it into standalone forces an artificial split; collapsing into discipline strips the per-member press release.
- Free-text
kindfield (no closed enum). Rejected: vocabulary drift compounds. Closed enum (standalone | bundle-member | discipline) letsinternal/core/lintenforce kind-specific lifecycle (e.g., disciplines never go toplanned/). - Subtype taxonomy for disciplines now (
methodology/documentation/audit). Rejected: insufficient samples to cluster meaningfully. Free-textkind_notesfield captures intent until the corpus warrants a closed enum.
Consequences
Gains:
- Each intent's delivery shape matches its forward-looking shape.
- Coupled work ships as one spec without duplicating plumbing.
- Cross-cutting rules have a home (
disciplines/) where they're not pretending to be features. - The framework is project-agnostic; any abcd-managed project picks up the same three kinds.
Costs / obligations:
kindenum becomes reserved vocabulary (registered in02-constraints/04-naming.md).internal/core/lintextends to verifykind+bundle:+ directory-matching contracts.- The
intent-fidelity-revieweragent grows a third role (continuous reclassification suggestions). Agent count stays at 15 — this is role extension, not agent splitting (per itd-31 precedent). - Discipline-shaped intents need a
## Ruletemplate separate from the press-release template. Discipline-format guidance lives in04-surfaces/05-intent.md. - Reclassification path (
/abcd:intent reclassify) must move files between directories AND recordreclassification_historyin frontmatter — without it, the directory-as-truth principle (ADR-3) collapses.
Downstream decisions enabled:
- ADR-3 (directory-as-truth) — once kinds map to directories, the directory IS the lifecycle state for all kinds uniformly.
Frontmatter
| id | adr-2 |
|---|---|
| type | adr |
| status | accepted |
| date | 2026-05-07 |
.abcd/development/decisions/adrs/0002-three-intent-kinds.md
open on GitHub ↗ · commit history ↗
References5
- adr-3Directory location is the source of truth for lifecycle staterelated
- itd-1Every Intent Has Acceptance Criteria; Every Audit Verifies Themrelated
- itd-5Every Agent Prompt Carries Its Version, Earns Its Lock, And Survives Injectionrelated
- itd-34Intents Promote In Three Flavours — Standalone, Bundle-Member, Disciplinerelated
- itd-37Every Spec Externalises How It Should And Shouldn't Be Modifiedrelated
Referenced by3
mentions7
- iss-351the three-disciplines roster itd-1 itd-5 itd-37 is hand-enumerated at six sites while intents/disciplines/ holds six active disciplines, dropping itd-79 itd-81 itd-84 from the mental model, scope, roadmap dashboard and phase-0 acceptance
- iss-2608221457227162a superseded Phase-0 pseudo-ADR sits in research/notes/ claiming ADR-01, outside every record gate
- iss-2609240227150859itd-6's Implementation status and its post-spc-5 Status paragraph say spc-5 built a typed RPUnavailable error in internal/core and a concrete MCPBridge (the ADR-02 spawn implementation and the ADR-03 host-reuse path), and its Resolved sections route failures through oracle.py. None of it is in the binary: grep for MCPBridge, RPUnavailable and RepoPrompt over internal/ and cmd/ finds only the scanner's RepoPrompt sessionKey pattern (internal/adapter/scanner/patterns.go) and a guard corpus line, and go.mod carries no MCP dependency. The sections describe an earlier Python lineage, so a planner or an implementer reading the planned record is told a foundation exists that does not; the re-filed scope (spc-2609211950427074) needs that foundation built from nothing.
- itd-6RepoPrompt over MCP is one opt-in reviewer route, beside the host's own agents
- itd-31The Brief and Every Intent Stay Consistent — Or Abcd Tells You Why They Don't
- itd-44A Capture-Time `decision` Verdict That Routes Infrastructure Choices Into The Existing ADR Store
- itd-160Eight typed cross-references point at targets absent from the tree — adr-22 supersedes adr-14, adr-15 and adr-17; adr-25 supersedes adr-8; adr-27 supersedes adr-16; adr-28 supersedes adr-18; adr-35 supersedes adr-4 (all retired under retire-the-name); itd-3 names spec_id spc-1 which has no file — and nothing checks supersedes targets today. The 2026-08-21 site investigation counted six; the in-session grep found eight. The planned site build arms the detector via the .abcd/site-baseline.json ratchet, seeded with what the build finds, and the tombstones-or-stubs question (itd-136/itd-137) decides how they render