feat(conformance): wire cross-feature reader pattern into docs and schema
Add reads field to UseCaseManifest, update CLAUDE.md with Q0-Q3 rules, add ./reader subpath to AGENTS.md exports table, and cascade reader conventions through conformance quickref, adding-a-feature guide, and scaffolding guide. Moves gen reader from deferred to planned.
This commit is contained in:
14
CLAUDE.md
14
CLAUDE.md
@@ -23,6 +23,7 @@ pnpm turbo gen feature # Scaffold a new feature package
|
||||
pnpm turbo gen event # Scaffold an event contract or handler
|
||||
pnpm turbo gen job # Scaffold a background job
|
||||
pnpm turbo gen realtime # Scaffold a realtime channel or handler
|
||||
pnpm turbo gen reader # Scaffold a cross-feature reader
|
||||
pnpm turbo gen core-package # Scaffold an optional core package
|
||||
pnpm turbo gen core-ui-component # Scaffold an atomic-design component
|
||||
docker compose up -d # Start PostgreSQL
|
||||
@@ -64,7 +65,7 @@ Turborepo + pnpm monorepo organized by vertical features. Each feature (`auth`,
|
||||
|
||||
## Conformance system
|
||||
|
||||
Every feature has a `src/feature.manifest.ts` declaring its use cases, audits, publishes, consumes, required cores, `rateLimit?: RateLimitBudget[]` (when applicable, for per-use-case rate-limit budgets), and (when applicable) `requiresConsent: ConsentCategory[]` for features that gate behaviour behind user consent. Drift is caught at five latencies:
|
||||
Every feature has a `src/feature.manifest.ts` declaring its use cases, audits, publishes, consumes, reads (cross-feature reader deps), required cores, `rateLimit?: RateLimitBudget[]` (when applicable, for per-use-case rate-limit budgets), and (when applicable) `requiresConsent: ConsentCategory[]` for features that gate behaviour behind user consent. Drift is caught at five latencies:
|
||||
|
||||
| Layer | Latency | Catches |
|
||||
| -------------------------------------- | ------- | ----------------------------------------------------------------------------------------------------------- |
|
||||
@@ -74,7 +75,7 @@ Every feature has a `src/feature.manifest.ts` declaring its use cases, audits, p
|
||||
| **CI drift gate** (`pnpm conformance`) | ~120s | orphan event consumers across features |
|
||||
| **Fallow** (`pnpm fallow`) | ~30–60s | dead exports / unused files; duplicate code; circular deps; complexity hotspots; AI-change audit drift |
|
||||
|
||||
The fifteen conformance ESLint rules: `feature-must-have-manifest` (error), `usecase-must-have-test-file` (error), `required-cores-installed` (error), `usecase-must-be-wired` (error), `no-undeclared-event-publish` (warn), `no-undeclared-audit` (warn), `no-undeclared-analytics-event` (warn), `pii-declaration-must-be-complete` (warn), `component-must-have-story` (warn), `component-must-have-test` (warn), `atomic-tier-import-direction` (warn), `no-undeclared-consent-check` (warn), `no-undeclared-rate-limit` (warn), `entity-must-have-test` (warn), `no-relative-parent-import-in-tests` (warn). Fallow runs as a fifth layer, post-ESLint, whole-codebase.
|
||||
The sixteen conformance ESLint rules: `feature-must-have-manifest` (error), `usecase-must-have-test-file` (error), `required-cores-installed` (error), `usecase-must-be-wired` (error), `no-undeclared-event-publish` (warn), `no-undeclared-audit` (warn), `no-undeclared-analytics-event` (warn), `no-undeclared-reader` (warn), `pii-declaration-must-be-complete` (warn), `component-must-have-story` (warn), `component-must-have-test` (warn), `atomic-tier-import-direction` (warn), `no-undeclared-consent-check` (warn), `no-undeclared-rate-limit` (warn), `entity-must-have-test` (warn), `no-relative-parent-import-in-tests` (warn). Fallow runs as a fifth layer, post-ESLint, whole-codebase.
|
||||
|
||||
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.
|
||||
|
||||
@@ -124,9 +125,16 @@ See `docs/guides/coverage.md` for the cookbook and ADR-020 for the full rational
|
||||
- **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`)
|
||||
- **`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`
|
||||
- **Cross-feature domain queries go through readers (Q0)** — When a use case needs another vertical's domain-evaluated answer on the request path (e.g., permission check), use a reader (`I<Feature>Reader`). For raw data joins, use Payload `relationTo`. For reactions/side effects, use the event bus
|
||||
- **Reader contracts are public; implementations are private (Q1)** — The owning feature exports `I<Feature>Reader` from `./reader` subpath (`integrations/readers/`). The implementation (`<Feature>Reader`) is internal, constructed by the binder. Consumers import the type only
|
||||
- **Readers are strictly read-only; cross-feature writes go through events (Q2)** — A reader may only wrap use cases declared `mutates: false`. Enforced by `ReadOnly<F>` brand at compile time and `assertReaderPurity` at boot time
|
||||
- **Reader cycles are a design error (Q3)** — If Feature A reads from Feature B and vice versa, the boundaries are wrong. Break via: (a) UI composition at app layer, (b) event for one direction, (c) merge the features
|
||||
- **Readers wrap existing use cases, not repositories** — The reader is a thin facade; if the domain logic doesn't exist as a use case yet, create the use case first (manifest-first). No `MockReader` needed — same class works in dev-seed because the use cases beneath it are backed by mock repos
|
||||
- **Manifest `reads` field** — Use cases that query another feature's reader declare `reads: ["<feature>"]` in `feature.manifest.ts`. Verified by `assertFeatureConformance` at boot and `no-undeclared-reader` ESLint rule
|
||||
- **Binders return readers; `bindAll()` threads them** — `bindProductionAuth(ctx)` returns `{ reader: IAuthReader }`. `bindAll()` passes it: `bindProductionBlog(ctx, { authReader: authResult.reader })`. Ordering in `bindAll()` is explicit — owning feature first, consumers after
|
||||
- **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
|
||||
- **`pnpm conformance`** — cross-feature event-closure and reader-closure check; fails CI on orphan consumers or unresolvable `reads` entries
|
||||
- **New runtime dependencies require a library trace** — adding a runtime dependency to a feature- or core-tier package requires a trace at `docs/library-decisions/<date>-<name>.md` produced by the `/evaluate-library` skill; see ADR-022 and `docs/guides/adding-a-library.md`
|
||||
- **CI security + supply-chain enforcement** — Renovate for bumps + Action SHA pinning, Socket for supply-chain behavior, weekly trace revalidation, CodeQL + audit signatures + gitleaks. See ADR-023 + `docs/guides/ci-security.md`
|
||||
|
||||
|
||||
Reference in New Issue
Block a user