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>
37 KiB
Conformance Milestone i — defineFeature + brands + auth.signIn proof
For agentic workers: REQUIRED SUB-SKILL: Use
superpowers:subagent-driven-development(recommended) orsuperpowers:executing-plansto 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— whatdocs/work/is, how it's useddocs/work/conformance-system-v1/_epic.md— epic recorddocs/work/conformance-system-v1/01-define-feature-helper/_story.md— first story recordpackages/core-shared/src/conformance/brands.ts—Instrumented<F>+Captured<F>phantom typespackages/core-shared/src/conformance/brands.test.ts— type-level brand assertionspackages/core-shared/src/conformance/define-feature.ts—defineFeaturehelper +FeatureManifest/UseCaseManifesttypespackages/core-shared/src/conformance/define-feature.test.ts— round-trip type inferencepackages/core-shared/src/conformance/production-use-case.ts—ProductionUseCase<I, O, M>branded slot typepackages/core-shared/src/conformance/production-use-case.test.ts— assignability tests (positive +@ts-expect-errornegative)packages/core-shared/src/conformance/index.ts— barrel re-exportpackages/core-audit/src/with-audit.ts—withAudituse-case wrapper attachingAudited<F>packages/core-audit/src/with-audit.test.ts— wrapper behavior + brand attachmentpackages/auth/src/feature.manifest.ts—authManifestdeclaringsignIn,signUp,signOut
Modify
packages/core-shared/src/instrumentation/with-span.ts— returnInstrumented<F>packages/core-shared/src/instrumentation/with-span.test.ts— assert brand on return typepackages/core-shared/src/instrumentation/with-capture.ts— returnCaptured<F>packages/core-shared/src/instrumentation/with-capture.test.ts— assert brand on return typepackages/core-shared/src/instrumentation/index.ts— re-export brand types from../conformancepackages/core-audit/src/index.ts— re-exportwithAuditandAuditedpackages/auth/src/di/bind-production.ts— rebindsignInthroughProductionUseCase<...>
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
# 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
---
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
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
---
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
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:
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:
/**
* 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
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:
import { describe, it, expect, vi } from "vitest";
to:
import { describe, it, expect, expectTypeOf, vi } from "vitest";
(b) Add this import line near the other imports at the top of the file:
import type { Instrumented } from "@/conformance/brands";
(c) Append this describe block at the end of the file (after the existing describe("withSpan", …) block):
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.tsto return the branded type
Replace the entire file packages/core-shared/src/instrumentation/with-span.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
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:
import { describe, it, expect, vi } from "vitest";
to:
import { describe, it, expect, expectTypeOf, vi } from "vitest";
(b) Add this import line near the other imports at the top of the file:
import type { Captured } from "@/conformance/brands";
(c) Append this describe block at the end of the file:
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.tsto 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):
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
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:
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:
/**
* 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
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:
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:
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
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:
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
./conformancetopackages/core-shared/package.jsonexports
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"):
"./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:
// 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
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:
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:
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:
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
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:
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:
signUpandsignOutdeclaremutates: truebut emptyauditsto keep this milestone scoped to brand checking. Adding audits is its own task in a future story; theAudited<F>requirement only fires whenmutates && 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
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
signInbinding
Read the existing binding block in packages/auth/src/di/bind-production.ts. The current pattern is:
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:
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:
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
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:
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-errormust 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
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
pnpm typecheck
pnpm test
Expected: PASS, no regressions.
- Step 2: Run lint to confirm no new violations
pnpm lint
Expected: PASS, no errors.
- Step 3: Verify the boundaries graph still validates
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
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/conformanceAudited<F>brand +withAudit(skeleton) in@repo/core-auditdefineFeaturehelper +FeatureManifest/UseCaseManifesttypesProductionUseCase<I, O, M>branded slot typeauthManifestdeclaredauth.signInbound 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:
assertConformanceboot-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 conformanceCI script (event closure, scaffold drift, required-cores) - Generator updates:
turbo gen featureemitsfeature.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/signOutthrough the slot (and add audits, exercisingAudited<F>)