diff --git a/packages/core-shared/src/conformance/brands.test.ts b/packages/core-shared/src/conformance/brands.test.ts new file mode 100644 index 0000000..3e09bee --- /dev/null +++ b/packages/core-shared/src/conformance/brands.test.ts @@ -0,0 +1,26 @@ +import { describe, it, expectTypeOf } from "vitest"; +import type { Instrumented, Captured } from "@/conformance/brands"; + +describe("brand types", () => { + it("Instrumented is structurally F plus a phantom flag", () => { + type Fn = (n: number) => Promise; + expectTypeOf>().toBeCallableWith(1); + expectTypeOf>().returns.resolves.toEqualTypeOf(); + // The flag is readonly and required for assignability checks. + expectTypeOf["__instrumented"]>().toEqualTypeOf(); + }); + + it("Captured is structurally F plus a phantom flag", () => { + type Fn = (n: number) => Promise; + expectTypeOf>().toBeCallableWith(1); + expectTypeOf["__captured"]>().toEqualTypeOf(); + }); + + it("brands compose without conflict", () => { + type Fn = (n: number) => Promise; + type Both = Instrumented & Captured; + expectTypeOf().toBeCallableWith(1); + expectTypeOf().toEqualTypeOf(); + expectTypeOf().toEqualTypeOf(); + }); +}); diff --git a/packages/core-shared/src/conformance/brands.ts b/packages/core-shared/src/conformance/brands.ts new file mode 100644 index 0000000..eeb1045 --- /dev/null +++ b/packages/core-shared/src/conformance/brands.ts @@ -0,0 +1,9 @@ +/** + * Phantom-type brands attached at wrap time by `withSpan`, `withCapture`, + * and `withAudit`. Pure type-level — no runtime cost, no proxy, no + * `Object.assign`. The conformance system uses these as the type-level + * seam the binding signature checks; a use-case factory that hasn't been + * wrapped is not assignable to a `ProductionUseCase<...>` slot. + */ +export type Instrumented = F & { readonly __instrumented: true }; +export type Captured = F & { readonly __captured: true };