/*
reflection — reflect component lifecycle state for read-only runtime inspection.
- Do not mutate state — reflection is read-only.
- RFC-1097: sweep — werkstatt-engine clean
Sweep batch 4: 73 Compass headers on headerless engine files (certification, component-runtime, isolation, evolution, testing), real KEY_DECISIONS on 75 files (kernel, cache, dht, swim, gitmesh, runtime), ~80 purpose expansions (CONTRACT-02/PURPOSE-02), non-goals on 13 CONTRACT-03 files, CS-07 history literal fix repo-wide (253 files). Policy: .template.ts/.template.astro excludedPaths. werkstatt-engine now 0 diagnostics.
*/
import type {
CapabilityId,
CapabilityProvideV1,
CapabilityRequireV1,
ComponentId,
ComponentDeclaration,
EffectDeclarationV1,
GrantRequestV1,
ResolvedComponentSetV1,
} from "@warpgogol/werkstatt-shared/component";
import type { ComponentLifecycleState } from "./lifecycle.ts";
import { computeSetHash } from "../component/identity.ts";
export type ReflectedLifecycleState = "waiting" | "active" | "draining" | "failed" | "quarantined";
export interface CapabilityCatalogEntryV1 {
readonly capability: CapabilityId;
readonly version: string;
readonly schemaHash: string;
readonly componentId: ComponentId;
readonly lifecycleState: ReflectedLifecycleState;
readonly callable: boolean;
}
export interface CapabilityCatalogV1 {
readonly schema: "werkstatt/capability-catalog@1";
readonly observedAt: string;
readonly resolvedComponentSetHash: string;
readonly entries: readonly CapabilityCatalogEntryV1[];
readonly catalogHash: string;
}
export interface LiveComponentObservation {
readonly componentId: ComponentId;
readonly lifecycleState: ComponentLifecycleState;
}
export interface ReflectionInput {
readonly activeSet: ResolvedComponentSetV1;
readonly manifests: ReadonlyMap;
readonly observations: ReadonlyMap;
readonly observedAt?: string;
readonly visibleCapabilities?: ReadonlySet;
}
const FORBIDDEN_FIELDS = new Set([
"secrets",
"credentials",
"privateState",
"rawGrants",
"prompts",
"executableBytes",
"leaseTokens",
"authorityMaterial",
"artifactBytes",
"sourceCode",
]);
export function toReflectedState(state: ComponentLifecycleState): ReflectedLifecycleState {
switch (state) {
case "waiting":
case "active":
case "draining":
case "failed":
case "quarantined":
return state;
case "declared":
case "loading":
return "waiting";
case "unloading":
return "draining";
case "disposed":
return "failed";
default:
return "failed";
}
}
function isCallable(state: ReflectedLifecycleState): boolean {
return state === "active";
}
function computeCatalogHash(
observedAt: string,
resolvedComponentSetHash: string,
entries: readonly CapabilityCatalogEntryV1[],
): string {
const payload = {
schema: "werkstatt/capability-catalog@1",
observedAt,
resolvedComponentSetHash,
entries: entries.map((e) => ({
capability: e.capability,
version: e.version,
schemaHash: e.schemaHash,
componentId: e.componentId,
lifecycleState: e.lifecycleState,
callable: e.callable,
})),
};
return computeSetHash({
schema: "werkstatt/capability-catalog@1" as never,
profileId: "reflection",
components: [],
dependencyGraphHash: resolvedComponentSetHash,
grantSetHash: observedAt,
effectPolicyHash: payload.entries.map((e) => e.capability).join(","),
isolationPolicyHash: payload.entries.map((e) => e.componentId).join(","),
});
}
export function createCapabilityCatalog(input: ReflectionInput): CapabilityCatalogV1 {
const observedAt = input.observedAt ?? new Date().toISOString();
const activeSetHash = input.activeSet.setHash;
const recomputedHash = computeSetHash({
schema: input.activeSet.schema,
profileId: input.activeSet.profileId,
components: input.activeSet.components,
dependencyGraphHash: input.activeSet.dependencyGraphHash,
grantSetHash: input.activeSet.grantSetHash,
effectPolicyHash: input.activeSet.effectPolicyHash,
isolationPolicyHash: input.activeSet.isolationPolicyHash,
});
if (recomputedHash !== activeSetHash) {
throw new Error(
`REFLECTION-01: active set hash mismatch — declared ${activeSetHash} but recomputed ${recomputedHash}`,
);
}
const entries: CapabilityCatalogEntryV1[] = [];
for (const identity of input.activeSet.components) {
const manifest = input.manifests.get(identity.componentId);
if (!manifest) {
continue;
}
const observation = input.observations.get(identity.componentId);
const rawState = observation?.lifecycleState ?? "declared";
const reflectedState = toReflectedState(rawState);
for (const provide of manifest.provides) {
if (input.visibleCapabilities && !input.visibleCapabilities.has(provide.capability)) {
continue;
}
entries.push({
capability: provide.capability,
version: provide.version,
schemaHash: provide.schemaHash,
componentId: manifest.componentId,
lifecycleState: reflectedState,
callable: isCallable(reflectedState),
});
}
}
entries.sort((a, b) => {
const capCmp = a.capability < b.capability ? -1 : a.capability > b.capability ? 1 : 0;
if (capCmp !== 0) return capCmp;
const verCmp = a.version < b.version ? -1 : a.version > b.version ? 1 : 0;
if (verCmp !== 0) return verCmp;
return a.componentId < b.componentId ? -1 : a.componentId > b.componentId ? 1 : 0;
});
const catalogHash = computeCatalogHash(observedAt, activeSetHash, entries);
return {
schema: "werkstatt/capability-catalog@1",
observedAt,
resolvedComponentSetHash: activeSetHash,
entries,
catalogHash,
};
}
export function assertNoForbiddenFields(catalog: CapabilityCatalogV1): void {
const json = JSON.stringify(catalog);
for (const forbidden of FORBIDDEN_FIELDS) {
if (json.includes(forbidden)) {
throw new Error(`REFLECTION-02: catalog contains forbidden field name: ${forbidden}`);
}
}
}
export interface LawKernelSummary {
readonly activeGrants: number;
readonly artifactStoreEntries: number;
readonly killSwitchArmed: boolean;
}
export interface ComponentReflectionV1 {
readonly componentId: ComponentId;
readonly version: string;
readonly artifactHash: string;
readonly fiberState: ReflectedLifecycleState;
readonly provides: readonly CapabilityProvideV1[];
readonly requires: ReadonlyArray<
CapabilityRequireV1 & {
resolvedBy?: ComponentId;
resolution: "resolved" | "waiting" | "missing";
}
>;
readonly commands: readonly string[];
readonly pipelines: readonly string[];
readonly inFlightCount: number;
readonly health?: {
status: "healthy" | "degraded" | "unhealthy" | "unknown";
lastCheckedAt?: string;
latencyMs?: number;
};
readonly requestedGrants: readonly GrantRequestV1[];
readonly effects: readonly EffectDeclarationV1[];
}
export interface RuntimeReflectionV1 {
readonly reflectedAt: string;
readonly components: readonly ComponentReflectionV1[];
readonly dependencyEdges: ReadonlyArray<{
from: ComponentId;
to: ComponentId;
capability: CapabilityId;
}>;
readonly lawKernel: LawKernelSummary;
readonly summary: {
total: number;
active: number;
waiting: number;
draining: number;
failed: number;
quarantined: number;
skippedDisposed: number;
};
}
function buildDependencyEdges(
manifests: ReadonlyMap,
componentIds: ReadonlySet,
): RuntimeReflectionV1["dependencyEdges"] {
const providerMap = new Map();
for (const [compId, manifest] of manifests) {
if (!componentIds.has(compId)) continue;
for (const provide of manifest.provides) {
const existing = providerMap.get(provide.capability);
if (existing) {
existing.push(compId);
} else {
providerMap.set(provide.capability, [compId]);
}
}
}
const edges: Array<{ from: ComponentId; to: ComponentId; capability: CapabilityId }> = [];
for (const [compId, manifest] of manifests) {
if (!componentIds.has(compId)) continue;
for (const require of manifest.requires) {
const providers = providerMap.get(require.capability);
if (providers) {
for (const providerId of providers) {
if (providerId !== compId) {
edges.push({ from: compId, to: providerId, capability: require.capability });
}
}
}
}
}
edges.sort((a, b) => {
const fromCmp = a.from < b.from ? -1 : a.from > b.from ? 1 : 0;
if (fromCmp !== 0) return fromCmp;
const toCmp = a.to < b.to ? -1 : a.to > b.to ? 1 : 0;
if (toCmp !== 0) return toCmp;
return a.capability < b.capability ? -1 : a.capability > b.capability ? 1 : 0;
});
return edges;
}
function resolveRequirement(
require: CapabilityRequireV1,
manifests: ReadonlyMap,
activeComponentIds: ReadonlySet,
): CapabilityRequireV1 & {
resolvedBy?: ComponentId;
resolution: "resolved" | "waiting" | "missing";
} {
for (const [compId, manifest] of manifests) {
if (!activeComponentIds.has(compId)) continue;
for (const provide of manifest.provides) {
if (provide.capability === require.capability) {
return { ...require, resolvedBy: compId, resolution: "resolved" as const };
}
}
}
return { ...require, resolution: require.optional ? "waiting" : "missing" };
}
export function reflectRuntime(
input: ReflectionInput,
lawKernel: LawKernelSummary,
): RuntimeReflectionV1 {
const reflectedAt = input.observedAt ?? new Date().toISOString();
const activeComponentIds = new Set();
for (const identity of input.activeSet.components) {
const observation = input.observations.get(identity.componentId);
if (observation && observation.lifecycleState === "disposed") {
continue;
}
activeComponentIds.add(identity.componentId);
}
const components: ComponentReflectionV1[] = [];
let skippedDisposed = 0;
for (const identity of input.activeSet.components) {
const manifest = input.manifests.get(identity.componentId);
if (!manifest) {
continue;
}
const observation = input.observations.get(identity.componentId);
const rawState = observation?.lifecycleState ?? "declared";
if (rawState === "disposed") {
skippedDisposed++;
continue;
}
const reflectedState = toReflectedState(rawState);
const resolvedRequires = manifest.requires.map((req) =>
resolveRequirement(req, input.manifests, activeComponentIds),
);
components.push({
componentId: manifest.componentId,
version: identity.version,
artifactHash: manifest.artifactHash,
fiberState: reflectedState,
provides: manifest.provides,
requires: resolvedRequires,
commands: [],
pipelines: [],
inFlightCount: 0,
requestedGrants: manifest.requestedGrants,
effects: manifest.effects,
});
}
const dependencyEdges = buildDependencyEdges(input.manifests, activeComponentIds);
const summary = {
total: components.length,
active: components.filter((c) => c.fiberState === "active").length,
waiting: components.filter((c) => c.fiberState === "waiting").length,
draining: components.filter((c) => c.fiberState === "draining").length,
failed: components.filter((c) => c.fiberState === "failed").length,
quarantined: components.filter((c) => c.fiberState === "quarantined").length,
skippedDisposed,
};
return {
reflectedAt,
components,
dependencyEdges,
lawKernel,
summary,
};
}