{"version":3,"file":"retry.d.ts","sourceRoot":"","sources":["../../src/evolve/retry.ts"],"names":[],"mappings":"AAOA,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,aAAa,CAAC;AAG5C,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,4BAA4B,CAAC;AAK9D,OAAO,KAAK,EACX,eAAe,EACf,gBAAgB,EAGhB,YAAY,EACZ,kBAAkB,EAClB,QAAQ,EACR,MAAM,aAAa,CAAC;AAGrB,OAAO,EAAE,KAAK,0BAA0B,EAAyB,MAAM,gBAAgB,CAAC;AACxF,OAAO,EAA+B,KAAK,sBAAsB,EAA6B,MAAM,cAAc,CAAC;AAqCnH,wBAAsB,0BAA0B,CAC/C,KAAK,EAAE,QAAQ,EACf,QAAQ,EAAE,QAAQ,EAClB,OAAO,GAAE;IAAE,OAAO,CAAC,EAAE,OAAO,CAAC;IAAC,gBAAgB,CAAC,EAAE,MAAM,CAAA;CAAO,GAC5D,OAAO,CAAC,MAAM,CAAC,CA+EjB;AAED,wBAAsB,4BAA4B,CAAC,OAAO,EAAE;IAC3D,KAAK,EAAE,QAAQ,CAAC;IAChB,WAAW,EAAE,MAAM,CAAC;IACpB,GAAG,EAAE,MAAM,CAAC;IACZ,OAAO,CAAC,EAAE,OAAO,CAAC;CAClB,GAAG,OAAO,CAAC;IAAE,KAAK,EAAE,MAAM,CAAC;IAAC,QAAQ,EAAE,QAAQ,CAAC;IAAC,MAAM,EAAE,kBAAkB,CAAA;CAAE,CAAC,CAyJ7E;AAED,MAAM,WAAW,6BAA6B;IAC7C,GAAG,EAAE,YAAY,CAAC;IAClB,OAAO,EAAE,sBAAsB,CAAC;IAChC,OAAO,EAAE,0BAA0B,CAAC;IACpC,mBAAmB,EAAE,eAAe,EAAE,CAAC;CACvC;AAED,MAAM,MAAM,kBAAkB,GAAG,UAAU,GAAG,UAAU,GAAG,YAAY,CAAC;AAaxE;;;GAGG;AACH,wBAAsB,gBAAgB,CAAC,KAAK,EAAE,QAAQ,EAAE,WAAW,EAAE,MAAM,GAAG,OAAO,CAAC,kBAAkB,CAAC,CAKxG;AAED;;;;GAIG;AACH,wBAAsB,qBAAqB,CAAC,OAAO,EAAE;IAAE,KAAK,EAAE,QAAQ,CAAC;IAAC,WAAW,EAAE,MAAM,CAAA;CAAE,GAAG,OAAO,CAAC,YAAY,CAAC,CAoCpH;AAED;;;;;GAKG;AACH,wBAAsB,uBAAuB,CAAC,OAAO,EAAE;IACtD,KAAK,EAAE,QAAQ,CAAC;IAChB,MAAM,EAAE,WAAW,CAAC;IACpB,KAAK,EAAE,MAAM,CAAC;IACd,GAAG,CAAC,EAAE,MAAM,CAAC;IACb,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,MAAM,CAAC,EAAE,gBAAgB,CAAC;IAC1B,MAAM,CAAC,EAAE,WAAW,CAAC;CACrB,GAAG,OAAO,CAAC,6BAA6B,CAAC,CAyGzC","sourcesContent":["import { copyFile, cp, readFile, rm } from \"node:fs/promises\";\nimport { join } from \"node:path\";\nimport { loadCompiledBundle } from \"../bundle/compile.ts\";\nimport { validateEvoComponentSelection } from \"../components/artifact.ts\";\nimport { EvoComponentProcess } from \"../components/process-runtime.ts\";\nimport { type CompactionV1Output, createDefaultEvoAbiRegistry } from \"../components/registry.ts\";\nimport { renderBundlePreferenceInstructions } from \"../memory/preferences.ts\";\nimport type { EvoPaths } from \"../paths.ts\";\nimport { attachProposalArtifact, loadProposal, proposalApproval, stageProposal } from \"../proposal.ts\";\nimport { validateEvaluationArtifact } from \"../proposal-artifacts.ts\";\nimport type { ModelRunner } from \"../reflect/model-runner.ts\";\nimport { type CounterfactualReplayResult, runCounterfactualReplay } from \"../reflect/replay.ts\";\nimport { BundleRegistry } from \"../registry/registry.ts\";\nimport { EvoService } from \"../service.ts\";\nimport { atomicWriteFile, atomicWriteJson, canonicalJson, sha256 } from \"../storage.ts\";\nimport type {\n\tEvoCheckProfile,\n\tEvoControlConfig,\n\tEvoExperimentSpec,\n\tEvolutionResearchPlan,\n\tEvolutionRun,\n\tEvolutionRunStatus,\n\tProposal,\n} from \"../types.ts\";\nimport { readProfileReceipts, writeProfileReceipt } from \"./check-profiles.ts\";\nimport { readEvoControlConfig } from \"./config.ts\";\nimport { type EvolutionEvaluationVerdict, runEvolutionEvaluator } from \"./evaluator.ts\";\nimport { applyEvolutionReleasePolicy, type EvolutionReleaseResult, RELEASE_ACTION_RUN_STATUS } from \"./release.ts\";\nimport { readMaterializedCorpus } from \"./research-corpus.ts\";\nimport { createEvolutionRun, evolutionRunDirectory, readEvolutionRun, updateEvolutionRun } from \"./run.ts\";\nimport { clearPendingVerification } from \"./verification-decision.ts\";\nimport { dryRunWorkflowSelection } from \"./workflow-dry-run.ts\";\n\nasync function candidateChanges(\n\tpaths: EvoPaths,\n\tproposal: Proposal,\n): Promise<Array<{ path: string; content: string | null }>> {\n\tif (!proposal.candidateDigest) throw new Error(`Proposal ${proposal.id} has no candidate bundle`);\n\tconst candidate = await loadCompiledBundle(paths, proposal.candidateDigest);\n\treturn Promise.all(\n\t\tproposal.changedPaths.map(async (path) => {\n\t\t\ttry {\n\t\t\t\treturn { path, content: await readFile(join(candidate.directory, path), \"utf8\") };\n\t\t\t} catch {\n\t\t\t\treturn { path, content: null };\n\t\t\t}\n\t\t}),\n\t);\n}\n\nasync function readBoundArtifact(paths: EvoPaths, proposal: Proposal, kind: \"review\" | \"replay\"): Promise<string> {\n\tconst reference = proposal.artifacts[kind];\n\tif (!reference) throw new Error(`Source proposal ${proposal.id} has no ${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\treturn readFile(join(paths.proposals, proposal.id, reference.file), \"utf8\");\n}\n\nexport async function validateComponentCandidate(\n\tpaths: EvoPaths,\n\tproposal: Proposal,\n\toptions: { sandbox?: boolean; requestTimeoutMs?: number } = {},\n): Promise<string> {\n\tif (!proposal.candidateDigest || !proposal.targetAbi)\n\t\tthrow new Error(\"Retry candidate is not a component selection\");\n\tconst bundle = await loadCompiledBundle(paths, proposal.candidateDigest);\n\tconst registry = createDefaultEvoAbiRegistry();\n\tconst abi = registry.require(proposal.targetAbi);\n\tif (abi.id === \"workflow/v1\") {\n\t\tconst selections = bundle.policy.workflows ?? [];\n\t\tif (selections.length === 0) throw new Error(\"Candidate bundle selects no workflows\");\n\t\tconst outcomes = await Promise.all(\n\t\t\tselections.map((selection) => dryRunWorkflowSelection(paths, selection, options)),\n\t\t);\n\t\tconst failed = outcomes.filter((outcome) => !outcome.passed);\n\t\tif (failed.length > 0) {\n\t\t\tthrow new Error(\n\t\t\t\t`Workflow dry run failed for ${failed.length} selection(s):\\n${failed\n\t\t\t\t\t.map((outcome) => outcome.markdown)\n\t\t\t\t\t.join(\"\\n\")}`,\n\t\t\t);\n\t\t}\n\t\treturn outcomes.map((outcome) => outcome.markdown).join(\"\\n\");\n\t}\n\tconst selection = bundle.policy.components?.[abi.surface];\n\tif (!selection) throw new Error(`Candidate bundle does not select ${abi.surface}`);\n\tconst config = registry.validateSelection(abi.surface, selection);\n\tconst artifact = await validateEvoComponentSelection(paths, abi.surface, selection, registry);\n\tconst process = new EvoComponentProcess(artifact, abi, config, {\n\t\tsandbox: options.sandbox,\n\t\t...(options.requestTimeoutMs === undefined ? {} : { requestTimeoutMs: options.requestTimeoutMs }),\n\t});\n\ttry {\n\t\tawait process.start();\n\t\tawait process.health();\n\t\tif (abi.id !== \"compaction/v1\") {\n\t\t\tthrow new Error(`Retry validation has no deterministic fixture for ${abi.id}`);\n\t\t}\n\t\tconst initialInput = {\n\t\t\tconversation: \"[User]: Preserve requirement alpha.\\n\\n[Assistant]: Requirement alpha is active.\",\n\t\t\tfirstKeptEntryId: \"entry-alpha\",\n\t\t\ttokensBefore: 4096,\n\t\t\treason: \"threshold\" as const,\n\t\t};\n\t\t// The ABI declares metrics as observational telemetry (wall-clock timing);\n\t\t// determinism binds the semantic effect, not the observation of producing it.\n\t\tconst semanticEffect = ({ metrics: _telemetry, ...effect }: CompactionV1Output) => effect;\n\t\tconst first = (await process.invoke(initialInput)) as CompactionV1Output;\n\t\tconst repeated = (await process.invoke(initialInput)) as CompactionV1Output;\n\t\tif (canonicalJson(semanticEffect(first)) !== canonicalJson(semanticEffect(repeated))) {\n\t\t\tthrow new Error(\"Component output is not deterministic\");\n\t\t}\n\t\tif (first.firstKeptEntryId !== initialInput.firstKeptEntryId) {\n\t\t\tthrow new Error(\"Component changed firstKeptEntryId\");\n\t\t}\n\t\tconst appended = (await process.invoke({\n\t\t\t...initialInput,\n\t\t\tconversation: \"[User]: Preserve requirement beta.\",\n\t\t\tpreviousSummary: first.summary,\n\t\t\ttokensBefore: 8192,\n\t\t})) as CompactionV1Output;\n\t\tif (!appended.summary.startsWith(first.summary)) {\n\t\t\tthrow new Error(\"Append compaction did not preserve previousSummary as an exact prefix\");\n\t\t}\n\t\treturn [\n\t\t\t\"# Reused component validation\",\n\t\t\t\"\",\n\t\t\t`- artifact: ${artifact.manifest.artifactDigest}`,\n\t\t\t`- ABI: ${abi.id}`,\n\t\t\t`- execution boundary: ${process.sandboxKind ?? \"unknown\"}`,\n\t\t\t\"- initialize: passed\",\n\t\t\t\"- health: passed\",\n\t\t\t\"- deterministic repeated invoke: passed\",\n\t\t\t\"- firstKeptEntryId preservation: passed\",\n\t\t\t\"- exact append-prefix preservation: passed\",\n\t\t\t\"- shutdown: passed\",\n\t\t\t\"\",\n\t\t].join(\"\\n\");\n\t} finally {\n\t\tawait process.shutdown();\n\t}\n}\n\nexport async function retryEvolutionFromValidation(options: {\n\tpaths: EvoPaths;\n\tsourceRunId: string;\n\tcwd: string;\n\tsandbox?: boolean;\n}): Promise<{ runId: string; proposal: Proposal; status: EvolutionRunStatus }> {\n\tlet sourceRun = await readEvolutionRun(options.paths, options.sourceRunId);\n\tif (![\"completed\", \"failed\", \"cancelled\"].includes(sourceRun.status)) {\n\t\tthrow new Error(`Evolution task ${sourceRun.id} is still active; pause or finish it before retrying`);\n\t}\n\tif (!sourceRun.proposalId) throw new Error(`Evolution task ${sourceRun.id} has no reusable proposal`);\n\tconst source = await loadProposal(options.paths, sourceRun.proposalId);\n\tif (!source.targetAbi || !source.candidateDigest)\n\t\tthrow new Error(\"Only built component candidates can resume at validation\");\n\tif (sourceRun.planFile !== \"plan.md\" || sourceRun.experimentFile !== \"experiment.json\") {\n\t\tthrow new Error(`Evolution task ${sourceRun.id} has no reusable frozen experiment`);\n\t}\n\tconst sourceDirectory = evolutionRunDirectory(options.paths, sourceRun.id);\n\tconst experiment = await readFile(join(sourceDirectory, sourceRun.experimentFile), \"utf8\");\n\tconst experimentSpec = JSON.parse(experiment) as Partial<EvoExperimentSpec>;\n\tconst experimentDigest = sha256(canonicalJson(experimentSpec));\n\tif (sourceRun.experimentDigest && experimentDigest !== sourceRun.experimentDigest) {\n\t\tthrow new Error(`Evolution task ${sourceRun.id} frozen experiment changed`);\n\t}\n\tif (!sourceRun.experimentDigest) {\n\t\t// Legacy runs predate experiment digests. Pin their existing immutable\n\t\t// experiment exactly once before creating the retry lineage.\n\t\tsourceRun = await updateEvolutionRun(options.paths, sourceRun.id, { experimentDigest });\n\t}\n\tconst [sourceReplay, sourceReview] = await Promise.all([\n\t\tsource.artifacts.replay ? readBoundArtifact(options.paths, source, \"replay\") : undefined,\n\t\tsource.artifacts.review ? readBoundArtifact(options.paths, source, \"review\") : undefined,\n\t]);\n\tconst stable = await new BundleRegistry(options.paths).readStableDigest();\n\tif (stable !== source.parentBundleDigest) throw new Error(\"Stable bundle changed; the candidate must be rebuilt\");\n\tconst run = await createEvolutionRun({\n\t\tpaths: options.paths,\n\t\ttrigger: \"request\",\n\t\trequest: sourceRun.request ?? source.motivation,\n\t\tevidenceDigest: sourceRun.evidenceDigest,\n\t\tstatus: \"validating\",\n\t});\n\tconst retryDirectory = evolutionRunDirectory(options.paths, run.id);\n\tlet retryProposal: Proposal | undefined;\n\ttry {\n\t\tawait copyFile(join(sourceDirectory, \"plan.md\"), join(retryDirectory, \"plan.md\"));\n\t\tawait copyFile(join(sourceDirectory, \"experiment.json\"), join(retryDirectory, \"experiment.json\"));\n\t\tawait updateEvolutionRun(options.paths, run.id, {\n\t\t\tretryOfRunId: sourceRun.id,\n\t\t\tsourceProposalId: source.id,\n\t\t\texperimentDigest: sourceRun.experimentDigest,\n\t\t\tplanFile: \"plan.md\",\n\t\t\texperimentFile: \"experiment.json\",\n\t\t});\n\t\tconst observations = await readFile(join(options.paths.proposals, source.id, \"observations.md\"), \"utf8\");\n\t\tconst proposal = await stageProposal({\n\t\t\tpaths: options.paths,\n\t\t\tparentDigest: source.parentBundleDigest,\n\t\t\tobservationsMarkdown: observations,\n\t\t\tdraft: {\n\t\t\t\tmotivation: source.motivation,\n\t\t\t\texpectedEffect: source.expectedEffect,\n\t\t\t\trisk: source.risk,\n\t\t\t\tverifyPlan: source.verifyPlan,\n\t\t\t\ttrialPlan: source.trialPlan,\n\t\t\t\tsource: source.source,\n\t\t\t\tevidence: source.evidence,\n\t\t\t\tinboxReferences: source.inboxReferences,\n\t\t\t\treplayScenarios: source.replayScenarios,\n\t\t\t\ttargetAbi: source.targetAbi,\n\t\t\t\trequiresNewAbi: source.requiresNewAbi,\n\t\t\t\tsuggestedTier: source.tier,\n\t\t\t\tchanges: await candidateChanges(options.paths, source),\n\t\t\t},\n\t\t\trepositoryCwd: options.cwd,\n\t\t});\n\t\tretryProposal = proposal;\n\t\tawait updateEvolutionRun(options.paths, run.id, { proposalId: proposal.id });\n\t\tif (!proposal.l1.passed) throw new Error(`Reused proposal ${proposal.id} failed L1`);\n\t\tif (\n\t\t\tproposal.diffDigest !== source.diffDigest ||\n\t\t\tproposal.candidateDigest !== source.candidateDigest ||\n\t\t\tproposal.targetAbi !== source.targetAbi\n\t\t) {\n\t\t\tthrow new Error(\"Reused component no longer matches the source candidate\");\n\t\t}\n\t\tconst validation = await validateComponentCandidate(options.paths, proposal, { sandbox: options.sandbox });\n\t\tawait atomicWriteFile(join(evolutionRunDirectory(options.paths, run.id), \"validation.md\"), validation);\n\t\tlet reviewed = await attachProposalArtifact({\n\t\t\tpaths: options.paths,\n\t\t\tproposalId: proposal.id,\n\t\t\texpected: proposalApproval(proposal),\n\t\t\tkind: \"replay\",\n\t\t\tcontent:\n\t\t\t\tsourceReplay ??\n\t\t\t\t`${validation}\\nThis trusted executable component replay replaces the unavailable pre-validation model replay. Provider and live-session effects remain Canary evidence.\\n`,\n\t\t\tallowedStatuses: [\"pending\"],\n\t\t});\n\t\treviewed = await attachProposalArtifact({\n\t\t\tpaths: options.paths,\n\t\t\tproposalId: reviewed.id,\n\t\t\texpected: proposalApproval(reviewed),\n\t\t\tkind: \"validation\",\n\t\t\tcontent: validation,\n\t\t\tallowedStatuses: [\"pending\"],\n\t\t});\n\t\treviewed = await attachProposalArtifact({\n\t\t\tpaths: options.paths,\n\t\t\tproposalId: reviewed.id,\n\t\t\texpected: proposalApproval(reviewed),\n\t\t\tkind: \"review\",\n\t\t\tcontent: [\n\t\t\t\t\"# Retry review\",\n\t\t\t\t\"\",\n\t\t\t\t\"## Prior independent evaluation\",\n\t\t\t\t\"\",\n\t\t\t\tsourceReview?.trim() ??\n\t\t\t\t\t\"No prior independent evaluation completed because the source run stopped during deterministic validation.\",\n\t\t\t\t\"\",\n\t\t\t\t\"## Trusted executable-validation addendum\",\n\t\t\t\t\"\",\n\t\t\t\t\"The exact same content-addressed candidate passed executable ABI, health, determinism, and prefix-preservation validation. Benefit and semantic non-inferiority remain unverified, so it may only enter the explicitly approved reversible Canary.\",\n\t\t\t\t\"\",\n\t\t\t\t\"Recommendation: needs-evidence\",\n\t\t\t\t\"\",\n\t\t\t].join(\"\\n\"),\n\t\t\tallowedStatuses: [\"pending\"],\n\t\t});\n\t\tconst strategy = experimentSpec.evidenceStrategy;\n\t\tconst canaryEligible =\n\t\t\tstrategy?.historicalReplay.mode !== \"required\" &&\n\t\t\tstrategy?.online.mode === \"canary\" &&\n\t\t\tstrategy.rollout === \"canary-first\";\n\t\tconst status: EvolutionRunStatus = canaryEligible ? \"awaiting-canary-approval\" : \"awaiting-evidence\";\n\t\tawait updateEvolutionRun(options.paths, run.id, {\n\t\t\tstatus,\n\t\t\tproposalId: reviewed.id,\n\t\t\t...(canaryEligible\n\t\t\t\t? {\n\t\t\t\t\t\tcanaryCandidateDigest: reviewed.candidateDigest,\n\t\t\t\t\t\tcanaryParentDigest: reviewed.parentBundleDigest,\n\t\t\t\t\t\tcanaryTargetAbi: reviewed.targetAbi,\n\t\t\t\t\t}\n\t\t\t\t: {}),\n\t\t});\n\t\treturn { runId: run.id, proposal: reviewed, status };\n\t} catch (error) {\n\t\tif (retryProposal?.status === \"pending\") {\n\t\t\tawait new EvoService(options.paths)\n\t\t\t\t.reject(retryProposal.id, \"Retry validation failed before Canary activation\")\n\t\t\t\t.catch(() => undefined);\n\t\t}\n\t\tawait updateEvolutionRun(options.paths, run.id, {\n\t\t\tstatus: \"failed\",\n\t\t\terror: error instanceof Error ? error.message : String(error),\n\t\t}).catch(() => undefined);\n\t\tthrow error;\n\t}\n}\n\nexport interface ResumeEvolutionEvidenceResult {\n\trun: EvolutionRun;\n\trelease: EvolutionReleaseResult;\n\tverdict: EvolutionEvaluationVerdict;\n\texecutedProfilesNow: EvoCheckProfile[];\n}\n\nexport type EvolutionRetryFrom = \"research\" | \"building\" | \"validating\";\n\n/** A terminal run's surviving assets decide where a retry can resume. */\nasync function frozenPlanResumable(paths: EvoPaths, run: EvolutionRun): Promise<boolean> {\n\tif (run.planFile !== \"plan.md\" || run.experimentFile !== \"experiment.json\" || !run.experimentDigest) {\n\t\treturn false;\n\t}\n\tconst directory = evolutionRunDirectory(paths, run.id);\n\tconst planJson = await readFile(join(directory, \"plan.json\"), \"utf8\").catch(() => undefined);\n\tif (!planJson) return false;\n\treturn (await readMaterializedCorpus(directory)) !== undefined;\n}\n\n/**\n * The default resume point for a retry: a built component continues at validation,\n * a frozen plan continues at the builder, anything else starts research over.\n */\nexport async function resolveRetryFrom(paths: EvoPaths, sourceRunId: string): Promise<EvolutionRetryFrom> {\n\tconst run = await readEvolutionRun(paths, sourceRunId);\n\tif (run.proposalId) return \"validating\";\n\tif (await frozenPlanResumable(paths, run)) return \"building\";\n\treturn \"research\";\n}\n\n/**\n * Queue a new run that resumes a terminal run at the builder: the frozen plan,\n * experiment, and evidence corpus are copied into the new run directory and the\n * cycle skips research. Spawning the worker is the caller's job.\n */\nexport async function prepareBuildingResume(options: { paths: EvoPaths; sourceRunId: string }): Promise<EvolutionRun> {\n\tconst source = await readEvolutionRun(options.paths, options.sourceRunId);\n\tif (![\"completed\", \"failed\", \"cancelled\"].includes(source.status)) {\n\t\tthrow new Error(`Evolution task ${source.id} is still active; pause or finish it before retrying`);\n\t}\n\tif (!(await frozenPlanResumable(options.paths, source))) {\n\t\tthrow new Error(\n\t\t\t`Evolution task ${source.id} has no reusable frozen plan; retry with --from research to start over`,\n\t\t);\n\t}\n\tconst run = await createEvolutionRun({\n\t\tpaths: options.paths,\n\t\ttrigger: source.trigger,\n\t\t...(source.request ? { request: source.request } : {}),\n\t\tevidenceDigest: source.evidenceDigest,\n\t\tstatus: \"queued\",\n\t});\n\tconst sourceDirectory = evolutionRunDirectory(options.paths, source.id);\n\tconst retryDirectory = evolutionRunDirectory(options.paths, run.id);\n\ttry {\n\t\tawait copyFile(join(sourceDirectory, \"plan.md\"), join(retryDirectory, \"plan.md\"));\n\t\tawait copyFile(join(sourceDirectory, \"plan.json\"), join(retryDirectory, \"plan.json\"));\n\t\tawait copyFile(join(sourceDirectory, \"experiment.json\"), join(retryDirectory, \"experiment.json\"));\n\t\tawait cp(join(sourceDirectory, \"corpus\"), join(retryDirectory, \"corpus\"), { recursive: true });\n\t} catch (error) {\n\t\tconst message = error instanceof Error ? error.message : String(error);\n\t\tawait updateEvolutionRun(options.paths, run.id, { status: \"failed\", error: message }).catch(() => undefined);\n\t\tthrow error;\n\t}\n\treturn updateEvolutionRun(options.paths, run.id, {\n\t\tretryOfRunId: source.id,\n\t\tresumeFrom: \"building\",\n\t\texperimentDigest: source.experimentDigest,\n\t\tplanFile: \"plan.md\",\n\t\texperimentFile: \"experiment.json\",\n\t});\n}\n\n/**\n * Complete an awaiting-evidence run by executing whichever required check profiles\n * can still run (currently paired-replay), then re-evaluating and re-applying the\n * deterministic release policy. The waiting state is defined by missing receipts,\n * so this is the state machine's own forward edge — not a special-case repair.\n */\nexport async function resumeEvolutionEvidence(options: {\n\tpaths: EvoPaths;\n\trunner: ModelRunner;\n\trunId: string;\n\tcwd?: string;\n\tagentDir?: string;\n\tconfig?: EvoControlConfig;\n\tsignal?: AbortSignal;\n}): Promise<ResumeEvolutionEvidenceResult> {\n\tconst run = await readEvolutionRun(options.paths, options.runId);\n\tif (run.status !== \"awaiting-evidence\") {\n\t\tthrow new Error(`Evolution run ${run.id} is ${run.status}, not awaiting-evidence`);\n\t}\n\tif (!run.proposalId) throw new Error(`Evolution run ${run.id} has no proposal to resume`);\n\tconst directory = evolutionRunDirectory(options.paths, run.id);\n\tconst planRaw = await readFile(join(directory, \"plan.json\"), \"utf8\").catch(() => undefined);\n\tif (!planRaw) {\n\t\tthrow new Error(`Evolution run ${run.id} predates plan persistence and cannot resume automatically`);\n\t}\n\tconst plan = JSON.parse(planRaw) as EvolutionResearchPlan;\n\tlet proposal = await loadProposal(options.paths, run.proposalId);\n\tconst config = options.config ?? (await readEvoControlConfig(options.paths));\n\tconst service = new EvoService(options.paths);\n\n\tconst strategy = plan.experiment.evidenceStrategy;\n\t// Resumption executes what the frozen plan declared: required profiles plus\n\t// recommended ones, which reach this path through an explicit execute decision.\n\tconst declared: EvoCheckProfile[] = [\n\t\t...(strategy.offline.mode === \"required\" ? strategy.offline.profiles : []),\n\t\t...(strategy.historicalReplay.mode === \"required\" || strategy.historicalReplay.mode === \"recommended\"\n\t\t\t? strategy.historicalReplay.profiles\n\t\t\t: []),\n\t];\n\tconst receipts = await readProfileReceipts(options.paths, run.id);\n\tconst missing = declared.filter((profile) => receipts.get(profile)?.passed !== true);\n\tconst executedProfilesNow: EvoCheckProfile[] = [];\n\n\tlet replay: CounterfactualReplayResult | undefined;\n\tif (missing.includes(\"paired-replay\") && proposal.replayScenarios.length > 0) {\n\t\tawait updateEvolutionRun(options.paths, run.id, { status: \"replaying\" });\n\t\treplay = await runCounterfactualReplay({\n\t\t\tpaths: options.paths,\n\t\t\trunner: options.runner,\n\t\t\tproposal,\n\t\t\t...(options.agentDir ? { agentDir: options.agentDir } : {}),\n\t\t\tmodel: config.models.evaluator.model,\n\t\t\t...(config.models.evaluator.thinkingLevel ? { thinkingLevel: config.models.evaluator.thinkingLevel } : {}),\n\t\t\t...(options.signal ? { signal: options.signal } : {}),\n\t\t});\n\t\tproposal = replay.proposal;\n\t\tawait writeProfileReceipt(options.paths, run.id, {\n\t\t\tprofile: \"paired-replay\",\n\t\t\tpassed: true,\n\t\t\tsummary: \"Counterfactual paired replay executed during evidence resumption\",\n\t\t\tartifact: sha256(replay.markdown),\n\t\t});\n\t\texecutedProfilesNow.push(\"paired-replay\");\n\t}\n\n\tconst materializedCorpus = await readMaterializedCorpus(directory);\n\tconst stable = await new BundleRegistry(options.paths).readStableDigest();\n\tconst activePreferences = stable\n\t\t? await renderBundlePreferenceInstructions(await loadCompiledBundle(options.paths, stable))\n\t\t: undefined;\n\tawait updateEvolutionRun(options.paths, run.id, { status: \"evaluating\" });\n\tconst evaluation = await runEvolutionEvaluator({\n\t\tpaths: options.paths,\n\t\tplan,\n\t\tproposal,\n\t\tcorpus: { text: \"\", truncated: false },\n\t\t// The revision's review artifact is immutable; this re-evaluation lives in\n\t\t// the run directory as evaluation-resume.md instead.\n\t\tattachArtifact: false,\n\t\t...(materializedCorpus ? { materializedCorpus } : {}),\n\t\t...(replay ? { replay } : {}),\n\t\trunner: options.runner,\n\t\tcwd: options.cwd ?? process.cwd(),\n\t\t...(options.agentDir ? { agentDir: options.agentDir } : {}),\n\t\tmodel: config.models.evaluator.model,\n\t\t...(config.models.evaluator.thinkingLevel ? { thinkingLevel: config.models.evaluator.thinkingLevel } : {}),\n\t\t...(activePreferences ? { activePreferences } : {}),\n\t\t...(options.signal ? { signal: options.signal } : {}),\n\t});\n\tproposal = evaluation.proposal;\n\tawait atomicWriteFile(join(directory, \"evaluation-resume.md\"), evaluation.markdown);\n\tawait atomicWriteJson(join(directory, \"release-intent.json\"), {\n\t\tschemaVersion: 1,\n\t\tproposalId: proposal.id,\n\t\tverdict: evaluation.verdict,\n\t\tintendedAt: new Date().toISOString(),\n\t});\n\tconst release = await applyEvolutionReleasePolicy({\n\t\tservice,\n\t\tconfig,\n\t\tproposal,\n\t\tverdict: evaluation.verdict,\n\t\tevidenceStrategy: strategy,\n\t\treceipts: await readProfileReceipts(options.paths, run.id),\n\t});\n\tconst finalRun = await updateEvolutionRun(options.paths, run.id, {\n\t\tstatus: RELEASE_ACTION_RUN_STATUS[release.action],\n\t\tproposalId: release.proposal.id,\n\t\t...(release.action === \"awaiting-canary-approval\"\n\t\t\t? {\n\t\t\t\t\tcanaryCandidateDigest: release.proposal.candidateDigest,\n\t\t\t\t\tcanaryParentDigest: release.proposal.parentBundleDigest,\n\t\t\t\t\tcanaryTargetAbi: release.proposal.targetAbi,\n\t\t\t\t}\n\t\t\t: {}),\n\t});\n\tawait clearPendingVerification(options.paths, run.id);\n\tawait rm(join(directory, \"release-intent.json\"), { force: true });\n\treturn { run: finalRun, release, verdict: evaluation.verdict, executedProfilesNow };\n}\n"]}