SDD Spec-Structure Governance
What kind of node a spec is, and where that kind lives in the project spec. This skill is the
canonical home consumers load instead of restating it — scaffold-project-spec when it lays a tree
out, place-node when it suggests a home, the formation Warden when it audits structure, and the
architect bars when they judge placement.
Taxonomy and placement are one rule, not two: the placement law is the taxonomy applied to
folders, and cannot be stated without it. A descriptive doc in design/ is correct where a
behavioral node in the same folder is a defect — the folder alone does not say which.
The lifecycle a spec moves through is sdd:lifecycle-governance; a node's internal section shape is
sdd:spec-format-governance; how the suite is written is sdd:suite-format-governance.
The node taxonomy — three kinds, declared
| Kind | Subject | Owns a suite | Carries | Marker |
|---|---|---|---|---|
| descriptive | none | no | ordinary prose | (none — the default) |
| reference artifact | a real thing with no testable surface of its own | no, by design | ## Subject in place of ## Use Cases |
spec-type: reference |
| behavioral artifact | a testable subject | yes | the node sections | spec-type: behavioral |
Declared, never inferred. The kind lives in frontmatter. Inference breaks both ways: a behavioral node has no suite yet while it is being authored, and descriptive indexes live outside the rules folder — so neither file-presence nor location classifies reliably. Declaring it up front makes an unfinished behavioral node read as incomplete rather than as an index.
A capability is what the project does. The everyday word for one behavioral node is a unit spec.
Placement — the taxonomy applied to folders
Screaming architecture is the default: top-level folders are named for capabilities, so the folder names say what the project does. Three folders are deliberately not capabilities:
| Folder | Holds |
|---|---|
design/ |
the rules — the model and the why (descriptive docs) |
workflows/ |
the usage — how capabilities compose into whole flows (the project-level suite) |
ledger/ |
the provenance — durable audit records; data, outside the node taxonomy |
Rule-in-design, behavior-in-capability. A rule and the behavior enacting it live apart: rules go
to design/ as descriptive docs, the scenarios that enact them go to the capability folders as
behavioral specs, and a reference artifact is homed in the capability that owns it. This keeps
design/ readable as a model while the capabilities stay testable as behavior.
Root files, not folders. Every mandated folder is an exception to screaming architecture, so the
mandated set stays minimal: anything that is one document lives as a root file beside spec.md.
glossary.md — the project's ubiquitous language, every load-bearing term defined once — is
required of every project spec.
Two levels, never three. A node is <capability>/<unit> and never sits three deep. A
sub-grouping inside a capability is a cross-cutting concern, so it is expressed as a concept:
tag and recovered through the generated by-concept index — never as a third folder level.
The concept axis. The tree can privilege only one axis, and it privileges capability; a concern
enacted across several capabilities is declared in concept: frontmatter and navigated through the
index instead.
Suite organization. Unit suites colocate with their capability node, one per unit. The
project-level suite lives in workflows/, where a workflow is the project-level analogue of a
use case — a path through the composed capabilities.
One spec per project
A project is the unit a spec maps to — a repo harness, an agent plugin, an npm package, a
website, or one package inside a monorepo. Each has exactly one spec: one spec.md, one suite,
one gate/freeze baseline. Growth is absorbed by adding folders, never by splitting into sibling
specs — splitting fragments the lifecycle, so one change touching three areas would re-open three
frozen specs and pay three approvals.
Colocate by default, nested projects included. Hoist only when the spec cannot be kept out of
what ships — the project dir is copied wholesale, with no include/exclude mechanism to leave
the spec behind. The one identified case is an agentic plugin: plugin install copies the whole
directory, so a colocated spec would reach every consumer. An npm package colocates — its files /
ignore list excludes the spec from the tarball. Nesting is never the reason; if a new packaging
format has the same all-or-nothing copy, it joins the hoisting case.
Strategy is policy; homes are data
Screaming architecture is the default, not the only layout. Whichever layout a project uses is a choice, and choices are declared. But the choices are not equals — see the partition stake below; declaring a layout does not license breaking node<->capability alignment.
- The strategy (
capability-first,mirror-source, …) is declared in the rootspec.mdplacement map and read. Never derive it from the tree: a greenfield project has no tree yet and still has a strategy; deriving it from a healthy tree is circular (it launders a past decision as an observation); and on a half-migrated tree it perpetuates the layout being migrated away from. - The homes — which folder a given concept's node sits in — are facts about the current tree,
derived from
concept:tags. A stored home list is a second source that rots, so homes are never stored. - They compose: the declared strategy parameterizes the derivation. Keep deriving homes, but ask the placement map which derivation to run.
The placement map has two parts, and a placement judgment must consult both. Beside the declared strategy it carries a routing table — the maintained "a concept of kind K lives in home H" taxonomy plus the human tie-break rows for genuinely contested overlaps. The table records decisions the strategy alone does not settle, so a node placed by an explicit routing-table row is correctly placed even when it does not match the strategy's derivation. Judging misplacement on the strategy alone therefore reports false findings against exactly the placements a human already adjudicated. The test is a disjunction: a node is misplaced only when it neither follows the declared strategy nor matches a routing-table row.
This is the same split the corpus-discovery rule already makes — fixed conventions are scanned, while an off-convention anchor list is declared and curated. "No drift" means do not store what you can observe, never do not declare a choice.
The partition stake — why capability-first is more than a preference
Capability-first is the partition the mission scheduler depends on. One mission owns one spec-node, so when node <-> capability is 1:1 a change touches one node and its collisions are legible; missions run in parallel. A layered / framework-first top level scatters one capability across many folders — the mapping breaks, a single behavior smears across nodes, collisions explode, and the schedule degrades toward serial (ADR-0025).
So layouts are ranked by whether they preserve that alignment, not by taste:
- capability-first — aligned by construction; the recommendation.
- mirror-source — inherits whatever alignment the source has. Best case is a feature-first source, which is already capability-aligned; over a layered source it is still offered, with the cost named (below).
- layered / framework-first as the top level — discouraged as a chosen layout for a project free to choose, and a declaration does not rescue it. Layering survives nested inside a capability.
The invariant that holds under every strategy: one capability per node, never smeared across nodes. A declared layout says where a node goes; it never licenses a capability to scatter. The scheduler's false-conflict rate is the standing metric of the partition's quality.
A coarse partition costs precision, not correctness
This is why an imperfect layout is workable rather than disqualifying. The scheduler is conservative — a collision it cannot resolve serializes — so the worst case of a poor partition is a slower schedule, never a corrupted one. Three mechanisms recover most of the loss:
- The collision ladder descends below the node — file, region, semantic, symbol — so two missions
sharing a node but touching different symbols classify soft and still co-wave. The residue is
symbol-rung-deferred: symbols that cannot be inferred stay hard. - Worktrees dissolve file-level false dependencies until write-back.
- The concept axis carries the capability view the folders do not: in a mirrored tree the folders
name source areas while
concept:tags still name capabilities.
Adoption over purity. Demanding a restructuring before a project may hold its first spec is an
entry toll, and an unadopted tool partitions nothing. A project adopts on the shape it has,
accumulates concept: tags as it writes nodes, and hoists one capability at a time when the
false-conflict rate earns the move — a concept spanning many nodes being the measured signal that a
capability wants its own home. Capability-first is the destination, reached on evidence, not the
entry condition.
Key points (read-check)
- Three kinds, declared — descriptive / reference (
## Subject, no suite) / behavioral (owns a suite); never inferred from files or location. - Placement is the taxonomy applied to folders — rules to
design/, behavior to the capability folder, reference artifacts to the capability that owns them. - Screaming architecture with three non-capability folders (
design/,workflows/,ledger/) and root files, not folders, for single documents (glossary.mdrequired). - Two levels, never three — deeper sub-grouping is a
concept:tag, not a folder. - One spec per project; colocate by default and hoist only when the spec cannot be kept out of what ships (the agentic plugin) — nesting is never the reason.
- Strategy is policy, homes are data — read the declared strategy from the placement map, derive
homes from
concept:tags, and let the strategy pick the derivation.