Merge branch 'worktree-agent-workflow-docs': agent-workflow-docs v1 — surface conformance system
This commit is contained in:
16
AGENTS.md
16
AGENTS.md
@@ -366,6 +366,22 @@ Each feature binder signature is `(ctx: BindProductionContext): void` for produc
|
|||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
|
### Conformance contract (every feature)
|
||||||
|
|
||||||
|
Every feature package MUST declare a `src/feature.manifest.ts` using `defineFeature` from `@repo/core-shared/conformance`. The manifest declares the use cases, what they audit/publish/consume, and which optional cores they require.
|
||||||
|
|
||||||
|
The feature's `src/di/bind-production.ts` MUST call `assertFeatureConformance(container, manifest, symbols, ctx)` at the tail of `bindProduction<Name>` so `pnpm dev` refuses to boot if a binding loses its brand.
|
||||||
|
|
||||||
|
Re-export the manifest from `src/index.ts`:
|
||||||
|
|
||||||
|
```ts
|
||||||
|
export { fooManifest, type FooManifest } from "./feature.manifest";
|
||||||
|
```
|
||||||
|
|
||||||
|
See `docs/guides/conformance-quickref.md` for the canonical pattern; the generator (`pnpm turbo gen feature <name>`) emits all of this correctly by default.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
### Cross-feature events and background jobs (Plan 10, ADR-015)
|
### Cross-feature events and background jobs (Plan 10, ADR-015)
|
||||||
|
|
||||||
Three rules:
|
Three rules:
|
||||||
|
|||||||
18
CLAUDE.md
18
CLAUDE.md
@@ -44,6 +44,21 @@ Turborepo + pnpm monorepo organized by vertical features. Each feature (`auth`,
|
|||||||
- `docs/guides/audit-and-compliance.md` — DPA-compliant audit logging cookbook (*requires `gen core-package audit`*)
|
- `docs/guides/audit-and-compliance.md` — DPA-compliant audit logging cookbook (*requires `gen core-package audit`*)
|
||||||
- `docs/architecture/template-tiers.md` — must-have vs optional packages and how to scaffold the optionals
|
- `docs/architecture/template-tiers.md` — must-have vs optional packages and how to scaffold the optionals
|
||||||
|
|
||||||
|
## Conformance system
|
||||||
|
|
||||||
|
Every feature has a `src/feature.manifest.ts` declaring its use cases, audits, publishes, consumes, and required cores. Drift is caught at four latencies:
|
||||||
|
|
||||||
|
| Layer | Latency | Catches |
|
||||||
|
|---|---|---|
|
||||||
|
| **TypeScript brands** | 0s | forgotten `withSpan` / `withCapture` / `withAudit` at bind time |
|
||||||
|
| **ESLint rules** | <1s | manifest ↔ code drift; undeclared `bus.publish` / `auditLog.record`; missing manifest; missing sibling test |
|
||||||
|
| **Boot assertion** (`pnpm dev`) | ~3s | binding without required brand at runtime; manifest edited without rebinder |
|
||||||
|
| **CI drift gate** (`pnpm conformance`) | ~120s | orphan event consumers across features |
|
||||||
|
|
||||||
|
The five conformance ESLint rules: `feature-must-have-manifest` (error), `usecase-must-have-test-file` (error), `required-cores-installed` (error), `no-undeclared-event-publish` (warn), `no-undeclared-audit` (warn).
|
||||||
|
|
||||||
|
See `docs/architecture/agent-first-workflow-and-conformance.md` for the full design and `docs/guides/conformance-quickref.md` for the day-to-day reference.
|
||||||
|
|
||||||
## Key Conventions
|
## Key Conventions
|
||||||
|
|
||||||
- **Relative imports in `src/`** — Source files use relative paths (`../repositories/...`), not `@/` alias
|
- **Relative imports in `src/`** — Source files use relative paths (`../repositories/...`), not `@/` alias
|
||||||
@@ -74,6 +89,9 @@ Turborepo + pnpm monorepo organized by vertical features. Each feature (`auth`,
|
|||||||
- **Realtime is for state delivery, not for replacing tRPC (R0)** — Persistent request/response operations belong on tRPC procedures. Use realtime when the server needs to push without a request or the data is too high-frequency for HTTP
|
- **Realtime is for state delivery, not for replacing tRPC (R0)** — Persistent request/response operations belong on tRPC procedures. Use realtime when the server needs to push without a request or the data is too high-frequency for HTTP
|
||||||
- **Realtime channel descriptors are exported; handlers are private (R1)** — A feature's `realtime/<name>.channel.ts` is re-exported from the root barrel; `realtime/handlers/*.handler.ts` is wired only in bind-* files and never re-exported (ESLint-enforced via `no-realtime-handler-reexport`)
|
- **Realtime channel descriptors are exported; handlers are private (R1)** — A feature's `realtime/<name>.channel.ts` is re-exported from the root barrel; `realtime/handlers/*.handler.ts` is wired only in bind-* files and never re-exported (ESLint-enforced via `no-realtime-handler-reexport`)
|
||||||
- **`socket.io` lives in `@repo/core-realtime` only (R2)** — Feature packages MUST NOT import `socket.io` or `socket.io-client`. ESLint rule `no-direct-socket-io` enforces this; allowlist covers `core-realtime/src/socket-io-*.ts` and `apps/*/server.ts`
|
- **`socket.io` lives in `@repo/core-realtime` only (R2)** — Feature packages MUST NOT import `socket.io` or `socket.io-client`. ESLint rule `no-direct-socket-io` enforces this; allowlist covers `core-realtime/src/socket-io-*.ts` and `apps/*/server.ts`
|
||||||
|
- **Manifest-first ordering** — for any new use case, the workflow is **(1) manifest entry** → **(2) contracts** (`xInputSchema`, `xOutputSchema`, `IXUseCase`) → **(3) tests (red)** → **(4) implementation (green)**. The generator emits the manifest + a self-asserting `bind-production.ts` so new features are conformance-compliant by default
|
||||||
|
- **Self-asserting `bindProductionX(ctx)`** — every feature's bind-production calls `assertFeatureConformance(container, manifest, symbols, ctx)` at its tail. `pnpm dev` refuses to boot on drift
|
||||||
|
- **`pnpm conformance`** — cross-feature event-closure check; fails CI on orphan consumers
|
||||||
|
|
||||||
## MCP Servers
|
## MCP Servers
|
||||||
|
|
||||||
|
|||||||
@@ -25,6 +25,21 @@ per-use-case patterns below.
|
|||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
|
## Workflow ordering
|
||||||
|
|
||||||
|
For any new use case, follow these four steps in order:
|
||||||
|
|
||||||
|
1. **Manifest entry** — declare the use case in `src/feature.manifest.ts` with its `mutates` flag and (initially empty) `audits` / `publishes` / `consumes` arrays.
|
||||||
|
2. **Contracts** — export `xInputSchema`, `xOutputSchema`, and the `IXUseCase` type alias from the use-case file. Factory body starts as `throw new Error("not implemented")`.
|
||||||
|
3. **Tests (red)** — write the failing test that exercises the contract via the factory + a mock repository.
|
||||||
|
4. **Implementation (green)** — fill the factory body until the tests pass.
|
||||||
|
|
||||||
|
The `feature-must-have-manifest` ESLint rule will catch step 1 omissions; `usecase-must-have-test-file` catches step 3. The boot assertion (`assertFeatureConformance` at the tail of `bindProductionX`) catches forgotten wrappers at startup.
|
||||||
|
|
||||||
|
For the fast path, run `pnpm turbo gen feature <name>` — the generator emits the manifest + contracts + bind-production with the assertion already wired in.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
## 1. Overview
|
## 1. Overview
|
||||||
|
|
||||||
Every feature package owns:
|
Every feature package owns:
|
||||||
|
|||||||
124
docs/guides/conformance-quickref.md
Normal file
124
docs/guides/conformance-quickref.md
Normal file
@@ -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.<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 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-<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.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
For the deeper design rationale see `docs/architecture/agent-first-workflow-and-conformance.md` and the interactive `feature-conformance-explainer.html`.
|
||||||
@@ -32,6 +32,17 @@ pnpm install # link the new workspace package
|
|||||||
pnpm --filter @repo/widgets lint typecheck test
|
pnpm --filter @repo/widgets lint typecheck test
|
||||||
```
|
```
|
||||||
|
|
||||||
|
## Conformance-ready by default
|
||||||
|
|
||||||
|
Since milestone v of the conformance system, `pnpm turbo gen feature <name>` emits two conformance artefacts:
|
||||||
|
|
||||||
|
- **`src/feature.manifest.ts`** declaring the scaffolded `getX` use case
|
||||||
|
- **`src/di/bind-production.ts`** with `assertFeatureConformance(...)` called at the tail
|
||||||
|
|
||||||
|
Run `pnpm conformance` after generating a feature — it should pass cleanly. If you add `bus.publish("X")` calls in a factory body, you'll also need to add `"X"` to the manifest's `publishes[]` array for that use case, or the `no-undeclared-event-publish` ESLint rule will warn.
|
||||||
|
|
||||||
|
See `docs/guides/conformance-quickref.md` for the manifest field reference.
|
||||||
|
|
||||||
## What it generates
|
## What it generates
|
||||||
|
|
||||||
- Package files: `package.json`, `tsconfig.json`, `vitest.config.ts`,
|
- Package files: `package.json`, `tsconfig.json`, `vitest.config.ts`,
|
||||||
|
|||||||
@@ -704,3 +704,16 @@ expect(logger.captures[0]).toMatchObject({
|
|||||||
For an end-to-end example (controller → use case → repo, all wrapped, asserting no double-capture across layers), see `packages/blog/tests/r44-no-double-capture.test.ts`.
|
For an end-to-end example (controller → use case → repo, all wrapped, asserting no double-capture across layers), see `packages/blog/tests/r44-no-double-capture.test.ts`.
|
||||||
|
|
||||||
**Default mocks** (when you don't need assertions): construct `new MockArticlesRepository()` with no args — the constructor defaults bind `NoopTracer` + `NoopLogger`.
|
**Default mocks** (when you don't need assertions): construct `new MockArticlesRepository()` with no args — the constructor defaults bind `NoopTracer` + `NoopLogger`.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Conformance gates (post-TDD)
|
||||||
|
|
||||||
|
After your tests are green and the impl is committed, four gates check that the new code stays consistent with the feature's manifest:
|
||||||
|
|
||||||
|
1. **TypeScript brands** — the `ProductionUseCase<I, O, M>` slot in `bind-production.ts` only accepts factories wrapped through `withSpan` + `withCapture` + (if mutating with audits) `withAudit`.
|
||||||
|
2. **ESLint rules** — five `conformance/*` rules check manifest ↔ code drift; see `docs/guides/conformance-quickref.md`.
|
||||||
|
3. **Boot assertion** — `assertFeatureConformance` runs at the tail of every `bindProductionX(ctx)`; `pnpm dev` refuses to start on drift.
|
||||||
|
4. **CI drift gate** — `pnpm conformance` runs after `pnpm lint` in CI; fails on orphan event consumers across features.
|
||||||
|
|
||||||
|
The TDD red-green cycle covers behavioural correctness; the conformance gates cover architectural correctness.
|
||||||
|
|||||||
42
docs/work/agent-workflow-docs-v1/01-docs-rewrite/_story.md
Normal file
42
docs/work/agent-workflow-docs-v1/01-docs-rewrite/_story.md
Normal file
@@ -0,0 +1,42 @@
|
|||||||
|
---
|
||||||
|
id: 01-docs-rewrite
|
||||||
|
epic: agent-workflow-docs-v1
|
||||||
|
title: Surface conformance system across top-level docs
|
||||||
|
type: technical-story
|
||||||
|
status: done
|
||||||
|
feature: docs
|
||||||
|
depends-on: []
|
||||||
|
blocks: []
|
||||||
|
---
|
||||||
|
|
||||||
|
## Goal
|
||||||
|
Update CLAUDE.md, AGENTS.md, and four guides so agents picking up this
|
||||||
|
repo discover and follow the manifest-first workflow by default.
|
||||||
|
|
||||||
|
## Done when
|
||||||
|
- CLAUDE.md has a "Conformance system" section + manifest-first ordering
|
||||||
|
added to Key Conventions
|
||||||
|
- AGENTS.md's per-feature conventions mention the manifest contract
|
||||||
|
- adding-a-feature.md updated to the 4-step ordering
|
||||||
|
- scaffolding-a-feature.md notes the manifest emission + boot assertion
|
||||||
|
- tdd-workflow.md links to the conformance enforcement
|
||||||
|
- New conformance-quickref.md exists as a single-page agent reference
|
||||||
|
|
||||||
|
## In scope
|
||||||
|
Mechanical doc updates pointing at existing artifacts (the design doc, the
|
||||||
|
feature-conformance-explainer.html, the actual rules / wrappers / script
|
||||||
|
already merged).
|
||||||
|
|
||||||
|
## Out of scope
|
||||||
|
- Rewriting the existing architecture docs (already in place)
|
||||||
|
- Updating per-package AGENTS.md inside packages/*
|
||||||
|
|
||||||
|
## Tasks
|
||||||
|
- [x] Epic + story scaffold
|
||||||
|
- [x] CLAUDE.md update
|
||||||
|
- [x] AGENTS.md update
|
||||||
|
- [x] adding-a-feature.md update
|
||||||
|
- [x] scaffolding-a-feature.md update
|
||||||
|
- [x] tdd-workflow.md update
|
||||||
|
- [x] conformance-quickref.md (new)
|
||||||
|
- [x] Final verification + closeout
|
||||||
36
docs/work/agent-workflow-docs-v1/_epic.md
Normal file
36
docs/work/agent-workflow-docs-v1/_epic.md
Normal file
@@ -0,0 +1,36 @@
|
|||||||
|
---
|
||||||
|
id: agent-workflow-docs-v1
|
||||||
|
prd: null
|
||||||
|
title: Agent-workflow docs rollout
|
||||||
|
type: epic
|
||||||
|
status: done
|
||||||
|
features: [docs]
|
||||||
|
created: 2026-05-13
|
||||||
|
---
|
||||||
|
|
||||||
|
## Goal
|
||||||
|
Surface the conformance-system-v1 mechanics across CLAUDE.md, AGENTS.md,
|
||||||
|
and the feature-development guides so agents picking up this codebase
|
||||||
|
follow the manifest-first workflow by default.
|
||||||
|
|
||||||
|
## Why
|
||||||
|
The conformance system is the substrate, but agents read the top-level
|
||||||
|
docs first. Until the docs say "feature.manifest.ts exists, here's what
|
||||||
|
it does, here's how the gates fire," the system stays invisible to
|
||||||
|
agents iterating on this repo for the first time.
|
||||||
|
|
||||||
|
## In scope
|
||||||
|
- CLAUDE.md: add conformance system section + manifest-first ordering
|
||||||
|
- AGENTS.md: add manifest convention to per-package conventions
|
||||||
|
- docs/guides/adding-a-feature.md: update workflow to manifest → contracts → tests → code
|
||||||
|
- docs/guides/scaffolding-a-feature.md: note that generator now emits manifest + boot assertion
|
||||||
|
- docs/guides/tdd-workflow.md: link conformance system as the enforcement layer
|
||||||
|
- New docs/guides/conformance-quickref.md: ~80-line agent-facing reference
|
||||||
|
|
||||||
|
## Out of scope
|
||||||
|
- Frontend-conformance docs (next epic)
|
||||||
|
- Work-system-v1 docs (those land with the work-system epic)
|
||||||
|
- Editing the original conformance design docs (already in place)
|
||||||
|
|
||||||
|
## Stories
|
||||||
|
- [x] [01 — Surface conformance system across CLAUDE.md / AGENTS.md / guides](01-docs-rewrite/_story.md)
|
||||||
Reference in New Issue
Block a user