The previous layout placed epic folders directly under docs/work/
alongside prds/ and _system/. Tightening: epics now live in their
own docs/work/epics/ subfolder, peer to prds/ and _system/. Same
shape as the existing prds/ bucket.
Final docs/work/ layout:
README.md
prds/<slug>.prd.md
_system/_state.json
epics/<slug>/_epic.md + <story-folder>/_story.md
Renames (git mv preserves history):
- docs/work/binder-wrap-helper/
-> docs/work/epics/binder-wrap-helper/
- docs/work/library-evaluation-policy/
-> docs/work/epics/library-evaluation-policy/
- docs/work/ci-security-and-supply-chain/
-> docs/work/epics/ci-security-and-supply-chain/
Tooling updates:
- state-builder.mjs walks workRoot/epics/ directly; SKIP_FOLDERS
obsoleted (no more sibling folders to filter out).
- dispatch.mjs's findNextTask, tickStoryBulletInEpic, and
flipEpicDoneIfAllStoriesDone all join with "epics" segment.
- prd-ship.mjs's deriveShippingCommits walks workRoot/epics/ and
git-logs docs/work/epics/<epic>/.
- decomposer.prompt.md emits epics under docs/work/epics/<epic-id>/.
- handoff + grill-with-docs glossary references updated.
- Glossary entry for Epic updated.
Reserved future shape: when a task-tracker integration (ClickUp,
Linear) ships, the epics/ subfolder hosts <task-id>-<slug>/
folders. Today it just hosts bare slugs.
27 KiB
Glossary
Canonical vocabulary for template-vertical. Shared by humans and AI agents — every term means the same thing here. Terms specific to this repo's domain (monorepo / Clean Architecture / agent-first workflow / conformance). General programming concepts (DI, retries, errors, etc.) don't belong unless they have a project-specific meaning.
Rules:
- One sentence per definition. Define what it is, not what it does.
- Pick one canonical term; list aliases under
_Avoid:_. - Relationships expressed with bold term names + cardinality.
- Flag ambiguities under "Flagged ambiguities" with a resolution.
- Reference ADRs by ID (
ADR-NNN); reference paths sparingly (they rot fast).
Packages
Package:
A workspace member under packages/ or apps/ with its own package.json and tag. The atomic unit of dependency, build, and boundary enforcement.
Avoid: module, library, app (unless specifically apps/).
Tag:
The boundary classification on a package (app, core, core-composition, feature, tooling). Enforced by ESLint (eslint-plugin-boundaries) and Turborepo boundaries. See AGENTS.md → Boundary Rules.
Feature (a.k.a. feature package):
A vertical slice owning its Clean Architecture layers + integrations under packages/<name>/. Currently: auth, blog, media, marketing-pages, navigation. In architecture-refactor conversations (the improve-codebase-architecture skill), "module" defaults to "feature" — they're the same unit at the highest level of granularity.
Avoid: domain, vertical (use "feature" or "vertical feature"). "Module" is acceptable inside the refactor skill specifically, where it abstractly covers "anything with interface + implementation" at any scale (use case, controller, repository port, or full feature).
Must-have core:
A core package the template can't run without — core-shared, core-cms, core-api.
Avoid: baseline core, base core.
Optional core:
A core package scaffolded on demand via pnpm turbo gen core-package <name> — core-realtime, core-events, core-trpc, core-ui, core-audit. See docs/architecture/template-tiers.md.
Core-composition:
A tag for packages that compose feature exports — core-api, core-cms, core-trpc. The only packages allowed to import from @repo/<feature>/api or @repo/<feature>/cms subpaths.
Tooling package:
A package providing build/lint/test infrastructure — core-eslint, core-typescript, core-testing. May only depend on other tooling.
App:
A runtime entry point under apps/ — web-next, web-tanstack, cms, storybook.
Clean Architecture layers (per feature)
Entities layer (packages/<feature>/src/entities/):
Domain models (models/<x>.ts) and domain errors (errors/<domain>.ts + errors/common.ts). No I/O.
Application layer (packages/<feature>/src/application/):
Use cases. Pure orchestration over repository + service ports.
Infrastructure layer (packages/<feature>/src/infrastructure/):
Repository and service implementations (real Payload-backed + mock).
Interface-adapters layer (packages/<feature>/src/interface-adapters/):
Controllers — one per use case. Translate unknown input → use-case input via Zod safeParse; thread the output through a presenter.
Integrations layer (packages/<feature>/src/integrations/):
Vendor-specific glue — integrations/api/ (tRPC routers), integrations/cms/ (Payload collections + tasks).
DI folder (packages/<feature>/src/di/):
Container, symbols, and the two binders. The wiring layer.
Feature building blocks
Use case:
A single business action expressed as a factory: (deps) => async (input) => output. Always paired with a Zod xInputSchema + (non-void) xOutputSchema co-located in the same file. Exports the type alias I*UseCase = ReturnType<typeof xUseCase>.
Avoid: command, action, service method.
Controller:
The thin adapter from unknown input to a use case. One per use case. Co-located top-level function presenter for non-void outputs.
Avoid: multi-method controller (forbidden — one verb-noun per file).
Repository:
A port for collection-style entity access. Lives in infrastructure/<x>.repository.{ts,mock.ts,interface.ts}. Real impls drop the payload- prefix; mocks use the .mock.ts suffix.
Service:
A port for non-collection capabilities (auth, mailer, etc.). Same .ts / .mock.ts / .interface.ts triad as repositories.
Manifest (feature.manifest.ts):
The per-feature contract declaring useCases, audits, publishes, consumes, and requiredCores. Source of truth for the conformance system. Always edited before code (manifest-first ordering).
Symbol:
A DI token in packages/<feature>/src/di/symbols.ts. Used by Inversify to identify a binding.
Binder:
A function that registers DI bindings — bindProductionX(ctx) (real Payload) or bindDevSeedX(ctx) (populated mock). Lives at src/di/bind-production.ts and src/di/bind-dev-seed.ts. Self-asserts conformance at its tail via assertFeatureConformance(...).
bindAll():
The app-level DI dispatcher (e.g. apps/web-next/src/server/bind-production.ts). Picks each feature's binder by env (USE_DEV_SEED, NODE_ENV) and threads a single shared ctx through them. Idempotent.
ctx (a.k.a. bind context):
The object passed to every binder. Required fields: tracer, logger, plus config for production. Optional fields: bus, queue, realtime, realtimeRegistry (only when the corresponding optional cores are scaffolded).
Dev-seed:
The populated in-memory mock mode. Lives in src/__seeds__/dev.ts per feature, exported as a lazy buildDev<Entities>() function. Selected by USE_DEV_SEED=true or by default when not in production.
Public surface:
A feature's allowed import surface — . (contracts: types, errors, schemas, I*UseCase aliases, router type), ./ui (queries, components), ./cms, ./api, ./di/bind-production, ./di/bind-dev-seed. No deep source paths exist in exports maps.
Anchor:
A // <gen:*> comment marking where a generator injects code (e.g. <gen:events>, <gen:job-symbols>, <gen:realtime-handlers>). A CI guard at packages/core-eslint/anchors.test.js keeps them present.
Architecture refactor vocabulary
Terms used by the improve-codebase-architecture skill (.claude/skills/improve-codebase-architecture/) when proposing refactors. Distinct from the everyday domain terms above — these are abstractions for reasoning about shape. Both vocabularies are in scope during refactor conversations.
Module (refactor sense, = feature by default):
The abstract unit a refactor operates on: anything with an interface + implementation. In this repo, "module" almost always means "feature" (packages/<name>/) since that's our primary unit of organization. At narrower scales it also covers a use case, controller, repository/service port, or binder. Don't introduce "module" as a competing top-level term in domain conversations — the refactor skill is the only place this abstraction is appropriate.
Avoid: using "module" outside the refactor skill (say "feature", "use case", "controller", "package" as appropriate).
Interface (refactor sense):
Everything a caller must know to use a module — type signatures, invariants, ordering, error modes, Zod schemas, manifest declarations, DI symbol contract. Strictly broader than the TS interface keyword.
Implementation: What's inside a module. Distinct from adapter: an adapter is a role (fills a slot at a seam); an implementation is the substance (the code itself).
Depth:
Leverage at the interface — behaviour per unit of interface a caller must learn. A deep module packs a lot of behaviour behind a small interface; a shallow module has an interface nearly as complex as its body. Pure factory-function use cases are usually deep; one-line wrappers and (x) => x presenters are usually shallow.
Seam (from Michael Feathers):
A place where behaviour can be altered without editing in place — the location of a module's interface. Concrete seams in this repo: *.interface.ts files, DI symbols (*_SYMBOLS.IX*), feature.manifest.ts entries, // <gen:*> anchors, the protocol types in core-shared/di/bind-protocols.ts.
Avoid: "boundary" — that term is reserved in this repo for ESLint workspace-tag rules (feature may depend on core + tooling only).
Adapter:
A concrete thing satisfying an interface at a seam. In this repo a typical port has two or three adapters: real (<x>.repository.ts), mock (<x>.repository.mock.ts), and sometimes recording (Recording* test doubles in core-testing).
Leverage: What callers get from depth — more capability per unit of interface they must learn.
Locality: What maintainers get from depth — change, bugs, knowledge, and verification concentrate in one place rather than spreading across callers.
Deletion test: Imagine deleting a module: if complexity vanishes, it was a pass-through; if complexity reappears across N callers, it was earning its keep. The signal for "this module is shallow" is when deletion just moves the complexity rather than reducing it.
Deepening:
A refactor that turns shallow modules into deeper ones — merging pass-throughs into the modules that justified them, exposing a smaller interface, or relocating the seam to a more useful place. Subject to the hard constraints in the skill's SKILL.md (no ADR violations, no boundary breaks, no manifest drift).
Conformance system
Conformance: The 5-gate enforcement chain that keeps a feature's manifest and code in sync. Layers:
- TypeScript brands (0s) —
Instrumented,Captured,Auditedon every wired use case / controller. - ESLint conformance rules (<1s) — five custom rules in
core-eslint. - Boot assertion (~3s) —
assertFeatureConformance(...)at the tail of every binder. - CI drift gate (
pnpm conformance, ~120s) — cross-feature event closure. - Fallow (
pnpm fallow, ~30–60s) — whole-codebase dead exports / dupes / complexity / AI-change audit.
Brand:
A TypeScript phantom type attached to a wired use case / controller — Instrumented (after withSpan), Captured (after withCapture), Audited (after withAudit). Enforces the wrapper composition at bind time.
Manifest-first ordering:
The non-negotiable authoring order for any new use case: (1) manifest entry → (2) contracts (xInputSchema, xOutputSchema, IXUseCase) → (3) tests (red) → (4) implementation (green). The generator scaffolds (1)+(2)+test stubs so new features are conformance-compliant by default.
Fallow:
The whole-codebase auditor (pnpm fallow) — dead exports, unused files, duplicate code, circular deps, complexity hotspots, AI-change audit drift. The fifth conformance gate. Run pnpm fallow:audit before commits.
Drift: Any disagreement between a feature's manifest and its code. The conformance gates are designed to catch drift at the earliest possible latency.
Coverage
Coverage band:
The per-path threshold declared in feature.manifest.ts under coverage.bands — one entry per Clean Architecture layer (entities, use-cases, controllers) plus a baseline for everything else. Single source of truth read by vitest, assertFeatureConformance, and pnpm coverage:diff. See ADR-020.
L0 / L1 / L2 / L3:
The four coverage layers (ADR-020). L0 = vitest per-layer thresholds (test-time). L1 = pnpm coverage:diff cover-the-diff gate (post-test, CI + dispatch loop). L2 = pnpm coverage:aggregate → committed coverage/summary.json (observability). L3 = pnpm mutate Stryker mutation testing on entities + use-cases (on-demand, not default).
Diff coverage:
The gate that asserts every changed executable line has execution count > 0 in the merged lcov. Cover-the-diff (modified + new lines both count), not cover-the-new-code. Run via pnpm coverage:diff [<base-ref>].
Avoid: patch coverage, delta coverage (use "diff coverage" canonically).
Mutation testing:
A test-quality signal that mutates source code and re-runs tests; surviving mutants mean the test exists + executes the code but doesn't actually assert behavior. Scoped to entities/ + application/use-cases/ per feature. Run via pnpm mutate [--filter @repo/<feature>].
Mutation score:
The percentage of mutants killed (i.e., caught by tests) out of all mutants generated. Per-feature threshold defaults to 80% (overridable in feature.manifest.ts via coverage.mutationThreshold).
coverage/summary.json:
The aggregated per-package + repo-level coverage snapshot, committed on merge to main. Grep-able from git history via git log -- coverage/summary.json. Includes timestamp + commit SHA for correlation with deploys.
Releasing
Conventional Commits:
The commit-message spec mandated by this template — <type>(<scope>): <imperative subject>. Types: feat | fix | docs | style | refactor | test | chore | perf | ci | build | revert. ! for breaking changes. See CLAUDE.md Key Conventions.
release-please:
The Google-maintained automation that reads Conventional Commits, derives semver bumps, and opens a rolling release PR with version bumps + per-package CHANGELOG entries on every push to main. Configured in release-please-config.json + .release-please-manifest.json. ADR-021.
Hybrid versioning:
The template's versioning strategy (ADR-021) — root template (template-vertical) + 5 feature packages (@repo/{auth,blog,media,marketing-pages,navigation}) version independently from 0.1.0. Core packages, tooling, and apps are NOT versioned (cascade-effect would invalidate the signal).
Tag prefix:
The per-package prefix release-please uses to avoid tag collisions in a monorepo — template-v0.1.0 for the root, auth-v0.1.0 / blog-v0.1.0 / etc. for features.
Rolling release PR:
The single PR release-please keeps open on main and updates idempotently on every push. Contains all pending version bumps + changelog entries since the last release. Merging it cuts the tags + GitHub releases.
Bump targeting (by commit-path):
How release-please decides which tracked package(s) a commit bumps — by the files changed, not the conventional-commit (scope). A commit touching packages/auth/** bumps @repo/auth; a commit touching docs/** or scripts/** bumps the root template; a commit touching both bumps both.
Pre-1.0 bump policy:
While each tracked package is <1.0.0: feat: bumps patch, feat!: bumps minor (NOT major). Configured via bump-patch-for-minor-pre-major: true. Once a package crosses 1.0.0, standard semver kicks in.
Release-As: trailer:
A commit-body trailer (Release-As: 0.5.0) that overrides release-please's auto-derived bump for the package(s) whose paths the commit touches. Used for manual semver corrections or explicitly crossing 1.0.0.
CHANGELOG.md:
A per-package, release-please-managed changelog (one at repo root for the template; one at packages/<feature>/CHANGELOG.md for each feature). Do not edit manually — release-please regenerates from commit history.
Cross-feature mechanisms
Event bus (IEventBus):
The vendor-neutral cross-feature pub/sub interface in @repo/core-events. Two implementations: InMemoryEventBus (dev/test, synchronous) and PayloadJobsEventBus (production, durable via Payload tasks). Selected by bindAll(). See ADR-015.
Use when: feature A's success must trigger feature B's reaction. Don't use for in-feature flow control — that's a direct use-case call (rule E0).
Event descriptor:
A defineEvent(...) declaration with a name (wire format) + Zod payload schema. Lives at packages/<feature>/src/events/<event>.event.ts. Publishers re-export from the feature root; consumers do not.
Event handler:
A consumer's reaction to a published event. Lives at packages/<feature>/src/events/handlers/on-<publisher>-<event>.handler.ts. Always private — never re-exported (rule E1, enforced by no-handler-reexport).
Job queue (IJobQueue):
The vendor-neutral deferred-work interface in @repo/core-shared/jobs/. Two implementations: InMemoryJobQueue (dev/test, setImmediate) and PayloadJobQueue (production). Feature packages enqueue via IJobQueue only — direct payload.jobs.queue() is ESLint-blocked (rule J0).
Realtime channel descriptor:
A defineRealtimeChannel(...) declaration in packages/<feature>/src/realtime/<channel>.channel.ts. Re-exported from the feature root. Carries a Zod payload schema + a scope.
Channel scope:
A channel's subscription rule — public | authenticated | role:<name> | user-scoped. See ADR-016.
Broadcaster (IRealtimeBroadcaster):
The server-side push interface in @repo/core-realtime. Use cases call broadcaster.broadcast(channel, payload) after the success path.
Realtime handler:
A consumer's reaction to an inbound client message on a channel. Lives at packages/<feature>/src/realtime/handlers/*.handler.ts. Always private — never re-exported (rule R1, enforced by no-realtime-handler-reexport).
Audit log:
A DPA-compliant record of a use case's side effects. Emitted via auditLog.record(...); declared in the manifest's audits: array. See ADR-018.
Instrumentation
Tracer (ITracer):
The span interface in @repo/core-shared/instrumentation/. Three impls: NoopTracer, OtelTracer, RecordingTracer (test-only, from core-testing).
Logger (ILogger):
The capture interface. Same three-impl shape as ITracer. Used for exception reporting and structured logs.
Metrics (IMetrics):
The counter/gauge/histogram interface. Same three-impl shape.
withSpan / withCapture / withAudit:
The three wrapper composers applied at DI bind time. Composition: withSpan(tracer, opts, withCapture(logger, tags, withAudit(auditLog, schema, factory(deps)))) — withSpan outermost. Together they attach the Instrumented / Captured / Audited brands.
Span:
A traced unit of work. Emitted by tracer.startSpan(...) (inline in repos) or withSpan(...) (composed for use cases + controllers).
Capture:
The act of recording an exception once. Guarded by the non-enumerable __sentryReported flag so a bubbled error surfaces exactly once.
PII scrub:
The server-side scrubbing of email/passwords/tokens/cookies/auth/IPs at the OTel processor layer (PiiScrubSpanProcessor + PiiScrubLogRecordProcessor) before any exporter sees the data. sendDefaultPii: false everywhere, CI grep-enforced. See ADR-017 §7.
Rule 0:
The bindAll() decision that selects OTel+Sentry instrumentation when a DSN env var is present, otherwise the Noop chain. Orthogonal to USE_DEV_SEED / NODE_ENV (those control repo bindings, not instrumentation).
Workflow
PRD:
The top-level requirements doc at docs/work/prds/<date>-<slug>.prd.md. Status flows draft → in-review → approved → shipped (pnpm work prd-ship auto-flips on epic completion). pnpm work decompose refuses to run on draft.
Avoid: spec (in this template the ADR is the durable design record; PRDs are the implementation seed).
Epic:
A large body of work, one folder under docs/work/epics/<epic-slug>/ with _epic.md. Decomposed from a PRD.
Story:
One use case or one technical capability, one folder under <epic>/<story-slug>/ with _story.md. Type metadata: user-story or technical-story.
Task:
One vertical slice = one PR = one commit. File <slug>.task.md under the story folder.
Slice (a.k.a. vertical slice): A change that exercises every Clean Architecture layer end-to-end for one capability — manifest entry + contracts + tests + impl + DI wiring + integration. The unit of a task.
Sandcastle:
The agent dispatch orchestrator (https://github.com/mattpocock/sandcastle). Invoked via pnpm work dispatch --execute. See ADR-019.
_state.json:
The orchestrator-managed derived index at docs/work/_system/_state.json. Regenerated from markdown via pnpm work rebuild-state. Committed.
Generator:
A pnpm turbo gen <kind> invocation. Generator-first is non-negotiable — agents always prefer the generator over hand-rolled scaffolding. Available kinds: feature, event, job, realtime, core-package, core-ui-component.
Slice = task = PR = commit: The shipping rhythm. One vertical slice closes one task, becomes one PR, lands as one commit.
Library trace:
A per-decision artifact at docs/library-decisions/<YYYY-MM-DD>-<package-name>.md recording the outcome of the evaluate-library skill against a candidate third-party dependency. Frontmatter is the machine surface (tier, decision, filter-results enum); headings are the human surface (one per filter + one per prompt). Both decision: approved and decision: rejected traces are first-class — the rejection record is the value, since it stops future agents from re-evaluating the same library. Required for any new runtime dep in a feature- or core-tagged package; the pre-commit hook blocks the commit if the trace is missing.
Pre-shipped trace:
A library trace emitted by pnpm turbo gen core-package <name> for each direct runtime dep of a pre-curated optional core. Pre-approved by the template's ADRs (015 events / 016 realtime / 018 audit). Generated alongside the core's frozen snapshot so optional cores satisfy the library-evaluation policy by default.
Trace revalidation:
The weekly + on-demand CI job (.github/workflows/trace-revalidation-weekly.yml) that re-runs every approved trace's verification-commands block and detects drift in license / maintenance / CVE / EU-residency / Socket-risk signals that don't show up as version bumps. Two-tier divergence action: soft divergence appends to a rolling dashboard issue; hard divergence (license changed, named-consumer gone, critical CVE disclosed, EU residency flipped, Socket-flagged) opens a per-dep issue labeled library-policy/re-evaluation for the dispatch loop to pick up. Never auto-edits the trace — the re-walk needs the evaluate-library skill, not a mechanical edit. Records the most-recent successful pass in the trace's last-revalidated frontmatter field (separate from the original adoption date).
Major-bump re-evaluation:
The Renovate-triggered re-walk of evaluate-library when a runtime dep's major version bumps (semver-major, distinct from minor/patch). Updates the existing trace in-place (refreshes version, filter-results, verification-commands, last-revalidated), preserves the original date. Catches license / maintenance / transitive-surface changes that semver-encodes as "breaking." Minor + patch bumps do not trigger re-evaluation.
Modes
Production mode:
NODE_ENV=production (and USE_DEV_SEED unset) → bindAll() wires Payload-backed repositories + PayloadJobsEventBus + PayloadJobQueue.
Dev-seed mode:
USE_DEV_SEED=true (or default fallback when not in production) → bindAll() wires populated mocks + InMemoryEventBus + InMemoryJobQueue. Developer default so pnpm dev boots without Payload running.
Relationships
- A PRD decomposes into one or more Epics.
- An Epic contains one or more Stories.
- A Story is implemented by one or more Tasks, each a Slice.
- A Use case is declared in a Manifest before it has code (manifest-first ordering).
- A Use case has exactly one Controller.
- A Controller has at most one
presenter(omitted for void outputs). - A Feature owns its Repositories, Services, Use cases, Controllers, Events, Jobs, Channels, and DI Container.
- Cross-feature reactions travel through the Event bus; in-feature reactions are direct use-case calls.
- An Event descriptor is public; its Event handler is always private.
- A Channel descriptor is public; its Realtime handler is always private.
- Brands are attached only at DI bind time, by
withSpan/withCapture/withAudit. - Conformance asserts the Manifest and code agree, at five latency tiers.
- A Coverage band in the Manifest is read by Vitest (L0),
assertFeatureConformance(boot), andpnpm coverage:diff(L1) — one decision, three enforcers. - Mutation testing is the third dimension of test quality, after "test file exists" (ESLint structural) and "test executes the code" (L0/L1 coverage).
Flagged ambiguities
- "feature" — always a vertical feature package; never a CMS-collection field or a generic capability.
- "service" — a DI-injected port (e.g.
IAuthenticationService); not a Kubernetes service, Payload collection, or generic "service object". - "handler" — qualify by context: event handler (cross-feature) | realtime handler (inbound socket message) | task handler (Payload job).
- "schema" — qualify: Zod schema (input/output contracts) | Payload collection schema (CMS field definitions).
- "config" — qualify: Payload config | Next config | Vitest config | TS config.
- "bind" — DI binding (
bind<I>(SYMBOL).toDynamicValue(...)); not aFunction.prototype.bind. - "spec" — avoid in this template. Durable design records are ADRs (
docs/decisions/adr-NNN-*.md); implementation seeds are PRDs (docs/work/prds/*.prd.md). Never use "spec" to refer to a Jest spec file either. - "controller" — one verb-noun pair per file; never a multi-method MVC controller.
- "module" — qualify by context: package (workspace member under
packages/orapps/) | Node ESM/CJS module (file-level import semantics) | refactor-vocabulary "module" (in theimprove-codebase-architectureskill, defaults to feature — i.e., apackages/<name>/; can also mean a narrower unit like a use case or controller when the refactor scope is that narrow). Default: prefer "feature" for the common case; "package" for the workspace-member shell; "module" only inside refactor conversations. - "seam" — refactor-vocabulary only (where a module's interface lives). Not a synonym for boundary (ESLint workspace-tag rule) or scope (Conventional Commits parenthetical).
- "adapter" — refactor-vocabulary only (a concrete thing satisfying an interface at a seam). Not a synonym for port (the interface itself) or service (a DI-injected port for non-collection capabilities).
- "coverage" — qualify when ambiguous: L0 coverage (per-layer thresholds, test-time) | diff coverage / L1 (cover-the-diff gate) | aggregate coverage / L2 (committed summary.json) | mutation coverage / L3 (Stryker mutation score, not line coverage).
Cross-references
- Architecture:
docs/architecture/overview.md,docs/architecture/vertical-feature-spec.md,docs/architecture/agent-first-workflow-and-conformance.md - ADRs:
docs/decisions/adr-NNN-<slug>.md - Workflow:
docs/work/README.md,AGENTS.md§ "Agent-driven development" - Guides:
docs/guides/runbook.md,docs/guides/conformance-quickref.md,docs/guides/tdd-workflow.md