/*
retention — certification artifact retention policy and protected reference tracking.
- Do not delete artifacts — retention only decides what is protected.
- 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";
export type ProtectedReference = "current" | "rollback-target" | "open-incident" | "audit-hold";
export interface RetentionPolicyInputV1 {
compactAudit: "indefinite";
certifiedFullDossierAfterSupersessionDays: number;
unsuccessfulEvidenceDays: number;
certifiedHeavyPayloadDays: number;
unsuccessfulHeavyPayloadDays: number;
protectedReferences: readonly ProtectedReference[];
}
export interface RetentionEntryV1 {
payloadDigest: Sha256Digest;
sizeBytes: number;
mediaType: string;
retentionPolicyHash: Sha256Digest;
reason: string;
deletedAt: string;
}
export interface RetentionTombstoneV1 {
schema: "werkstatt/retention-tombstone@1";
tombstoneId: string;
payloadDigest: Sha256Digest;
sizeBytes: number;
mediaType: string;
retentionPolicyHash: Sha256Digest;
reason: string;
tombstonedAt: string;
}
export interface RetentionGcCheckInputV1 {
payloadDigest: Sha256Digest;
policy: RetentionPolicyInputV1;
protectedRefs: Set;
ageDays: number;
isCertified: boolean;
}
export interface RetentionGcCheckResultV1 {
canDelete: boolean;
reason: string;
}
export function checkRetentionGc(input: RetentionGcCheckInputV1): RetentionGcCheckResultV1 {
for (const ref of input.protectedRefs) {
if (input.policy.protectedReferences.includes(ref)) {
return {
canDelete: false,
reason: `payload is protected by reference "${ref}"`,
};
}
}
const maxDays = input.isCertified
? input.policy.certifiedHeavyPayloadDays
: input.policy.unsuccessfulHeavyPayloadDays;
if (input.ageDays < maxDays) {
return {
canDelete: false,
reason: `payload age ${input.ageDays} days is below retention threshold ${maxDays} days`,
};
}
return {
canDelete: true,
reason: `payload age ${input.ageDays} days exceeds retention threshold ${maxDays} days and no protected references`,
};
}
export function createTombstone(
entry: RetentionEntryV1,
tombstoneId: string,
): RetentionTombstoneV1 {
return {
schema: "werkstatt/retention-tombstone@1",
tombstoneId,
payloadDigest: entry.payloadDigest,
sizeBytes: entry.sizeBytes,
mediaType: entry.mediaType,
retentionPolicyHash: entry.retentionPolicyHash,
reason: entry.reason,
tombstonedAt: entry.deletedAt,
};
}
export interface DurableReplicaVerifyInputV1 {
adapterId: string;
locator: string;
verifiedRootHash: Sha256Digest;
verifiedAt: string;
}
export interface DurableReplicaV1 {
adapterId: string;
locator: string;
verifiedRootHash: Sha256Digest;
verifiedAt: string;
}
export interface DurableReplicaVerifyResultV1 {
ok: true;
replica: DurableReplicaV1;
}
export interface DurableReplicaVerifyFailureV1 {
ok: false;
ruleId: "CERT-STORAGE-03";
message: string;
}
export type DurableReplicaVerifyOutcomeV1 =
DurableReplicaVerifyResultV1 | DurableReplicaVerifyFailureV1;
export function verifyDurableReplica(
input: DurableReplicaVerifyInputV1,
expectedRootHash: Sha256Digest,
): DurableReplicaVerifyOutcomeV1 {
if (input.verifiedRootHash !== expectedRootHash) {
return {
ok: false,
ruleId: "CERT-STORAGE-03",
message: `durable replica root hash mismatch: expected ${expectedRootHash}, got ${input.verifiedRootHash}`,
};
}
return {
ok: true,
replica: {
adapterId: input.adapterId,
locator: input.locator,
verifiedRootHash: input.verifiedRootHash,
verifiedAt: input.verifiedAt,
},
};
}