From d128106fd2249946163b7521ffd724bbf76375aa Mon Sep 17 00:00:00 2001 From: Danijel Martinek Date: Mon, 18 May 2026 11:41:48 +0000 Subject: [PATCH] feat(core-shared): add Analyzed brand and isAnalyzed type guard Adds the `Analyzed` phantom-type brand to brands.ts and the `isAnalyzed` type-guard to brand-runtime.ts, mirroring the existing Instrumented/Captured/Audited pattern. Exports both from the conformance index so Story 04 (wireUseCase) can key off the brand without a circular dep. Co-Authored-By: Claude Sonnet 4.6 --- .../core-shared/src/conformance/brand-runtime.test.ts | 11 +++++++++++ packages/core-shared/src/conformance/brand-runtime.ts | 8 +++++++- packages/core-shared/src/conformance/brands.ts | 1 + packages/core-shared/src/conformance/index.ts | 3 ++- 4 files changed, 21 insertions(+), 2 deletions(-) diff --git a/packages/core-shared/src/conformance/brand-runtime.test.ts b/packages/core-shared/src/conformance/brand-runtime.test.ts index f106b8f..85bcb41 100644 --- a/packages/core-shared/src/conformance/brand-runtime.test.ts +++ b/packages/core-shared/src/conformance/brand-runtime.test.ts @@ -4,6 +4,7 @@ import { isInstrumented, isCaptured, isAudited, + isAnalyzed, } from "@/conformance/brand-runtime"; describe("brand-runtime", () => { @@ -21,6 +22,7 @@ describe("brand-runtime", () => { expect(isInstrumented(fn)).toBe(false); expect(isCaptured(fn)).toBe(false); expect(isAudited(fn)).toBe(false); + expect(isAnalyzed(fn)).toBe(false); }); it("predicates discriminate between brands", () => { @@ -39,6 +41,10 @@ describe("brand-runtime", () => { const audited = () => {}; attachBrand(audited, "__audited"); expect(isAudited(audited)).toBe(true); + + const analyzed = () => {}; + attachBrand(analyzed, "__analyzed"); + expect(isAnalyzed(analyzed)).toBe(true); }); it("composing brands stacks them on the same function", () => { @@ -46,9 +52,11 @@ describe("brand-runtime", () => { attachBrand(fn, "__instrumented"); attachBrand(fn, "__captured"); attachBrand(fn, "__audited"); + attachBrand(fn, "__analyzed"); expect(isInstrumented(fn)).toBe(true); expect(isCaptured(fn)).toBe(true); expect(isAudited(fn)).toBe(true); + expect(isAnalyzed(fn)).toBe(true); }); it("attached brands are non-writable and non-configurable", () => { @@ -67,5 +75,8 @@ describe("brand-runtime", () => { expect(isInstrumented(42)).toBe(false); expect(isInstrumented("string")).toBe(false); expect(isInstrumented({})).toBe(false); + expect(isAnalyzed(null)).toBe(false); + expect(isAnalyzed(undefined)).toBe(false); + expect(isAnalyzed(42)).toBe(false); }); }); diff --git a/packages/core-shared/src/conformance/brand-runtime.ts b/packages/core-shared/src/conformance/brand-runtime.ts index c378fe5..6b5fda7 100644 --- a/packages/core-shared/src/conformance/brand-runtime.ts +++ b/packages/core-shared/src/conformance/brand-runtime.ts @@ -13,7 +13,9 @@ * commitment, not a mutable flag. */ -type Brand = "__instrumented" | "__captured" | "__audited"; +import type { Analyzed } from "./brands"; + +type Brand = "__instrumented" | "__captured" | "__audited" | "__analyzed"; /** * Attaches the brand as a non-enumerable property on the given function. @@ -51,3 +53,7 @@ export function isCaptured(fn: unknown): boolean { export function isAudited(fn: unknown): boolean { return hasBrand(fn, "__audited"); } + +export function isAnalyzed(fn: unknown): fn is Analyzed { + return hasBrand(fn, "__analyzed"); +} diff --git a/packages/core-shared/src/conformance/brands.ts b/packages/core-shared/src/conformance/brands.ts index 4ff9fe3..762e370 100644 --- a/packages/core-shared/src/conformance/brands.ts +++ b/packages/core-shared/src/conformance/brands.ts @@ -9,3 +9,4 @@ */ export type Instrumented = F & { readonly __instrumented: true }; export type Captured = F & { readonly __captured: true }; +export type Analyzed = F & { readonly __analyzed: true }; diff --git a/packages/core-shared/src/conformance/index.ts b/packages/core-shared/src/conformance/index.ts index 37d5839..9bc3be6 100644 --- a/packages/core-shared/src/conformance/index.ts +++ b/packages/core-shared/src/conformance/index.ts @@ -1,4 +1,4 @@ -export type { Instrumented, Captured } from "./brands"; +export type { Instrumented, Captured, Analyzed } from "./brands"; export type { FeatureManifest, UseCaseManifest } from "./define-feature"; export { defineFeature } from "./define-feature"; export type { @@ -22,6 +22,7 @@ export { isInstrumented, isCaptured, isAudited, + isAnalyzed, } from "./brand-runtime"; export { ConformanceError } from "./conformance-error"; export { assertFeatureConformance } from "./assert-bindings";