/* Unit tests for reflection covering createCapabilityCatalog; assertNoForbiddenFields; negative: catalog must not leak private data. Not a contract source of truth — the module under test owns the authoritative behavior; fixtures are minimal scaffolding only. RFC-1311: Compass header sweep — declares the reflection suite's contract for test-file scaffold coverage. */ import { describe, it, expect } from "vitest"; import { createCapabilityCatalog, assertNoForbiddenFields, toReflectedState, reflectRuntime, type CapabilityCatalogV1, type LiveComponentObservation, type LawKernelSummary, } from "../reflection.ts"; import type { ComponentDeclaration, ComponentId, CapabilityId, ResolvedComponentSetV1, ResolvedComponentIdentityV1, } from "@warpgogol/werkstatt-shared/component"; import { computeSetHash } from "../../component/identity.ts"; import type { Sha256Digest } from "@warpgogol/werkstatt-shared/fingerprint"; const VALID_SHA = ("sha256:" + "a".repeat(64)) as Sha256Digest; const _VALID_SHA_2 = ("sha256:" + "b".repeat(64)) as Sha256Digest; function cid(id: string): ComponentId { return id as ComponentId; } function makeManifest(overrides: Partial = {}): ComponentDeclaration { const componentId = overrides.componentId ?? "werkstatt/engine"; return { schema: "werkstatt/component-declaration@1", componentId, version: "1.0.0", artifactHash: VALID_SHA, scope: "per-workshop", provides: [ { capability: "werkstatt/kernel", version: "1.0.0", schemaHash: VALID_SHA as string }, ], requires: [], requestedGrants: [], effects: [], isolation: { tier: 0, adapterId: null }, resources: [{ kind: "cpu", limit: "100ms", owner: componentId, lifecycle: "process" }], priority: 0, active: true, ...overrides, }; } function makeResolvedIdentity( overrides: Partial = {}, ): ResolvedComponentIdentityV1 { return { componentId: cid("werkstatt/engine"), version: "1.0.0", artifactHash: VALID_SHA, ...overrides, }; } function makeResolvedSet( overrides: Partial> = {}, ): ResolvedComponentSetV1 { const base: Omit = { schema: "werkstatt/resolved-component-set@1", profileId: "site", components: [makeResolvedIdentity()], dependencyGraphHash: VALID_SHA as string, grantSetHash: VALID_SHA as string, effectPolicyHash: VALID_SHA as string, isolationPolicyHash: VALID_SHA as string, ...overrides, }; const setHash = computeSetHash(base); return { ...base, setHash }; } describe("createCapabilityCatalog", () => { it("creates a catalog with correct schema and hash binding", () => { const set = makeResolvedSet(); const manifests = new Map([ ["werkstatt/engine", makeManifest()], ]); const observations = new Map([ ["werkstatt/engine", { componentId: cid("werkstatt/engine"), lifecycleState: "active" }], ]); const catalog = createCapabilityCatalog({ activeSet: set, manifests, observations, observedAt: "2026-01-01T00:00:00Z", }); expect(catalog.schema).toBe("werkstatt/capability-catalog@1"); expect(catalog.observedAt).toBe("2026-01-01T00:00:00Z"); expect(catalog.resolvedComponentSetHash).toBe(set.setHash); expect(catalog.entries).toHaveLength(1); expect(catalog.entries[0]!.capability).toBe("werkstatt/kernel"); expect(catalog.entries[0]!.componentId).toBe("werkstatt/engine"); expect(catalog.entries[0]!.lifecycleState).toBe("active"); expect(catalog.entries[0]!.callable).toBe(true); expect(catalog.catalogHash).toBeTruthy(); }); it("throws REFLECTION-01 when set hash is mismatched", () => { const set = makeResolvedSet(); const tampered: ResolvedComponentSetV1 = { ...set, setHash: ("sha256:" + "0".repeat(64)) as string, }; const manifests = new Map([ ["werkstatt/engine", makeManifest()], ]); const observations = new Map([ ["werkstatt/engine", { componentId: cid("werkstatt/engine"), lifecycleState: "active" }], ]); expect(() => createCapabilityCatalog({ activeSet: tampered, manifests, observations, }), ).toThrow(/REFLECTION-01/); }); it("maps lifecycle states to reflected states correctly", () => { const identity = makeResolvedIdentity(); const _set = makeResolvedSet({ components: [identity] }); const _manifests = new Map([ ["werkstatt/engine", makeManifest()], ]); const cases: Array<{ input: import("../lifecycle.ts").ComponentLifecycleState; expected: import("../reflection.ts").ReflectedLifecycleState; }> = [ { input: "declared", expected: "waiting" }, { input: "waiting", expected: "waiting" }, { input: "loading", expected: "waiting" }, { input: "active", expected: "active" }, { input: "draining", expected: "draining" }, { input: "unloading", expected: "draining" }, { input: "disposed", expected: "failed" }, { input: "failed", expected: "failed" }, { input: "quarantined", expected: "quarantined" }, ]; for (const { input, expected } of cases) { expect(toReflectedState(input)).toBe(expected); } }); it("callable is false for non-active states", () => { const identity = makeResolvedIdentity(); const set = makeResolvedSet({ components: [identity] }); const manifests = new Map([ ["werkstatt/engine", makeManifest()], ]); const nonActiveStates: import("../lifecycle.ts").ComponentLifecycleState[] = [ "waiting", "draining", "failed", "quarantined", ]; for (const state of nonActiveStates) { const observations = new Map([ ["werkstatt/engine", { componentId: cid("werkstatt/engine"), lifecycleState: state }], ]); const catalog = createCapabilityCatalog({ activeSet: set, manifests, observations, }); expect(catalog.entries[0]!.callable).toBe(false); } }); it("filters by visible capabilities when provided", () => { const manifest = makeManifest({ provides: [ { capability: "werkstatt/kernel", version: "1.0.0", schemaHash: VALID_SHA as string }, { capability: "werkstatt/secret", version: "1.0.0", schemaHash: VALID_SHA as string }, ], }); const identity = makeResolvedIdentity(); const set = makeResolvedSet({ components: [identity] }); const manifests = new Map([["werkstatt/engine", manifest]]); const observations = new Map([ ["werkstatt/engine", { componentId: cid("werkstatt/engine"), lifecycleState: "active" }], ]); const visible = new Set(["werkstatt/kernel"]); const catalog = createCapabilityCatalog({ activeSet: set, manifests, observations, visibleCapabilities: visible, }); expect(catalog.entries).toHaveLength(1); expect(catalog.entries[0]!.capability).toBe("werkstatt/kernel"); }); it("omits components without manifests", () => { const identity = makeResolvedIdentity(); const set = makeResolvedSet({ components: [identity] }); const observations = new Map([ ["werkstatt/engine", { componentId: cid("werkstatt/engine"), lifecycleState: "active" }], ]); const catalog = createCapabilityCatalog({ activeSet: set, manifests: new Map(), observations, }); expect(catalog.entries).toHaveLength(0); }); it("sorts entries canonically by capability, version, componentId", () => { const manifestA = makeManifest({ componentId: cid("werkstatt/alpha"), provides: [ { capability: "werkstatt/zeta", version: "1.0.0", schemaHash: VALID_SHA as string }, { capability: "werkstatt/alpha", version: "1.0.0", schemaHash: VALID_SHA as string }, ], }); const manifestB = makeManifest({ componentId: cid("werkstatt/beta"), provides: [ { capability: "werkstatt/alpha", version: "1.0.0", schemaHash: VALID_SHA as string }, ], }); const set = makeResolvedSet({ components: [ makeResolvedIdentity({ componentId: cid("werkstatt/alpha") }), makeResolvedIdentity({ componentId: cid("werkstatt/beta"), artifactHash: VALID_SHA, }), ], }); const manifests = new Map([ ["werkstatt/alpha", manifestA], ["werkstatt/beta", manifestB], ]); const observations = new Map([ ["werkstatt/alpha", { componentId: cid("werkstatt/alpha"), lifecycleState: "active" }], ["werkstatt/beta", { componentId: cid("werkstatt/beta"), lifecycleState: "active" }], ]); const catalog = createCapabilityCatalog({ activeSet: set, manifests, observations, }); expect(catalog.entries).toHaveLength(3); expect(catalog.entries[0]!.capability).toBe("werkstatt/alpha"); expect(catalog.entries[0]!.componentId).toBe("werkstatt/alpha"); expect(catalog.entries[1]!.capability).toBe("werkstatt/alpha"); expect(catalog.entries[1]!.componentId).toBe("werkstatt/beta"); expect(catalog.entries[2]!.capability).toBe("werkstatt/zeta"); }); it("catalog hash is deterministic for same inputs", () => { const set = makeResolvedSet(); const manifests = new Map([ ["werkstatt/engine", makeManifest()], ]); const observations = new Map([ ["werkstatt/engine", { componentId: cid("werkstatt/engine"), lifecycleState: "active" }], ]); const c1 = createCapabilityCatalog({ activeSet: set, manifests, observations, observedAt: "2026-01-01T00:00:00Z", }); const c2 = createCapabilityCatalog({ activeSet: set, manifests, observations, observedAt: "2026-01-01T00:00:00Z", }); expect(c1.catalogHash).toBe(c2.catalogHash); }); }); describe("assertNoForbiddenFields", () => { it("passes for a clean catalog", () => { const set = makeResolvedSet(); const manifests = new Map([ ["werkstatt/engine", makeManifest()], ]); const observations = new Map([ ["werkstatt/engine", { componentId: cid("werkstatt/engine"), lifecycleState: "active" }], ]); const catalog = createCapabilityCatalog({ activeSet: set, manifests, observations, }); expect(() => assertNoForbiddenFields(catalog)).not.toThrow(); }); it("throws REFLECTION-02 for catalog containing forbidden field name", () => { const fakeCatalog = { schema: "werkstatt/capability-catalog@1", observedAt: "2026-01-01", resolvedComponentSetHash: "sha256:abc", entries: [], catalogHash: "sha256:def", secrets: "leaked", } as unknown as CapabilityCatalogV1; expect(() => assertNoForbiddenFields(fakeCatalog)).toThrow(/REFLECTION-02/); }); it("throws for credentials field", () => { const fakeCatalog = { schema: "werkstatt/capability-catalog@1", observedAt: "2026-01-01", resolvedComponentSetHash: "sha256:abc", entries: [], catalogHash: "sha256:def", credentials: "leaked", } as unknown as CapabilityCatalogV1; expect(() => assertNoForbiddenFields(fakeCatalog)).toThrow(/REFLECTION-02/); }); it("throws for rawGrants field", () => { const fakeCatalog = { schema: "werkstatt/capability-catalog@1", observedAt: "2026-01-01", resolvedComponentSetHash: "sha256:abc", entries: [], catalogHash: "sha256:def", rawGrants: [], } as unknown as CapabilityCatalogV1; expect(() => assertNoForbiddenFields(fakeCatalog)).toThrow(/REFLECTION-02/); }); }); describe("negative: catalog must not leak private data", () => { it("catalog entries contain only allowed fields", () => { const set = makeResolvedSet(); const manifests = new Map([ ["werkstatt/engine", makeManifest()], ]); const observations = new Map([ ["werkstatt/engine", { componentId: cid("werkstatt/engine"), lifecycleState: "active" }], ]); const catalog = createCapabilityCatalog({ activeSet: set, manifests, observations, }); const entry = catalog.entries[0]!; const keys = Object.keys(entry); expect(keys.sort()).toEqual([ "callable", "capability", "componentId", "lifecycleState", "schemaHash", "version", ]); }); it("catalog top-level contains only allowed fields", () => { const set = makeResolvedSet(); const manifests = new Map([ ["werkstatt/engine", makeManifest()], ]); const observations = new Map([ ["werkstatt/engine", { componentId: cid("werkstatt/engine"), lifecycleState: "active" }], ]); const catalog = createCapabilityCatalog({ activeSet: set, manifests, observations, }); const keys = Object.keys(catalog); expect(keys.sort()).toEqual([ "catalogHash", "entries", "observedAt", "resolvedComponentSetHash", "schema", ]); }); }); const STUB_LAW_KERNEL: LawKernelSummary = { activeGrants: 3, artifactStoreEntries: 12, killSwitchArmed: false, }; function makeTwoComponentSet(): { set: ResolvedComponentSetV1; manifests: Map; observations: Map; } { const manifestA = makeManifest({ componentId: cid("werkstatt/alpha"), provides: [ { capability: "werkstatt/kernel", version: "1.0.0", schemaHash: VALID_SHA as string }, ], requires: [ { capability: "werkstatt/storage", compatibility: "1.0.0", schemaHash: VALID_SHA as string, optional: false, }, ], }); const manifestB = makeManifest({ componentId: cid("werkstatt/beta"), provides: [ { capability: "werkstatt/storage", version: "1.0.0", schemaHash: VALID_SHA as string }, ], requires: [], }); const set = makeResolvedSet({ components: [ makeResolvedIdentity({ componentId: cid("werkstatt/alpha") }), makeResolvedIdentity({ componentId: cid("werkstatt/beta") }), ], }); const manifests = new Map([ ["werkstatt/alpha", manifestA], ["werkstatt/beta", manifestB], ]); const observations = new Map([ ["werkstatt/alpha", { componentId: cid("werkstatt/alpha"), lifecycleState: "active" }], ["werkstatt/beta", { componentId: cid("werkstatt/beta"), lifecycleState: "active" }], ]); return { set, manifests, observations }; } describe("reflectRuntime", () => { it("returns RuntimeReflectionV1 with correct component states (AC-1)", () => { const { set, manifests, observations } = makeTwoComponentSet(); const reflection = reflectRuntime( { activeSet: set, manifests, observations, observedAt: "2026-01-01T00:00:00Z" }, STUB_LAW_KERNEL, ); expect(reflection.reflectedAt).toBe("2026-01-01T00:00:00Z"); expect(reflection.components).toHaveLength(2); expect(reflection.components[0]!.componentId).toBe("werkstatt/alpha"); expect(reflection.components[0]!.fiberState).toBe("active"); expect(reflection.components[1]!.componentId).toBe("werkstatt/beta"); expect(reflection.components[1]!.fiberState).toBe("active"); expect(reflection.lawKernel).toEqual(STUB_LAW_KERNEL); }); it("skips disposed components and increments skippedDisposed (AC-1, failure mode)", () => { const { set, manifests } = makeTwoComponentSet(); const observations = new Map([ ["werkstatt/alpha", { componentId: cid("werkstatt/alpha"), lifecycleState: "active" }], ["werkstatt/beta", { componentId: cid("werkstatt/beta"), lifecycleState: "disposed" }], ]); const reflection = reflectRuntime({ activeSet: set, manifests, observations }, STUB_LAW_KERNEL); expect(reflection.components).toHaveLength(1); expect(reflection.components[0]!.componentId).toBe("werkstatt/alpha"); expect(reflection.summary.skippedDisposed).toBe(1); expect(reflection.summary.total).toBe(1); }); it("skips components without manifests without error", () => { const { set, observations } = makeTwoComponentSet(); const reflection = reflectRuntime( { activeSet: set, manifests: new Map(), observations }, STUB_LAW_KERNEL, ); expect(reflection.components).toHaveLength(0); expect(reflection.summary.total).toBe(0); }); it("builds dependency edges from requires/provides mapping", () => { const { set, manifests, observations } = makeTwoComponentSet(); const reflection = reflectRuntime({ activeSet: set, manifests, observations }, STUB_LAW_KERNEL); expect(reflection.dependencyEdges).toHaveLength(1); expect(reflection.dependencyEdges[0]!.from).toBe("werkstatt/alpha"); expect(reflection.dependencyEdges[0]!.to).toBe("werkstatt/beta"); expect(reflection.dependencyEdges[0]!.capability).toBe("werkstatt/storage"); }); it("resolves requirements with resolvedBy and resolution status", () => { const { set, manifests, observations } = makeTwoComponentSet(); const reflection = reflectRuntime({ activeSet: set, manifests, observations }, STUB_LAW_KERNEL); const alpha = reflection.components.find((c) => c.componentId === "werkstatt/alpha")!; expect(alpha.requires).toHaveLength(1); expect(alpha.requires[0]!.resolution).toBe("resolved"); expect(alpha.requires[0]!.resolvedBy).toBe("werkstatt/beta"); }); it("marks unresolved non-optional requires as missing", () => { const manifestA = makeManifest({ componentId: cid("werkstatt/alpha"), provides: [], requires: [ { capability: "werkstatt/missing", compatibility: "1.0.0", schemaHash: VALID_SHA as string, optional: false, }, ], }); const set = makeResolvedSet({ components: [makeResolvedIdentity({ componentId: cid("werkstatt/alpha") })], }); const manifests = new Map([["werkstatt/alpha", manifestA]]); const observations = new Map([ ["werkstatt/alpha", { componentId: cid("werkstatt/alpha"), lifecycleState: "active" }], ]); const reflection = reflectRuntime({ activeSet: set, manifests, observations }, STUB_LAW_KERNEL); const alpha = reflection.components[0]!; expect(alpha.requires[0]!.resolution).toBe("missing"); expect(alpha.requires[0]!.resolvedBy).toBeUndefined(); }); it("marks unresolved optional requires as waiting", () => { const manifestA = makeManifest({ componentId: cid("werkstatt/alpha"), provides: [], requires: [ { capability: "werkstatt/optional", compatibility: "1.0.0", schemaHash: VALID_SHA as string, optional: true, }, ], }); const set = makeResolvedSet({ components: [makeResolvedIdentity({ componentId: cid("werkstatt/alpha") })], }); const manifests = new Map([["werkstatt/alpha", manifestA]]); const observations = new Map([ ["werkstatt/alpha", { componentId: cid("werkstatt/alpha"), lifecycleState: "active" }], ]); const reflection = reflectRuntime({ activeSet: set, manifests, observations }, STUB_LAW_KERNEL); const alpha = reflection.components[0]!; expect(alpha.requires[0]!.resolution).toBe("waiting"); }); it("summary counts states correctly", () => { const manifestA = makeManifest({ componentId: cid("werkstatt/alpha") }); const manifestB = makeManifest({ componentId: cid("werkstatt/beta") }); const manifestC = makeManifest({ componentId: cid("werkstatt/gamma") }); const set = makeResolvedSet({ components: [ makeResolvedIdentity({ componentId: cid("werkstatt/alpha") }), makeResolvedIdentity({ componentId: cid("werkstatt/beta") }), makeResolvedIdentity({ componentId: cid("werkstatt/gamma") }), ], }); const manifests = new Map([ ["werkstatt/alpha", manifestA], ["werkstatt/beta", manifestB], ["werkstatt/gamma", manifestC], ]); const observations = new Map([ ["werkstatt/alpha", { componentId: cid("werkstatt/alpha"), lifecycleState: "active" }], ["werkstatt/beta", { componentId: cid("werkstatt/beta"), lifecycleState: "waiting" }], ["werkstatt/gamma", { componentId: cid("werkstatt/gamma"), lifecycleState: "failed" }], ]); const reflection = reflectRuntime({ activeSet: set, manifests, observations }, STUB_LAW_KERNEL); expect(reflection.summary.total).toBe(3); expect(reflection.summary.active).toBe(1); expect(reflection.summary.waiting).toBe(1); expect(reflection.summary.failed).toBe(1); expect(reflection.summary.skippedDisposed).toBe(0); }); it("does not leak forbidden fields in reflection output (AC-7)", () => { const { set, manifests, observations } = makeTwoComponentSet(); const reflection = reflectRuntime({ activeSet: set, manifests, observations }, STUB_LAW_KERNEL); const json = JSON.stringify(reflection); const forbidden = [ "secrets", "credentials", "privateState", "rawGrants", "prompts", "executableBytes", "leaseTokens", "authorityMaterial", "artifactBytes", "sourceCode", ]; for (const field of forbidden) { expect(json, `Reflection must not contain forbidden field: ${field}`).not.toContain(field); } }); it("returns RuntimeReflectionV1 with correct top-level shape", () => { const { set, manifests, observations } = makeTwoComponentSet(); const reflection = reflectRuntime({ activeSet: set, manifests, observations }, STUB_LAW_KERNEL); const keys = Object.keys(reflection).sort(); expect(keys).toEqual(["components", "dependencyEdges", "lawKernel", "reflectedAt", "summary"]); }); }); describe("reflectRuntime: capability catalog reuse (AC-2)", () => { it("createCapabilityCatalog and reflectRuntime share the same activeSet hash", () => { const { set, manifests, observations } = makeTwoComponentSet(); const catalog = createCapabilityCatalog({ activeSet: set, manifests, observations }); const reflection = reflectRuntime({ activeSet: set, manifests, observations }, STUB_LAW_KERNEL); expect(catalog.resolvedComponentSetHash).toBe(set.setHash); expect(reflection.components.length).toBeGreaterThan(0); }); });