/*
evolution guards — guard results gating evolution stage transitions between states.
- Do not transition — guards only permit or deny.
- 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 { Sha256Digest } from "@warpgogol/werkstatt-shared/fingerprint";
import { byteHash } from "@warpgogol/werkstatt-shared/fingerprint";
import type {
CapabilityCandidateV1,
EvolutionEvidenceBundleV1,
TransitionRequestV1,
BoundedIntentV1,
KillSwitchStateV1,
} from "./contracts.ts";
export type GuardResultV1 = {
ok: boolean;
ruleId: string;
message: string;
};
const FORBIDDEN_SELF_CHANGE_SCOPES = new Set([
"law-kernel",
"permissions",
"effect-policy",
"isolation-contract",
"canonical-identity",
"diagnostic-schema",
"controller-code",
"evaluator-policy",
]);
export function checkSelfChangeBoundary(intent: BoundedIntentV1): GuardResultV1 {
if (FORBIDDEN_SELF_CHANGE_SCOPES.has(intent.scope)) {
return {
ok: false,
ruleId: "CERT-EVO-GUARD-01",
message: `controller cannot propose changes to forbidden scope "${intent.scope}" — requires human-governed RFC/program/release path`,
};
}
return { ok: true, ruleId: "", message: "" };
}
export function checkEvidenceImmutability(
candidate: CapabilityCandidateV1,
evidence: EvolutionEvidenceBundleV1,
): GuardResultV1 {
if (evidence.artifact.candidateHash !== candidate.artifactHash) {
return {
ok: false,
ruleId: "CERT-EVO-GUARD-02",
message: `evidence artifact candidate hash ${evidence.artifact.candidateHash} does not match candidate artifact hash ${candidate.artifactHash}`,
};
}
if (evidence.artifact.parentHash !== candidate.parentArtifactHash) {
return {
ok: false,
ruleId: "CERT-EVO-GUARD-03",
message: `evidence artifact parent hash ${evidence.artifact.parentHash} does not match candidate parent hash ${candidate.parentArtifactHash}`,
};
}
return { ok: true, ruleId: "", message: "" };
}
export function checkKillSwitch(killSwitch: KillSwitchStateV1): GuardResultV1 {
if (killSwitch.active) {
return {
ok: false,
ruleId: "CERT-EVO-GUARD-04",
message: `kill switch is active: ${killSwitch.reason} — all transitions denied`,
};
}
return { ok: true, ruleId: "", message: "" };
}
export function checkAuthorityExpiry(
evidence: EvolutionEvidenceBundleV1,
currentTimestamp: string,
): GuardResultV1 {
if (evidence.authority.expiry <= currentTimestamp) {
return {
ok: false,
ruleId: "CERT-EVO-GUARD-05",
message: `authority evidence expired: expiry ${evidence.authority.expiry} <= current ${currentTimestamp}`,
};
}
return { ok: true, ruleId: "", message: "" };
}
export function checkShadowSideEffects(evidence: EvolutionEvidenceBundleV1): GuardResultV1 {
if (evidence.observation.exposure === "shadow") {
if (evidence.observation.incidents.some((i) => i.severity === "critical")) {
return {
ok: false,
ruleId: "CERT-EVO-GUARD-06",
message: "shadow stage produced critical incident — side effects are forbidden in shadow",
};
}
}
return { ok: true, ruleId: "", message: "" };
}
export function checkCanaryBoundaries(
evidence: EvolutionEvidenceBundleV1,
maxCanaryDuration: number,
minSampleSize: number,
): GuardResultV1 {
if (evidence.observation.exposure !== "canary") {
return { ok: true, ruleId: "", message: "" };
}
if (evidence.observation.duration > maxCanaryDuration) {
return {
ok: false,
ruleId: "CERT-EVO-GUARD-07",
message: `canary duration ${evidence.observation.duration} exceeds max ${maxCanaryDuration}`,
};
}
if (evidence.observation.sampleSize < minSampleSize) {
return {
ok: false,
ruleId: "CERT-EVO-GUARD-08",
message: `canary sample size ${evidence.observation.sampleSize} below minimum ${minSampleSize}`,
};
}
if (evidence.observation.uncertaintyScore > 0.3) {
return {
ok: false,
ruleId: "CERT-EVO-GUARD-09",
message: `canary uncertainty score ${evidence.observation.uncertaintyScore} exceeds threshold 0.3`,
};
}
return { ok: true, ruleId: "", message: "" };
}
export function checkEvidencePoisoning(evidence: EvolutionEvidenceBundleV1): GuardResultV1 {
const allHashes: string[] = [
evidence.definition.evidenceHash,
evidence.evaluation.evidenceHash,
evidence.observation.evidenceHash,
evidence.authority.evidenceHash,
evidence.artifact.evidenceHash,
evidence.bundleHash,
];
const uniqueHashes = new Set(allHashes);
if (uniqueHashes.size !== allHashes.length) {
return {
ok: false,
ruleId: "CERT-EVO-GUARD-10",
message: "evidence hash collision detected — possible evidence poisoning",
};
}
const recomputedBundle = byteHash(
JSON.stringify({
definition: evidence.definition.evidenceHash,
evaluation: evidence.evaluation.evidenceHash,
observation: evidence.observation.evidenceHash,
authority: evidence.authority.evidenceHash,
artifact: evidence.artifact.evidenceHash,
}),
) as Sha256Digest;
if (recomputedBundle !== evidence.bundleHash) {
return {
ok: false,
ruleId: "CERT-EVO-GUARD-11",
message: `bundle hash mismatch: recomputed ${recomputedBundle} does not match declared ${evidence.bundleHash}`,
};
}
return { ok: true, ruleId: "", message: "" };
}
export function checkActivatingTransaction(evidence: EvolutionEvidenceBundleV1): GuardResultV1 {
if (!evidence.artifact) {
return {
ok: false,
ruleId: "CERT-EVO-GUARD-12",
message: "activating stage requires artifact evidence for transaction boundary",
};
}
if (!evidence.authority) {
return {
ok: false,
ruleId: "CERT-EVO-GUARD-13",
message: "activating stage requires authority evidence for transaction boundary",
};
}
return { ok: true, ruleId: "", message: "" };
}
export function checkActiveHealthMonitoring(evidence: EvolutionEvidenceBundleV1): GuardResultV1 {
if (!evidence.observation) {
return {
ok: false,
ruleId: "CERT-EVO-GUARD-14",
message: "active stage requires observation evidence for health monitoring",
};
}
return { ok: true, ruleId: "", message: "" };
}
export function runAllGuards(
candidate: CapabilityCandidateV1,
request: TransitionRequestV1,
killSwitch: KillSwitchStateV1,
currentTimestamp: string,
maxCanaryDuration: number,
minSampleSize: number,
): GuardResultV1 {
const checks: GuardResultV1[] = [
checkKillSwitch(killSwitch),
checkEvidenceImmutability(candidate, request.evidence),
checkAuthorityExpiry(request.evidence, currentTimestamp),
checkShadowSideEffects(request.evidence),
checkCanaryBoundaries(request.evidence, maxCanaryDuration, minSampleSize),
checkEvidencePoisoning(request.evidence),
checkActivatingTransaction(request.evidence),
checkActiveHealthMonitoring(request.evidence),
];
for (const check of checks) {
if (!check.ok) {
return check;
}
}
return { ok: true, ruleId: "", message: "" };
}