/*
cutover marker — clean-cutover marker type written when certification cutover completes.
- Do not perform the cutover — this module only defines the marker.
- 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 { ResolvedComponentSetV1 } from "@warpgogol/werkstatt-shared/component";
import type { CertificationHealth } from "../health/monitor.ts";
export interface CleanCutoverMarkerV1 {
schema: "werkstatt/clean-cutover-marker@1";
markerId: string;
systemId: string;
candidateId: string;
releaseId: string;
devDecisionId: string;
altDecisionId: string;
mainVerificationDecisionId: string;
evaluatorDecisionIds: string[];
dossierRoot: Sha256Digest;
mainIdentity: {
candidateId: string;
artifactHash: Sha256Digest;
deployedAt: string;
deploymentUrl: string | null;
};
healthState: CertificationHealth;
healthDecisionId: string | null;
rollbackTarget: {
candidateId: string;
artifactHash: Sha256Digest;
protectedReason: "bootstrap" | "prior-certified";
};
bootstrapExceptionClosed: boolean;
continuousHealthWindowCompleted: boolean;
markerHash: Sha256Digest;
committedAt: string;
}
export interface CutoverVerificationInputV1 {
candidateId: string;
devDecisionId: string;
altDecisionId: string;
mainVerificationDecisionId: string;
evaluatorDecisionIds: string[];
dossierRoot: Sha256Digest;
resolvedComponentSet: ResolvedComponentSetV1;
healthState: CertificationHealth;
healthDecisionId: string | null;
rollbackCandidateId: string;
rollbackArtifactHash: Sha256Digest;
continuousHealthWindowCompleted: boolean;
legacyStateReads: LegacyStateRead[];
}
export interface LegacyStateRead {
command: string;
artifactPath: string;
artifactType: "release-certification" | "grace" | "mission-artifact";
usedForSuccess: boolean;
}
export interface CutoverVerificationResultV1 {
ok: true;
verified: boolean;
marker: CleanCutoverMarkerV1;
}
export interface CutoverVerificationFailureV1 {
ok: false;
ruleId: string;
message: string;
}
export type CutoverVerificationOutcomeV1 =
CutoverVerificationResultV1 | CutoverVerificationFailureV1;
export function verifyCutover(
input: CutoverVerificationInputV1,
systemId: string,
releaseId: string,
deployedAt: string,
deploymentUrl: string | null,
): CutoverVerificationOutcomeV1 {
if (input.legacyStateReads.length > 0) {
const legacyForSuccess = input.legacyStateReads.filter((r) => r.usedForSuccess);
if (legacyForSuccess.length > 0) {
const first = legacyForSuccess[0]!;
return {
ok: false,
ruleId: "CERT-CUTOVER-01",
message: `command "${first.command}" reads legacy ${first.artifactType} at ${first.artifactPath} for success — no runtime command may read legacy state for success`,
};
}
}
if (!input.devDecisionId) {
return {
ok: false,
ruleId: "CERT-CUTOVER-02",
message: "dev gate decision ID is required for cutover",
};
}
if (!input.altDecisionId) {
return {
ok: false,
ruleId: "CERT-CUTOVER-03",
message: "alt gate decision ID is required for cutover",
};
}
if (!input.mainVerificationDecisionId) {
return {
ok: false,
ruleId: "CERT-CUTOVER-04",
message: "main verification decision ID is required for cutover",
};
}
if (input.evaluatorDecisionIds.length === 0) {
return {
ok: false,
ruleId: "CERT-CUTOVER-05",
message: "at least one evaluator decision ID is required for cutover",
};
}
if (!input.continuousHealthWindowCompleted) {
return {
ok: false,
ruleId: "CERT-CUTOVER-06",
message:
"at least one continuous health schedule window must complete successfully before cutover",
};
}
if (input.healthState === "revoked") {
return {
ok: false,
ruleId: "CERT-CUTOVER-07",
message: "health state is revoked — cutover cannot proceed with revoked health",
};
}
if (input.rollbackCandidateId === input.candidateId) {
return {
ok: false,
ruleId: "CERT-CUTOVER-08",
message: "rollback target candidate must differ from the new cutover candidate",
};
}
if (!input.resolvedComponentSet.setHash) {
return {
ok: false,
ruleId: "CERT-CUTOVER-09",
message: "resolved component set must have a valid set hash",
};
}
const marker = buildCutoverMarker({
systemId,
releaseId,
candidateId: input.candidateId,
devDecisionId: input.devDecisionId,
altDecisionId: input.altDecisionId,
mainVerificationDecisionId: input.mainVerificationDecisionId,
evaluatorDecisionIds: input.evaluatorDecisionIds,
dossierRoot: input.dossierRoot,
mainIdentity: {
candidateId: input.candidateId,
artifactHash: input.resolvedComponentSet.setHash as string as Sha256Digest,
deployedAt,
deploymentUrl,
},
healthState: input.healthState,
healthDecisionId: input.healthDecisionId,
rollbackTarget: {
candidateId: input.rollbackCandidateId,
artifactHash: input.rollbackArtifactHash,
protectedReason: "bootstrap",
},
bootstrapExceptionClosed: true,
continuousHealthWindowCompleted: input.continuousHealthWindowCompleted,
committedAt: deployedAt,
});
return {
ok: true,
verified: true,
marker,
};
}
export interface CutoverMarkerBuilderInputV1 {
systemId: string;
releaseId: string;
candidateId: string;
devDecisionId: string;
altDecisionId: string;
mainVerificationDecisionId: string;
evaluatorDecisionIds: string[];
dossierRoot: Sha256Digest;
mainIdentity: {
candidateId: string;
artifactHash: Sha256Digest;
deployedAt: string;
deploymentUrl: string | null;
};
healthState: CertificationHealth;
healthDecisionId: string | null;
rollbackTarget: {
candidateId: string;
artifactHash: Sha256Digest;
protectedReason: "bootstrap" | "prior-certified";
};
bootstrapExceptionClosed: boolean;
continuousHealthWindowCompleted: boolean;
committedAt: string;
}
export function buildCutoverMarker(input: CutoverMarkerBuilderInputV1): CleanCutoverMarkerV1 {
const markerId = `cutover-${input.systemId}-${input.candidateId}`;
const markerData = {
systemId: input.systemId,
releaseId: input.releaseId,
candidateId: input.candidateId,
devDecisionId: input.devDecisionId,
altDecisionId: input.altDecisionId,
mainVerificationDecisionId: input.mainVerificationDecisionId,
evaluatorDecisionIds: input.evaluatorDecisionIds,
dossierRoot: input.dossierRoot,
mainIdentity: input.mainIdentity,
healthState: input.healthState,
healthDecisionId: input.healthDecisionId,
rollbackTarget: input.rollbackTarget,
bootstrapExceptionClosed: input.bootstrapExceptionClosed,
continuousHealthWindowCompleted: input.continuousHealthWindowCompleted,
committedAt: input.committedAt,
};
const markerHash = byteHash(JSON.stringify(markerData)) as Sha256Digest;
return {
schema: "werkstatt/clean-cutover-marker@1",
markerId,
...markerData,
markerHash,
};
}
export interface LegacyProhibitionResultV1 {
ok: true;
clean: boolean;
violations: LegacyStateRead[];
}
export interface LegacyProhibitionFailureV1 {
ok: false;
ruleId: string;
message: string;
violations: LegacyStateRead[];
}
export type LegacyProhibitionOutcomeV1 = LegacyProhibitionResultV1 | LegacyProhibitionFailureV1;
export function checkLegacyStateProhibition(reads: LegacyStateRead[]): LegacyProhibitionOutcomeV1 {
const successReads = reads.filter((r) => r.usedForSuccess);
if (successReads.length === 0) {
return {
ok: true,
clean: true,
violations: [],
};
}
return {
ok: false,
ruleId: "CERT-CUTOVER-01",
message: `${successReads.length} runtime command(s) read legacy state for success — no runtime command may read legacy release certification, grace, or mission artifacts for success after cutover`,
violations: successReads,
};
}
export function isBootstrapExceptionClosed(marker: CleanCutoverMarkerV1): boolean {
return marker.bootstrapExceptionClosed;
}
export function isRollbackTargetProtected(marker: CleanCutoverMarkerV1): boolean {
return marker.rollbackTarget.protectedReason === "bootstrap"
? !marker.bootstrapExceptionClosed
: true;
}
export function verifyMarkerIntegrity(marker: CleanCutoverMarkerV1): boolean {
const markerData = {
systemId: marker.systemId,
releaseId: marker.releaseId,
candidateId: marker.candidateId,
devDecisionId: marker.devDecisionId,
altDecisionId: marker.altDecisionId,
mainVerificationDecisionId: marker.mainVerificationDecisionId,
evaluatorDecisionIds: marker.evaluatorDecisionIds,
dossierRoot: marker.dossierRoot,
mainIdentity: marker.mainIdentity,
healthState: marker.healthState,
healthDecisionId: marker.healthDecisionId,
rollbackTarget: marker.rollbackTarget,
bootstrapExceptionClosed: marker.bootstrapExceptionClosed,
continuousHealthWindowCompleted: marker.continuousHealthWindowCompleted,
committedAt: marker.committedAt,
};
const expectedHash = byteHash(JSON.stringify(markerData)) as Sha256Digest;
return expectedHash === marker.markerHash;
}