Files
agentic-dev/docs/glossary.md
Danijel Martinek a74b7360e1 docs(compliance): document policy template convention in README and glossary
Add a "Policy templates" section to docs/compliance/README.md explaining
the docs/compliance/templates/ directory, the copy-to-compliance/ workflow,
the [FILL IN:] placeholder convention, and the verification one-liner.

Add four glossary entries: fill-in template, [FILL IN:] marker,
pre-launch compliance checklist, and compliance overview.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-20 12:49:50 +00:00

38 KiB
Raw Blame History

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, requiredCores, and (when applicable) requiresConsent: ConsentCategory[]. 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:

  1. TypeScript brands (0s) — Instrumented, Captured, Audited on every wired use case / controller.
  2. ESLint conformance rules (<1s) — five custom rules in core-eslint.
  3. Boot assertion (~3s) — assertFeatureConformance(...) at the tail of every binder.
  4. CI drift gate (pnpm conformance, ~120s) — cross-feature event closure.
  5. Fallow (pnpm fallow, ~3060s) — 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.

SBOM (Software Bill of Materials): A machine-readable inventory of all direct and transitive package dependencies attached as a JSON file to each GitHub release. Generated in CycloneDX JSON format via @cyclonedx/cyclonedx-npm in the release-please.yml CI workflow (runs only when a release is actually cut). Named sbom-<tag>.cdx.json and uploaded as a GitHub release asset. Enables downstream supply-chain scanning and NTIA minimum-elements compliance. Not committed to the repository. Avoid: confusing SBOM (a release asset produced by CI) with library traces (developer-authored, per-decision records in docs/library-decisions/).

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.

Analytics (IAnalytics): The product-analytics interface, lives in @repo/core-analytics (optional core, scaffolded via pnpm turbo gen core-package analytics). Three methods: track(event, properties?, user?), identify(user), pageView(path, properties?). Same three-impl shape as ITracer/ILogger/IMetrics. Used for funnel/cohort/retention analysis; structurally distinct from IAuditLog (which is compliance-driven) and from IEventBus (which is cross-feature action routing). Server-only by manifest gate; consumer wires React provider for client-side via @repo/core-analytics/react. Avoid: confusing analytics events with cross-feature events (bus) or audit events (compliance) — they're three orthogonal capture channels.

withSpan / withCapture / withAudit / withAnalytics / withConsent / withRateLimit: The six wrapper composers applied at DI bind time. Composition is nested with withSpan outermost; each layer attaches a brand: withSpanInstrumented, withCaptureCaptured, withAuditAudited (when manifest declares audits), withAnalyticsAnalyzed (when manifest declares analyticsEvents), withConsentConsentChecked (when manifest declares requiresConsent), withRateLimitRateLimited (when manifest declares rateLimit). See ADR-025 for the compliance-baseline channels.

SubjectLink: A field-level declaration in a Payload collection's custom.subject array that maps a relationship field to a data subject. Carries field (Payload field name), kind ("self" | "owner" | "reference"), optional target (auth-collection slug), and optional role (semantic label). The DSR cascade walks these at runtime to determine which rows to include in Art. 15 exports and which rows to erase or redact on Art. 17 requests. See docs/compliance/subject-linkage.example.md. Avoid: confusing kind: "self" (the subject's own account row) with kind: "owner" (rows the subject authored/owns) — both are treated as owned data for DSR purposes, but "self" targets the auth-collection record itself.

DeletionCertificate: The immutable proof-of-erasure record returned by IDataDelete.deleteSubjectData(subjectId, mode). Carries subjectId (or "erased-{hash}" after the ID itself is purged), mode ("soft" | "cascade-hard"), timestamp (ISO 8601), reason, affected (per-collection summary of collection, rowsAffected, action, and fields NULLed), and auditEntryId linking to the immutable audit log entry. GDPR Art. 17 compliance artifact — store permanently, never mutate. See docs/guides/dsr.md. Avoid: generating a DeletionCertificate manually — it is emitted only by the production IDataDelete implementation to ensure correctness.

DSR (@repo/core-dsr): The Data Subject Rights handler — optional core package providing four interfaces that operate on user data in response to GDPR Arts. 1518 requests. IDataExport (Art. 15 access + Art. 20 portability), IDataDelete (Art. 17 erasure cascade), IDataRectify (Art. 16), IProcessingRestriction (Art. 18). Sibling to core-audit — audit records access, DSR acts on the underlying data. Walks Payload collections at runtime using field-level custom.pii tags. See ADR-025. Avoid: confusing DSR with audit; audit is the immutable journal, DSR is the mutator.

Consent (@repo/core-consent): The runtime gate for granular consent categories (essential/functional/analytics/marketing). Optional core package providing IConsent interface (isGranted/grant/withdraw/getCategories) + ConsentChecked brand + requiresConsent: [...] manifest field + no-undeclared-consent-check ESLint rule. Cookie banner UI ships as atomic component in core-ui. See ADR-025.

UserConsentState: A per-category consent record returned by IConsent.getCategories(). Carries category (ConsentCategory), state ("granted" | "denied" | "pending"), optional grantedAt and withdrawnAt ISO 8601 timestamps, bannerVersion, policyVersion, and method (how the category was consented — e.g., "banner-accept", "signup-migration"). The element type of the getCategories() return value; used for compliance audits and cookie-versioning migration-on-read logic.

ConsentChecked: The phantom-type brand ConsentChecked<F> attached by withConsent(consent, factory(deps)) at DI bind time. Signals that the wrapped use case's requiresConsent categories will be checked before execution. TypeScript rejects binding an unwrapped factory to a ConsentChecked-typed DI symbol when the manifest declares requiresConsent. The boot assertion (assertFeatureConformance) verifies the brand is present at runtime. See withConsent in @repo/core-consent. Avoid: applying withConsent in test files — tests inject mocks directly into the factory and do not go through the DI wrappers.

