repositoryStandards v1.1.20

scripts/spec-structure.mjs

The spec layout guard. It checks that every capability spec is shaped like one and serves somebody who exists.

node scripts/spec-structure.mjs --block

What it refuses to let happen

A spec that serves nobody. Every capability spec names a persona from docs/personas.md, and this reads that table as the live roster. "The user" is not a persona; neither is a name invented in the spec itself, and a **Serves:** value is checked against the roster rather than merely for being filled in.

A roster it cannot read. If the table parses to no personas - rows without backticks around the name, or a placeholder never filled - the run fails saying so. Every check here is a membership test, so an empty roster would otherwise pass every spec by having nothing left to contradict, which is how this check once switched itself off.

A spec shaped like a ticket. Layout by capability, not 001-feature/.

A spec missing the sections that make it usable at the tier it declares. A spec that says **Spec tier:** buildable carries ## Data contracts, ## Interface contracts and ## Acceptance criteria, each with something under the heading - a section that genuinely does not apply keeps its heading and says so, the way ## Open questions says "None known." behavioral is still the escape hatch (R9); a spec declaring no tier at all is warned about, not blocked, because a guard that fails every undeclared spec in a repo mid-adoption is a guard somebody turns off.

A section heading written twice. The clarify gate greps for the first ## Clarifications and stops; so does a reader. A second one hides everything under it from both while every guard stays green. A later clarify session is a new ### Session YYYY-MM-DD under the heading that is already there.

What it deliberately ignores

The scaffolding /spec-plan and /spec-tasks write beside a spec - plan.md, tasks.md, research.md, data-model.md, quickstart.md, contracts/, checklists/ - is ephemeral by rule (ADR-010) and is not a capability spec, so none of it is held to the persona gate. A PR opened between /spec-plan and /spec-reconcile legitimately carries all of it. In full-tree mode the same list is reported as a warning instead: still committed once the work closed means the cleanup did not run.

Why the roster is a table

Because a guard has to read it. That is also why the shipped personas.md keeps its worked example out of the roster table: this script reads that table, so example names left in it would let a spec claim to serve a persona from a rental-property demo and pass.

Decisions behind it

  • ADR-006 - personas gate rather than decorate. Without a mechanical check, "for whom?" is a question asked in review when somebody remembers.

Reference

  • Status. Required - part of the core profile.
  • How it arrives. copy - ship verbatim; the repo gets a byte-for-byte copy (guards, templates that carry no repo-specific content)
  • Shipped form. read it in the standard's own tree

The rule that requires it

Compliance MUST be enforced by tooling, not prose:

Read this rule in full

What checks it

  • node scripts/spec-structure.mjs --block - spec files are where and shaped as the standard requires Blocks the build when it fails.