Decisions

ADR-139: Folder and component layout standards

The fleet ships a visual canon (warp#865 is mid-rollout) but no structural canon. Folder and component layout — where source files live, where tests live, where helpers live, where ratchet state-keeping lives, where…

#139

ADR-139: Folder and component layout standards

Status: Accepted Date: 2026-05-06 Related: ADR-117 (module guidelines — the tier this ADR instantiates), ADR-115 (principle-aware reviewers — first consumer), warp#865 (visual canon — peer epic), warp#974 (first-use smoke gate — most-visible contradiction this ADR resolves), warp#1385 (parent epic), warp#1388 (living-standards feedback loop)

Context

The fleet ships a visual canon (warp#865 is mid-rollout) but no structural canon. Folder and component layout — where source files live, where tests live, where helpers live, where ratchet state-keeping lives, where smoke specs live — is folklore in agent memory and chat context. Each agent brief re-derives the layout from "what looks like the existing pattern", which (a) varies repo-to-repo (distill3 vs ai-frontend vs warp), (b) varies surface-to-surface within a repo (tests/e2e/ vs e2e/ vs __tests__/), and (c) drifts when the canonical-layout grid moves under a sweeping refactor.

Three structural-shape failures surfaced during the 2026-05-05 four-agent canon-token sweep (warp#1376–1380, parent warp#1070, under whole-site visual-canon epic warp#865) that would not have happened with a layout standard in place:

  1. Spec-path mismatch in dispatched briefs. The briefs wrote e2e/<feature>.spec.ts because that is the convention stamped in the global CLAUDE.md ## UI work requires a real user-journey test section (warp#974 first-use smoke gate). The actual ai-frontend repo puts specs at tests/e2e/. The mismatch would have shipped four wrong-path specs in parallel; it was caught only because one agent (warp#1377) refused to write into a directory it could not reconcile with the working tree.
  2. Ratchet state-keeping has no canonical home. Files like hex-no-drift.test.ts and font-size-px-no-drift.test.ts live at src/lib/styles/ — neither in the test convention path (tests/) nor in a recognised primitive-tokens location. Four parallel agents collided on these files because every brief routed through them; the merge sequence required serial rebases (PR#539 went DIRTY when PR#540 merged the same file).
  3. The smoke-gate's docs disagree with the repo it governs. Global CLAUDE.md says e2e/<feature>.spec.ts; ai-frontend's own gates fire on tests/e2e/. The stamped guidance is from a sibling repo. The contradiction will keep biting briefs until both surfaces agree on which is canon.

These are not three bugs. They are one missing artefact: a layout standard with the same shape as the visual-canon work warp#865 is shipping for the design system. Tokens (visual canon) and folders (structural canon) are peer concerns; the fleet has the first, not the second.

ADR-117 already declared the module-guideline tier as the canonical home for per-module-type rules and stored guidelines at dark-factory/guidelines/<id>@v<n>.md. ADR-117's seven founding guidelines (db-changes, playbook-authoring, api-endpoint, ui-component, ansible-role, agent-brief, docs-authoring) cover content shape and required steps but do not declare layout (the where-does-this-file-go axis). This ADR closes that gap by extending the existing tier rather than adding a new one.

Decision

Folder and component layout standards live in the existing module-guideline tier declared by ADR-117 — one versioned guideline per module type at dark-factory/guidelines/<module-type>@v<n>.md. Each layout-bearing guideline declares: where source files live, where tests live, where helpers live, where ratchet state-keeping lives, where smoke specs live, naming conventions, and any per-language overrides. The PR body cites the guideline-version (Guideline: <module-type>@v<n>) so the persona reviewers (per ADR-115) verify the implementation against that exact version.

This is the structural sister of warp#865's visual canon: same shape (per-cluster cleanup, regression-suite ratchet, fleet-wide rollout), different axis (filesystem layout vs CSS tokens).

The decision lands in four phases under parent epic warp#1385. Phase 0 (this ADR + the seed playwright-smoke@v1 guideline) is shipped in the same PR. Phases 1-3 (additional module-type guidelines, first-consumer adoption in ai-frontend, fleet rollout) stay open under warp#1385 and warp#1388's living-standards feedback loop. Per the operator's scope-revision:doc-starts-small-one-seed-guideline tag on warp#1385, Phase 1 ships one guideline at a time as work surfaces what is needed — not five at once.

Reconciliation policy

When stamped guidance (e.g. global CLAUDE.md prose, repo-local CLAUDE.md prose, agent-brief templates) disagrees with a published module-type guideline, the guideline wins. The stamped prose is updated in a follow-up PR to either (a) cross-reference the guideline rather than duplicating the path, or (b) be removed if the guideline fully subsumes it. The remediation PR is filed under warp#1388's feedback loop. Stamped prose is never the source of truth once a guideline exists.

The first concrete reconciliation lands with the seed guideline: playwright-smoke@v1 declares tests/e2e/<feature>.spec.ts as canon. The global CLAUDE.md ## UI work requires a real user-journey test section currently says e2e/<feature>.spec.ts; updating that prose is Phase 2 work tracked under warp#1388 — this PR does not yet rewrite the stamped guidance.

Bumping process

Same shape as ADR-117. A guideline-version bump (@v<n>@v<n+1>) is a new immutable file (the previous version stays at its filename for retrospective verification of in-flight cites). The bumping PR includes the regression-suite update, an explicit migration note for in-flight cites, and a list of consumer repos whose stamped prose needs cross-reference updates. Per ADR-117's constrained_by field, each guideline cites the principles it rests on so a principle change cascades into a guideline revision rather than silent drift.

Phase plan

  • Phase 0 (this PR, warp#1418) — ADR-139 declares the layout-standards principle and ships the first seed guideline playwright-smoke@v1. The seed is the one whose contradiction bit hardest in the 2026-05-05 dispatch.
  • Phase 1 (under warp#1385, dispatched as work surfaces it via warp#1388) — additional module-type guidelines surface as new contradictions appear: svelte-route, svelte-component, ratchet-state-keeping, agent-brief, and others. One guideline per PR, max 800 lines, ≤20 files.
  • Phase 2 (under warp#1385, sequential after Phase 1) — first-consumer adoption in ai-frontend: stamp guideline-cites into ai-frontend/CLAUDE.md, reconcile contradictions (the global e2e/<feature>.spec.ts rewrite), backfill citations on ≥3 in-flight PRs to prove the cite-the-version flow works under load.
  • Phase 3 (under warp#1385, parallel per consumer-team capacity) — fleet rollout: distill3, warp, devtools, dark-factory, gyrum-ui, gyrum-knowledge-base. Each is its own ticket; each cites the same Phase-1 guidelines and adapts per-repo overrides where module types differ.

Consequences

What becomes easier.

  • Agent briefs stop re-deriving layout per ticket. The brief cites Guideline: playwright-smoke@v1 (and later, one per module type touched), the persona reviewer fetches that exact version, and "the brief said X but the repo expects Y" stops being a 50/50 dice roll.
  • Cross-PR layout collisions become structural rather than serendipitous. When ratchet-state-keeping@v1 declares one canonical home for *-no-drift.test.ts files, four parallel agents touching that home is a known coordination cost (warp#1385 acceptance tracks it), not an accidental one.
  • The smoke-gate-path contradiction has a published answer. Future contradictions of the same shape (stamped prose vs repo convention) reach for "file a guideline that names the canon" rather than "patch one of the two surfaces and hope".
  • The persona reviewers (ADR-115) gain an exact-version artefact to anchor against. "Implementation matches the guideline cited in the PR body" is structurally checkable; "implementation matches the layout convention" was prose-bound and unverifiable.

What becomes harder.

  • Each new layout-bearing module type adds a guideline file, a regression suite, and per-repo override notes. Phase 1 ships these one-at-a-time as work surfaces them, not in a single sweep, so the per-PR overhead is bounded — but it is non-zero.
  • Stamped prose in CLAUDE.md (global and repo-local) needs sweeping reconciliation as guidelines land. The reconciliation is mechanical (cross-reference the guideline, or delete the duplicated path) but requires fleet-wide PRs as Phase 2 / Phase 3 work.
  • Two surfaces now declare the canonical path during the transition: the guideline (canon) and the still-stamped CLAUDE.md prose (legacy). Reviewers must resolve in favour of the guideline; until Phase 2 lands the CLAUDE.md update, the legacy prose is misleading. The reconciliation policy above is the rule reviewers apply.

What we sign up to maintain.

  • The dark-factory/guidelines/ directory and the seed playwright-smoke@v1.md guideline file. Future module-type guidelines join under the same convention.
  • The cite-the-version discipline in PR bodies (Guideline: <module-type>@v<n>). The persona reviewer (per ADR-115) is the load-bearing consumer; without the cite, the verification step cannot fire.
  • The reconciliation backlog: stamped CLAUDE.md prose that duplicates a published guideline must be migrated to a cross-reference under warp#1388's feedback loop. Each guideline that lands triggers a small reconciliation ticket.
  • Phase 1 dispatch discipline: one guideline per PR, surfaced by need, not a five-at-once sweep. The operator's scope-revision:doc-starts-small-one-seed-guideline tag is binding — overruling it on a future PR requires an operator-level scope revision, not an agent decision.

Alternatives considered

  • Stamp directly into per-repo CLAUDE.md. Rejected: the failure mode this ADR is reacting to. CLAUDE.md prose is unaddressable (no version-cite shape), drifts independently per-repo, and produces exactly the contradiction (e2e/ vs tests/e2e/) that motivated the work. The cite-the-version flow plus the existing module-guideline tier is the structural shape.
  • One global standards file in dark-factory (single document covering all module types). Rejected: too coarse. ADR-117 already chose per-module-type because reviewers fetch exactly one guideline per module touched; a single document forces reviewers to grep for the relevant section, re-introducing the prose-grep failure mode of CLAUDE.md.
  • Fork a new tier — "layout guidelines" separate from module guidelines. Rejected: ADR-117's existing tier is the right grain. Layout is one of several module-typed concerns (alongside required steps, structural checks, forbidden patterns, etc.); splitting layout into its own tier doubles the cite-and-fetch cost on every PR for no semantic gain. Extending the existing tier with layout-bearing fields keeps one source of truth per module type.
  • Five seed guidelines in Phase 0 (the warp#1385 original Scope). Rejected by operator scope revision (scope-revision:doc-starts-small-one-seed-guideline, 2026-05-05). The reasoning: shipping five guidelines speculatively risks declaring canon for module types whose contradictions have not yet surfaced, leaving the seed as folklore-by-different-name. One seed (the contradiction that bit hardest) plus warp#1388's feedback loop (one guideline per surfaced contradiction) is the smaller, evidence-driven shape.
  • No layout standard, "authoring discipline" only. Rejected: the failure mode the ADR is reacting to. "Each agent should look at the existing pattern" is exactly the prose contract that produced the spec-path mismatch and the four-agent collision on *-no-drift.test.ts.

Migration to gyrum-knowledge-base

Per ADR-116, the gyrum-knowledge-base RAG service is proposed — not yet shipped. ADR-117 already specifies guidelines live at dark-factory/guidelines/ today. When the gyrum-knowledge-base service ships and ADR-116 transitions to accepted, the canonical guideline path migrates to gyrum-knowledge-base/guidelines/<module-type>.md@v<n> per ADR-116's indexing contract, and the dark-factory copies become symlinks or redirects. The migration is tracked separately under ADR-116; this ADR follows ADR-117's current path until the gyrum-knowledge-base substrate is live.

References

  • ADR-117 — module guidelines (the existing tier this ADR instantiates)
  • ADR-115 — principle-aware PR reviewers (first consumer of the cite-the-version flow)
  • ADR-116 — gyrum-knowledge-base RAG service (proposed; future canonical home)
  • warp#865 — EPIC: whole-site canon migration (peer epic, visual canon)
  • warp#974 — first-use smoke gate (the gate whose stamped path is the most-visible contradiction)
  • warp#1385 — EPIC: Folder + component layout standards (parent of this ADR)
  • warp#1388 — living-standards feedback loop (Phase 1+ dispatch surface)
  • warp#1418 — Phase 0 child ticket (this PR)

Supersedes: none Superseded by: none yet