Files
agentic-dev/docs/guides/conformance-quickref.md

138 lines
7.0 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# 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.<name>.mutates` | boolean | True for create/update/delete; drives whether `__audited` brand is required |
| `useCases.<name>.audits` | string[] | Audit event types this use case emits via `auditLog.record({ type: "X" })` |
| `useCases.<name>.publishes` | string[] | Cross-feature events this use case publishes via `bus.publish("X")` |
| `useCases.<name>.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 five 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 |
| `pnpm fallow` | ~3060s | unused exports/files, dupes, circular deps, complexity, AI-change audit | warn (currently) |
## 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-<name>` 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 <name>` 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.
## Fallow audit for AI changes
When you (the agent) finish a task and are about to commit, run:
```
pnpm fallow:audit
```
This runs `fallow audit --base main`, comparing your branch's diff against main. If your change adds dead exports, dupes, or complexity hotspots, fallow tells you exactly what and where. Fix or accept (with --gate flag to ignore inherited findings).
This is the catch-all for whole-codebase drift the per-file gates can't see.
---
For the deeper design rationale see `docs/architecture/agent-first-workflow-and-conformance.md` and the interactive `feature-conformance-explainer.html`.