# Conformance Milestone ii — `assertFeatureConformance` + boot wiring > **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 boot-time runtime check of the conformance system. `withSpan` / `withCapture` / `withAudit` attach non-enumerable runtime markers; `assertFeatureConformance(container, manifest, symbols, ctx)` resolves each manifest-declared use case from the feature's container and verifies the bound function carries the required markers. Each feature's `bindProductionX(ctx)` self-asserts at the tail; `pnpm dev` refuses to boot on drift. **Architecture:** The phantom-type seam shipped in milestone i becomes a *reified* type seam at runtime. Each wrapper attaches its brand as a non-enumerable, non-configurable property (`Object.defineProperty(wrapped, "__instrumented", { value: true, enumerable: false, writable: false, configurable: false })`). Predicate helpers (`isInstrumented`, `isCaptured`, `isAudited`) read those markers. `assertFeatureConformance` walks the manifest's `useCases`, resolves each binding by the symbol map the binder declares inline, and throws `ConformanceError` synchronously when a binding is missing a required brand. Per-feature self-assertion (called at the end of `bindProductionAuth(ctx)`) is forward-compatible: when `cms` and `web-tanstack` eventually wire `bindProductionAuth`, they inherit the boot check for free. **Tech Stack:** TypeScript (strict), Vitest, Inversify (existing DI). Object.defineProperty for non-enumerable runtime markers. No new dependencies. --- ## File structure ### Create - `packages/core-shared/src/conformance/brand-runtime.ts` — runtime marker attachment + predicates - `packages/core-shared/src/conformance/brand-runtime.test.ts` — attachBrand + predicate tests - `packages/core-shared/src/conformance/conformance-error.ts` — `ConformanceError` class - `packages/core-shared/src/conformance/assert-bindings.ts` — `assertFeatureConformance` - `packages/core-shared/src/conformance/assert-bindings.test.ts` — positive + negative assertion tests - `docs/work/conformance-system-v1/02-boot-assertions/_story.md` — story 02 record ### Modify - `packages/core-shared/src/instrumentation/with-span.ts` — call `attachBrand(wrapped, "__instrumented")` - `packages/core-shared/src/instrumentation/with-capture.ts` — call `attachBrand(wrapped, "__captured")` - `packages/core-shared/src/instrumentation/with-span.test.ts` — add runtime brand assertion - `packages/core-shared/src/instrumentation/with-capture.test.ts` — add runtime brand assertion - `packages/core-audit/src/with-audit.ts` — wrap (not passthrough) + `attachBrand(wrapped, "__audited")` + TODO breadcrumb - `packages/core-audit/src/with-audit.test.ts` — add runtime brand assertion + adjust passthrough expectations - `packages/core-shared/src/conformance/index.ts` — re-export brand-runtime helpers + `ConformanceError` + `assertFeatureConformance` - `packages/auth/src/index.ts` — re-export `authManifest` + `AuthManifest` at the auth package root - `packages/auth/src/di/bind-production.ts` — call `assertFeatureConformance` at the tail - `docs/work/conformance-system-v1/_epic.md` — flip story 02 status and checkbox at closeout ### No file changes (verified at closeout) - `packages/blog/`, `packages/media/`, `packages/navigation/`, `packages/marketing-pages/` — no manifests yet; their bind-production stays unchanged - `apps/cms/`, `apps/web-tanstack/` — no server bootstrap yet; nothing to wire --- ## Task 1: Re-export `authManifest` from auth root barrel Surfaces the manifest at the package boundary so future stories (and the boot-assertion's caller) can read it without a deep import. **Files:** - Modify: `packages/auth/src/index.ts` - [ ] **Step 1: Append the re-export** Add these lines at the bottom of `packages/auth/src/index.ts`: ```ts // Feature conformance manifest (added in conformance milestone i, exposed // here in milestone ii so the boot-time assertion and future tooling can // read the contract from the package boundary). export { authManifest, type AuthManifest } from "./feature.manifest"; ``` - [ ] **Step 2: Verify typecheck passes** Run: `pnpm --filter @repo/auth typecheck` Expected: PASS. - [ ] **Step 3: Commit** ```bash git add packages/auth/src/index.ts git commit -m "feat(auth): re-export authManifest + AuthManifest from package root" ``` --- ## Task 2: Add TODO breadcrumb in `withAudit` The `withAudit` wrapper currently does nothing but cast (with a `void auditLog;` to silence unused). Leave a clear forward pointer for the future story that wires automated audit recording. **Files:** - Modify: `packages/core-audit/src/with-audit.ts` - [ ] **Step 1: Replace the function body's existing comment block** In `packages/core-audit/src/with-audit.ts`, the body currently looks like: ```ts 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>; } ``` Adjust the inline comment to point at the future story that wires automated recording. Replace ONLY the inline comment on `auditLog` (do not touch the function body yet — Task 7 changes the body): ```ts export function withAudit( // TODO(conformance milestone iii+): wire automated recording from manifest // `audits[]` declarations. For now, the wrapper exists to: // (1) require callers to pass the auditLog at bind time (dep is available) // (2) attach the `__audited` brand so the boot-time assertion can verify // mutating use cases were bound through the audit-aware path. auditLog: IAuditLog, fn: (...args: Args) => Promise, ): Audited<(...args: Args) => Promise> { void auditLog; return fn as Audited<(...args: Args) => Promise>; } ``` The function body is unchanged in this task; we only enriched the comment. - [ ] **Step 2: Verify typecheck and tests still pass** ``` pnpm --filter @repo/core-audit typecheck pnpm --filter @repo/core-audit test with-audit ``` Both PASS, no regressions. - [ ] **Step 3: Commit** ```bash git add packages/core-audit/src/with-audit.ts git commit -m "docs(core-audit): TODO breadcrumb for future audit recording automation" ``` --- ## Task 3: Scaffold docs/work record for story 02 **Files:** - Create: `docs/work/conformance-system-v1/02-boot-assertions/_story.md` - [ ] **Step 1: Write the story file** Create `docs/work/conformance-system-v1/02-boot-assertions/_story.md` with EXACTLY this content: ```markdown --- id: 02-boot-assertions epic: conformance-system-v1 title: assertFeatureConformance + boot wiring type: technical-story status: in-progress feature: core-shared depends-on: [01-define-feature-helper] blocks: [03-eslint-rules] --- ## Goal Runtime boot-time verification that every manifest-declared use case is bound through the brand-attaching wrappers. Each feature's `bindProductionX(ctx)` self-asserts at the tail; `pnpm dev` refuses to boot on drift. ## Why Type casts can mask unwrapped factories; manifest edits can drift from binders without TypeScript noticing. Boot assertions catch what the type system can't see — at zero cost during the inner agent feedback loop, and synchronously at startup so failures fire loudly. ## Done when - `withSpan`, `withCapture`, `withAudit` attach non-enumerable runtime markers matching the type-level brand names - `assertFeatureConformance(container, manifest, symbols, ctx)` resolves each manifest use case and throws `ConformanceError` on a missing brand - `auth.bindProductionAuth(ctx)` self-asserts at the tail - `pnpm dev` boots cleanly for the existing `auth` wiring; rebinding `signIn` with an unwrapped factory causes `pnpm dev` to throw at startup ## In scope - Runtime marker attachment via `Object.defineProperty(fn, "__brand", { … })` (non-enumerable, non-writable, non-configurable) - `isInstrumented` / `isCaptured` / `isAudited` predicates - `ConformanceError` class (extends `Error`) - `assertFeatureConformance(container, manifest, symbols, ctx)` helper - Wiring into `packages/auth/src/di/bind-production.ts` (tail-of-binder self-assertion) - `withAudit` upgraded from passthrough to a thin wrapper that attaches its runtime brand without changing observable behaviour ## Out of scope - `assertConformance` over a multi-feature container collection at the app's `bindAll()` (current per-feature self-assertion is sufficient and forward-compatible) - Wiring boot assertions into `cms` and `web-tanstack` — neither has a `bind-production.ts` yet; they'll inherit the check whenever they grow one - Manifests for `blog`, `media`, `navigation`, `marketing-pages` (their `bindProductionX` stays unchanged in this story) - Automated audit recording driven by manifest `audits[]` declarations (deferred to a later story) ## Tasks - [ ] Re-export `authManifest` from auth root barrel - [ ] TODO breadcrumb in `withAudit` pointing at future automation - [ ] Runtime marker helpers (`attachBrand`, `isInstrumented`, `isCaptured`, `isAudited`) - [ ] `withSpan` attaches runtime `__instrumented` marker - [ ] `withCapture` attaches runtime `__captured` marker - [ ] `withAudit` wraps + attaches runtime `__audited` marker - [ ] `ConformanceError` class - [ ] `assertFeatureConformance` helper + tests - [ ] Conformance barrel + subpath exports updated - [ ] `bindProductionAuth` self-asserts at the tail - [ ] Final verification + story closeout ``` - [ ] **Step 2: Commit** ```bash git add docs/work/conformance-system-v1/02-boot-assertions/_story.md git commit -m "docs(work): story 02 — assertFeatureConformance + boot wiring" ``` --- ## Task 4: Runtime marker helpers — `attachBrand` + predicates The runtime marker module is the heart of milestone ii: a small set of helpers that attach non-enumerable brand flags and read them back. The phantom-type brand names (`__instrumented`, `__captured`, `__audited`) become real (non-enumerable) properties at runtime. **Files:** - Create: `packages/core-shared/src/conformance/brand-runtime.ts` - Create: `packages/core-shared/src/conformance/brand-runtime.test.ts` - [ ] **Step 1: Write the failing test** Create `packages/core-shared/src/conformance/brand-runtime.test.ts` with EXACTLY this content: ```ts import { describe, it, expect } from "vitest"; import { attachBrand, isInstrumented, isCaptured, isAudited, } from "@/conformance/brand-runtime"; describe("brand-runtime", () => { it("attachBrand adds a non-enumerable property and returns the same reference", () => { const fn = () => {}; const result = attachBrand(fn, "__instrumented"); expect(result).toBe(fn); expect(isInstrumented(fn)).toBe(true); // Non-enumerable: must not show up in Object.keys expect(Object.keys(fn)).not.toContain("__instrumented"); }); it("predicates return false for unwrapped functions", () => { const fn = () => {}; expect(isInstrumented(fn)).toBe(false); expect(isCaptured(fn)).toBe(false); expect(isAudited(fn)).toBe(false); }); it("predicates discriminate between brands", () => { const instrumented = () => {}; attachBrand(instrumented, "__instrumented"); expect(isInstrumented(instrumented)).toBe(true); expect(isCaptured(instrumented)).toBe(false); expect(isAudited(instrumented)).toBe(false); const captured = () => {}; attachBrand(captured, "__captured"); expect(isInstrumented(captured)).toBe(false); expect(isCaptured(captured)).toBe(true); expect(isAudited(captured)).toBe(false); const audited = () => {}; attachBrand(audited, "__audited"); expect(isAudited(audited)).toBe(true); }); it("composing brands stacks them on the same function", () => { const fn = () => {}; attachBrand(fn, "__instrumented"); attachBrand(fn, "__captured"); attachBrand(fn, "__audited"); expect(isInstrumented(fn)).toBe(true); expect(isCaptured(fn)).toBe(true); expect(isAudited(fn)).toBe(true); }); it("attached brands are non-writable and non-configurable", () => { const fn = () => {}; attachBrand(fn, "__instrumented"); const desc = Object.getOwnPropertyDescriptor(fn, "__instrumented"); expect(desc?.writable).toBe(false); expect(desc?.configurable).toBe(false); expect(desc?.enumerable).toBe(false); expect(desc?.value).toBe(true); }); it("predicates return false for non-function inputs", () => { expect(isInstrumented(null)).toBe(false); expect(isInstrumented(undefined)).toBe(false); expect(isInstrumented(42)).toBe(false); expect(isInstrumented("string")).toBe(false); expect(isInstrumented({})).toBe(false); }); }); ``` - [ ] **Step 2: Run test to verify it fails** ``` pnpm --filter @repo/core-shared test conformance/brand-runtime ``` Expected: FAIL — module not found. - [ ] **Step 3: Write `brand-runtime.ts`** Create `packages/core-shared/src/conformance/brand-runtime.ts` with EXACTLY this content: ```ts /** * Runtime brand attachment + predicates. The companion to the phantom-type * brands defined in `./brands.ts`: at compile time the brand is a structural * intersection; at runtime it is a non-enumerable, non-writable, * non-configurable property with the same name. * * Why non-enumerable: a wrapped function should not leak the marker through * `Object.keys`, `JSON.stringify`, `for…in`, or spread. The marker only * shows up to explicit lookups via `Reflect.has` or direct property access. * * Why non-writable + non-configurable: the marker is meant to be permanent * once attached. The wrapper is the only caller; the marker is a one-shot * commitment, not a mutable flag. */ type Brand = "__instrumented" | "__captured" | "__audited"; /** * Attaches the brand as a non-enumerable property on the given function. * Returns the same reference (no allocation). Idempotent: calling twice with * the same brand throws because the property is non-configurable — wrappers * never double-wrap, so this is the correct safety net. */ export function attachBrand(fn: F, brand: Brand): F { Object.defineProperty(fn, brand, { value: true, enumerable: false, writable: false, configurable: false, }); return fn; } function hasBrand(fn: unknown, brand: Brand): boolean { if (typeof fn !== "function" && (typeof fn !== "object" || fn === null)) { return false; } return (fn as Record)[brand] === true; } export function isInstrumented(fn: unknown): boolean { return hasBrand(fn, "__instrumented"); } export function isCaptured(fn: unknown): boolean { return hasBrand(fn, "__captured"); } export function isAudited(fn: unknown): boolean { return hasBrand(fn, "__audited"); } ``` - [ ] **Step 4: Run tests to verify they pass** ``` pnpm --filter @repo/core-shared test conformance/brand-runtime ``` Expected: PASS, 6 tests. - [ ] **Step 5: Re-export from the conformance barrel** Append to `packages/core-shared/src/conformance/index.ts` (preserving existing exports): ```ts export { attachBrand, isInstrumented, isCaptured, isAudited, } from "./brand-runtime"; ``` Re-run typecheck to confirm the barrel resolves: ``` pnpm --filter @repo/core-shared typecheck ``` PASS. - [ ] **Step 6: Commit** ```bash git add packages/core-shared/src/conformance/brand-runtime.ts packages/core-shared/src/conformance/brand-runtime.test.ts packages/core-shared/src/conformance/index.ts git commit -m "feat(core-shared/conformance): runtime brand markers + isX predicates" ``` --- ## Task 5: `withSpan` attaches runtime `__instrumented` marker **Files:** - Modify: `packages/core-shared/src/instrumentation/with-span.ts` - Modify: `packages/core-shared/src/instrumentation/with-span.test.ts` - [ ] **Step 1: Add a failing runtime brand assertion to `with-span.test.ts`** Append this new describe block at the END of `packages/core-shared/src/instrumentation/with-span.test.ts` (after the existing `describe("withSpan — brand", …)` block): ```ts import { isInstrumented } from "@/conformance/brand-runtime"; describe("withSpan — runtime brand", () => { it("attaches __instrumented as a non-enumerable property on the wrapped function", async () => { const { tracer } = makeRecordingTracer(); const fn = async (a: number) => a + 1; const wrapped = withSpan(tracer, { name: "test.brand", op: "use-case" }, fn); expect(isInstrumented(wrapped)).toBe(true); expect(Object.keys(wrapped)).not.toContain("__instrumented"); }); }); ``` Note: place the `import { isInstrumented }` line near the top of the file alongside the other imports — the location of the import was shown inline above for context but it belongs in the imports block. - [ ] **Step 2: Run test to verify it fails** ``` pnpm --filter @repo/core-shared test with-span ``` Expected: at least one new test FAILS — `wrapped` does not yet have `__instrumented` attached. - [ ] **Step 3: Update `with-span.ts` to attach the runtime marker** Update `packages/core-shared/src/instrumentation/with-span.ts`. The current file (after milestone i Task 11's brand-preserving overload) needs an `attachBrand` call before the return. Replace the body of the implementation function so it looks like this — note the new import and the `attachBrand(wrapped, "__instrumented")` line: ```ts import type { ITracer, SpanOpts } from "./tracer.interface"; import type { Instrumented } from "../conformance/brands"; import { attachBrand } from "../conformance/brand-runtime"; export function withSpan( tracer: ITracer, opts: SpanOpts | ((args: Args) => SpanOpts), fn: ((...args: Args) => Promise) & Extra, ): Instrumented<((...args: Args) => Promise) & Extra>; export function withSpan( tracer: ITracer, opts: SpanOpts | ((args: Args) => SpanOpts), fn: (...args: Args) => Promise, ): Instrumented<(...args: Args) => Promise>; 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)); }; attachBrand(wrapped, "__instrumented"); // Cast is the type-level concession — the brand is now also a non-enumerable // runtime property attached above by `attachBrand`. return wrapped as Instrumented<(...args: Args) => Promise>; } ``` Important: keep BOTH overload signatures (the brand-preserving one added in milestone i Task 11 plus the original). The implementation signature is the third one shown; only the body changes (adds `attachBrand` call, updated comment). - [ ] **Step 4: Run typecheck and tests** ``` pnpm --filter @repo/core-shared typecheck pnpm --filter @repo/core-shared test with-span ``` Both PASS. All existing tests still green plus the new runtime 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 attaches runtime __instrumented marker" ``` --- ## Task 6: `withCapture` attaches runtime `__captured` marker Same pattern as Task 5. **Files:** - Modify: `packages/core-shared/src/instrumentation/with-capture.ts` - Modify: `packages/core-shared/src/instrumentation/with-capture.test.ts` - [ ] **Step 1: Add a failing runtime brand assertion to `with-capture.test.ts`** Append this new describe block at the END of `packages/core-shared/src/instrumentation/with-capture.test.ts`. Also add `import { isCaptured } from "@/conformance/brand-runtime";` near the other imports at the top of the file: ```ts describe("withCapture — runtime brand", () => { it("attaches __captured as a non-enumerable property on the wrapped function", async () => { const logger = makeLogger(); const fn = async (a: number) => a + 1; const wrapped = withCapture(logger, { layer: "use-case" }, fn); expect(isCaptured(wrapped)).toBe(true); expect(Object.keys(wrapped)).not.toContain("__captured"); }); }); ``` - [ ] **Step 2: Run test to verify it fails** ``` pnpm --filter @repo/core-shared test with-capture ``` Expected: new test FAILS. - [ ] **Step 3: Update `with-capture.ts` to attach the runtime marker** Modify `packages/core-shared/src/instrumentation/with-capture.ts`. Add the `attachBrand` import alongside the existing imports at the top: ```ts import type { ILogger } from "./logger.interface"; import type { Captured } from "../conformance/brands"; import { attachBrand } from "../conformance/brand-runtime"; import { isReported, markReported } from "./reported-flag"; ``` Keep the existing JSDoc block intact (do not modify the doc text). Modify the body of `withCapture` so it ends with `attachBrand(wrapped, "__captured");` before the return: ```ts 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; } }; attachBrand(wrapped, "__captured"); return wrapped as Captured<(...args: Args) => Promise>; } ``` - [ ] **Step 4: Run typecheck and tests** ``` pnpm --filter @repo/core-shared typecheck pnpm --filter @repo/core-shared test with-capture ``` Both PASS. - [ ] **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 attaches runtime __captured marker" ``` --- ## Task 7: `withAudit` wraps + attaches runtime `__audited` marker Milestone i implemented `withAudit` as a passthrough cast (`return fn as Audited<...>`). For runtime brand detection that doesn't pollute the caller's `fn`, the wrapper now creates a thin wrapper function and attaches the marker to that new function. Observable behaviour is unchanged. **Files:** - Modify: `packages/core-audit/src/with-audit.ts` - Modify: `packages/core-audit/src/with-audit.test.ts` - [ ] **Step 1: Update the test to assert the runtime brand AND identity invariants** Replace the entire contents of `packages/core-audit/src/with-audit.test.ts` with: ```ts import { describe, it, expect, expectTypeOf, vi } from "vitest"; import { withAudit, type Audited } from "@/with-audit"; import type { IAuditLog } from "@/audit-log.interface"; import { isAudited } from "@repo/core-shared/conformance"; 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("attaches __audited as a non-enumerable property on the wrapped function", () => { const auditLog = makeAuditLog(); const fn = async () => ({ ok: true }); const wrapped = withAudit(auditLog, fn); expect(isAudited(wrapped)).toBe(true); expect(Object.keys(wrapped)).not.toContain("__audited"); }); it("does NOT pollute the original input function with the brand", () => { const auditLog = makeAuditLog(); const fn = async () => ({ ok: true }); const wrapped = withAudit(auditLog, fn); expect(isAudited(fn)).toBe(false); expect(wrapped).not.toBe(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); }); }); ``` Note the new third test: `wrapped !== fn` and `fn` is NOT branded. This pins down the wrap-not-passthrough invariant. - [ ] **Step 2: Run tests to verify they fail** ``` pnpm --filter @repo/core-audit test with-audit ``` Expected: at least the new tests FAIL (the current passthrough makes `wrapped === fn` and `isAudited(wrapped)` returns false). - [ ] **Step 3: Update `with-audit.ts` to wrap + attach the runtime brand** Replace the entire body of `packages/core-audit/src/with-audit.ts` with: ```ts import type { IAuditLog } from "./audit-log.interface"; import { attachBrand } from "@repo/core-shared/conformance"; /** * 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. At runtime the brand is a non-enumerable property attached by * `attachBrand` from `@repo/core-shared/conformance`, so the boot-time * assertion can verify the binding went through the audit-aware path. */ export type Audited = F & { readonly __audited: true }; /** * Use-case wrapper applied at DI bind time. The wrapper is a thin closure * that forwards to `fn` unchanged and carries the `__audited` brand. The * forward closure (instead of returning `fn` directly) keeps the brand on * a fresh function so the caller's original `fn` is not mutated — important * when the same factory output is used elsewhere unwrapped (dev-seed paths, * tests). */ export function withAudit( // TODO(conformance milestone iii+): wire automated recording from manifest // `audits[]` declarations. For now, the wrapper exists to: // (1) require callers to pass the auditLog at bind time (dep is available) // (2) attach the `__audited` brand so the boot-time assertion can verify // mutating use cases were bound through the audit-aware path. auditLog: IAuditLog, fn: (...args: Args) => Promise, ): Audited<(...args: Args) => Promise> { void auditLog; const wrapped: (...args: Args) => Promise = (...args) => fn(...args); attachBrand(wrapped, "__audited"); return wrapped as Audited<(...args: Args) => Promise>; } ``` `attachBrand` is imported from `@repo/core-shared/conformance` — Task 4 added that export to the conformance barrel, so the import should resolve cleanly. If it doesn't, complete Task 4 first. - [ ] **Step 4: Run typecheck and tests** ``` pnpm --filter @repo/core-audit typecheck pnpm --filter @repo/core-audit test with-audit ``` Both PASS. 5 tests green. - [ ] **Step 5: Commit** ```bash git add packages/core-audit/src/with-audit.ts packages/core-audit/src/with-audit.test.ts git commit -m "feat(core-audit): withAudit wraps and attaches runtime __audited marker" ``` --- ## Task 8: `ConformanceError` class **Files:** - Create: `packages/core-shared/src/conformance/conformance-error.ts` - Create: `packages/core-shared/src/conformance/conformance-error.test.ts` - [ ] **Step 1: Write the failing test** Create `packages/core-shared/src/conformance/conformance-error.test.ts` with EXACTLY this content: ```ts import { describe, it, expect } from "vitest"; import { ConformanceError } from "@/conformance/conformance-error"; describe("ConformanceError", () => { it("extends Error with the standard shape", () => { const err = new ConformanceError("auth.signIn: missing __instrumented brand"); expect(err).toBeInstanceOf(Error); expect(err).toBeInstanceOf(ConformanceError); expect(err.message).toBe("auth.signIn: missing __instrumented brand"); expect(err.name).toBe("ConformanceError"); }); it("preserves a stack trace", () => { const err = new ConformanceError("test"); expect(typeof err.stack).toBe("string"); }); }); ``` - [ ] **Step 2: Run test to verify it fails** ``` pnpm --filter @repo/core-shared test conformance/conformance-error ``` Expected: FAIL (module not found). - [ ] **Step 3: Write `conformance-error.ts`** Create `packages/core-shared/src/conformance/conformance-error.ts` with EXACTLY this content: ```ts /** * Thrown by `assertFeatureConformance` when a binding does not match the * manifest's declared shape. The boot assertion lets this propagate * synchronously so `pnpm dev` refuses to start on drift. */ export class ConformanceError extends Error { constructor(message: string) { super(message); this.name = "ConformanceError"; // Maintain a proper prototype chain across down-compilation. Object.setPrototypeOf(this, ConformanceError.prototype); } } ``` - [ ] **Step 4: Run test to verify it passes** ``` pnpm --filter @repo/core-shared test conformance/conformance-error ``` Expected: PASS, 2 tests. - [ ] **Step 5: Commit** ```bash git add packages/core-shared/src/conformance/conformance-error.ts packages/core-shared/src/conformance/conformance-error.test.ts git commit -m "feat(core-shared/conformance): ConformanceError class" ``` --- ## Task 9: Extend conformance barrel with `ConformanceError` Brand-runtime exports were added to the barrel in Task 4. This task adds the `ConformanceError` export. (`assertFeatureConformance` will be added in Task 10.) **Files:** - Modify: `packages/core-shared/src/conformance/index.ts` - [ ] **Step 1: Append the `ConformanceError` re-export** Add this line to the bottom of `packages/core-shared/src/conformance/index.ts`: ```ts export { ConformanceError } from "./conformance-error"; ``` - [ ] **Step 2: Verify typecheck** ``` pnpm --filter @repo/core-shared typecheck ``` PASS. - [ ] **Step 3: Commit** ```bash git add packages/core-shared/src/conformance/index.ts git commit -m "feat(core-shared/conformance): re-export ConformanceError" ``` --- ## Task 10: `assertFeatureConformance` helper **Files:** - Create: `packages/core-shared/src/conformance/assert-bindings.ts` - Create: `packages/core-shared/src/conformance/assert-bindings.test.ts` - Modify: `packages/core-shared/src/conformance/index.ts` (one more export) - [ ] **Step 1: Write the failing test** Create `packages/core-shared/src/conformance/assert-bindings.test.ts` with EXACTLY this content: ```ts import { describe, it, expect, vi } from "vitest"; import "reflect-metadata"; import { Container } from "inversify"; import { defineFeature } from "@/conformance/define-feature"; import { ConformanceError } from "@/conformance/conformance-error"; import { assertFeatureConformance } from "@/conformance/assert-bindings"; import { withSpan } from "@/instrumentation/with-span"; import { withCapture } from "@/instrumentation/with-capture"; import type { ITracer, ISpan, SpanOpts } from "@/instrumentation/tracer.interface"; import type { ILogger } from "@/instrumentation/logger.interface"; import type { BindContext } from "@/di/bind-context"; function makeTracer(): ITracer { return { startSpan: async (opts: SpanOpts, fn: (span: ISpan) => Promise) => { const span: ISpan = { setAttribute: () => {}, setStatus: () => {} }; return fn(span); }, }; } function makeLogger(): ILogger { return { captureException: vi.fn(), captureMessage: vi.fn(), addBreadcrumb: vi.fn(), setUser: vi.fn(), }; } function makeCtx(): BindContext { return { tracer: makeTracer(), logger: makeLogger() }; } describe("assertFeatureConformance", () => { it("passes when every use case is bound through withSpan + withCapture", () => { const container = new Container(); const sym = Symbol("test.signIn"); const ctx = makeCtx(); const bound = withSpan( ctx.tracer, { name: "test.signIn", op: "use-case" }, withCapture(ctx.logger, { feature: "test", layer: "use-case" }, async (x: number) => x + 1), ); container.bind(sym).toConstantValue(bound); const manifest = defineFeature({ name: "test", requiredCores: [], useCases: { signIn: { mutates: false, audits: [], publishes: [], consumes: [] }, }, realtimeChannels: [], jobs: [], } as const); expect(() => assertFeatureConformance(container, manifest, { signIn: sym }, ctx), ).not.toThrow(); }); it("throws when a use case binding is missing the __instrumented brand", () => { const container = new Container(); const sym = Symbol("test.signIn"); const ctx = makeCtx(); const unwrapped = async (x: number) => x + 1; container.bind(sym).toConstantValue(unwrapped); const manifest = defineFeature({ name: "test", requiredCores: [], useCases: { signIn: { mutates: false, audits: [], publishes: [], consumes: [] }, }, realtimeChannels: [], jobs: [], } as const); expect(() => assertFeatureConformance(container, manifest, { signIn: sym }, ctx), ).toThrow(ConformanceError); expect(() => assertFeatureConformance(container, manifest, { signIn: sym }, ctx), ).toThrow(/test\.signIn.*__instrumented/); }); it("throws when a use case binding is missing the __captured brand", () => { const container = new Container(); const sym = Symbol("test.signIn"); const ctx = makeCtx(); // withSpan-only — no withCapture wrap const partial = withSpan( ctx.tracer, { name: "test.signIn", op: "use-case" }, async (x: number) => x + 1, ); container.bind(sym).toConstantValue(partial); const manifest = defineFeature({ name: "test", requiredCores: [], useCases: { signIn: { mutates: false, audits: [], publishes: [], consumes: [] }, }, realtimeChannels: [], jobs: [], } as const); expect(() => assertFeatureConformance(container, manifest, { signIn: sym }, ctx), ).toThrow(/__captured/); }); it("throws when a mutating use case with audits is missing the __audited brand", () => { const container = new Container(); const sym = Symbol("test.signUp"); const ctx = makeCtx(); const wrappedNoAudit = withSpan( ctx.tracer, { name: "test.signUp", op: "use-case" }, withCapture(ctx.logger, { feature: "test" }, async (x: number) => x + 1), ); container.bind(sym).toConstantValue(wrappedNoAudit); const manifest = defineFeature({ name: "test", requiredCores: [], useCases: { signUp: { mutates: true, audits: ["user.created"], publishes: [], consumes: [], }, }, realtimeChannels: [], jobs: [], } as const); expect(() => assertFeatureConformance(container, manifest, { signUp: sym }, ctx), ).toThrow(/__audited/); }); it("passes for a mutating use case with empty audits (no __audited required)", () => { const container = new Container(); const sym = Symbol("test.signUp"); const ctx = makeCtx(); const wrappedNoAudit = withSpan( ctx.tracer, { name: "test.signUp", op: "use-case" }, withCapture(ctx.logger, { feature: "test" }, async (x: number) => x + 1), ); container.bind(sym).toConstantValue(wrappedNoAudit); const manifest = defineFeature({ name: "test", requiredCores: [], useCases: { signUp: { mutates: true, audits: [], publishes: [], consumes: [], }, }, realtimeChannels: [], jobs: [], } as const); expect(() => assertFeatureConformance(container, manifest, { signUp: sym }, ctx), ).not.toThrow(); }); it("throws when no symbol is provided for a manifest use case", () => { const container = new Container(); const ctx = makeCtx(); const manifest = defineFeature({ name: "test", requiredCores: [], useCases: { signIn: { mutates: false, audits: [], publishes: [], consumes: [] }, }, realtimeChannels: [], jobs: [], } as const); expect(() => assertFeatureConformance(container, manifest, {}, ctx)).toThrow( /no symbol provided/, ); }); it("throws when the container cannot resolve the symbol", () => { const container = new Container(); const sym = Symbol("test.signIn"); const ctx = makeCtx(); const manifest = defineFeature({ name: "test", requiredCores: [], useCases: { signIn: { mutates: false, audits: [], publishes: [], consumes: [] }, }, realtimeChannels: [], jobs: [], } as const); expect(() => assertFeatureConformance(container, manifest, { signIn: sym }, ctx), ).toThrow(ConformanceError); }); }); ``` - [ ] **Step 2: Run test to verify it fails** ``` pnpm --filter @repo/core-shared test conformance/assert-bindings ``` Expected: FAIL (module not found). - [ ] **Step 3: Write `assert-bindings.ts`** Create `packages/core-shared/src/conformance/assert-bindings.ts` with EXACTLY this content: ```ts import type { Container } from "inversify"; import type { FeatureManifest } from "./define-feature"; import { isInstrumented, isCaptured, isAudited } from "./brand-runtime"; import { ConformanceError } from "./conformance-error"; import type { BindContext } from "../di/bind-context"; /** * Runtime check that every manifest-declared use case is bound through the * brand-attaching wrappers (`withSpan` → `__instrumented`, * `withCapture` → `__captured`, `withAudit` → `__audited` when required). * * Called at the tail of each feature's `bindProductionX(ctx)` so: * - `pnpm dev` refuses to boot on drift (synchronous throw) * - The check runs once per feature, not per request * - Future apps that wire `bindProductionX` inherit the check for free * * The `symbols` map is declared inline by the feature's binder; the manifest * holds the contract, the binder holds the container symbols. This keeps * the manifest free of DI-coupling while letting each feature declare its * own wiring keys. */ export function assertFeatureConformance( container: Container, manifest: FeatureManifest, symbols: Record, _ctx: BindContext, ): void { void _ctx; // future: also check ctx.bus / ctx.auditLog presence vs requiredCores for (const [name, useCase] of Object.entries(manifest.useCases)) { const sym = symbols[name]; if (!sym) { throw new ConformanceError( `${manifest.name}.${name}: no symbol provided in symbols map`, ); } let bound: unknown; try { bound = container.get(sym); } catch (cause) { throw new ConformanceError( `${manifest.name}.${name}: container could not resolve symbol (${String(cause)})`, ); } if (!isInstrumented(bound)) { throw new ConformanceError( `${manifest.name}.${name}: missing __instrumented brand — was withSpan applied at bind time?`, ); } if (!isCaptured(bound)) { throw new ConformanceError( `${manifest.name}.${name}: missing __captured brand — was withCapture applied at bind time?`, ); } if (useCase.mutates && useCase.audits.length > 0) { if (!isAudited(bound)) { throw new ConformanceError( `${manifest.name}.${name}: declares audits but binding is missing __audited brand — was withAudit applied at bind time?`, ); } } } } ``` - [ ] **Step 4: Re-export `assertFeatureConformance` from the conformance barrel** Append to `packages/core-shared/src/conformance/index.ts`: ```ts export { assertFeatureConformance } from "./assert-bindings"; ``` - [ ] **Step 5: Run tests and typecheck** ``` pnpm --filter @repo/core-shared typecheck pnpm --filter @repo/core-shared test conformance/assert-bindings ``` Both PASS. 7 tests green. - [ ] **Step 6: Commit** ```bash git add packages/core-shared/src/conformance/assert-bindings.ts packages/core-shared/src/conformance/assert-bindings.test.ts packages/core-shared/src/conformance/index.ts git commit -m "feat(core-shared/conformance): assertFeatureConformance helper" ``` --- ## Task 11: Wire `assertFeatureConformance` into `bindProductionAuth` **Files:** - Modify: `packages/auth/src/di/bind-production.ts` - [ ] **Step 1: Add the imports** Open `packages/auth/src/di/bind-production.ts`. Add these two import lines alongside the existing imports at the top: ```ts import { assertFeatureConformance } from "@repo/core-shared/conformance"; import { authManifest } from "../feature.manifest"; ``` (The `AuthManifest` type import added in milestone i Task 11 stays as-is.) - [ ] **Step 2: Call the assertion at the tail** At the very end of the `bindProductionAuth(ctx)` function body — AFTER all bindings have been registered and AFTER the existing `// ` / `// ` / `// ` anchor comments — add: ```ts // Boot-time conformance check: refuses to start if any use-case binding // is missing a required brand (withSpan / withCapture / withAudit). assertFeatureConformance( authContainer, authManifest, { signIn: AUTH_SYMBOLS.ISignInUseCase, signUp: AUTH_SYMBOLS.ISignUpUseCase, signOut: AUTH_SYMBOLS.ISignOutUseCase, }, ctx, ); ``` The closing `}` of the function should come immediately after. - [ ] **Step 3: Run typecheck and tests** ``` pnpm --filter @repo/auth typecheck pnpm --filter @repo/auth test ``` Both PASS. 81 tests green, no regressions. - [ ] **Step 4: Verify web-next boots cleanly** ``` pnpm --filter @repo/web-next test ``` PASS. The existing `bind-production.test.ts` exercises `bindAllDevSeed` / `bindAllProduction`; both should now silently pass the conformance assertion for `auth`. - [ ] **Step 5: Commit** ```bash git add packages/auth/src/di/bind-production.ts git commit -m "feat(auth): bindProductionAuth self-asserts conformance at tail" ``` --- ## Task 12: Final verification + story closeout **Files:** - Modify: `docs/work/conformance-system-v1/02-boot-assertions/_story.md` - Modify: `docs/work/conformance-system-v1/_epic.md` - [ ] **Step 1: Run the full verification matrix** ``` pnpm typecheck pnpm test pnpm lint pnpm turbo boundaries ``` All four MUST pass. If any fail, STOP and report — do not attempt to fix in this task. - [ ] **Step 2: Tick the story's task checkboxes and flip status** Edit `docs/work/conformance-system-v1/02-boot-assertions/_story.md`: (a) Change frontmatter `status: in-progress` → `status: done`. (b) Change all 11 `- [ ]` task checkboxes in the Tasks section to `- [x]`: ```markdown ## Tasks - [x] Re-export `authManifest` from auth root barrel - [x] TODO breadcrumb in `withAudit` pointing at future automation - [x] Runtime marker helpers (`attachBrand`, `isInstrumented`, `isCaptured`, `isAudited`) - [x] `withSpan` attaches runtime `__instrumented` marker - [x] `withCapture` attaches runtime `__captured` marker - [x] `withAudit` wraps + attaches runtime `__audited` marker - [x] `ConformanceError` class - [x] `assertFeatureConformance` helper + tests - [x] Conformance barrel + subpath exports updated - [x] `bindProductionAuth` self-asserts at the tail - [x] Final verification + story closeout ``` - [ ] **Step 3: Tick story 02 in the epic** Edit `docs/work/conformance-system-v1/_epic.md`. Change: ```markdown - [ ] 02 — `assertConformance` + boot wiring (later plan) ``` to: ```markdown - [x] [02 — `assertFeatureConformance` + boot wiring](02-boot-assertions/_story.md) ``` (Note: changes the text to a proper link AND ticks the checkbox in one edit.) - [ ] **Step 4: Commit the milestone close** ```bash git add docs/work/conformance-system-v1/02-boot-assertions/_story.md docs/work/conformance-system-v1/_epic.md git commit -m "docs(work): close story 02 — assertFeatureConformance + boot wiring" ``` --- ## Done — what this leaves behind - `attachBrand` / `isInstrumented` / `isCaptured` / `isAudited` in `@repo/core-shared/conformance` (non-enumerable runtime markers) - `ConformanceError` class - `assertFeatureConformance(container, manifest, symbols, ctx)` helper - `withSpan` / `withCapture` / `withAudit` all attach their runtime brand - `withAudit` upgraded from passthrough to thin-wrapper (preserves input function purity) - `authManifest` exposed at `packages/auth/src/index.ts` - `bindProductionAuth` self-asserts conformance at its tail - `pnpm dev` (which calls `bindAllDevSeed` → `bindDevSeedAuth` → currently does NOT call `bindProductionAuth`, so this story does not yet break dev seed even if the assertion would fail; production paths via `bindProductionAuth` are covered) - All gates green (typecheck, test, lint, boundaries) ## What comes next (separate plans) - **Milestone iii — AST-aware ESLint rules.** Five rules in `@repo/core-eslint`: `manifest-usecase-signature-matches`, `no-undeclared-event-publish`, `no-undeclared-audit`, `required-cores-installed`, `feature-must-have-manifest`. Read the manifest, walk the use-case AST, surface drift in the editor. - **Milestone iv — CI drift gate.** `pnpm conformance` aggregates event closure, generator drift, required-cores ↔ workspace mismatch. - **Generator updates.** `turbo gen feature` emits `feature.manifest.ts` + contracts stubs + test stubs in canonical shape; CI re-runs and diffs. - **Apply the conformance pattern to `blog`, `media`, `navigation`, `marketing-pages`.** Each gets its own manifest + self-asserting bind-production (1 small plan per feature, or one bundled plan). - **Dev-seed slot typing.** Extend `ProductionUseCase` (or define a sibling `DevSeedUseCase<...>`) to `bind-dev-seed.ts` paths so the same drift catches in dev mode. - **Future: `withAudit` automation.** Wire automated audit emission from manifest `audits[]` declarations (deferred per TODO in `withAudit`).