Rate-limit (core-shared/rate-limit): Fourth conformance channel after audit/analytics/consent. IRateLimit interface (consume/reset) + withRateLimit wrapper + RateLimited brand + rateLimit: { window, budget } manifest field. Default budgets in manifest, runtime overrides via ctx.rateLimit config. Consumer wires Redis/Upstash impl. See ADR-025.

IRateLimit: The vendor-neutral rate-limit interface in @repo/core-shared/rate-limit/. Two methods: consume(budgetName, key, weight?)RateLimitDecision (fields: allowed: boolean, remaining: number, resetAt: Date) and reset(budgetName, key). Two implementations: InMemoryRateLimit (dev/test) and a consumer-wired production backend (Redis/Upstash). Feature packages receive it via ctx.rateLimit at DI bind time; declared in the manifest's rateLimit field.

RateLimited: The phantom-type brand RateLimited<F> attached by withRateLimit(rateLimit, fn) at DI bind time. Signals that the wrapped use case's declared RateLimitBudget[] entries will be enforced before execution. TypeScript rejects binding an unwrapped factory to a RateLimited-typed DI symbol when the manifest declares rateLimit. The boot assertion verifies the brand at runtime. Avoid: applying withRateLimit in test files — tests inject mocks directly and do not go through the DI wrappers.

withRateLimit: The wrapper composer that attaches the RateLimited brand at DI bind time. Signature: withRateLimit(rateLimit: IRateLimit, fn). Part of the six-wrapper composition chain — see the combined withSpan / withCapture / … entry above. Outermost position is withSpan; withRateLimit sits between withAnalytics and the inner factory.

PII inventory: Personal-data declaration at the Payload field level via custom.pii = { category, purpose, retention, exportable, restrictable }. Generator emits compliance/data-map.yml. DSR cascade walks this at runtime. See ADR-025.

Retention policy: Per-Payload-collection retention via custom.retention = { activeRetention, postDeletion, purgeSchedule, hardDeleteAfter }. Generator emits compliance/retention-policy.yml. Background purge job in core-shared/jobs reads this at boot. Per-PII-field retention overrides apply (more-specific wins). See ADR-025.

Sub-processor: External service that processes personal data on behalf of the controller (Stripe, SendGrid, PostHog, etc.). Declared via extended ADR-022 library traces — docs/library-decisions/<date>-<pkg>.md frontmatter gains is-sub-processor, processes-pii, data-sent, region, dpa-signed, sccs-required, contact fields. Generator emits compliance/sub-processors.yml from traces filtered to is-sub-processor: true. See ADR-025.

docs/compliance/ vs compliance/: Split locations for compliance artifacts. docs/compliance/ (template-shipped) holds FILL-IN TEMPLATES (.template.md runbooks, .example.yml references). Root compliance/ (consumer-created) holds LIVE ARTIFACTS (data-map.yml, retention-policy.yml, sub-processors.yml from generators + consumer-authored filled runbooks). Audit evidence lives in compliance/. See ADR-025.

Fill-in template: A .template.md file under docs/compliance/templates/ containing placeholder markers ([FILL IN:]) that a project team copies to compliance/ and completes with project-specific values before launch.

[FILL IN:] marker: The literal string [FILL IN:] used in fill-in templates to mark every placeholder that requires a project-specific value; machine-detectable via grep -rn '\[FILL IN:' compliance/ to audit completion status.

Pre-launch compliance checklist: The operational checklist at docs/guides/pre-launch-compliance-checklist.md enumerating the GDPR and security controls that must be verified before going live with a production workload — covers data mapping, retention policy, DSR readiness, consent UI, sub-processor contracts, and security headers.

Compliance overview: The hub document at docs/guides/compliance-overview.md that maps the full GDPR compliance surface of the template — explains where each artifact lives, which generator or guide produces it, and how the pieces connect (PII inventory, retention, DSR, audit, consent, sub-processors, policy templates, and the pre-launch checklist).

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).

Security

SecurityHeadersConfig: The TypeScript type accepted by buildSecurityHeaders(). Fields: mode ("prod" | "dev"), optional nonce (per-request CSP nonce string), and optional allowlist arrays allowedConnectOrigins, allowedImgOrigins, allowedFontOrigins (absolute URLs). Lives at @repo/core-shared/security.

buildSecurityHeaders(): The pure function in @repo/core-shared/security that accepts a SecurityHeadersConfig and returns a Record<string, string> of HTTP security headers: Strict-Transport-Security, X-Frame-Options, X-Content-Type-Options, Referrer-Policy, Permissions-Policy, and Content-Security-Policy. In "prod" mode the CSP uses strict-dynamic + nonce; in "dev" mode it relaxes to unsafe-inline/unsafe-eval to allow HMR tooling.

nonce (CSP context): A per-request cryptographically random token (typically 16+ bytes, base64-encoded) threaded into the Content-Security-Policy header's script-src directive as 'nonce-<value>'. Required when using strict-dynamic CSP to permit specific server-rendered inline <script> tags (e.g. Next.js bootstrap chunk, Sentry SDK loader) without unsafe-inline. Passed to buildSecurityHeaders({ nonce }) and set as the nonce attribute on matching <script> elements. Avoid: reusing the same nonce across requests — it must be regenerated per HTTP response to preserve the security guarantee.

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), and pnpm 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 a Function.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/ or apps/) | Node ESM/CJS module (file-level import semantics) | refactor-vocabulary "module" (in the improve-codebase-architecture skill, defaults to feature — i.e., a packages/<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