Files
agentic-dev/docs/superpowers/plans/2026-05-12-conformance-milestone-i.md
Danijel Martinek 31906b9e3e plan(conformance): milestone i — defineFeature + brands + auth.signIn proof
13-task implementation plan for the type-level seam of the conformance system.
Ships brand types (Instrumented/Captured/Audited), defineFeature helper,
ProductionUseCase branded slot, withAudit wrapper, authManifest, and rebinds
auth.signIn through the branded slot — with a negative type test asserting
unwrapped factories are rejected at the slot.

Each task is a single red→green TDD cycle with one commit. Companion
docs/work/conformance-system-v1/ epic + story records are created early in
the plan so subsequent agents have full context when picking up later
stories.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-12 21:18:23 +02:00

1107 lines
37 KiB
Markdown

# 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<F>`, `Captured<F>`, `Audited<F>`), a `defineFeature` manifest helper, and a branded `ProductionUseCase<I, O, M>` 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<F>` only when `mutates: true && audits.length > 0`. For `auth.signIn` (read-only) the slot is `Instrumented<F> & Captured<F>`; 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<F>` + `Captured<F>` 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<I, O, M>` 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<F>`
- `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<F>`
- `packages/core-shared/src/instrumentation/with-span.test.ts` — assert brand on return type
- `packages/core-shared/src/instrumentation/with-capture.ts` — return `Captured<F>`
- `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/<date>-<slug>.prd.md` — source PRDs
- `<epic-slug>/_epic.md` — one folder per epic
- `<epic-slug>/<story-slug>/_story.md` — one folder per story
- `<epic-slug>/<story-slug>/<task-slug>.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<F>` and `Captured<F>` brand types in `@repo/core-shared/conformance`
- Brand attachment in `withSpan` and `withCapture`
- `Audited<F>` brand and `withAudit` wrapper in `@repo/core-audit`
- `defineFeature` helper + `FeatureManifest` / `UseCaseManifest` types
- `ProductionUseCase<I, O, M>` 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<F>` 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<F>` and `Captured<F>`
- [ ] `withSpan` attaches `Instrumented`
- [ ] `withCapture` attaches `Captured`
- [ ] `defineFeature` helper + manifest types
- [ ] `ProductionUseCase<I, O, M>` 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<F>` and `Captured<F>`
**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<F> is structurally F plus a phantom flag", () => {
type Fn = (n: number) => Promise<string>;
expectTypeOf<Instrumented<Fn>>().toBeCallableWith(1);
expectTypeOf<Instrumented<Fn>>().returns.resolves.toEqualTypeOf<string>();
// The flag is readonly and required for assignability checks.
expectTypeOf<Instrumented<Fn>["__instrumented"]>().toEqualTypeOf<true>();
});
it("Captured<F> is structurally F plus a phantom flag", () => {
type Fn = (n: number) => Promise<string>;
expectTypeOf<Captured<Fn>>().toBeCallableWith(1);
expectTypeOf<Captured<Fn>["__captured"]>().toEqualTypeOf<true>();
});
it("brands compose without conflict", () => {
type Fn = (n: number) => Promise<string>;
type Both = Instrumented<Fn> & Captured<Fn>;
expectTypeOf<Both>().toBeCallableWith(1);
expectTypeOf<Both["__instrumented"]>().toEqualTypeOf<true>();
expectTypeOf<Both["__captured"]>().toEqualTypeOf<true>();
});
});
```
- [ ] **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> = F & { readonly __instrumented: true };
export type Captured<F> = 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<F> and Captured<F> 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<F>", () => {
const { tracer } = makeRecordingTracer();
const fn = async (a: number) => a + 1;
const wrapped = withSpan(tracer, { name: "test.brand", op: "use-case" }, fn);
expectTypeOf(wrapped).toMatchTypeOf<Instrumented<typeof fn>>();
});
});
```
- [ ] **Step 2: Run test to verify it fails**
Run: `pnpm --filter @repo/core-shared test with-span`
Expected: FAIL — `wrapped` is `(a: number) => Promise<number>`, not assignable to `Instrumented<typeof fn>`.
- [ ] **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<Args extends unknown[], R>(
tracer: ITracer,
opts: SpanOpts | ((args: Args) => SpanOpts),
fn: (...args: Args) => Promise<R>,
): Instrumented<(...args: Args) => Promise<R>> {
const wrapped: (...args: Args) => Promise<R> = (...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<R>>;
}
```
- [ ] **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<F>"
```
---
## 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<F>", () => {
const logger = makeLogger();
const fn = async (a: number) => a + 1;
const wrapped = withCapture(logger, { layer: "use-case" }, fn);
expectTypeOf(wrapped).toMatchTypeOf<Captured<typeof fn>>();
});
});
```
- [ ] **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<typeof fn>`.
- [ ] **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<Args extends unknown[], R>(
logger: ILogger,
tags: Record<string, string>,
fn: (...args: Args) => Promise<R>,
): Captured<(...args: Args) => Promise<R>> {
const wrapped: (...args: Args) => Promise<R> = 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<R>>;
}
```
- [ ] **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<F>"
```
---
## 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<readonly ["user.created"]>();
expectTypeOf(manifest.useCases.signUp.mutates).toEqualTypeOf<true>();
expectTypeOf(manifest.useCases.signIn.mutates).toEqualTypeOf<false>();
});
it("FeatureManifest type accepts the shape", () => {
const manifest = defineFeature({
name: "blog",
requiredCores: [],
useCases: {},
realtimeChannels: [],
jobs: [],
} as const);
expectTypeOf(manifest).toMatchTypeOf<FeatureManifest>();
});
});
```
- [ ] **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 `<featureName>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<I, O, M>`) consume the
* preserved literals to derive binding-slot brand requirements.
*
* Usage:
*
* export const authManifest = defineFeature({ name: "auth", ... } as const);
*/
export function defineFeature<const M extends FeatureManifest>(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<I, O, M>` 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<I, O, M>", () => {
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<Wrapped>().toMatchTypeOf<Slot>();
});
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<I, O, AuthManifest["useCases"]["signIn"]>`.
*
* 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<F>` from core-audit satisfies it.
*/
export type ProductionUseCase<I, O, M extends UseCaseManifest> =
& Instrumented<(input: I) => Promise<O>>
& Captured<(input: I) => Promise<O>>
& (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<I, O, M> 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<F>", () => {
const auditLog = makeAuditLog();
const fn = async (input: { id: string }) => ({ ok: true });
const wrapped = withAudit(auditLog, fn);
expectTypeOf(wrapped).toMatchTypeOf<Audited<typeof fn>>();
});
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<I, O, M>` when M demands
* it.
*/
export type Audited<F> = 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<Args extends unknown[], R>(
// 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<R>,
): Audited<(...args: Args) => Promise<R>> {
void auditLog;
return fn as Audited<(...args: Args) => Promise<R>>;
}
```
- [ ] **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<F> 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<F>` 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<I, O, M> 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<F>`, `Captured<F>` brands in `@repo/core-shared/conformance`
- `Audited<F>` brand + `withAudit` (skeleton) in `@repo/core-audit`
- `defineFeature` helper + `FeatureManifest` / `UseCaseManifest` types
- `ProductionUseCase<I, O, M>` 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<F>`)