From 4966191c8bbc91ad0cdc5044be88fbb24cc36564 Mon Sep 17 00:00:00 2001 From: Danijel Martinek Date: Wed, 13 May 2026 07:28:46 +0200 Subject: [PATCH] docs(guides): conformance-quickref single-page agent reference --- docs/guides/conformance-quickref.md | 124 ++++++++++++++++++++++++++++ 1 file changed, 124 insertions(+) create mode 100644 docs/guides/conformance-quickref.md diff --git a/docs/guides/conformance-quickref.md b/docs/guides/conformance-quickref.md new file mode 100644 index 0000000..8d6847b --- /dev/null +++ b/docs/guides/conformance-quickref.md @@ -0,0 +1,124 @@ +# Conformance system — quick reference + +Day-to-day reference for the manifest-first workflow. For design rationale see `docs/architecture/agent-first-workflow-and-conformance.md` and the interactive `feature-conformance-explainer.html`. + +--- + +## The manifest + +Every feature has one at `src/feature.manifest.ts`: + +```ts +import { defineFeature } from "@repo/core-shared/conformance"; + +export const fooManifest = defineFeature({ + name: "foo", + requiredCores: [], + useCases: { + getThing: { mutates: false, audits: [], publishes: [], consumes: [] }, + createThing: { + mutates: true, + audits: ["thing.created"], + publishes: ["foo.thing-created"], + consumes: [], + }, + }, + realtimeChannels: [], + jobs: [], +} as const); + +export type FooManifest = typeof fooManifest; +``` + +Field reference: + +| Field | Type | Meaning | +|---|---|---| +| `name` | string literal | Feature name (kebab-case, matches package name) | +| `requiredCores` | string[] | Optional cores this feature requires (e.g. `["audit", "events"]`) | +| `useCases..mutates` | boolean | True for create/update/delete; drives whether `__audited` brand is required | +| `useCases..audits` | string[] | Audit event types this use case emits via `auditLog.record({ type: "X" })` | +| `useCases..publishes` | string[] | Cross-feature events this use case publishes via `bus.publish("X")` | +| `useCases..consumes` | string[] | Cross-feature events this use case consumes (via an event handler) | +| `realtimeChannels` | string[] | Realtime channels this feature owns | +| `jobs` | string[] | Job slugs this feature enqueues | + +Re-export from `src/index.ts`: + +```ts +export { fooManifest, type FooManifest } from "./feature.manifest"; +``` + +## bindProductionX self-assertion + +Every feature's `bind-production.ts` calls the assertion at the tail: + +```ts +import { assertFeatureConformance } from "@repo/core-shared/conformance"; +import { fooManifest } from "../feature.manifest"; + +export function bindProductionFoo(ctx: BindProductionContext): void { + // ... bind use cases, wrapped with withSpan + withCapture + (if mutating + audits) withAudit ... + + assertFeatureConformance( + fooContainer, + fooManifest, + { + getThing: FOO_SYMBOLS.IGetThingUseCase, + createThing: FOO_SYMBOLS.ICreateThingUseCase, + }, + ctx, + ); +} +``` + +The symbol map declares which container symbol each manifest use-case key resolves to. + +## The four gates + +| Gate | When it fires | What it catches | Severity | +|---|---|---|---| +| `tsc` | on save | forgotten wrappers; manifest-derived slot type rejects unwrapped factory | error | +| `eslint` | on save / `pnpm lint` | manifest ↔ code drift; missing sibling test; missing manifest | error or warn | +| `pnpm dev` | at boot | binding lost its runtime brand; manifest declares more than wired | throws synchronously | +| `pnpm conformance` | CI | orphan event consumers across features | exits non-zero | + +## ESLint rules + +| Rule | Severity | What it does | +|---|---|---| +| `conformance/feature-must-have-manifest` | error | Use-case files require a sibling manifest | +| `conformance/usecase-must-have-test-file` | error | Every `*.use-case.ts` has a sibling `*.use-case.test.ts` | +| `conformance/required-cores-installed` | error | Manifest's `requiredCores` must exist as `core-` packages in pnpm-workspace.yaml | +| `conformance/no-undeclared-event-publish` | warn | `bus.publish("X")` literal must match the manifest's `publishes` for the use case | +| `conformance/no-undeclared-audit` | warn | `auditLog.record({ type: "X" })` literal must match the manifest's `audits` | + +## Workflow ordering for new use cases + +1. **Manifest** — add the use case to `feature.manifest.ts` with empty `audits` / `publishes` / `consumes` +2. **Contracts** — export `xInputSchema`, `xOutputSchema`, `IXUseCase` from the use-case file (factory body throws "not implemented") +3. **Tests (red)** — write the test importing the contracts; verify it fails +4. **Implementation (green)** — fill the factory body until tests pass + +For the fast path: `pnpm turbo gen feature ` scaffolds steps 1 + 2 in a single command. + +## Common drift patterns and the gate that catches them + +- **Forgot `withSpan` at bind time** → tsc TS2322 + boot assertion +- **Manifest declares `audits: ["X"]` but factory doesn't call `auditLog.record({type:"X"})`** → no automatic catch yet; future story +- **Factory calls `bus.publish("Y")` but manifest doesn't declare it** → `conformance/no-undeclared-event-publish` (warn) +- **Feature has use cases but no manifest** → `conformance/feature-must-have-manifest` (error) +- **Manifest references `requiredCores: ["X"]` but no `core-X` package exists** → `conformance/required-cores-installed` (error) +- **One feature consumes `Y` but no feature publishes `Y`** → `pnpm conformance` orphan check (CI gate) + +## Pinning down a drift + +When a gate fires, the error message tells you what to run. For example: + +> `Feature blog has use cases but no feature.manifest.ts. Run 'pnpm turbo gen feature blog' or scaffold the manifest manually at packages/blog/src/feature.manifest.ts.` + +That's the "fix" line — follow it. + +--- + +For the deeper design rationale see `docs/architecture/agent-first-workflow-and-conformance.md` and the interactive `feature-conformance-explainer.html`.