# 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`      | ~30–60s               | 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`                                                       |
| `conformance/usecase-must-be-wired`            | error    | Every manifest use case must be bound via `wireUseCase({ name: "<key>" })` in `bind-production.ts` / `bind-dev-seed.ts`           |
| `conformance/no-undeclared-analytics-event`    | warn     | `analytics.track("X")` literal must match the manifest's `analyticsEvents` for the use case                                       |
| `conformance/pii-declaration-must-be-complete` | warn     | `custom.pii` blocks in Payload config files must declare all required fields: `category`, `purpose`, `exportable`, `restrictable` |

## 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 + `usecase-must-be-wired` ESLint error + boot assertion + bind-production smoke test
- **Manifest entry has no `wireUseCase` call in either binder** → `usecase-must-be-wired` ESLint error + boot assertion + bind-production smoke test
- **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)
- **Factory calls `analytics.track("X")` but manifest doesn't declare it in `analyticsEvents`** → `conformance/no-undeclared-analytics-event` (warn); add the event slug to the manifest or remove the call

## 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`.
