Files
agentic-dev/docs/glossary.md
Danijel Martinek b96cce5d74 feat: hybrid versioning + automated CHANGELOG via release-please
Closes the user's ask: versioning + a changelog generated on merging
to main, building on the just-mandated Conventional Commits substrate
(CLAUDE.md Key Conventions).

Architecture: ADR-021. Cookbook: docs/guides/releasing.md.

Initial state — six tracked packages at v0.1.0:
  - .                          -> template-vertical  (tag: template-v...)
  - packages/auth              -> @repo/auth         (tag: auth-v...)
  - packages/blog              -> @repo/blog         (tag: blog-v...)
  - packages/media             -> @repo/media        (tag: media-v...)
  - packages/marketing-pages   -> @repo/marketing-pages (tag: marketing-pages-v...)
  - packages/navigation        -> @repo/navigation   (tag: navigation-v...)

Core packages, tooling, and apps are NOT independently versioned
(ADR-021 rationale: core bumps cascade; apps aren't consumables;
surfacing them would create noise without information).

Configuration:
  - release-please-config.json   - 6 tracked packages, hybrid scope,
                                   pre-1.0 conservative bump policy
                                   (feat: -> patch, feat!: -> minor),
                                   conventional-commit type mapping
  - .release-please-manifest.json - baseline 0.1.0 for all 6 packages
  - .github/workflows/release-please.yml - googleapis/release-please-
                                   action@v4 on push to main,
                                   concurrency-gated, write
                                   permissions for the rolling PR

Workflow: on every push to main, release-please scans commits since
the last release tag PER PACKAGE (using commit-path, not the
conventional-commit scope), updates a single rolling release PR with
version bumps + per-package CHANGELOG entries. Merging that PR cuts
per-package tags + GitHub releases.

CHANGELOG files seeded at v0.1.0 baseline:
  - CHANGELOG.md (root)
  - packages/<feature>/CHANGELOG.md (5 features)
Subsequent versions are appended by release-please from commit
history. Do not edit manually.

Visibility surfaces updated (every agent entry point):
  - CLAUDE.md Read First + new "Versioning is hybrid" Key Conventions
    bullet (with bump policy summary)
  - AGENTS.md preamble - new "Releases:" callout alongside Commits
  - docs/glossary.md - new Releasing section with 8 terms (Conventional
    Commits, release-please, Hybrid versioning, Tag prefix, Rolling
    release PR, Bump targeting, Pre-1.0 bump policy, Release-As trailer,
    CHANGELOG.md)
  - docs/README.md - guides tree updated with releasing.md
  - .claude/hooks/session-start.sh - one-line release reminder
  - .claude/hooks/prompt-context.sh - new keyword group for
    release/version/bump/semver/tag prompts

Package.json version bumps:
  - root: name "template" -> "template-vertical", version "0.1.0"
  - packages/auth, blog, media, marketing-pages, navigation: "0.0.0" -> "0.1.0"

Root rename rationale: release-please tags use the package-name + the
component prefix; "template-vertical" matches the repo identity (and
the user's question preview).

First release-please PR after this lands will sweep all subsequent
post-baseline commits into 0.1.1 / 0.2.0 bumps as appropriate.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-13 17:17:16 +02:00

20 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. Avoid: domain, module, vertical (use "feature" or "vertical 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.

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.

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/<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/_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.

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" — avoid for packages (use "package"); reserve for Node ESM/CJS module semantics if needed.
  • "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