# Conformance Milestone i — defineFeature + brands + auth.signIn proof > **For agentic workers:** REQUIRED SUB-SKILL: Use `superpowers:subagent-driven-development` (recommended) or `superpowers:executing-plans` to implement this plan task-by-task. Steps use checkbox (`- [ ]`) syntax for tracking. **Goal:** Ship the type-level seam of the conformance system — branded wrapper types (`Instrumented`, `Captured`, `Audited`), a `defineFeature` manifest helper, and a branded `ProductionUseCase` binding slot — then prove the seam works end-to-end by rebinding `auth.signIn` through the new slot, and demonstrate a TS error fires when an unwrapped factory is bound. **Architecture:** Phantom-type brands attached at wrap time (no runtime cost). The manifest is a typed `as const` object; the binding-slot type is derived from the manifest's use-case entry, demanding `Audited` only when `mutates: true && audits.length > 0`. For `auth.signIn` (read-only) the slot is `Instrumented & Captured`; the existing `withSpan ∘ withCapture` composition satisfies it once the brands are attached. **Tech Stack:** TypeScript (strict), Vitest, Inversify (existing DI container), `@repo/core-shared/instrumentation` (`withSpan`, `withCapture`), `@repo/core-audit` (audit-log impls). No new dependencies. --- ## File structure ### Create - `docs/work/README.md` — what `docs/work/` is, how it's used - `docs/work/conformance-system-v1/_epic.md` — epic record - `docs/work/conformance-system-v1/01-define-feature-helper/_story.md` — first story record - `packages/core-shared/src/conformance/brands.ts` — `Instrumented` + `Captured` phantom types - `packages/core-shared/src/conformance/brands.test.ts` — type-level brand assertions - `packages/core-shared/src/conformance/define-feature.ts` — `defineFeature` helper + `FeatureManifest` / `UseCaseManifest` types - `packages/core-shared/src/conformance/define-feature.test.ts` — round-trip type inference - `packages/core-shared/src/conformance/production-use-case.ts` — `ProductionUseCase` branded slot type - `packages/core-shared/src/conformance/production-use-case.test.ts` — assignability tests (positive + `@ts-expect-error` negative) - `packages/core-shared/src/conformance/index.ts` — barrel re-export - `packages/core-audit/src/with-audit.ts` — `withAudit` use-case wrapper attaching `Audited` - `packages/core-audit/src/with-audit.test.ts` — wrapper behavior + brand attachment - `packages/auth/src/feature.manifest.ts` — `authManifest` declaring `signIn`, `signUp`, `signOut` ### Modify - `packages/core-shared/src/instrumentation/with-span.ts` — return `Instrumented` - `packages/core-shared/src/instrumentation/with-span.test.ts` — assert brand on return type - `packages/core-shared/src/instrumentation/with-capture.ts` — return `Captured` - `packages/core-shared/src/instrumentation/with-capture.test.ts` — assert brand on return type - `packages/core-shared/src/instrumentation/index.ts` — re-export brand types from `../conformance` - `packages/core-audit/src/index.ts` — re-export `withAudit` and `Audited` - `packages/auth/src/di/bind-production.ts` — rebind `signIn` through `ProductionUseCase<...>` --- ## Task 1: Scaffold `docs/work/` skeleton and epic record This is a documentation-only task. No TDD; no tests. Establishes the address space for everything else in the plan. **Files:** - Create: `docs/work/README.md` - Create: `docs/work/conformance-system-v1/_epic.md` - [ ] **Step 1: Create `docs/work/README.md`** ```markdown # docs/work — the local task system Filesystem-backed Epic/Story/Task hierarchy used by AI agents and humans alike. See `docs/architecture/agent-first-workflow-and-conformance.md` for the full design. Until the `work-system-v1` epic ships orchestration tooling, this folder is human-driven — agents read the files for context, humans flip checkboxes. ## Layout - `prds/-.prd.md` — source PRDs - `/_epic.md` — one folder per epic - `//_story.md` — one folder per story - `//.task.md` — one file per task - `_templates/` — copy-paste templates (added in work-system-v1) - `_state.json` — derived index (added in work-system-v1) ``` - [ ] **Step 2: Create the epic file `docs/work/conformance-system-v1/_epic.md`** ```markdown --- id: conformance-system-v1 prd: null title: Conformance system v1 type: epic status: in-progress features: [cross-cutting] created: 2026-05-12 --- ## Goal Build the feature-conformance enforcement system so AI agents get layered, sub-second feedback on drift between manifest and code. ## Why See `docs/architecture/feature-conformance-explainer.html` and `docs/architecture/agent-first-workflow-and-conformance.md`. ## In scope - defineFeature helper + brand types - assertConformance + boot wiring - AST-aware ESLint rules - CI drift gate - Generator updates for manifest + contracts + test stubs - Documentation rewrite (manifest-first workflow) - Migration of auth feature as the reference ## Out of scope - Migration of blog, media, navigation, marketing-pages (Phase 3) - Sandcastle orchestration (Phase 2: work-system-v1 epic) ## Stories - [ ] [01 — defineFeature helper + Instrumented/Captured/Audited brands](01-define-feature-helper/_story.md) - [ ] 02 — `assertConformance` + boot wiring (later plan) - [ ] 03 — AST-aware ESLint rules (later plan) - [ ] 04 — CI drift gate (later plan) - [ ] 05 — Generator emits manifest + contracts + test stubs (later plan) - [ ] 06 — Documentation rewrite (later plan) - [ ] 07 — Migrate auth feature reference (later plan) ``` - [ ] **Step 3: Commit** ```bash git add docs/work/README.md docs/work/conformance-system-v1/_epic.md git commit -m "docs(work): scaffold docs/work and conformance-system-v1 epic" ``` --- ## Task 2: First story record — `01-define-feature-helper/_story.md` **Files:** - Create: `docs/work/conformance-system-v1/01-define-feature-helper/_story.md` - [ ] **Step 1: Write the story file** ```markdown --- id: 01-define-feature-helper epic: conformance-system-v1 title: defineFeature helper + Instrumented/Captured/Audited brands type: technical-story status: in-progress feature: core-shared depends-on: [] blocks: [02-boot-assertions] --- ## Goal Manifest helper + brand types enable type-level enforcement that every use-case binding is wrapped with `withSpan` + `withCapture` (and `withAudit` when mutating with audits declared). ## Why Compile-time feedback is the cheapest layer and the foundation every other milestone reads. ## Done when Compile-time TS2322 fires when an unwrapped factory is bound through `ProductionUseCase<...>`, and `auth.signIn` is rebound through the new slot. ## In scope - `Instrumented` and `Captured` brand types in `@repo/core-shared/conformance` - Brand attachment in `withSpan` and `withCapture` - `Audited` brand and `withAudit` wrapper in `@repo/core-audit` - `defineFeature` helper + `FeatureManifest` / `UseCaseManifest` types - `ProductionUseCase` branded slot type - `authManifest` declaring `signIn`, `signUp`, `signOut` - `auth.signIn` rebound through the branded slot ## Out of scope - `auth.signUp` / `auth.signOut` rebinding through branded slots (separate tasks within this story or a follow-up story; signUp requires `Audited` once we declare its `audits`) - Boot-time `assertConformance` (story 02) - ESLint rules reading the manifest (story 03) - Generator emitting manifest stubs (story 05) ## Tasks - [ ] Brand types: `Instrumented` and `Captured` - [ ] `withSpan` attaches `Instrumented` - [ ] `withCapture` attaches `Captured` - [ ] `defineFeature` helper + manifest types - [ ] `ProductionUseCase` slot type - [ ] `withAudit` wrapper + `Audited` brand - [ ] `authManifest` declaration - [ ] `auth.signIn` rebound through branded slot - [ ] Negative test: unwrapped factory rejected at type level ``` - [ ] **Step 2: Commit** ```bash git add docs/work/conformance-system-v1/01-define-feature-helper/_story.md git commit -m "docs(work): story 01 — defineFeature helper + brands" ``` --- ## Task 3: Brand types `Instrumented` and `Captured` **Files:** - Create: `packages/core-shared/src/conformance/brands.ts` - Create: `packages/core-shared/src/conformance/brands.test.ts` - [ ] **Step 1: Write the failing test** `packages/core-shared/src/conformance/brands.test.ts`: ```ts import { describe, it, expectTypeOf } from "vitest"; import type { Instrumented, Captured } from "@/conformance/brands"; describe("brand types", () => { it("Instrumented is structurally F plus a phantom flag", () => { type Fn = (n: number) => Promise; expectTypeOf>().toBeCallableWith(1); expectTypeOf>().returns.resolves.toEqualTypeOf(); // The flag is readonly and required for assignability checks. expectTypeOf["__instrumented"]>().toEqualTypeOf(); }); it("Captured is structurally F plus a phantom flag", () => { type Fn = (n: number) => Promise; expectTypeOf>().toBeCallableWith(1); expectTypeOf["__captured"]>().toEqualTypeOf(); }); it("brands compose without conflict", () => { type Fn = (n: number) => Promise; type Both = Instrumented & Captured; expectTypeOf().toBeCallableWith(1); expectTypeOf().toEqualTypeOf(); expectTypeOf().toEqualTypeOf(); }); }); ``` - [ ] **Step 2: Run test to verify it fails** Run: `pnpm --filter @repo/core-shared test conformance/brands` Expected: FAIL — `Cannot find module '@/conformance/brands'` - [ ] **Step 3: Write the brand types** `packages/core-shared/src/conformance/brands.ts`: ```ts /** * Phantom-type brands attached at wrap time by `withSpan`, `withCapture`, * and `withAudit`. Pure type-level — no runtime cost, no proxy, no * `Object.assign`. The conformance system uses these as the type-level * seam the binding signature checks; a use-case factory that hasn't been * wrapped is not assignable to a `ProductionUseCase<...>` slot. */ export type Instrumented = F & { readonly __instrumented: true }; export type Captured = F & { readonly __captured: true }; ``` - [ ] **Step 4: Run test to verify it passes** Run: `pnpm --filter @repo/core-shared test conformance/brands` Expected: PASS, 3 tests. - [ ] **Step 5: Commit** ```bash git add packages/core-shared/src/conformance/brands.ts packages/core-shared/src/conformance/brands.test.ts git commit -m "feat(core-shared/conformance): Instrumented and Captured brand types" ``` --- ## Task 4: `withSpan` attaches `Instrumented` brand **Files:** - Modify: `packages/core-shared/src/instrumentation/with-span.ts` - Modify: `packages/core-shared/src/instrumentation/with-span.test.ts` - [ ] **Step 1: Add the failing brand assertion to `with-span.test.ts`** Make two changes to `packages/core-shared/src/instrumentation/with-span.test.ts`: (a) Extend the existing first import line to include `expectTypeOf`. Change: ```ts import { describe, it, expect, vi } from "vitest"; ``` to: ```ts import { describe, it, expect, expectTypeOf, vi } from "vitest"; ``` (b) Add this import line near the other imports at the top of the file: ```ts import type { Instrumented } from "@/conformance/brands"; ``` (c) Append this `describe` block at the end of the file (after the existing `describe("withSpan", …)` block): ```ts describe("withSpan — brand", () => { it("returns an Instrumented", () => { const { tracer } = makeRecordingTracer(); const fn = async (a: number) => a + 1; const wrapped = withSpan(tracer, { name: "test.brand", op: "use-case" }, fn); expectTypeOf(wrapped).toMatchTypeOf>(); }); }); ``` - [ ] **Step 2: Run test to verify it fails** Run: `pnpm --filter @repo/core-shared test with-span` Expected: FAIL — `wrapped` is `(a: number) => Promise`, not assignable to `Instrumented`. - [ ] **Step 3: Update `with-span.ts` to return the branded type** Replace the entire file `packages/core-shared/src/instrumentation/with-span.ts`: ```ts import type { ITracer, SpanOpts } from "./tracer.interface"; import type { Instrumented } from "../conformance/brands"; export function withSpan( tracer: ITracer, opts: SpanOpts | ((args: Args) => SpanOpts), fn: (...args: Args) => Promise, ): Instrumented<(...args: Args) => Promise> { const wrapped: (...args: Args) => Promise = (...args) => { const resolved = typeof opts === "function" ? opts(args) : opts; return tracer.startSpan(resolved, () => fn(...args)); }; // Cast is the only runtime concession — the brand is a phantom type; // there is no real `__instrumented` property at runtime. return wrapped as Instrumented<(...args: Args) => Promise>; } ``` - [ ] **Step 4: Run test to verify it passes** Run: `pnpm --filter @repo/core-shared test with-span` Expected: PASS, all existing tests + the new brand test. - [ ] **Step 5: Commit** ```bash git add packages/core-shared/src/instrumentation/with-span.ts packages/core-shared/src/instrumentation/with-span.test.ts git commit -m "feat(core-shared/instrumentation): withSpan returns Instrumented" ``` --- ## Task 5: `withCapture` attaches `Captured` brand **Files:** - Modify: `packages/core-shared/src/instrumentation/with-capture.ts` - Modify: `packages/core-shared/src/instrumentation/with-capture.test.ts` - [ ] **Step 1: Add the failing brand assertion to `with-capture.test.ts`** Make two changes to `packages/core-shared/src/instrumentation/with-capture.test.ts`: (a) Extend the existing first import line to include `expectTypeOf`. Change: ```ts import { describe, it, expect, vi } from "vitest"; ``` to: ```ts import { describe, it, expect, expectTypeOf, vi } from "vitest"; ``` (b) Add this import line near the other imports at the top of the file: ```ts import type { Captured } from "@/conformance/brands"; ``` (c) Append this `describe` block at the end of the file: ```ts describe("withCapture — brand", () => { it("returns a Captured", () => { const logger = makeLogger(); const fn = async (a: number) => a + 1; const wrapped = withCapture(logger, { layer: "use-case" }, fn); expectTypeOf(wrapped).toMatchTypeOf>(); }); }); ``` - [ ] **Step 2: Run test to verify it fails** Run: `pnpm --filter @repo/core-shared test with-capture` Expected: FAIL — wrapped is not assignable to `Captured`. - [ ] **Step 3: Update `with-capture.ts` to return the branded type** Replace the function in `packages/core-shared/src/instrumentation/with-capture.ts` (keep the docblock as-is; only the function signature + body change): ```ts import type { ILogger } from "./logger.interface"; import type { Captured } from "../conformance/brands"; import { isReported, markReported } from "./reported-flag"; // (keep the existing docblock here) export function withCapture( logger: ILogger, tags: Record, fn: (...args: Args) => Promise, ): Captured<(...args: Args) => Promise> { const wrapped: (...args: Args) => Promise = async (...args) => { try { return await fn(...args); } catch (err) { if (!isReported(err)) { logger.captureException(err, { tags }); markReported(err); } throw err; } }; return wrapped as Captured<(...args: Args) => Promise>; } ``` - [ ] **Step 4: Run test to verify it passes** Run: `pnpm --filter @repo/core-shared test with-capture` Expected: PASS, all existing tests + the new brand test. - [ ] **Step 5: Commit** ```bash git add packages/core-shared/src/instrumentation/with-capture.ts packages/core-shared/src/instrumentation/with-capture.test.ts git commit -m "feat(core-shared/instrumentation): withCapture returns Captured" ``` --- ## Task 6: `defineFeature` helper + manifest types **Files:** - Create: `packages/core-shared/src/conformance/define-feature.ts` - Create: `packages/core-shared/src/conformance/define-feature.test.ts` - [ ] **Step 1: Write the failing test** `packages/core-shared/src/conformance/define-feature.test.ts`: ```ts import { describe, it, expectTypeOf } from "vitest"; import { defineFeature, type FeatureManifest } from "@/conformance/define-feature"; describe("defineFeature", () => { it("preserves literal types of a manifest declared with `as const`", () => { const manifest = defineFeature({ name: "auth", requiredCores: ["audit"], useCases: { signIn: { mutates: false, audits: [], publishes: [], consumes: [], }, signUp: { mutates: true, audits: ["user.created"], publishes: ["auth.signed-up"], consumes: [], }, }, realtimeChannels: [], jobs: [], } as const); // Literal preservation: name is the literal "auth", not string expectTypeOf(manifest.name).toEqualTypeOf<"auth">(); // Use-case keys preserved expectTypeOf(manifest.useCases.signUp.audits).toEqualTypeOf(); expectTypeOf(manifest.useCases.signUp.mutates).toEqualTypeOf(); expectTypeOf(manifest.useCases.signIn.mutates).toEqualTypeOf(); }); it("FeatureManifest type accepts the shape", () => { const manifest = defineFeature({ name: "blog", requiredCores: [], useCases: {}, realtimeChannels: [], jobs: [], } as const); expectTypeOf(manifest).toMatchTypeOf(); }); }); ``` - [ ] **Step 2: Run test to verify it fails** Run: `pnpm --filter @repo/core-shared test define-feature` Expected: FAIL — module not found. - [ ] **Step 3: Write `define-feature.ts`** `packages/core-shared/src/conformance/define-feature.ts`: ```ts /** * Per-use-case manifest entry. Declares what the use case does at the contract * level: whether it mutates state, what audit events it emits, what cross-feature * events it publishes or consumes. The conformance system reads these to * derive binding-slot types and to verify code against manifest declarations. */ export type UseCaseManifest = { readonly mutates: boolean; readonly audits: readonly string[]; readonly publishes: readonly string[]; readonly consumes: readonly string[]; }; /** * The feature-level manifest. One per feature package, conventionally exported * as `Manifest` from `src/feature.manifest.ts`. */ export type FeatureManifest = { readonly name: string; readonly requiredCores: readonly string[]; readonly useCases: { readonly [k: string]: UseCaseManifest }; readonly realtimeChannels: readonly string[]; readonly jobs: readonly string[]; }; /** * Identity helper that exists purely to widen the input type to satisfy * `FeatureManifest` while preserving the literal types of the `as const` * input. Downstream types (`ProductionUseCase`) consume the * preserved literals to derive binding-slot brand requirements. * * Usage: * * export const authManifest = defineFeature({ name: "auth", ... } as const); */ export function defineFeature(manifest: M): M { return manifest; } ``` - [ ] **Step 4: Run test to verify it passes** Run: `pnpm --filter @repo/core-shared test define-feature` Expected: PASS, 2 tests. - [ ] **Step 5: Commit** ```bash git add packages/core-shared/src/conformance/define-feature.ts packages/core-shared/src/conformance/define-feature.test.ts git commit -m "feat(core-shared/conformance): defineFeature helper + manifest types" ``` --- ## Task 7: `ProductionUseCase` branded slot type **Files:** - Create: `packages/core-shared/src/conformance/production-use-case.ts` - Create: `packages/core-shared/src/conformance/production-use-case.test.ts` - [ ] **Step 1: Write the failing test** `packages/core-shared/src/conformance/production-use-case.test.ts`: ```ts import { describe, it, expectTypeOf } from "vitest"; import type { ProductionUseCase } from "@/conformance/production-use-case"; import type { Instrumented, Captured } from "@/conformance/brands"; describe("ProductionUseCase", () => { it("requires Instrumented + Captured for any use case", () => { type Manifest = { mutates: false; audits: readonly []; publishes: readonly []; consumes: readonly []; }; type Slot = ProductionUseCase<{ x: number }, { y: string }, Manifest>; type Wrapped = Instrumented<(input: { x: number }) => Promise<{ y: string }>> & Captured<(input: { x: number }) => Promise<{ y: string }>>; expectTypeOf().toMatchTypeOf(); }); it("a plain factory is NOT assignable to the slot", () => { type Manifest = { mutates: false; audits: readonly []; publishes: readonly []; consumes: readonly []; }; type Slot = ProductionUseCase<{ x: number }, { y: string }, Manifest>; const factory = async (input: { x: number }) => ({ y: String(input.x) }); // @ts-expect-error — factory has no __instrumented / __captured brand const bad: Slot = factory; void bad; }); }); ``` - [ ] **Step 2: Run test to verify it fails** Run: `pnpm --filter @repo/core-shared test production-use-case` Expected: FAIL — module not found. - [ ] **Step 3: Write `production-use-case.ts`** `packages/core-shared/src/conformance/production-use-case.ts`: ```ts import type { UseCaseManifest } from "./define-feature"; import type { Instrumented, Captured } from "./brands"; /** * Type-level binding slot for production use cases. Derived from the manifest * entry: every binding must be Instrumented + Captured; mutating use cases * that declare audits additionally must be Audited. The Audited brand lives * in `@repo/core-audit` because the wrap helper that attaches it depends on * `IAuditLog` — feature packages import the merged slot type implicitly * by typing their bindings as `ProductionUseCase`. * * The Audited requirement is encoded conditionally so this type stays usable * without depending on core-audit. When `mutates: true` AND `audits` is * non-empty, the slot demands a marker type with a `__audited` flag; the * concrete `Audited` from core-audit satisfies it. */ export type ProductionUseCase = & Instrumented<(input: I) => Promise> & Captured<(input: I) => Promise> & (M["mutates"] extends true ? M["audits"]["length"] extends 0 ? unknown : { readonly __audited: true } : unknown); ``` - [ ] **Step 4: Run test to verify it passes** Run: `pnpm --filter @repo/core-shared test production-use-case` Expected: PASS, 2 tests. - [ ] **Step 5: Commit** ```bash git add packages/core-shared/src/conformance/production-use-case.ts packages/core-shared/src/conformance/production-use-case.test.ts git commit -m "feat(core-shared/conformance): ProductionUseCase branded slot type" ``` --- ## Task 8: Conformance barrel + package subpath export **Files:** - Create: `packages/core-shared/src/conformance/index.ts` - Modify: `packages/core-shared/src/instrumentation/index.ts` - Modify: `packages/core-shared/package.json` - [ ] **Step 1: Create the conformance barrel** `packages/core-shared/src/conformance/index.ts`: ```ts export type { Instrumented, Captured } from "./brands"; export type { FeatureManifest, UseCaseManifest } from "./define-feature"; export { defineFeature } from "./define-feature"; export type { ProductionUseCase } from "./production-use-case"; ``` - [ ] **Step 2: Add `./conformance` to `packages/core-shared/package.json` exports** Open `packages/core-shared/package.json` and find the `"exports"` object. Add a new line for `./conformance` next to the existing subpath exports (e.g. immediately after `"./audit"`): ```json "./conformance": "./src/conformance/index.ts", ``` The resulting `exports` map should include `"./conformance"` alongside the existing `"."`, `"./audit"`, `"./di"`, `"./instrumentation"`, etc. - [ ] **Step 3: Re-export brand types from instrumentation/index for ergonomics** Modify `packages/core-shared/src/instrumentation/index.ts` — append at the bottom: ```ts // Re-export brand types alongside the wrappers that attach them, so callers // can `import { withSpan, type Instrumented } from "@repo/core-shared/instrumentation"`. export type { Instrumented, Captured } from "../conformance/brands"; ``` - [ ] **Step 4: Verify the package builds** Run: `pnpm --filter @repo/core-shared typecheck` Expected: PASS, no errors. - [ ] **Step 5: Commit** ```bash git add packages/core-shared/src/conformance/index.ts packages/core-shared/src/instrumentation/index.ts packages/core-shared/package.json git commit -m "feat(core-shared/conformance): barrel + ./conformance subpath export" ``` --- ## Task 9: `withAudit` wrapper + `Audited` brand in `@repo/core-audit` **Files:** - Create: `packages/core-audit/src/with-audit.ts` - Create: `packages/core-audit/src/with-audit.test.ts` - Modify: `packages/core-audit/src/index.ts` - [ ] **Step 1: Write the failing test** `packages/core-audit/src/with-audit.test.ts`: ```ts import { describe, it, expect, expectTypeOf, vi } from "vitest"; import { withAudit, type Audited } from "@/with-audit"; import type { IAuditLog } from "@/audit-log.interface"; function makeAuditLog(): IAuditLog { return { record: vi.fn().mockResolvedValue(undefined), eraseSubject: vi.fn().mockResolvedValue(undefined), }; } describe("withAudit", () => { it("returns an Audited", () => { const auditLog = makeAuditLog(); const fn = async (input: { id: string }) => ({ ok: true }); const wrapped = withAudit(auditLog, fn); expectTypeOf(wrapped).toMatchTypeOf>(); }); it("passes input and output through unchanged", async () => { const auditLog = makeAuditLog(); const fn = async (input: { id: string }) => ({ ok: true, id: input.id }); const wrapped = withAudit(auditLog, fn); const result = await wrapped({ id: "abc" }); expect(result).toEqual({ ok: true, id: "abc" }); }); it("propagates errors", async () => { const auditLog = makeAuditLog(); const err = new Error("boom"); const wrapped = withAudit(auditLog, async () => { throw err; }); await expect(wrapped({})).rejects.toBe(err); }); }); ``` - [ ] **Step 2: Run test to verify it fails** Run: `pnpm --filter @repo/core-audit test with-audit` Expected: FAIL — module not found. - [ ] **Step 3: Write `with-audit.ts`** `packages/core-audit/src/with-audit.ts`: ```ts import type { IAuditLog } from "./audit-log.interface"; /** * Phantom-type brand attached at wrap time by `withAudit`. The conformance * system uses this as the type-level seam for mutating use cases that * declare `audits: [...]` in their manifest — without `__audited`, the * binding is not assignable to `ProductionUseCase` when M demands * it. */ export type Audited = F & { readonly __audited: true }; /** * Use-case wrapper applied at DI bind time. In milestone i this is a * brand-only attachment: it does not yet automatically call `auditLog.record`. * Use cases continue to call `auditLog.record(...)` in their own bodies; the * wrapper exists to make "binding was bound through the audit-aware path" * type-checkable at compile time. * * A future story may move auditing logic out of factory bodies and into the * wrapper itself (driven by manifest declarations) — but that requires the * manifest's `audits[]` entries to fully specify what gets recorded, which * is out of scope here. */ export function withAudit( // The auditLog is part of the signature for two reasons: (1) callers must // pass it at bind time, ensuring the dep is available, and (2) future // versions of this wrapper will use it to emit audit events from the // declarative manifest entry directly. auditLog: IAuditLog, fn: (...args: Args) => Promise, ): Audited<(...args: Args) => Promise> { void auditLog; return fn as Audited<(...args: Args) => Promise>; } ``` - [ ] **Step 4: Run test to verify it passes** Run: `pnpm --filter @repo/core-audit test with-audit` Expected: PASS, 3 tests. - [ ] **Step 5: Re-export from core-audit barrel** Append to `packages/core-audit/src/index.ts`: ```ts export { withAudit, type Audited } from "./with-audit"; ``` - [ ] **Step 6: Verify the package builds** Run: `pnpm --filter @repo/core-audit typecheck` Expected: PASS. - [ ] **Step 7: Commit** ```bash git add packages/core-audit/src/with-audit.ts packages/core-audit/src/with-audit.test.ts packages/core-audit/src/index.ts git commit -m "feat(core-audit): withAudit wrapper + Audited brand" ``` --- ## Task 10: `authManifest` declaration **Files:** - Create: `packages/auth/src/feature.manifest.ts` - [ ] **Step 1: Write the manifest** `packages/auth/src/feature.manifest.ts`: ```ts import { defineFeature } from "@repo/core-shared/conformance"; /** * The auth feature's conformance manifest. Drives binding-slot types in * `di/bind-production.ts` and is read by ESLint, the boot assertion, and * the CI drift gate (later milestones). * * Conventions: * - `mutates: true` for any use case that creates, updates, or deletes state * - `audits` lists every audit event the use case emits (must match calls * to `auditLog.record(...)` in the factory body — ESLint enforces this * in a later story) * - `publishes` / `consumes` cover cross-feature events through `IEventBus` */ export const authManifest = defineFeature({ name: "auth", requiredCores: [], useCases: { signIn: { mutates: false, audits: [], publishes: [], consumes: [], }, signUp: { mutates: true, audits: [], publishes: [], consumes: [], }, signOut: { mutates: true, audits: [], publishes: [], consumes: [], }, }, realtimeChannels: [], jobs: [], } as const); export type AuthManifest = typeof authManifest; ``` > Note: `signUp` and `signOut` declare `mutates: true` but empty `audits` to keep this milestone scoped to brand checking. Adding audits is its own task in a future story; the `Audited` requirement only fires when `mutates && audits.length > 0`. - [ ] **Step 2: Verify the manifest typechecks** Run: `pnpm --filter @repo/auth typecheck` Expected: PASS — manifest infers correctly and `AuthManifest` derives without error. - [ ] **Step 3: Commit** ```bash git add packages/auth/src/feature.manifest.ts git commit -m "feat(auth): declare authManifest with signIn/signUp/signOut" ``` --- ## Task 11: Rebind `auth.signIn` through `ProductionUseCase<...>` **Files:** - Modify: `packages/auth/src/di/bind-production.ts` - [ ] **Step 1: Locate the existing `signIn` binding** Read the existing binding block in `packages/auth/src/di/bind-production.ts`. The current pattern is: ```ts const wrappedSignIn = withSpan( tracer, { name: "auth.signIn", op: "use-case" }, withCapture( logger, { feature: "auth", layer: "use-case", name: "auth.signIn" }, signInUseCase(repo, authService), ), ); ``` There is no explicit slot type today. - [ ] **Step 2: Add the typed binding for `signIn`** Replace the `wrappedSignIn` assignment with an explicitly typed slot: ```ts import type { ProductionUseCase } from "@repo/core-shared/conformance"; import { authManifest, type AuthManifest } from "../feature.manifest"; import type { SignInInput, SignInOutput } from "../application/use-cases/sign-in.use-case"; // ... existing imports ... // Inside bindProductionAuth, replacing the existing wrappedSignIn assignment: const wrappedSignIn: ProductionUseCase< SignInInput, SignInOutput, AuthManifest["useCases"]["signIn"] > = withSpan( tracer, { name: "auth.signIn", op: "use-case" }, withCapture( logger, { feature: "auth", layer: "use-case", name: "auth.signIn" }, signInUseCase(repo, authService), ), ); ``` Place the new imports next to the existing imports at the top of the file. Keep the `void authManifest` no-op or use it (see Step 3) so TypeScript doesn't warn about the unused import — but since `AuthManifest` is referenced in the slot type, only `authManifest` may be unused. If unused, drop the value import and keep only the type: ```ts import type { AuthManifest } from "../feature.manifest"; ``` - [ ] **Step 3: Run typecheck and the auth test suite** Run: `pnpm --filter @repo/auth typecheck` Expected: PASS. Run: `pnpm --filter @repo/auth test` Expected: PASS, no test regressions. - [ ] **Step 4: Commit** ```bash git add packages/auth/src/di/bind-production.ts git commit -m "feat(auth): bind signIn through ProductionUseCase branded slot" ``` --- ## Task 12: Negative test — unwrapped factory rejected at the type level **Files:** - Create: `packages/auth/src/di/bind-production.types.test.ts` This is a type-only test file that asserts the *failure* of an unwrapped factory to satisfy the branded slot. The `@ts-expect-error` annotation makes the file pass `tsc` only when the suppressed line actually has an error. - [ ] **Step 1: Write the type test** `packages/auth/src/di/bind-production.types.test.ts`: ```ts import { describe, it } from "vitest"; import type { ProductionUseCase } from "@repo/core-shared/conformance"; import type { AuthManifest } from "../feature.manifest"; import type { SignInInput, SignInOutput, } from "../application/use-cases/sign-in.use-case"; import { signInUseCase } from "../application/use-cases/sign-in.use-case"; describe("auth.signIn binding slot (type-level)", () => { it("rejects an unwrapped factory", () => { type Slot = ProductionUseCase< SignInInput, SignInOutput, AuthManifest["useCases"]["signIn"] >; // Build the unwrapped factory exactly as it would be at the use-case file. // It returns a function with no brand attached — must not be assignable. const fakeRepo = {} as never; const fakeAuth = {} as never; const unwrapped = signInUseCase(fakeRepo, fakeAuth); // @ts-expect-error — unwrapped factory has no __instrumented / __captured brand const _bad: Slot = unwrapped; void _bad; }); }); ``` - [ ] **Step 2: Run typecheck — the `@ts-expect-error` must consume a real error** Run: `pnpm --filter @repo/auth typecheck` Expected: PASS. If the line under `@ts-expect-error` does NOT error, tsc will report "Unused '@ts-expect-error' directive." That signals a hole in the brand check. - [ ] **Step 3: Run the test (no runtime assertions; it just needs to load)** Run: `pnpm --filter @repo/auth test bind-production.types` Expected: PASS — the test body has no runtime assertions, so the test passes as long as the module loads. - [ ] **Step 4: Commit** ```bash git add packages/auth/src/di/bind-production.types.test.ts git commit -m "test(auth): assert unwrapped factory rejected at branded slot" ``` --- ## Task 13: Final verification + story checkbox **Files:** - Modify: `docs/work/conformance-system-v1/01-define-feature-helper/_story.md` - Modify: `docs/work/conformance-system-v1/_epic.md` - [ ] **Step 1: Run the full type-check and test suite** ```bash pnpm typecheck pnpm test ``` Expected: PASS, no regressions. - [ ] **Step 2: Run lint to confirm no new violations** ```bash pnpm lint ``` Expected: PASS, no errors. - [ ] **Step 3: Verify the boundaries graph still validates** ```bash pnpm turbo boundaries ``` Expected: PASS. - [ ] **Step 4: Tick the story's task checkboxes and flip the story status** Edit `docs/work/conformance-system-v1/01-define-feature-helper/_story.md`: - Change frontmatter `status: in-progress` → `status: done` - Change all `- [ ]` task checkboxes in the Tasks section to `- [x]` - [ ] **Step 5: Tick the story in the epic** Edit `docs/work/conformance-system-v1/_epic.md`: - Change `- [ ] [01 — defineFeature helper + Instrumented/Captured/Audited brands](01-define-feature-helper/_story.md)` to `- [x] [01 — defineFeature helper + Instrumented/Captured/Audited brands](01-define-feature-helper/_story.md)` - [ ] **Step 6: Commit the milestone close** ```bash git add docs/work/conformance-system-v1/01-define-feature-helper/_story.md docs/work/conformance-system-v1/_epic.md git commit -m "docs(work): close story 01 — defineFeature + brands + auth.signIn proof" ``` --- ## Done — what this leaves behind - `Instrumented`, `Captured` brands in `@repo/core-shared/conformance` - `Audited` brand + `withAudit` (skeleton) in `@repo/core-audit` - `defineFeature` helper + `FeatureManifest` / `UseCaseManifest` types - `ProductionUseCase` branded slot type - `authManifest` declared - `auth.signIn` bound through the typed slot - Negative test confirms an unwrapped factory does NOT type-check at the slot - All existing tests pass; no behavioral changes; pure type-system seam added ## What comes next (separate plans) - Milestone ii: `assertConformance` boot-time runtime check + wire into all three apps' `bindAll` - Milestone iii: AST-aware ESLint rules (`manifest-usecase-signature-matches`, `no-undeclared-event-publish`, `no-undeclared-audit`, `required-cores-installed`) - Milestone iv: `pnpm conformance` CI script (event closure, scaffold drift, required-cores) - Generator updates: `turbo gen feature` emits `feature.manifest.ts` + contracts + test stubs - Documentation rewrite: `CLAUDE.md`, `AGENTS.md`, `docs/guides/tdd-workflow.md`, new agent-workflow guide - Auth migration polish: bind `signUp` / `signOut` through the slot (and add audits, exercising `Audited`)