{"version":3,"file":"registry.d.ts","sourceRoot":"","sources":["../../src/registry/registry.ts"],"names":[],"mappings":"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","sourcesContent":["import { randomUUID } from \"node:crypto\";\nimport { readdir, readFile } from \"node:fs/promises\";\nimport { join } from \"node:path\";\nimport { loadCompiledBundle } from \"../bundle/compile.ts\";\nimport { isDigest } from \"../bundle/schema.ts\";\nimport { loadEvoComponentArtifact } from \"../components/artifact.ts\";\nimport { EvoMemoryStore, usesEvoComponentMemory } from \"../components/memory/store.ts\";\nimport { type EvoPaths, getEvoPaths } from \"../paths.ts\";\nimport { saveProposalRevisionSnapshot, validateEvaluationArtifact } from \"../proposal-artifacts.ts\";\nimport {\n\tappendJsonLine,\n\tatomicWriteFile,\n\tatomicWriteJson,\n\tcanonicalJson,\n\tdurableUnlink,\n\treadJsonIfExists,\n\tsha256,\n\ttruncateIncompleteFinalLine,\n\twithFileLock,\n} from \"../storage.ts\";\nimport type {\n\tCompiledBundle,\n\tDataApprovalActivation,\n\tEvoStatus,\n\tHistoryEntry,\n\tProposal,\n\tProposalStatus,\n\tTrialState,\n} from \"../types.ts\";\n\nasync function readTextIfExists(path: string): Promise<string | undefined> {\n\ttry {\n\t\treturn await readFile(path, \"utf8\");\n\t} catch (error) {\n\t\tif (typeof error === \"object\" && error !== null && \"code\" in error && error.code === \"ENOENT\") return undefined;\n\t\tthrow error;\n\t}\n}\n\nexport type RegistryTransitionAction =\n\t| \"initialize\"\n\t| \"activate-trial\"\n\t| \"approve-data\"\n\t| \"approve-code\"\n\t| \"keep-trial\"\n\t| \"direct-keep-trial\"\n\t| \"keep-proposal\"\n\t| \"rollback\"\n\t| \"rollback-proposal\"\n\t| \"record-decision\"\n\t| \"record-deferral\"\n\t| \"record-reopen\"\n\t| \"reject-proposal\"\n\t| \"defer-proposal\"\n\t| \"reopen-proposal\"\n\t| \"pause\"\n\t| \"resume\";\n\nexport type RegistryTransitionStep =\n\t| \"prepared\"\n\t| \"trial-written\"\n\t| \"stable-written\"\n\t| \"paused-written\"\n\t| \"proposal-written\"\n\t| \"snapshot-written\"\n\t| \"history-appended\"\n\t| \"memory-rolled-back\"\n\t| \"trial-cleared\"\n\t| \"paused-cleared\"\n\t| \"receipt-appended\"\n\t| \"committed\";\n\nexport interface BundleRegistryOptions {\n\tafterTransitionStep?: (step: RegistryTransitionStep, action: RegistryTransitionAction) => void | Promise<void>;\n}\n\nexport interface RegistryOperationState {\n\tdigest: string;\n\tlatestOperationId?: string;\n}\n\nexport interface RegistryMemoryLifecycleState {\n\tstableDigest: string | undefined;\n\ttrial: TrialState | undefined;\n\tverifiedBundleLineage: readonly string[];\n}\n\ntype HistoryTemplate = Omit<HistoryEntry, \"eventId\" | \"timestamp\">;\n\ninterface PendingTransition {\n\tschemaVersion: 1;\n\toperationId: string;\n\trequestKey: string;\n\taction: RegistryTransitionAction;\n\tcreatedAt: string;\n\tstableBefore: string | null;\n\tstableAfter: string | null;\n\ttrialBefore: TrialState | null;\n\ttrialAfter: TrialState | null;\n\tpausedBefore: string | null;\n\tpausedAfter: string | null;\n\tproposalBefore: Proposal | null;\n\tproposalAfter: Proposal | null;\n\thistory: HistoryEntry[];\n}\n\ninterface OperationReceipt {\n\tschemaVersion: 1;\n\toperationId: string;\n\trequestKey: string;\n\taction: RegistryTransitionAction;\n\tcompletedAt: string;\n\tstableBefore: string | null;\n\tstableAfter: string | null;\n\ttrialBefore: TrialState | null;\n\ttrialAfter: TrialState | null;\n\tproposalAfter: Proposal | null;\n}\n\ninterface ProposalTransitionOptions {\n\tidempotencyKey?: string;\n\texpectedStateDigest?: string;\n\taction: \"reject-proposal\" | \"defer-proposal\" | \"reopen-proposal\";\n\tproposalBefore: Proposal;\n\tproposalAfter: Proposal;\n\treason: string;\n}\n\nconst TRANSITION_ACTIONS = new Set<RegistryTransitionAction>([\n\t\"initialize\",\n\t\"activate-trial\",\n\t\"approve-data\",\n\t\"approve-code\",\n\t\"keep-trial\",\n\t\"direct-keep-trial\",\n\t\"keep-proposal\",\n\t\"rollback\",\n\t\"rollback-proposal\",\n\t\"record-decision\",\n\t\"record-deferral\",\n\t\"record-reopen\",\n\t\"reject-proposal\",\n\t\"defer-proposal\",\n\t\"reopen-proposal\",\n\t\"pause\",\n\t\"resume\",\n]);\nconst PROPOSAL_STATUSES = new Set<ProposalStatus>([\n\t\"pending\",\n\t\"deferred\",\n\t\"approved\",\n\t\"rejected\",\n\t\"trialing\",\n\t\"kept\",\n\t\"rolled-back\",\n]);\n\nfunction assertTrialState(value: unknown, label: string): asserts value is TrialState | null {\n\tif (value === null) return;\n\tif (typeof value !== \"object\" || Array.isArray(value)) throw new Error(`${label} is invalid`);\n\tconst trial = value as Record<string, unknown>;\n\tif (\n\t\ttypeof trial.digest !== \"string\" ||\n\t\t!isDigest(trial.digest) ||\n\t\ttypeof trial.parent !== \"string\" ||\n\t\t!isDigest(trial.parent) ||\n\t\ttypeof trial.proposalId !== \"string\" ||\n\t\t!/^[A-Za-z0-9][A-Za-z0-9._-]{0,127}$/.test(trial.proposalId) ||\n\t\ttypeof trial.startedAt !== \"string\" ||\n\t\t!Number.isFinite(Date.parse(trial.startedAt)) ||\n\t\ttypeof trial.plan !== \"string\" ||\n\t\t!trial.plan.trim() ||\n\t\t(trial.canary !== undefined &&\n\t\t\t(typeof trial.canary !== \"object\" ||\n\t\t\t\ttrial.canary === null ||\n\t\t\t\tArray.isArray(trial.canary) ||\n\t\t\t\t((trial.canary as Record<string, unknown>).customization !== \"default\" &&\n\t\t\t\t\t(trial.canary as Record<string, unknown>).customization !== \"custom\") ||\n\t\t\t\t!Number.isSafeInteger((trial.canary as Record<string, unknown>).minimumSamples) ||\n\t\t\t\t((trial.canary as Record<string, unknown>).minimumSamples as number) <= 0 ||\n\t\t\t\t!Number.isSafeInteger((trial.canary as Record<string, unknown>).maximumDurationDays) ||\n\t\t\t\t((trial.canary as Record<string, unknown>).maximumDurationDays as number) <= 0))\n\t) {\n\t\tthrow new Error(`${label} is invalid`);\n\t}\n}\n\nfunction assertProposalState(value: Proposal | null, label: string): void {\n\tif (value === null) return;\n\tif (\n\t\tvalue.schemaVersion !== 2 ||\n\t\t!/^p-[A-Za-z0-9._-]+$/.test(value.id) ||\n\t\t!Number.isSafeInteger(value.revision) ||\n\t\tvalue.revision <= 0 ||\n\t\t!isDigest(value.parentBundleDigest) ||\n\t\t!isDigest(value.diffDigest) ||\n\t\t!PROPOSAL_STATUSES.has(value.status)\n\t) {\n\t\tthrow new Error(`${label} is invalid`);\n\t}\n}\n\nfunction isOperationId(value: unknown): value is string {\n\treturn typeof value === \"string\" && /^[A-Za-z0-9._:-]{1,256}$/.test(value);\n}\n\nfunction assertOperationReceipt(value: OperationReceipt): void {\n\tif (\n\t\tvalue.schemaVersion !== 1 ||\n\t\t!isOperationId(value.operationId) ||\n\t\t!isDigest(value.requestKey) ||\n\t\t!TRANSITION_ACTIONS.has(value.action) ||\n\t\t!Number.isFinite(Date.parse(value.completedAt))\n\t) {\n\t\tthrow new Error(\"registry/receipts.jsonl contains an invalid receipt\");\n\t}\n\tfor (const digest of [value.stableBefore, value.stableAfter]) {\n\t\tif (digest !== null && !isDigest(digest)) {\n\t\t\tthrow new Error(\"registry/receipts.jsonl contains an invalid digest\");\n\t\t}\n\t}\n\tassertTrialState(value.trialBefore, \"Registry receipt trialBefore\");\n\tassertTrialState(value.trialAfter, \"Registry receipt trialAfter\");\n\tassertProposalState(value.proposalAfter, \"Registry receipt proposalAfter\");\n\tif (value.trialAfter && value.stableAfter !== value.trialAfter.digest) {\n\t\tthrow new Error(\"Registry receipt trial does not match its stable after-image\");\n\t}\n}\n\nfunction receiptFromTransition(transition: PendingTransition): OperationReceipt {\n\treturn {\n\t\tschemaVersion: 1,\n\t\toperationId: transition.operationId,\n\t\trequestKey: transition.requestKey,\n\t\taction: transition.action,\n\t\tcompletedAt: transition.createdAt,\n\t\tstableBefore: transition.stableBefore,\n\t\tstableAfter: transition.stableAfter,\n\t\ttrialBefore: transition.trialBefore,\n\t\ttrialAfter: transition.trialAfter,\n\t\tproposalAfter: transition.proposalAfter,\n\t};\n}\n\nfunction assertPendingTransition(value: PendingTransition): void {\n\tif (\n\t\tvalue.schemaVersion !== 1 ||\n\t\t!isOperationId(value.operationId) ||\n\t\t!isDigest(value.requestKey) ||\n\t\t!TRANSITION_ACTIONS.has(value.action) ||\n\t\t!Number.isFinite(Date.parse(value.createdAt)) ||\n\t\t!Array.isArray(value.history)\n\t) {\n\t\tthrow new Error(\"registry/transition.json is invalid\");\n\t}\n\tfor (const [label, digest] of [\n\t\t[\"stableBefore\", value.stableBefore],\n\t\t[\"stableAfter\", value.stableAfter],\n\t] as const) {\n\t\tif (digest !== null && !isDigest(digest)) throw new Error(`Registry transition ${label} is invalid`);\n\t}\n\tassertTrialState(value.trialBefore, \"Registry transition trialBefore\");\n\tassertTrialState(value.trialAfter, \"Registry transition trialAfter\");\n\tassertProposalState(value.proposalBefore, \"Registry transition proposalBefore\");\n\tassertProposalState(value.proposalAfter, \"Registry transition proposalAfter\");\n\tif ((value.proposalBefore === null) !== (value.proposalAfter === null)) {\n\t\tthrow new Error(\"Registry transition proposal before/after images disagree\");\n\t}\n\tif (\n\t\tvalue.proposalBefore &&\n\t\tvalue.proposalAfter &&\n\t\t(value.proposalBefore.id !== value.proposalAfter.id ||\n\t\t\tvalue.proposalBefore.revision !== value.proposalAfter.revision ||\n\t\t\tvalue.proposalBefore.diffDigest !== value.proposalAfter.diffDigest)\n\t) {\n\t\tthrow new Error(\"Registry transition proposal identity changed\");\n\t}\n\tif (value.trialAfter && value.stableAfter !== value.trialAfter.digest) {\n\t\tthrow new Error(\"Registry transition trial does not match its stable after-image\");\n\t}\n\tfor (const [index, entry] of value.history.entries()) {\n\t\tif (\n\t\t\tentry.eventId !== `${value.operationId}:${index}` ||\n\t\t\tentry.timestamp !== value.createdAt ||\n\t\t\t!entry.reason ||\n\t\t\t(entry.actor !== \"human\" && entry.actor !== \"system\")\n\t\t) {\n\t\t\tthrow new Error(\"Registry transition history is invalid\");\n\t\t}\n\t}\n}\n\nfunction sameState(left: unknown, right: unknown): boolean {\n\treturn canonicalJson(left) === canonicalJson(right);\n}\n\nfunction transitionRequestKey(value: unknown): string {\n\treturn sha256(canonicalJson(value));\n}\n\nasync function rollbackBundleMemory(\n\tpaths: EvoPaths,\n\tsourceBundleDigest: string,\n\ttargetBundleDigest: string,\n): Promise<void> {\n\tconst lineage: CompiledBundle[] = [];\n\tlet digest = sourceBundleDigest;\n\tconst visited = new Set<string>();\n\twhile (!visited.has(digest)) {\n\t\tvisited.add(digest);\n\t\tconst bundle = await loadCompiledBundle(paths, digest);\n\t\tlineage.push(bundle);\n\t\tif (digest === targetBundleDigest) break;\n\t\tif (!bundle.manifest.parentDigest) {\n\t\t\tthrow new Error(`Rollback target ${targetBundleDigest} is not in the source bundle lineage`);\n\t\t}\n\t\tdigest = bundle.manifest.parentDigest;\n\t}\n\tif (lineage.at(-1)?.digest !== targetBundleDigest) {\n\t\tthrow new Error(`Rollback target ${targetBundleDigest} is not in the source bundle lineage`);\n\t}\n\tconst targetIndex = lineage.length - 1;\n\tconst targetAncestorBundleDigests: string[] = [];\n\tlet ancestorDigest = lineage[targetIndex]?.manifest.parentDigest ?? null;\n\twhile (ancestorDigest) {\n\t\tif (visited.has(ancestorDigest)) throw new Error(\"Rollback target ancestor lineage contains a cycle\");\n\t\tvisited.add(ancestorDigest);\n\t\tconst ancestor = await loadCompiledBundle(paths, ancestorDigest);\n\t\ttargetAncestorBundleDigests.push(ancestor.digest);\n\t\tancestorDigest = ancestor.manifest.parentDigest;\n\t}\n\tconst participants = new Map<string, { store: EvoMemoryStore; targetSelected: boolean }>();\n\tfor (const [index, bundle] of lineage.entries()) {\n\t\tconst selections = [\n\t\t\t...Object.entries(bundle.policy.components ?? {}).map(([surface, selection]) => ({ surface, selection })),\n\t\t\t...(bundle.policy.tools ?? []).map((selection) => ({ surface: \"tool\", selection })),\n\t\t\t...(bundle.policy.workflows ?? []).map((selection) => ({ surface: \"workflow\", selection })),\n\t\t];\n\t\tfor (const { surface, selection } of selections) {\n\t\t\tconst artifact = await loadEvoComponentArtifact(paths, selection.artifactDigest);\n\t\t\tif (artifact.manifest.id !== selection.id || artifact.manifest.abi !== selection.abi) {\n\t\t\t\tthrow new Error(`Component selection identity changed before memory rollback: ${selection.id}`);\n\t\t\t}\n\t\t\tif (!usesEvoComponentMemory(surface, artifact.manifest.capabilities)) continue;\n\t\t\tconst existing = participants.get(artifact.manifest.artifactDigest);\n\t\t\tif (existing) {\n\t\t\t\tif (index === targetIndex) existing.targetSelected = true;\n\t\t\t\tcontinue;\n\t\t\t}\n\t\t\tparticipants.set(artifact.manifest.artifactDigest, {\n\t\t\t\tstore: new EvoMemoryStore({\n\t\t\t\t\tpaths,\n\t\t\t\t\tcomponentId: artifact.manifest.id,\n\t\t\t\t\tartifactDigest: artifact.manifest.artifactDigest,\n\t\t\t\t}),\n\t\t\t\ttargetSelected: index === targetIndex,\n\t\t\t});\n\t\t}\n\t}\n\tconst rolledBackBundleDigests = lineage.slice(0, -1).map((bundle) => bundle.digest);\n\tfor (const participant of participants.values()) {\n\t\tawait participant.store.preflightBundleRollback({\n\t\t\trolledBackBundleDigests,\n\t\t\ttargetBundleDigest,\n\t\t\ttargetAncestorBundleDigests,\n\t\t\ttargetSelected: participant.targetSelected,\n\t\t});\n\t}\n\tfor (const participant of participants.values()) {\n\t\tawait participant.store.rollbackBundles({\n\t\t\trolledBackBundleDigests,\n\t\t\ttargetBundleDigest,\n\t\t\ttargetAncestorBundleDigests,\n\t\t\ttargetSelected: participant.targetSelected,\n\t\t});\n\t}\n}\n\nasync function assertRequiredApprovalArtifact(\n\tpaths: EvoPaths,\n\tproposal: Proposal,\n\tkind: \"review\" | \"replay\" | \"validation\",\n): Promise<void> {\n\tconst reference = proposal.artifacts[kind];\n\tif (!reference) throw new Error(`Proposal ${proposal.id} is missing required ${kind} artifact`);\n\tawait validateEvaluationArtifact({\n\t\tpaths,\n\t\tproposalId: proposal.id,\n\t\trevision: proposal.revision,\n\t\tdiffDigest: proposal.diffDigest,\n\t\tkind,\n\t\treference,\n\t});\n}\n\nexport class BundleRegistry {\n\treadonly paths: EvoPaths;\n\tprivate readonly options: BundleRegistryOptions;\n\n\tconstructor(paths: EvoPaths = getEvoPaths(), options: BundleRegistryOptions = {}) {\n\t\tthis.paths = paths;\n\t\tthis.options = options;\n\t}\n\n\tprivate async readStableDigestUnlocked(): Promise<string | undefined> {\n\t\tconst digest = (await readTextIfExists(this.paths.stable))?.trim();\n\t\tif (!digest) return undefined;\n\t\tif (!isDigest(digest)) throw new Error(\"registry/stable contains an invalid digest\");\n\t\treturn digest;\n\t}\n\n\tasync readStableDigest(): Promise<string | undefined> {\n\t\treturn withFileLock(this.paths, \"registry\", async () => {\n\t\t\tawait this.recoverPendingUnlocked();\n\t\t\treturn this.readStableDigestUnlocked();\n\t\t});\n\t}\n\n\tprivate async readTrialUnlocked(): Promise<TrialState | undefined> {\n\t\tconst trial = await readJsonIfExists<TrialState>(this.paths.trial);\n\t\tif (!trial) return undefined;\n\t\tassertTrialState(trial, \"registry/trial.json\");\n\t\treturn trial;\n\t}\n\n\tasync readTrial(): Promise<TrialState | undefined> {\n\t\treturn withFileLock(this.paths, \"registry\", async () => {\n\t\t\tawait this.recoverPendingUnlocked();\n\t\t\treturn this.readTrialUnlocked();\n\t\t});\n\t}\n\n\tasync withMemoryLifecycle<T>(operation: (state: RegistryMemoryLifecycleState) => Promise<T>): Promise<T> {\n\t\treturn withFileLock(this.paths, \"registry\", async () => {\n\t\t\tawait this.recoverPendingUnlocked();\n\t\t\tconst stableDigest = await this.readStableDigestUnlocked();\n\t\t\tconst trial = await this.readTrialUnlocked();\n\t\t\tconst verifiedBundleLineage: string[] = [];\n\t\t\tconst visited = new Set<string>();\n\t\t\tlet digest = stableDigest;\n\t\t\twhile (digest) {\n\t\t\t\tif (visited.has(digest)) throw new Error(\"Active bundle lineage contains a cycle\");\n\t\t\t\tvisited.add(digest);\n\t\t\t\tconst bundle = await loadCompiledBundle(this.paths, digest);\n\t\t\t\tverifiedBundleLineage.push(bundle.digest);\n\t\t\t\tdigest = bundle.manifest.parentDigest ?? undefined;\n\t\t\t}\n\t\t\tif (trial && (verifiedBundleLineage[0] !== trial.digest || verifiedBundleLineage[1] !== trial.parent)) {\n\t\t\t\tthrow new Error(\"Active trial does not match the verified bundle lineage\");\n\t\t\t}\n\t\t\treturn operation({\n\t\t\t\tstableDigest,\n\t\t\t\ttrial,\n\t\t\t\tverifiedBundleLineage,\n\t\t\t});\n\t\t});\n\t}\n\n\tprivate async readPausedUnlocked(): Promise<string | null> {\n\t\treturn (await readTextIfExists(this.paths.paused)) ?? null;\n\t}\n\n\tprivate async readProposal(id: string): Promise<Proposal | null> {\n\t\treturn (await readJsonIfExists<Proposal>(join(this.paths.proposals, id, \"proposal.json\"))) ?? null;\n\t}\n\n\tprivate withProposalLock<T>(id: string, operation: () => Promise<T>): Promise<T> {\n\t\treturn withFileLock(this.paths, `proposal-${id}`, operation, { staleAfterMs: 4 * 60 * 60 * 1000 });\n\t}\n\n\tprivate async afterStep(step: RegistryTransitionStep, action: RegistryTransitionAction): Promise<void> {\n\t\tawait this.options.afterTransitionStep?.(step, action);\n\t}\n\n\tprivate async readHistory(): Promise<HistoryEntry[]> {\n\t\tconst content = await readTextIfExists(this.paths.history);\n\t\tif (content === undefined || content === \"\") return [];\n\t\tif (!content.endsWith(\"\\n\")) throw new Error(\"registry/history.jsonl has an incomplete final line\");\n\t\treturn content\n\t\t\t.slice(0, -1)\n\t\t\t.split(\"\\n\")\n\t\t\t.map((line, index) => {\n\t\t\t\tlet entry: HistoryEntry;\n\t\t\t\ttry {\n\t\t\t\t\tentry = JSON.parse(line) as HistoryEntry;\n\t\t\t\t} catch {\n\t\t\t\t\tthrow new Error(`registry/history.jsonl line ${index + 1} is invalid JSON`);\n\t\t\t\t}\n\t\t\t\tif (!entry.timestamp || !entry.action || !entry.reason) {\n\t\t\t\t\tthrow new Error(`registry/history.jsonl line ${index + 1} is invalid`);\n\t\t\t\t}\n\t\t\t\treturn entry;\n\t\t\t});\n\t}\n\tprivate async assertRollbackTargetAllowed(stableDigest: string, targetDigest: string): Promise<void> {\n\t\tif (!isDigest(stableDigest) || !isDigest(targetDigest)) throw new Error(\"Rollback target digest is invalid\");\n\t\tconst wasCommittedStable = (await this.readHistory()).some(\n\t\t\t(entry) =>\n\t\t\t\tentry.toDigest === targetDigest &&\n\t\t\t\t(entry.action === \"initialize\" ||\n\t\t\t\t\tentry.action === \"trial-start\" ||\n\t\t\t\t\tentry.action === \"proposal-approved\" ||\n\t\t\t\t\tentry.action === \"rollback\"),\n\t\t);\n\t\tif (!wasCommittedStable) {\n\t\t\tthrow new Error(`Rollback target ${targetDigest} was never committed as stable`);\n\t\t}\n\n\t\tconst visited = new Set<string>();\n\t\tlet cursor = stableDigest;\n\t\twhile (!visited.has(cursor)) {\n\t\t\tvisited.add(cursor);\n\t\t\tconst parent = (await loadCompiledBundle(this.paths, cursor)).manifest.parentDigest;\n\t\t\tif (!parent) break;\n\t\t\tif (parent === targetDigest) return;\n\t\t\tcursor = parent;\n\t\t}\n\t\tthrow new Error(`Rollback target ${targetDigest} is not an ancestor of stable bundle ${stableDigest}`);\n\t}\n\n\tprivate async appendHistoryOnce(entry: HistoryEntry): Promise<void> {\n\t\tif (!entry.eventId) throw new Error(\"Transactional history entries require an eventId\");\n\t\tconst existing = (await this.readHistory()).find((candidate) => candidate.eventId === entry.eventId);\n\t\tif (existing) {\n\t\t\tif (!sameState(existing, entry)) throw new Error(`History event ${entry.eventId} has conflicting content`);\n\t\t\treturn;\n\t\t}\n\t\tawait appendJsonLine(this.paths.history, entry);\n\t}\n\n\tprivate async readReceipts(): Promise<OperationReceipt[]> {\n\t\tconst content = await readTextIfExists(this.paths.receipts);\n\t\tif (content === undefined || content === \"\") return [];\n\t\tif (!content.endsWith(\"\\n\")) throw new Error(\"registry/receipts.jsonl has an incomplete final line\");\n\t\treturn content\n\t\t\t.slice(0, -1)\n\t\t\t.split(\"\\n\")\n\t\t\t.map((line, index) => {\n\t\t\t\tlet receipt: OperationReceipt;\n\t\t\t\ttry {\n\t\t\t\t\treceipt = JSON.parse(line) as OperationReceipt;\n\t\t\t\t} catch {\n\t\t\t\t\tthrow new Error(`registry/receipts.jsonl line ${index + 1} is invalid JSON`);\n\t\t\t\t}\n\t\t\t\tassertOperationReceipt(receipt);\n\t\t\t\treturn receipt;\n\t\t\t});\n\t}\n\n\tprivate async appendReceiptOnce(receipt: OperationReceipt): Promise<void> {\n\t\tassertOperationReceipt(receipt);\n\t\tconst existing = (await this.readReceipts()).find((candidate) => candidate.operationId === receipt.operationId);\n\t\tif (existing) {\n\t\t\tif (!sameState(existing, receipt)) {\n\t\t\t\tthrow new Error(`Operation receipt ${receipt.operationId} has conflicting content`);\n\t\t\t}\n\t\t\treturn;\n\t\t}\n\t\tawait appendJsonLine(this.paths.receipts, receipt);\n\t}\n\n\tprivate async readMatchingReceipt(\n\t\toperationId: string | undefined,\n\t\taction: RegistryTransitionAction,\n\t\trequestKey: string,\n\t): Promise<OperationReceipt | undefined> {\n\t\tif (operationId === undefined) return undefined;\n\t\tif (!isOperationId(operationId)) throw new Error(\"Invalid idempotency key\");\n\t\tconst receipt = (await this.readReceipts()).find((candidate) => candidate.operationId === operationId);\n\t\tif (!receipt) return undefined;\n\t\tif (receipt.action !== action || receipt.requestKey !== requestKey) {\n\t\t\tthrow new Error(`Idempotency key ${operationId} was already used for another operation`);\n\t\t}\n\t\treturn receipt;\n\t}\n\n\tprivate async readOperationStateUnlocked(): Promise<RegistryOperationState> {\n\t\tconst [stable, trial, paused, receipts] = await Promise.all([\n\t\t\tthis.readStableDigestUnlocked(),\n\t\t\tthis.readTrialUnlocked(),\n\t\t\tthis.readPausedUnlocked(),\n\t\t\tthis.readReceipts(),\n\t\t]);\n\t\tconst latestOperationId = receipts[receipts.length - 1]?.operationId;\n\t\treturn {\n\t\t\tdigest: transitionRequestKey({\n\t\t\t\tstable: stable ?? null,\n\t\t\t\ttrial: trial ?? null,\n\t\t\t\tpaused,\n\t\t\t\tlatestOperationId: latestOperationId ?? null,\n\t\t\t}),\n\t\t\t...(latestOperationId ? { latestOperationId } : {}),\n\t\t};\n\t}\n\n\tasync getOperationState(): Promise<RegistryOperationState> {\n\t\treturn withFileLock(this.paths, \"registry\", async () => {\n\t\t\tawait this.recoverPendingUnlocked();\n\t\t\treturn this.readOperationStateUnlocked();\n\t\t});\n\t}\n\n\tprivate async assertExpectedOperationState(\n\t\texpectedStateDigest: string | undefined,\n\t\tidempotencyKey: string | undefined,\n\t): Promise<void> {\n\t\tif (expectedStateDigest === undefined) return;\n\t\tif (!isDigest(expectedStateDigest)) throw new Error(\"Invalid expected registry state digest\");\n\t\tconst current = await this.readOperationStateUnlocked();\n\t\tif (\n\t\t\tcurrent.digest === expectedStateDigest ||\n\t\t\t(idempotencyKey !== undefined && current.latestOperationId === idempotencyKey)\n\t\t)\n\t\t\treturn;\n\t\tthrow new Error(\"Evo-Pi registry state changed before the operation; retry it against the current state\");\n\t}\n\n\tprivate createTransition(options: {\n\t\taction: RegistryTransitionAction;\n\t\trequestKey: string;\n\t\toperationId?: string;\n\t\tstableBefore: string | null;\n\t\tstableAfter: string | null;\n\t\ttrialBefore: TrialState | null;\n\t\ttrialAfter: TrialState | null;\n\t\tpausedBefore: string | null;\n\t\tpausedAfter: string | null;\n\t\tproposalBefore?: Proposal | null;\n\t\tproposalAfter?: Proposal | null;\n\t\thistory: HistoryTemplate[];\n\t}): PendingTransition {\n\t\tconst operationId = options.operationId ?? randomUUID();\n\t\tconst createdAt = new Date().toISOString();\n\t\tconst transition: PendingTransition = {\n\t\t\tschemaVersion: 1,\n\t\t\toperationId,\n\t\t\trequestKey: options.requestKey,\n\t\t\taction: options.action,\n\t\t\tcreatedAt,\n\t\t\tstableBefore: options.stableBefore,\n\t\t\tstableAfter: options.stableAfter,\n\t\t\ttrialBefore: options.trialBefore,\n\t\t\ttrialAfter: options.trialAfter,\n\t\t\tpausedBefore: options.pausedBefore,\n\t\t\tpausedAfter: options.pausedAfter,\n\t\t\tproposalBefore: options.proposalBefore ?? null,\n\t\t\tproposalAfter: options.proposalAfter ?? null,\n\t\t\thistory: options.history.map((entry, index) => ({\n\t\t\t\t...entry,\n\t\t\t\teventId: `${operationId}:${index}`,\n\t\t\t\ttimestamp: createdAt,\n\t\t\t})),\n\t\t};\n\t\tassertPendingTransition(transition);\n\t\treturn transition;\n\t}\n\n\tprivate async assertTransitionState(transition: PendingTransition, allowAfterImage: boolean): Promise<void> {\n\t\tconst proposalId = transition.proposalBefore?.id ?? transition.proposalAfter?.id;\n\t\tconst [stable, trial, paused, proposal] = await Promise.all([\n\t\t\tthis.readStableDigestUnlocked(),\n\t\t\tthis.readTrialUnlocked(),\n\t\t\tthis.readPausedUnlocked(),\n\t\t\tproposalId ? this.readProposal(proposalId) : Promise.resolve(null),\n\t\t]);\n\t\tfor (const [label, current, before, after] of [\n\t\t\t[\"stable\", stable ?? null, transition.stableBefore, transition.stableAfter],\n\t\t\t[\"trial\", trial ?? null, transition.trialBefore, transition.trialAfter],\n\t\t\t[\"paused\", paused, transition.pausedBefore, transition.pausedAfter],\n\t\t\t[\"proposal\", proposal, transition.proposalBefore, transition.proposalAfter],\n\t\t] as const) {\n\t\t\tif (!sameState(current, before) && (!allowAfterImage || !sameState(current, after))) {\n\t\t\t\tthrow new Error(`Registry transition found unexpected ${label} state; refusing to overwrite it`);\n\t\t\t}\n\t\t}\n\t}\n\n\tprivate async applyTransitionUnlocked(transition: PendingTransition): Promise<void> {\n\t\tassertPendingTransition(transition);\n\t\tawait this.assertTransitionState(transition, true);\n\t\tif (transition.action === \"rollback\" || transition.action === \"rollback-proposal\") {\n\t\t\tif (!transition.stableBefore || !transition.stableAfter) {\n\t\t\t\tthrow new Error(\"Rollback transition has incomplete stable state\");\n\t\t\t}\n\t\t\tawait this.assertRollbackTargetAllowed(transition.stableBefore, transition.stableAfter);\n\t\t}\n\t\tif (transition.stableAfter) await loadCompiledBundle(this.paths, transition.stableAfter);\n\t\tif (\n\t\t\t(transition.action === \"rollback\" || transition.action === \"rollback-proposal\") &&\n\t\t\ttransition.stableBefore &&\n\t\t\ttransition.stableAfter &&\n\t\t\ttransition.stableBefore !== transition.stableAfter\n\t\t) {\n\t\t\tawait rollbackBundleMemory(this.paths, transition.stableBefore, transition.stableAfter);\n\t\t\tawait this.afterStep(\"memory-rolled-back\", transition.action);\n\t\t}\n\t\tconst [stable, trial, paused, proposal] = await Promise.all([\n\t\t\tthis.readStableDigestUnlocked(),\n\t\t\tthis.readTrialUnlocked(),\n\t\t\tthis.readPausedUnlocked(),\n\t\t\ttransition.proposalAfter ? this.readProposal(transition.proposalAfter.id) : Promise.resolve(null),\n\t\t]);\n\n\t\tif (transition.trialAfter && !sameState(trial ?? null, transition.trialAfter)) {\n\t\t\tawait atomicWriteJson(this.paths.trial, transition.trialAfter);\n\t\t\tawait this.afterStep(\"trial-written\", transition.action);\n\t\t}\n\t\tif (transition.stableAfter && stable !== transition.stableAfter) {\n\t\t\tawait atomicWriteFile(this.paths.stable, `${transition.stableAfter}\\n`);\n\t\t\tawait this.afterStep(\"stable-written\", transition.action);\n\t\t}\n\t\tif (transition.pausedAfter !== null && paused !== transition.pausedAfter) {\n\t\t\tawait atomicWriteFile(this.paths.paused, transition.pausedAfter);\n\t\t\tawait this.afterStep(\"paused-written\", transition.action);\n\t\t}\n\t\tif (transition.proposalAfter && !sameState(proposal, transition.proposalAfter)) {\n\t\t\tawait atomicWriteJson(\n\t\t\t\tjoin(this.paths.proposals, transition.proposalAfter.id, \"proposal.json\"),\n\t\t\t\ttransition.proposalAfter,\n\t\t\t);\n\t\t\tawait this.afterStep(\"proposal-written\", transition.action);\n\t\t}\n\t\tif (transition.proposalAfter) {\n\t\t\tawait saveProposalRevisionSnapshot(this.paths, transition.proposalAfter);\n\t\t\tawait this.afterStep(\"snapshot-written\", transition.action);\n\t\t}\n\t\tfor (const entry of transition.history) {\n\t\t\tawait this.appendHistoryOnce(entry);\n\t\t\tawait this.afterStep(\"history-appended\", transition.action);\n\t\t}\n\t\tif (transition.trialAfter === null) {\n\t\t\tawait durableUnlink(this.paths.trial);\n\t\t\tif (trial !== undefined) await this.afterStep(\"trial-cleared\", transition.action);\n\t\t}\n\t\tif (transition.pausedAfter === null) {\n\t\t\tawait durableUnlink(this.paths.paused);\n\t\t\tif (paused !== null) await this.afterStep(\"paused-cleared\", transition.action);\n\t\t}\n\t\tawait this.appendReceiptOnce(receiptFromTransition(transition));\n\t\tawait this.afterStep(\"receipt-appended\", transition.action);\n\t\tawait durableUnlink(this.paths.transition);\n\t\tawait this.afterStep(\"committed\", transition.action);\n\t}\n\n\tprivate async executeTransitionUnlocked(transition: PendingTransition): Promise<void> {\n\t\tawait this.assertTransitionState(transition, false);\n\t\tawait atomicWriteJson(this.paths.transition, transition);\n\t\tawait this.afterStep(\"prepared\", transition.action);\n\t\tawait this.applyTransitionUnlocked(transition);\n\t}\n\n\tprivate async readPendingTransition(): Promise<PendingTransition | undefined> {\n\t\tconst transition = await readJsonIfExists<PendingTransition>(this.paths.transition);\n\t\tif (transition) assertPendingTransition(transition);\n\t\treturn transition;\n\t}\n\n\tprivate async recoverPendingUnlocked(): Promise<PendingTransition | undefined> {\n\t\tconst transition = await this.readPendingTransition();\n\t\tif (!transition) return undefined;\n\t\tawait truncateIncompleteFinalLine(this.paths.history);\n\t\tawait truncateIncompleteFinalLine(this.paths.receipts);\n\t\tconst proposalId = transition.proposalBefore?.id ?? transition.proposalAfter?.id;\n\t\tif (proposalId) {\n\t\t\tawait this.withProposalLock(proposalId, () => this.applyTransitionUnlocked(transition));\n\t\t} else {\n\t\t\tawait this.applyTransitionUnlocked(transition);\n\t\t}\n\t\treturn transition;\n\t}\n\n\tasync recoverPendingTransition(): Promise<void> {\n\t\tawait withFileLock(this.paths, \"registry\", async () => {\n\t\t\tawait this.recoverPendingUnlocked();\n\t\t});\n\t}\n\n\tprivate async runHistoryTransitionUnlocked(\n\t\taction: \"record-decision\" | \"record-deferral\" | \"record-reopen\",\n\t\trequestKey: string,\n\t\thistory: HistoryTemplate[],\n\t): Promise<void> {\n\t\tconst recovered = await this.recoverPendingUnlocked();\n\t\tif (recovered?.action === action && recovered.requestKey === requestKey) return;\n\t\tconst [stable, trial, paused] = await Promise.all([\n\t\t\tthis.readStableDigestUnlocked(),\n\t\t\tthis.readTrialUnlocked(),\n\t\t\tthis.readPausedUnlocked(),\n\t\t]);\n\t\tawait this.executeTransitionUnlocked(\n\t\t\tthis.createTransition({\n\t\t\t\taction,\n\t\t\t\trequestKey,\n\t\t\t\tstableBefore: stable ?? null,\n\t\t\t\tstableAfter: stable ?? null,\n\t\t\t\ttrialBefore: trial ?? null,\n\t\t\t\ttrialAfter: trial ?? null,\n\t\t\t\tpausedBefore: paused,\n\t\t\t\tpausedAfter: paused,\n\t\t\t\thistory,\n\t\t\t}),\n\t\t);\n\t}\n\n\tasync initialize(digest: string, reason = \"Initialize Evo-Pi registry\"): Promise<void> {\n\t\tawait withFileLock(this.paths, \"registry\", async () => {\n\t\t\tconst requestKey = transitionRequestKey({ action: \"initialize\", digest, reason });\n\t\t\tconst recovered = await this.recoverPendingUnlocked();\n\t\t\tif (recovered?.action === \"initialize\" && recovered.requestKey === requestKey) return;\n\t\t\tconst stable = await this.readStableDigestUnlocked();\n\t\t\tif (stable) throw new Error(\"Evo-Pi registry is already initialized\");\n\t\t\tconst trial = await this.readTrialUnlocked();\n\t\t\tif (trial) throw new Error(\"Uninitialized registry cannot contain a trial\");\n\t\t\tawait loadCompiledBundle(this.paths, digest);\n\t\t\tconst paused = await this.readPausedUnlocked();\n\t\t\tawait this.executeTransitionUnlocked(\n\t\t\t\tthis.createTransition({\n\t\t\t\t\taction: \"initialize\",\n\t\t\t\t\trequestKey,\n\t\t\t\t\tstableBefore: null,\n\t\t\t\t\tstableAfter: digest,\n\t\t\t\t\ttrialBefore: null,\n\t\t\t\t\ttrialAfter: null,\n\t\t\t\t\tpausedBefore: paused,\n\t\t\t\t\tpausedAfter: paused,\n\t\t\t\t\thistory: [{ action: \"initialize\", actor: \"human\", toDigest: digest, reason }],\n\t\t\t\t}),\n\t\t\t);\n\t\t});\n\t}\n\n\tasync activateTrial(options: { digest: string; proposalId: string; plan: string }): Promise<TrialState> {\n\t\treturn withFileLock(this.paths, \"registry\", async () => {\n\t\t\tconst requestKey = transitionRequestKey({\n\t\t\t\taction: \"activate-trial\",\n\t\t\t\tdigest: options.digest,\n\t\t\t\tproposalId: options.proposalId,\n\t\t\t\tplan: options.plan,\n\t\t\t});\n\t\t\tconst recovered = await this.recoverPendingUnlocked();\n\t\t\tif (recovered?.action === \"activate-trial\" && recovered.requestKey === requestKey) {\n\t\t\t\tif (!recovered.trialAfter) throw new Error(\"Recovered trial transition has no trial after-image\");\n\t\t\t\treturn recovered.trialAfter;\n\t\t\t}\n\t\t\tconst stable = await this.readStableDigestUnlocked();\n\t\t\tif (!stable) throw new Error(\"Evo-Pi registry is not initialized\");\n\t\t\tif (await this.readTrialUnlocked()) throw new Error(\"A trial is already active; keep or rollback it first\");\n\t\t\tconst candidate = await loadCompiledBundle(this.paths, options.digest);\n\t\t\tif (candidate.manifest.parentDigest !== stable) {\n\t\t\t\tthrow new Error(\"Proposal parent is no longer stable; regenerate or rebase the proposal\");\n\t\t\t}\n\t\t\tconst trial: TrialState = {\n\t\t\t\tdigest: options.digest,\n\t\t\t\tparent: stable,\n\t\t\t\tproposalId: options.proposalId,\n\t\t\t\tstartedAt: new Date().toISOString(),\n\t\t\t\tplan: options.plan,\n\t\t\t};\n\t\t\tconst paused = await this.readPausedUnlocked();\n\t\t\tawait this.executeTransitionUnlocked(\n\t\t\t\tthis.createTransition({\n\t\t\t\t\taction: \"activate-trial\",\n\t\t\t\t\trequestKey,\n\t\t\t\t\tstableBefore: stable,\n\t\t\t\t\tstableAfter: options.digest,\n\t\t\t\t\ttrialBefore: null,\n\t\t\t\t\ttrialAfter: trial,\n\t\t\t\t\tpausedBefore: paused,\n\t\t\t\t\tpausedAfter: paused,\n\t\t\t\t\thistory: [\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\taction: \"trial-start\",\n\t\t\t\t\t\t\tactor: \"human\",\n\t\t\t\t\t\t\tfromDigest: stable,\n\t\t\t\t\t\t\ttoDigest: options.digest,\n\t\t\t\t\t\t\tproposalId: options.proposalId,\n\t\t\t\t\t\t\treason: options.plan,\n\t\t\t\t\t\t},\n\t\t\t\t\t],\n\t\t\t\t}),\n\t\t\t);\n\t\t\treturn trial;\n\t\t});\n\t}\n\n\tasync approveDataProposal(options: {\n\t\tidempotencyKey?: string;\n\t\texpectedStateDigest?: string;\n\t\tproposalId: string;\n\t\trevision: number;\n\t\tdiffDigest: string;\n\t\tparentDigest: string;\n\t\tcandidateDigest: string;\n\t\ttier: \"T0\" | \"T1\" | \"T2\";\n\t\tplan: string;\n\t\tactivation?: DataApprovalActivation;\n\t\treason: string;\n\t\tproposalBefore: Proposal;\n\t\tproposalAfter: Proposal;\n\t}): Promise<{ proposal: Proposal; trial: TrialState | undefined }> {\n\t\tconst activation: DataApprovalActivation = options.activation ?? { mode: \"trial\" };\n\t\treturn withFileLock(this.paths, \"registry\", async () => {\n\t\t\tconst requestKey = transitionRequestKey({\n\t\t\t\taction: \"approve-data\",\n\t\t\t\tproposalId: options.proposalId,\n\t\t\t\trevision: options.revision,\n\t\t\t\tdiffDigest: options.diffDigest,\n\t\t\t\tparentDigest: options.parentDigest,\n\t\t\t\tcandidateDigest: options.candidateDigest,\n\t\t\t\ttier: options.tier,\n\t\t\t\tplan: options.plan,\n\t\t\t\tactivation,\n\t\t\t\treason: options.reason,\n\t\t\t});\n\t\t\tconst recovered = await this.recoverPendingUnlocked();\n\t\t\tconst receipt = await this.readMatchingReceipt(options.idempotencyKey, \"approve-data\", requestKey);\n\t\t\tawait this.assertExpectedOperationState(options.expectedStateDigest, options.idempotencyKey);\n\t\t\tif (receipt) {\n\t\t\t\tif (!receipt.proposalAfter) throw new Error(\"Approval receipt has no proposal after-image\");\n\t\t\t\treturn { proposal: receipt.proposalAfter, trial: receipt.trialAfter ?? undefined };\n\t\t\t}\n\t\t\tif (\n\t\t\t\toptions.idempotencyKey === undefined &&\n\t\t\t\trecovered?.action === \"approve-data\" &&\n\t\t\t\trecovered.requestKey === requestKey\n\t\t\t) {\n\t\t\t\tif (!recovered.proposalAfter) throw new Error(\"Recovered approval has no proposal after-image\");\n\t\t\t\treturn { proposal: recovered.proposalAfter, trial: recovered.trialAfter ?? undefined };\n\t\t\t}\n\t\t\treturn this.withProposalLock(options.proposalId, async () => {\n\t\t\t\tconst current = await this.readProposal(options.proposalId);\n\t\t\t\tif (!sameState(current, options.proposalBefore)) {\n\t\t\t\t\tthrow new Error(\"Proposal changed before registry approval; refusing to overwrite it\");\n\t\t\t\t}\n\t\t\t\tif (\n\t\t\t\t\toptions.proposalBefore.kind !== \"data\" ||\n\t\t\t\t\toptions.proposalBefore.tier !== options.tier ||\n\t\t\t\t\toptions.proposalBefore.id !== options.proposalId ||\n\t\t\t\t\toptions.proposalBefore.revision !== options.revision ||\n\t\t\t\t\toptions.proposalBefore.diffDigest !== options.diffDigest ||\n\t\t\t\t\toptions.proposalBefore.parentBundleDigest !== options.parentDigest ||\n\t\t\t\t\toptions.proposalBefore.candidateDigest !== options.candidateDigest\n\t\t\t\t) {\n\t\t\t\t\tthrow new Error(\"Data approval options do not match the proposal\");\n\t\t\t\t}\n\t\t\t\tif (options.proposalBefore.status !== \"pending\") {\n\t\t\t\t\tthrow new Error(`Proposal ${options.proposalId} is ${options.proposalBefore.status}`);\n\t\t\t\t}\n\t\t\t\tif (options.tier !== \"T0\")\n\t\t\t\t\tawait assertRequiredApprovalArtifact(this.paths, options.proposalBefore, \"review\");\n\t\t\t\tif (options.tier === \"T2\")\n\t\t\t\t\tawait assertRequiredApprovalArtifact(this.paths, options.proposalBefore, \"replay\");\n\t\t\t\tif (activation.mode === \"direct\") {\n\t\t\t\t\tawait assertRequiredApprovalArtifact(this.paths, options.proposalBefore, \"validation\");\n\t\t\t\t}\n\t\t\t\tif (activation.mode === \"direct\" && options.proposalBefore.targetAbi === undefined) {\n\t\t\t\t\tthrow new Error(\"Direct activation is limited to an existing host-defined component ABI\");\n\t\t\t\t}\n\t\t\t\tconst direct = options.tier === \"T0\" || activation.mode === \"direct\";\n\t\t\t\tconst expectedAfter = {\n\t\t\t\t\t...options.proposalBefore,\n\t\t\t\t\tstatus: direct ? (\"kept\" as const) : (\"trialing\" as const),\n\t\t\t\t};\n\t\t\t\tif (!sameState(options.proposalAfter, expectedAfter)) {\n\t\t\t\t\tthrow new Error(\"Data approval proposal after-image is invalid\");\n\t\t\t\t}\n\t\t\t\tconst stable = await this.readStableDigestUnlocked();\n\t\t\t\tif (!stable) throw new Error(\"Evo-Pi registry is not initialized\");\n\t\t\t\tif (stable !== options.parentDigest) {\n\t\t\t\t\tthrow new Error(\"Proposal parent is no longer stable; regenerate or rebase the proposal\");\n\t\t\t\t}\n\t\t\t\tif (await this.readTrialUnlocked()) throw new Error(\"A trial is already active; keep or rollback it first\");\n\t\t\t\tconst candidate = await loadCompiledBundle(this.paths, options.candidateDigest);\n\t\t\t\tif (candidate.manifest.parentDigest !== stable) {\n\t\t\t\t\tthrow new Error(\"Candidate bundle parent does not match the stable bundle\");\n\t\t\t\t}\n\t\t\t\tconst trial = direct\n\t\t\t\t\t? undefined\n\t\t\t\t\t: {\n\t\t\t\t\t\t\tdigest: options.candidateDigest,\n\t\t\t\t\t\t\tparent: stable,\n\t\t\t\t\t\t\tproposalId: options.proposalId,\n\t\t\t\t\t\t\tstartedAt: new Date().toISOString(),\n\t\t\t\t\t\t\tplan: options.plan,\n\t\t\t\t\t\t\t...(activation.mode === \"trial\" && activation.canary ? { canary: activation.canary } : {}),\n\t\t\t\t\t\t};\n\t\t\t\tconst paused = await this.readPausedUnlocked();\n\t\t\t\tconst history: HistoryTemplate[] = [];\n\t\t\t\tif (trial) {\n\t\t\t\t\thistory.push({\n\t\t\t\t\t\taction: \"trial-start\",\n\t\t\t\t\t\tactor: \"human\",\n\t\t\t\t\t\tfromDigest: stable,\n\t\t\t\t\t\ttoDigest: options.candidateDigest,\n\t\t\t\t\t\tproposalId: options.proposalId,\n\t\t\t\t\t\trevision: options.revision,\n\t\t\t\t\t\tdiffDigest: options.diffDigest,\n\t\t\t\t\t\tcandidateDigest: options.candidateDigest,\n\t\t\t\t\t\treason: options.plan,\n\t\t\t\t\t});\n\t\t\t\t} else if (activation.mode === \"direct\") {\n\t\t\t\t\thistory.push({\n\t\t\t\t\t\taction: \"human-direct-keep\",\n\t\t\t\t\t\tactor: \"human\",\n\t\t\t\t\t\tfromDigest: stable,\n\t\t\t\t\t\ttoDigest: options.candidateDigest,\n\t\t\t\t\t\tproposalId: options.proposalId,\n\t\t\t\t\t\trevision: options.revision,\n\t\t\t\t\t\tdiffDigest: options.diffDigest,\n\t\t\t\t\t\tcandidateDigest: options.candidateDigest,\n\t\t\t\t\t\treason: \"Human explicitly bypassed Canary after reviewing the exact validated component\",\n\t\t\t\t\t});\n\t\t\t\t}\n\t\t\t\thistory.push({\n\t\t\t\t\taction: \"proposal-approved\",\n\t\t\t\t\tactor: \"human\",\n\t\t\t\t\tfromDigest: stable,\n\t\t\t\t\ttoDigest: options.candidateDigest,\n\t\t\t\t\tproposalId: options.proposalId,\n\t\t\t\t\trevision: options.revision,\n\t\t\t\t\tdiffDigest: options.diffDigest,\n\t\t\t\t\tcandidateDigest: options.candidateDigest,\n\t\t\t\t\treason: options.reason,\n\t\t\t\t});\n\t\t\t\tawait this.executeTransitionUnlocked(\n\t\t\t\t\tthis.createTransition({\n\t\t\t\t\t\taction: \"approve-data\",\n\t\t\t\t\t\trequestKey,\n\t\t\t\t\t\toperationId: options.idempotencyKey,\n\t\t\t\t\t\tstableBefore: stable,\n\t\t\t\t\t\tstableAfter: options.candidateDigest,\n\t\t\t\t\t\ttrialBefore: null,\n\t\t\t\t\t\ttrialAfter: trial ?? null,\n\t\t\t\t\t\tpausedBefore: paused,\n\t\t\t\t\t\tpausedAfter: paused,\n\t\t\t\t\t\tproposalBefore: options.proposalBefore,\n\t\t\t\t\t\tproposalAfter: options.proposalAfter,\n\t\t\t\t\t\thistory,\n\t\t\t\t\t}),\n\t\t\t\t);\n\t\t\t\treturn { proposal: options.proposalAfter, trial };\n\t\t\t});\n\t\t});\n\t}\n\n\tasync approveCodeProposal(options: {\n\t\tidempotencyKey?: string;\n\t\tproposalId: string;\n\t\texpectedStateDigest?: string;\n\t\tparentDigest: string;\n\t\trevision: number;\n\t\tdiffDigest: string;\n\t\tapprovalDigest: string;\n\t\tbranch: string;\n\t\treason: string;\n\t\tproposalBefore: Proposal;\n\t\tproposalAfter: Proposal;\n\t}): Promise<Proposal> {\n\t\treturn withFileLock(this.paths, \"registry\", async () => {\n\t\t\tconst requestKey = transitionRequestKey({\n\t\t\t\taction: \"approve-code\",\n\t\t\t\tproposalId: options.proposalId,\n\t\t\t\tparentDigest: options.parentDigest,\n\t\t\t\trevision: options.revision,\n\t\t\t\tdiffDigest: options.diffDigest,\n\t\t\t\tapprovalDigest: options.approvalDigest,\n\t\t\t\tbranch: options.branch,\n\t\t\t\treason: options.reason,\n\t\t\t});\n\t\t\tconst recovered = await this.recoverPendingUnlocked();\n\t\t\tconst receipt = await this.readMatchingReceipt(options.idempotencyKey, \"approve-code\", requestKey);\n\t\t\tawait this.assertExpectedOperationState(options.expectedStateDigest, options.idempotencyKey);\n\t\t\tif (receipt) {\n\t\t\t\tif (!receipt.proposalAfter) throw new Error(\"Approval receipt has no proposal after-image\");\n\t\t\t\treturn receipt.proposalAfter;\n\t\t\t}\n\t\t\tif (\n\t\t\t\toptions.idempotencyKey === undefined &&\n\t\t\t\trecovered?.action === \"approve-code\" &&\n\t\t\t\trecovered.requestKey === requestKey\n\t\t\t) {\n\t\t\t\tif (!recovered.proposalAfter) throw new Error(\"Recovered approval has no proposal after-image\");\n\t\t\t\treturn recovered.proposalAfter;\n\t\t\t}\n\t\t\treturn this.withProposalLock(options.proposalId, async () => {\n\t\t\t\tconst current = await this.readProposal(options.proposalId);\n\t\t\t\tif (!sameState(current, options.proposalBefore)) {\n\t\t\t\t\tthrow new Error(\"Proposal changed before registry approval; refusing to overwrite it\");\n\t\t\t\t}\n\t\t\t\tif (\n\t\t\t\t\toptions.proposalBefore.id !== options.proposalId ||\n\t\t\t\t\toptions.proposalBefore.kind !== \"code\" ||\n\t\t\t\t\toptions.proposalBefore.tier !== \"T2\" ||\n\t\t\t\t\toptions.proposalBefore.codeWorkspace?.branch !== options.branch ||\n\t\t\t\t\toptions.proposalBefore.revision !== options.revision ||\n\t\t\t\t\toptions.proposalBefore.diffDigest !== options.diffDigest ||\n\t\t\t\t\toptions.proposalBefore.parentBundleDigest !== options.parentDigest ||\n\t\t\t\t\toptions.proposalBefore.approvalDigest !== options.approvalDigest\n\t\t\t\t) {\n\t\t\t\t\tthrow new Error(\"Code approval options do not match the proposal\");\n\t\t\t\t}\n\t\t\t\tif (options.proposalBefore.status !== \"pending\") {\n\t\t\t\t\tthrow new Error(`Proposal ${options.proposalId} is ${options.proposalBefore.status}`);\n\t\t\t\t}\n\t\t\t\tif (!options.proposalBefore.replayScenarios[0]) {\n\t\t\t\t\tthrow new Error(`Code proposal ${options.proposalId} requires a replay scenario`);\n\t\t\t\t}\n\t\t\t\tawait assertRequiredApprovalArtifact(this.paths, options.proposalBefore, \"validation\");\n\t\t\t\tawait assertRequiredApprovalArtifact(this.paths, options.proposalBefore, \"replay\");\n\t\t\t\tawait assertRequiredApprovalArtifact(this.paths, options.proposalBefore, \"review\");\n\t\t\t\tif (!sameState(options.proposalAfter, { ...options.proposalBefore, status: \"approved\" })) {\n\t\t\t\t\tthrow new Error(\"Code approval proposal after-image is invalid\");\n\t\t\t\t}\n\t\t\t\tconst stable = await this.readStableDigestUnlocked();\n\t\t\t\tif (stable !== options.parentDigest) {\n\t\t\t\t\tthrow new Error(\"Proposal parent is no longer stable; regenerate or rebase the proposal\");\n\t\t\t\t}\n\t\t\t\tconst trial = await this.readTrialUnlocked();\n\t\t\t\tconst paused = await this.readPausedUnlocked();\n\t\t\t\tawait this.executeTransitionUnlocked(\n\t\t\t\t\tthis.createTransition({\n\t\t\t\t\t\taction: \"approve-code\",\n\t\t\t\t\t\trequestKey,\n\t\t\t\t\t\toperationId: options.idempotencyKey,\n\t\t\t\t\t\tstableBefore: stable,\n\t\t\t\t\t\tstableAfter: stable,\n\t\t\t\t\t\ttrialBefore: trial ?? null,\n\t\t\t\t\t\ttrialAfter: trial ?? null,\n\t\t\t\t\t\tpausedBefore: paused,\n\t\t\t\t\t\tpausedAfter: paused,\n\t\t\t\t\t\tproposalBefore: options.proposalBefore,\n\t\t\t\t\t\tproposalAfter: options.proposalAfter,\n\t\t\t\t\t\thistory: [\n\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\taction: \"proposal-approved\",\n\t\t\t\t\t\t\t\tactor: \"human\",\n\t\t\t\t\t\t\t\tfromDigest: stable,\n\t\t\t\t\t\t\t\tproposalId: options.proposalId,\n\t\t\t\t\t\t\t\trevision: options.revision,\n\t\t\t\t\t\t\t\tdiffDigest: options.diffDigest,\n\t\t\t\t\t\t\t\tapprovalDigest: options.approvalDigest,\n\t\t\t\t\t\t\t\tbranch: options.branch,\n\t\t\t\t\t\t\t\treason: options.reason,\n\t\t\t\t\t\t\t},\n\t\t\t\t\t\t],\n\t\t\t\t\t}),\n\t\t\t\t);\n\t\t\t\treturn options.proposalAfter;\n\t\t\t});\n\t\t});\n\t}\n\n\tasync keepTrial(reason: string): Promise<TrialState> {\n\t\treturn withFileLock(this.paths, \"registry\", async () => {\n\t\t\tconst requestKey = transitionRequestKey({ action: \"keep-trial\", reason });\n\t\t\tconst recovered = await this.recoverPendingUnlocked();\n\t\t\tif (recovered?.action === \"keep-trial\" && recovered.requestKey === requestKey) {\n\t\t\t\tif (!recovered.trialBefore) throw new Error(\"Recovered keep transition has no trial before-image\");\n\t\t\t\treturn recovered.trialBefore;\n\t\t\t}\n\t\t\tconst trial = await this.readTrialUnlocked();\n\t\t\tif (!trial) throw new Error(\"No trial is active\");\n\t\t\tconst stable = await this.readStableDigestUnlocked();\n\t\t\tif (stable !== trial.digest) throw new Error(\"Trial and stable pointer disagree\");\n\t\t\tconst paused = await this.readPausedUnlocked();\n\t\t\tawait this.executeTransitionUnlocked(\n\t\t\t\tthis.createTransition({\n\t\t\t\t\taction: \"keep-trial\",\n\t\t\t\t\trequestKey,\n\t\t\t\t\tstableBefore: stable,\n\t\t\t\t\tstableAfter: stable,\n\t\t\t\t\ttrialBefore: trial,\n\t\t\t\t\ttrialAfter: null,\n\t\t\t\t\tpausedBefore: paused,\n\t\t\t\t\tpausedAfter: paused,\n\t\t\t\t\thistory: [\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\taction: \"trial-keep\",\n\t\t\t\t\t\t\tactor: \"human\",\n\t\t\t\t\t\t\tfromDigest: trial.parent,\n\t\t\t\t\t\t\ttoDigest: trial.digest,\n\t\t\t\t\t\t\tproposalId: trial.proposalId,\n\t\t\t\t\t\t\treason,\n\t\t\t\t\t\t},\n\t\t\t\t\t],\n\t\t\t\t}),\n\t\t\t);\n\t\t\treturn trial;\n\t\t});\n\t}\n\n\tasync directKeepComponentTrial(options: {\n\t\tproposalId: string;\n\t\treason: string;\n\t\tidempotencyKey?: string;\n\t\texpectedStateDigest?: string;\n\t}): Promise<Proposal> {\n\t\treturn withFileLock(this.paths, \"registry\", async () => {\n\t\t\tconst requestKey = transitionRequestKey({\n\t\t\t\taction: \"direct-keep-trial\",\n\t\t\t\tproposalId: options.proposalId,\n\t\t\t\treason: options.reason,\n\t\t\t});\n\t\t\tconst recovered = await this.recoverPendingUnlocked();\n\t\t\tconst receipt = await this.readMatchingReceipt(options.idempotencyKey, \"direct-keep-trial\", requestKey);\n\t\t\tawait this.assertExpectedOperationState(options.expectedStateDigest, options.idempotencyKey);\n\t\t\tif (receipt) {\n\t\t\t\tif (!receipt.proposalAfter) throw new Error(\"Direct keep receipt has no proposal after-image\");\n\t\t\t\treturn receipt.proposalAfter;\n\t\t\t}\n\t\t\tif (\n\t\t\t\toptions.idempotencyKey === undefined &&\n\t\t\t\trecovered?.action === \"direct-keep-trial\" &&\n\t\t\t\trecovered.requestKey === requestKey\n\t\t\t) {\n\t\t\t\tif (!recovered.proposalAfter) throw new Error(\"Recovered direct keep has no proposal after-image\");\n\t\t\t\treturn recovered.proposalAfter;\n\t\t\t}\n\t\t\tconst trial = await this.readTrialUnlocked();\n\t\t\tif (!trial || trial.proposalId !== options.proposalId) {\n\t\t\t\tthrow new Error(\"The requested component Canary is not active\");\n\t\t\t}\n\t\t\tconst stable = await this.readStableDigestUnlocked();\n\t\t\tif (stable !== trial.digest) throw new Error(\"Component Canary and stable pointer disagree\");\n\t\t\treturn this.withProposalLock(options.proposalId, async () => {\n\t\t\t\tconst proposal = await this.readProposal(options.proposalId);\n\t\t\t\tif (\n\t\t\t\t\t!proposal ||\n\t\t\t\t\tproposal.status !== \"trialing\" ||\n\t\t\t\t\tproposal.kind !== \"data\" ||\n\t\t\t\t\tproposal.targetAbi === undefined ||\n\t\t\t\t\tproposal.candidateDigest !== trial.digest\n\t\t\t\t) {\n\t\t\t\t\tthrow new Error(\"Active trial is not a component proposal eligible for direct keep\");\n\t\t\t\t}\n\t\t\t\tawait assertRequiredApprovalArtifact(this.paths, proposal, \"validation\");\n\t\t\t\tconst proposalAfter: Proposal = { ...proposal, status: \"kept\" };\n\t\t\t\tconst paused = await this.readPausedUnlocked();\n\t\t\t\tawait this.executeTransitionUnlocked(\n\t\t\t\t\tthis.createTransition({\n\t\t\t\t\t\taction: \"direct-keep-trial\",\n\t\t\t\t\t\trequestKey,\n\t\t\t\t\t\toperationId: options.idempotencyKey,\n\t\t\t\t\t\tstableBefore: stable,\n\t\t\t\t\t\tstableAfter: stable,\n\t\t\t\t\t\ttrialBefore: trial,\n\t\t\t\t\t\ttrialAfter: null,\n\t\t\t\t\t\tpausedBefore: paused,\n\t\t\t\t\t\tpausedAfter: paused,\n\t\t\t\t\t\tproposalBefore: proposal,\n\t\t\t\t\t\tproposalAfter,\n\t\t\t\t\t\thistory: [\n\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\taction: \"human-direct-keep\",\n\t\t\t\t\t\t\t\tactor: \"human\",\n\t\t\t\t\t\t\t\tfromDigest: trial.parent,\n\t\t\t\t\t\t\t\ttoDigest: trial.digest,\n\t\t\t\t\t\t\t\tproposalId: proposal.id,\n\t\t\t\t\t\t\t\trevision: proposal.revision,\n\t\t\t\t\t\t\t\tdiffDigest: proposal.diffDigest,\n\t\t\t\t\t\t\t\tcandidateDigest: proposal.candidateDigest,\n\t\t\t\t\t\t\t\treason: options.reason,\n\t\t\t\t\t\t\t},\n\t\t\t\t\t\t],\n\t\t\t\t\t}),\n\t\t\t\t);\n\t\t\t\treturn proposalAfter;\n\t\t\t});\n\t\t});\n\t}\n\n\tasync keepProposal(\n\t\treason: string,\n\t\tidempotencyKey?: string,\n\t\texpectedStateDigest?: string,\n\t\texpectedEvidenceDigest?: string,\n\t): Promise<Proposal> {\n\t\treturn withFileLock(this.paths, \"registry\", async () => {\n\t\t\tconst requestKey = transitionRequestKey({ action: \"keep-proposal\", reason });\n\t\t\tconst recovered = await this.recoverPendingUnlocked();\n\t\t\tconst receipt = await this.readMatchingReceipt(idempotencyKey, \"keep-proposal\", requestKey);\n\t\t\tawait this.assertExpectedOperationState(expectedStateDigest, idempotencyKey);\n\t\t\tif (receipt) {\n\t\t\t\tif (!receipt.proposalAfter) throw new Error(\"Keep receipt has no proposal after-image\");\n\t\t\t\treturn receipt.proposalAfter;\n\t\t\t}\n\t\t\tif (\n\t\t\t\tidempotencyKey === undefined &&\n\t\t\t\trecovered?.action === \"keep-proposal\" &&\n\t\t\t\trecovered.requestKey === requestKey\n\t\t\t) {\n\t\t\t\tif (!recovered.proposalAfter) throw new Error(\"Recovered keep transition has no proposal after-image\");\n\t\t\t\treturn recovered.proposalAfter;\n\t\t\t}\n\t\t\tif (!expectedEvidenceDigest || !isDigest(expectedEvidenceDigest)) {\n\t\t\t\tthrow new Error(\"Keeping a trial requires the current evidence snapshot digest\");\n\t\t\t}\n\t\t\tconst trial = await this.readTrialUnlocked();\n\t\t\tif (!trial) throw new Error(\"No trial is active\");\n\t\t\tconst stable = await this.readStableDigestUnlocked();\n\t\t\tif (stable !== trial.digest) throw new Error(\"Trial and stable pointer disagree\");\n\t\t\treturn this.withProposalLock(trial.proposalId, async () => {\n\t\t\t\tconst proposal = await this.readProposal(trial.proposalId);\n\t\t\t\tif (!proposal) throw new Error(`Proposal ${trial.proposalId} does not exist`);\n\t\t\t\tif (proposal.status !== \"trialing\" || proposal.candidateDigest !== trial.digest) {\n\t\t\t\t\tthrow new Error(`Proposal ${trial.proposalId} does not match the active trial`);\n\t\t\t\t}\n\t\t\t\tconst retrospective = proposal.artifacts.retrospective;\n\t\t\t\tif (!retrospective?.evidence) {\n\t\t\t\t\tthrow new Error(`Proposal ${proposal.id} is missing an evidence-bound retrospective`);\n\t\t\t\t}\n\t\t\t\tawait validateEvaluationArtifact({\n\t\t\t\t\tpaths: this.paths,\n\t\t\t\t\tproposalId: proposal.id,\n\t\t\t\t\trevision: proposal.revision,\n\t\t\t\t\tdiffDigest: proposal.diffDigest,\n\t\t\t\t\tkind: \"retrospective\",\n\t\t\t\t\treference: retrospective,\n\t\t\t\t});\n\t\t\t\tconst proposalAfter: Proposal = { ...proposal, status: \"kept\" };\n\t\t\t\tif (retrospective.evidence.digest !== expectedEvidenceDigest) {\n\t\t\t\t\tthrow new Error(`Proposal ${proposal.id} retrospective does not cover current evidence`);\n\t\t\t\t}\n\t\t\t\tconst paused = await this.readPausedUnlocked();\n\t\t\t\tawait this.executeTransitionUnlocked(\n\t\t\t\t\tthis.createTransition({\n\t\t\t\t\t\taction: \"keep-proposal\",\n\t\t\t\t\t\trequestKey,\n\t\t\t\t\t\toperationId: idempotencyKey,\n\t\t\t\t\t\tstableBefore: stable,\n\t\t\t\t\t\tstableAfter: stable,\n\t\t\t\t\t\ttrialBefore: trial,\n\t\t\t\t\t\ttrialAfter: null,\n\t\t\t\t\t\tpausedBefore: paused,\n\t\t\t\t\t\tpausedAfter: paused,\n\t\t\t\t\t\tproposalBefore: proposal,\n\t\t\t\t\t\tproposalAfter,\n\t\t\t\t\t\thistory: [\n\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\taction: \"trial-keep\",\n\t\t\t\t\t\t\t\tactor: \"human\",\n\t\t\t\t\t\t\t\tfromDigest: trial.parent,\n\t\t\t\t\t\t\t\ttoDigest: trial.digest,\n\t\t\t\t\t\t\t\tproposalId: trial.proposalId,\n\t\t\t\t\t\t\t\tevidenceDigest: retrospective.evidence.digest,\n\t\t\t\t\t\t\t\tcomparisonDigest: proposal.artifacts.comparison?.evidence?.digest,\n\t\t\t\t\t\t\t\treason,\n\t\t\t\t\t\t\t},\n\t\t\t\t\t\t],\n\t\t\t\t\t}),\n\t\t\t\t);\n\t\t\t\treturn proposalAfter;\n\t\t\t});\n\t\t});\n\t}\n\n\tasync rollback(\n\t\ttargetDigest: string | undefined,\n\t\treason: string,\n\t\tproposalId?: string,\n\t): Promise<{ from: string; to: string }> {\n\t\treturn withFileLock(this.paths, \"registry\", async () => {\n\t\t\tconst requestKey = transitionRequestKey({\n\t\t\t\taction: \"rollback\",\n\t\t\t\ttargetDigest: targetDigest ?? null,\n\t\t\t\treason,\n\t\t\t\tproposalId: proposalId ?? null,\n\t\t\t});\n\t\t\tconst recovered = await this.recoverPendingUnlocked();\n\t\t\tif (recovered?.action === \"rollback\" && recovered.requestKey === requestKey) {\n\t\t\t\tif (!recovered.stableBefore || !recovered.stableAfter) {\n\t\t\t\t\tthrow new Error(\"Recovered rollback transition has incomplete stable state\");\n\t\t\t\t}\n\t\t\t\treturn { from: recovered.stableBefore, to: recovered.stableAfter };\n\t\t\t}\n\t\t\tconst stable = await this.readStableDigestUnlocked();\n\t\t\tif (!stable) throw new Error(\"Evo-Pi registry is not initialized\");\n\t\t\tconst trial = await this.readTrialUnlocked();\n\t\t\tconst currentBundle = await loadCompiledBundle(this.paths, stable);\n\t\t\tconst target =\n\t\t\t\ttargetDigest ??\n\t\t\t\t(trial?.digest === stable ? trial.parent : (currentBundle.manifest.parentDigest ?? undefined));\n\t\t\tif (!target) throw new Error(\"Current bundle has no parent; provide an explicit rollback digest\");\n\t\t\tif (target === stable) throw new Error(\"Rollback target is already stable\");\n\t\t\tawait this.assertRollbackTargetAllowed(stable, target);\n\t\t\tawait loadCompiledBundle(this.paths, target);\n\t\t\tconst paused = await this.readPausedUnlocked();\n\t\t\tawait this.executeTransitionUnlocked(\n\t\t\t\tthis.createTransition({\n\t\t\t\t\taction: \"rollback\",\n\t\t\t\t\trequestKey,\n\t\t\t\t\tstableBefore: stable,\n\t\t\t\t\tstableAfter: target,\n\t\t\t\t\ttrialBefore: trial ?? null,\n\t\t\t\t\ttrialAfter: null,\n\t\t\t\t\tpausedBefore: paused,\n\t\t\t\t\tpausedAfter: paused,\n\t\t\t\t\thistory: [\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\taction: \"rollback\",\n\t\t\t\t\t\t\tactor: \"human\",\n\t\t\t\t\t\t\tfromDigest: stable,\n\t\t\t\t\t\t\ttoDigest: target,\n\t\t\t\t\t\t\tproposalId: trial?.proposalId ?? proposalId,\n\t\t\t\t\t\t\treason,\n\t\t\t\t\t\t},\n\t\t\t\t\t],\n\t\t\t\t}),\n\t\t\t);\n\t\t\treturn { from: stable, to: target };\n\t\t});\n\t}\n\n\tasync rollbackProposal(\n\t\ttargetDigest: string | undefined,\n\t\treason: string,\n\t\tidempotencyKey?: string,\n\t\texpectedStateDigest?: string,\n\t): Promise<{ from: string; to: string; proposal?: Proposal }> {\n\t\treturn withFileLock(this.paths, \"registry\", async () => {\n\t\t\tconst requestKey = transitionRequestKey({\n\t\t\t\taction: \"rollback-proposal\",\n\t\t\t\ttargetDigest: targetDigest ?? null,\n\t\t\t\treason,\n\t\t\t});\n\t\t\tconst recovered = await this.recoverPendingUnlocked();\n\t\t\tconst receipt = await this.readMatchingReceipt(idempotencyKey, \"rollback-proposal\", requestKey);\n\t\t\tawait this.assertExpectedOperationState(expectedStateDigest, idempotencyKey);\n\t\t\tif (receipt) {\n\t\t\t\tif (!receipt.stableBefore || !receipt.stableAfter) {\n\t\t\t\t\tthrow new Error(\"Rollback receipt has incomplete stable state\");\n\t\t\t\t}\n\t\t\t\treturn {\n\t\t\t\t\tfrom: receipt.stableBefore,\n\t\t\t\t\tto: receipt.stableAfter,\n\t\t\t\t\t...(receipt.proposalAfter ? { proposal: receipt.proposalAfter } : {}),\n\t\t\t\t};\n\t\t\t}\n\t\t\tif (\n\t\t\t\tidempotencyKey === undefined &&\n\t\t\t\trecovered?.action === \"rollback-proposal\" &&\n\t\t\t\trecovered.requestKey === requestKey\n\t\t\t) {\n\t\t\t\tif (!recovered.stableBefore || !recovered.stableAfter) {\n\t\t\t\t\tthrow new Error(\"Recovered rollback transition has incomplete stable state\");\n\t\t\t\t}\n\t\t\t\treturn {\n\t\t\t\t\tfrom: recovered.stableBefore,\n\t\t\t\t\tto: recovered.stableAfter,\n\t\t\t\t\t...(recovered.proposalAfter ? { proposal: recovered.proposalAfter } : {}),\n\t\t\t\t};\n\t\t\t}\n\t\t\tconst stable = await this.readStableDigestUnlocked();\n\t\t\tif (!stable) throw new Error(\"Evo-Pi registry is not initialized\");\n\t\t\tconst trial = await this.readTrialUnlocked();\n\t\t\tconst currentBundle = await loadCompiledBundle(this.paths, stable);\n\t\t\tconst target =\n\t\t\t\ttargetDigest ??\n\t\t\t\t(trial?.digest === stable ? trial.parent : (currentBundle.manifest.parentDigest ?? undefined));\n\t\t\tif (!target) throw new Error(\"Current bundle has no parent; provide an explicit rollback digest\");\n\t\t\tif (target === stable) throw new Error(\"Rollback target is already stable\");\n\t\t\tawait this.assertRollbackTargetAllowed(stable, target);\n\t\t\tawait loadCompiledBundle(this.paths, target);\n\n\t\t\tlet proposalId = trial?.proposalId;\n\t\t\tif (!proposalId) {\n\t\t\t\tfor (const entry of await readdir(this.paths.proposals, { withFileTypes: true })) {\n\t\t\t\t\tif (!entry.isDirectory() || !entry.name.startsWith(\"p-\")) continue;\n\t\t\t\t\tconst proposal = await this.readProposal(entry.name);\n\t\t\t\t\tif (proposal?.status === \"kept\" && proposal.candidateDigest === stable) {\n\t\t\t\t\t\tproposalId = proposal.id;\n\t\t\t\t\t\tbreak;\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t\tconst paused = await this.readPausedUnlocked();\n\t\t\tconst applyRollback = async (\n\t\t\t\tproposal: Proposal | null,\n\t\t\t): Promise<{ from: string; to: string; proposal?: Proposal }> => {\n\t\t\t\tif (proposal) {\n\t\t\t\t\tconst expectedStatus = trial ? \"trialing\" : \"kept\";\n\t\t\t\t\tif (proposal.status !== expectedStatus || proposal.candidateDigest !== stable) {\n\t\t\t\t\t\tthrow new Error(`Proposal ${proposal.id} does not match the rollback source`);\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\tconst proposalAfter = proposal ? ({ ...proposal, status: \"rolled-back\" } satisfies Proposal) : null;\n\t\t\t\tawait this.executeTransitionUnlocked(\n\t\t\t\t\tthis.createTransition({\n\t\t\t\t\t\taction: \"rollback-proposal\",\n\t\t\t\t\t\trequestKey,\n\t\t\t\t\t\toperationId: idempotencyKey,\n\t\t\t\t\t\tstableBefore: stable,\n\t\t\t\t\t\tstableAfter: target,\n\t\t\t\t\t\ttrialBefore: trial ?? null,\n\t\t\t\t\t\ttrialAfter: null,\n\t\t\t\t\t\tpausedBefore: paused,\n\t\t\t\t\t\tpausedAfter: paused,\n\t\t\t\t\t\tproposalBefore: proposal,\n\t\t\t\t\t\tproposalAfter,\n\t\t\t\t\t\thistory: [\n\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\taction: \"rollback\",\n\t\t\t\t\t\t\t\tactor: \"human\",\n\t\t\t\t\t\t\t\tfromDigest: stable,\n\t\t\t\t\t\t\t\ttoDigest: target,\n\t\t\t\t\t\t\t\tproposalId: proposal?.id ?? trial?.proposalId,\n\t\t\t\t\t\t\t\tevidenceDigest: proposal?.artifacts.retrospective?.evidence?.digest,\n\t\t\t\t\t\t\t\tcomparisonDigest: proposal?.artifacts.comparison?.evidence?.digest,\n\t\t\t\t\t\t\t\treason,\n\t\t\t\t\t\t\t},\n\t\t\t\t\t\t],\n\t\t\t\t\t}),\n\t\t\t\t);\n\t\t\t\treturn { from: stable, to: target, ...(proposalAfter ? { proposal: proposalAfter } : {}) };\n\t\t\t};\n\n\t\t\tif (!proposalId) return applyRollback(null);\n\t\t\treturn this.withProposalLock(proposalId, async () => applyRollback(await this.readProposal(proposalId)));\n\t\t});\n\t}\n\n\tasync recordDecision(options: {\n\t\tproposalId: string;\n\t\tapproved: boolean;\n\t\treason: string;\n\t\trevision?: number;\n\t\tdiffDigest?: string;\n\t}): Promise<void> {\n\t\tawait withFileLock(this.paths, \"registry\", async () => {\n\t\t\tconst requestKey = transitionRequestKey({\n\t\t\t\taction: \"record-decision\",\n\t\t\t\tproposalId: options.proposalId,\n\t\t\t\tapproved: options.approved,\n\t\t\t\trevision: options.revision ?? null,\n\t\t\t\tdiffDigest: options.diffDigest ?? null,\n\t\t\t\treason: options.reason,\n\t\t\t});\n\t\t\tawait this.runHistoryTransitionUnlocked(\"record-decision\", requestKey, [\n\t\t\t\t{\n\t\t\t\t\taction: options.approved ? \"proposal-approved\" : \"proposal-rejected\",\n\t\t\t\t\tactor: \"human\",\n\t\t\t\t\tproposalId: options.proposalId,\n\t\t\t\t\trevision: options.revision,\n\t\t\t\t\tdiffDigest: options.diffDigest,\n\t\t\t\t\treason: options.reason,\n\t\t\t\t},\n\t\t\t]);\n\t\t});\n\t}\n\n\tasync recordDeferral(options: {\n\t\tproposalId: string;\n\t\trevision: number;\n\t\tdiffDigest: string;\n\t\treason: string;\n\t}): Promise<void> {\n\t\tawait withFileLock(this.paths, \"registry\", async () => {\n\t\t\tconst requestKey = transitionRequestKey({\n\t\t\t\taction: \"record-deferral\",\n\t\t\t\tproposalId: options.proposalId,\n\t\t\t\trevision: options.revision,\n\t\t\t\tdiffDigest: options.diffDigest,\n\t\t\t\treason: options.reason,\n\t\t\t});\n\t\t\tawait this.runHistoryTransitionUnlocked(\"record-deferral\", requestKey, [\n\t\t\t\t{\n\t\t\t\t\taction: \"proposal-deferred\",\n\t\t\t\t\tactor: \"human\",\n\t\t\t\t\tproposalId: options.proposalId,\n\t\t\t\t\trevision: options.revision,\n\t\t\t\t\tdiffDigest: options.diffDigest,\n\t\t\t\t\treason: options.reason,\n\t\t\t\t},\n\t\t\t]);\n\t\t});\n\t}\n\n\tasync recordReopen(options: {\n\t\tproposalId: string;\n\t\trevision: number;\n\t\tdiffDigest: string;\n\t\treason: string;\n\t}): Promise<void> {\n\t\tawait withFileLock(this.paths, \"registry\", async () => {\n\t\t\tconst requestKey = transitionRequestKey({\n\t\t\t\taction: \"record-reopen\",\n\t\t\t\tproposalId: options.proposalId,\n\t\t\t\trevision: options.revision,\n\t\t\t\tdiffDigest: options.diffDigest,\n\t\t\t\treason: options.reason,\n\t\t\t});\n\t\t\tawait this.runHistoryTransitionUnlocked(\"record-reopen\", requestKey, [\n\t\t\t\t{\n\t\t\t\t\taction: \"proposal-reopened\",\n\t\t\t\t\tactor: \"human\",\n\t\t\t\t\tproposalId: options.proposalId,\n\t\t\t\t\trevision: options.revision,\n\t\t\t\t\tdiffDigest: options.diffDigest,\n\t\t\t\t\treason: options.reason,\n\t\t\t\t},\n\t\t\t]);\n\t\t});\n\t}\n\n\tasync transitionProposal(options: ProposalTransitionOptions): Promise<Proposal> {\n\t\treturn withFileLock(this.paths, \"registry\", async () => {\n\t\t\tconst requestKey = transitionRequestKey({\n\t\t\t\taction: options.action,\n\t\t\t\tproposalId: options.proposalBefore.id,\n\t\t\t\trevision: options.proposalBefore.revision,\n\t\t\t\tdiffDigest: options.proposalBefore.diffDigest,\n\t\t\t\treason: options.reason,\n\t\t\t\tuntil: options.action === \"defer-proposal\" ? (options.proposalAfter.defer?.until ?? null) : null,\n\t\t\t});\n\t\t\tconst recovered = await this.recoverPendingUnlocked();\n\t\t\tconst receipt = await this.readMatchingReceipt(options.idempotencyKey, options.action, requestKey);\n\t\t\tawait this.assertExpectedOperationState(options.expectedStateDigest, options.idempotencyKey);\n\t\t\tif (receipt) {\n\t\t\t\tif (!receipt.proposalAfter) throw new Error(\"Decision receipt has no proposal after-image\");\n\t\t\t\treturn receipt.proposalAfter;\n\t\t\t}\n\t\t\tif (\n\t\t\t\toptions.idempotencyKey === undefined &&\n\t\t\t\trecovered?.action === options.action &&\n\t\t\t\trecovered.requestKey === requestKey\n\t\t\t) {\n\t\t\t\tif (!recovered.proposalAfter) throw new Error(\"Recovered decision has no proposal after-image\");\n\t\t\t\treturn recovered.proposalAfter;\n\t\t\t}\n\t\t\treturn this.withProposalLock(options.proposalBefore.id, async () => {\n\t\t\t\tconst current = await this.readProposal(options.proposalBefore.id);\n\t\t\t\tif (!sameState(current, options.proposalBefore)) {\n\t\t\t\t\tthrow new Error(\"Proposal changed before registry decision; refusing to overwrite it\");\n\t\t\t\t}\n\t\t\t\tlet historyAction: \"proposal-rejected\" | \"proposal-deferred\" | \"proposal-reopened\";\n\t\t\t\tlet expectedAfter: Proposal;\n\t\t\t\tif (options.action === \"reject-proposal\") {\n\t\t\t\t\tif (options.proposalBefore.status !== \"pending\" && options.proposalBefore.status !== \"deferred\") {\n\t\t\t\t\t\tthrow new Error(`Proposal ${options.proposalBefore.id} is ${options.proposalBefore.status}`);\n\t\t\t\t\t}\n\t\t\t\t\texpectedAfter = { ...options.proposalBefore, status: \"rejected\" };\n\t\t\t\t\tdelete expectedAfter.defer;\n\t\t\t\t\thistoryAction = \"proposal-rejected\";\n\t\t\t\t} else if (options.action === \"defer-proposal\") {\n\t\t\t\t\tif (options.proposalBefore.status !== \"pending\") {\n\t\t\t\t\t\tthrow new Error(`Proposal ${options.proposalBefore.id} is ${options.proposalBefore.status}`);\n\t\t\t\t\t}\n\t\t\t\t\tif (!options.proposalAfter.defer || options.proposalAfter.defer.reason !== options.reason) {\n\t\t\t\t\t\tthrow new Error(\"Deferred proposal after-image is invalid\");\n\t\t\t\t\t}\n\t\t\t\t\texpectedAfter = {\n\t\t\t\t\t\t...options.proposalBefore,\n\t\t\t\t\t\tstatus: \"deferred\",\n\t\t\t\t\t\tdefer: options.proposalAfter.defer,\n\t\t\t\t\t};\n\t\t\t\t\thistoryAction = \"proposal-deferred\";\n\t\t\t\t} else {\n\t\t\t\t\tif (options.proposalBefore.status !== \"deferred\") {\n\t\t\t\t\t\tthrow new Error(`Proposal ${options.proposalBefore.id} is ${options.proposalBefore.status}`);\n\t\t\t\t\t}\n\t\t\t\t\texpectedAfter = { ...options.proposalBefore, status: \"pending\" };\n\t\t\t\t\tdelete expectedAfter.defer;\n\t\t\t\t\thistoryAction = \"proposal-reopened\";\n\t\t\t\t}\n\t\t\t\tif (!sameState(expectedAfter, options.proposalAfter)) {\n\t\t\t\t\tthrow new Error(\"Proposal decision after-image is invalid\");\n\t\t\t\t}\n\t\t\t\tconst [stable, trial, paused] = await Promise.all([\n\t\t\t\t\tthis.readStableDigestUnlocked(),\n\t\t\t\t\tthis.readTrialUnlocked(),\n\t\t\t\t\tthis.readPausedUnlocked(),\n\t\t\t\t]);\n\t\t\t\tawait this.executeTransitionUnlocked(\n\t\t\t\t\tthis.createTransition({\n\t\t\t\t\t\taction: options.action,\n\t\t\t\t\t\trequestKey,\n\t\t\t\t\t\tstableBefore: stable ?? null,\n\t\t\t\t\t\tstableAfter: stable ?? null,\n\t\t\t\t\t\toperationId: options.idempotencyKey,\n\t\t\t\t\t\ttrialBefore: trial ?? null,\n\t\t\t\t\t\ttrialAfter: trial ?? null,\n\t\t\t\t\t\tpausedBefore: paused,\n\t\t\t\t\t\tpausedAfter: paused,\n\t\t\t\t\t\tproposalBefore: options.proposalBefore,\n\t\t\t\t\t\tproposalAfter: options.proposalAfter,\n\t\t\t\t\t\thistory: [\n\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\taction: historyAction,\n\t\t\t\t\t\t\t\tactor: \"human\",\n\t\t\t\t\t\t\t\tproposalId: options.proposalBefore.id,\n\t\t\t\t\t\t\t\trevision: options.proposalBefore.revision,\n\t\t\t\t\t\t\t\tdiffDigest: options.proposalBefore.diffDigest,\n\t\t\t\t\t\t\t\treason: options.reason,\n\t\t\t\t\t\t\t},\n\t\t\t\t\t\t],\n\t\t\t\t\t}),\n\t\t\t\t);\n\t\t\t\treturn options.proposalAfter;\n\t\t\t});\n\t\t});\n\t}\n\n\tasync pause(reason: string): Promise<void> {\n\t\tawait withFileLock(this.paths, \"registry\", async () => {\n\t\t\tconst requestKey = transitionRequestKey({ action: \"pause\", reason });\n\t\t\tconst recovered = await this.recoverPendingUnlocked();\n\t\t\tif (recovered?.action === \"pause\" && recovered.requestKey === requestKey) return;\n\t\t\tconst [stable, trial, paused] = await Promise.all([\n\t\t\t\tthis.readStableDigestUnlocked(),\n\t\t\t\tthis.readTrialUnlocked(),\n\t\t\t\tthis.readPausedUnlocked(),\n\t\t\t]);\n\t\t\tawait this.executeTransitionUnlocked(\n\t\t\t\tthis.createTransition({\n\t\t\t\t\taction: \"pause\",\n\t\t\t\t\trequestKey,\n\t\t\t\t\tstableBefore: stable ?? null,\n\t\t\t\t\tstableAfter: stable ?? null,\n\t\t\t\t\ttrialBefore: trial ?? null,\n\t\t\t\t\ttrialAfter: trial ?? null,\n\t\t\t\t\tpausedBefore: paused,\n\t\t\t\t\tpausedAfter: `${new Date().toISOString()}\\t${reason}\\n`,\n\t\t\t\t\thistory: [{ action: \"pause\", actor: \"human\", reason }],\n\t\t\t\t}),\n\t\t\t);\n\t\t});\n\t}\n\n\tasync resume(reason: string): Promise<void> {\n\t\tawait withFileLock(this.paths, \"registry\", async () => {\n\t\t\tconst requestKey = transitionRequestKey({ action: \"resume\", reason });\n\t\t\tconst recovered = await this.recoverPendingUnlocked();\n\t\t\tif (recovered?.action === \"resume\" && recovered.requestKey === requestKey) return;\n\t\t\tconst [stable, trial, paused] = await Promise.all([\n\t\t\t\tthis.readStableDigestUnlocked(),\n\t\t\t\tthis.readTrialUnlocked(),\n\t\t\t\tthis.readPausedUnlocked(),\n\t\t\t]);\n\t\t\tawait this.executeTransitionUnlocked(\n\t\t\t\tthis.createTransition({\n\t\t\t\t\taction: \"resume\",\n\t\t\t\t\trequestKey,\n\t\t\t\t\tstableBefore: stable ?? null,\n\t\t\t\t\tstableAfter: stable ?? null,\n\t\t\t\t\ttrialBefore: trial ?? null,\n\t\t\t\t\ttrialAfter: trial ?? null,\n\t\t\t\t\tpausedBefore: paused,\n\t\t\t\t\tpausedAfter: null,\n\t\t\t\t\thistory: [{ action: \"resume\", actor: \"human\", reason }],\n\t\t\t\t}),\n\t\t\t);\n\t\t});\n\t}\n\n\tasync isPaused(): Promise<boolean> {\n\t\treturn withFileLock(this.paths, \"registry\", async () => {\n\t\t\tawait this.recoverPendingUnlocked();\n\t\t\treturn (await this.readPausedUnlocked()) !== null;\n\t\t});\n\t}\n\n\tasync getStatus(): Promise<EvoStatus> {\n\t\treturn withFileLock(this.paths, \"registry\", async () => {\n\t\t\tawait this.recoverPendingUnlocked();\n\t\t\tlet pendingProposals = 0;\n\t\t\tlet deferredProposals = 0;\n\t\t\tfor (const entry of await readdir(this.paths.proposals, { withFileTypes: true })) {\n\t\t\t\tif (!entry.isDirectory()) continue;\n\t\t\t\ttry {\n\t\t\t\t\tconst proposal = JSON.parse(\n\t\t\t\t\t\tawait readFile(join(this.paths.proposals, entry.name, \"proposal.json\"), \"utf8\"),\n\t\t\t\t\t) as Proposal;\n\t\t\t\t\tif (proposal.status === \"pending\") pendingProposals++;\n\t\t\t\t\tif (proposal.status === \"deferred\") deferredProposals++;\n\t\t\t\t} catch (error) {\n\t\t\t\t\tif (typeof error !== \"object\" || error === null || !(\"code\" in error) || error.code !== \"ENOENT\")\n\t\t\t\t\t\tthrow error;\n\t\t\t\t}\n\t\t\t}\n\t\t\tconst stableDigest = await this.readStableDigestUnlocked();\n\t\t\treturn {\n\t\t\t\tinitialized: stableDigest !== undefined,\n\t\t\t\tstableDigest,\n\t\t\t\ttrial: await this.readTrialUnlocked(),\n\t\t\t\tpendingProposals,\n\t\t\t\tdeferredProposals,\n\t\t\t\tpaused: (await this.readPausedUnlocked()) !== null,\n\t\t\t};\n\t\t});\n\t}\n}\n"]}