{"version":3,"file":"cycle.d.ts","sourceRoot":"","sources":["../../src/evolve/cycle.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,sBAAsB,EAAE,MAAM,qBAAqB,CAAC;AAWlE,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,aAAa,CAAC;AAW5C,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,4BAA4B,CAAC;AAE9D,OAAO,EAAE,UAAU,EAAE,MAAM,eAAe,CAAC;AAE3C,OAAO,KAAK,EAAE,gBAAgB,EAAE,qBAAqB,EAAE,YAAY,EAAE,QAAQ,EAAE,MAAM,aAAa,CAAC;AAInG,OAAO,EAAE,KAAK,yBAAyB,EAAyB,MAAM,gBAAgB,CAAC;AACvF,OAAO,EAGN,KAAK,sBAAsB,EAE3B,MAAM,cAAc,CAAC;AAKtB,OAAO,EAGN,KAAK,wBAAwB,EAC7B,MAAM,kBAAkB,CAAC;AAG1B,MAAM,WAAW,wBAAwB;IACxC,KAAK,EAAE,QAAQ,CAAC;IAChB,MAAM,EAAE,WAAW,CAAC;IACpB,OAAO,CAAC,EAAE,UAAU,CAAC;IACrB,GAAG,CAAC,EAAE,MAAM,CAAC;IACb,yFAAyF;IACzF,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,OAAO,CAAC,EAAE,YAAY,CAAC,SAAS,CAAC,CAAC;IAClC,MAAM,CAAC,EAAE,gBAAgB,CAAC;IAC1B,cAAc,CAAC,EAAE,MAAM,CAAC;IACxB,sBAAsB,CAAC,EAAE,sBAAsB,CAAC;IAChD,sEAAsE;IACtE,gBAAgB,CAAC,EAAE,OAAO,CAAC;IAC3B,MAAM,CAAC,EAAE,WAAW,CAAC;IACrB,8DAA8D;IAC9D,KAAK,CAAC,EAAE,MAAM,CAAC;CACf;AAED,MAAM,WAAW,oBAAoB;IACpC,GAAG,EAAE,YAAY,CAAC;IAClB,SAAS,EAAE,QAAQ,EAAE,CAAC;IACtB,UAAU,CAAC,EAAE,yBAAyB,CAAC;IACvC,OAAO,CAAC,EAAE,sBAAsB,CAAC;CACjC;AAED,MAAM,WAAW,gCAAgC;IAChD,KAAK,EAAE,QAAQ,CAAC;IAChB,OAAO,EAAE,OAAO,CAAC;IACjB,MAAM,EAAE,WAAW,CAAC;IACpB,OAAO,CAAC,EAAE,UAAU,CAAC;IACrB,kFAAkF;IAClF,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,GAAG,CAAC,EAAE,MAAM,CAAC;IACb,8GAA8G;IAC9G,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,MAAM,CAAC,EAAE,gBAAgB,CAAC;IAC1B,sBAAsB,CAAC,EAAE,sBAAsB,CAAC;IAChD,MAAM,CAAC,EAAE,WAAW,CAAC;CACrB;AAED,MAAM,WAAW,4BAA4B;IAC5C,GAAG,EAAE,YAAY,CAAC;IAClB,QAAQ,EAAE,QAAQ,CAAC;IACnB,OAAO,EAAE,wBAAwB,CAAC;IAClC,IAAI,EAAE,qBAAqB,CAAC;IAC5B,UAAU,EAAE,MAAM,CAAC;CACnB;AAoVD,wBAAsB,iBAAiB,CAAC,OAAO,EAAE,wBAAwB,GAAG,OAAO,CAAC,oBAAoB,CAAC,CAKxG;AAyLD,wBAAsB,yBAAyB,CAC9C,OAAO,EAAE,gCAAgC,GACvC,OAAO,CAAC,4BAA4B,CAAC,CAKvC","sourcesContent":["import { readFile, rm } from \"node:fs/promises\";\nimport { join } from \"node:path\";\nimport { loadCompiledBundle } from \"../bundle/compile.ts\";\nimport type { CodeValidationExecutor } from \"../code/worktree.ts\";\nimport { resolveEvoSourceRepositoryRoot } from \"../code/worktree.ts\";\nimport { createDefaultEvoAbiRegistry } from \"../components/registry.ts\";\nimport {\n\tapplyInboxDecisions,\n\tgarbageCollectInbox,\n\tlinkProposalInbox,\n\treadInboxEntry,\n\treadInboxLifecycleStates,\n} from \"../inbox.ts\";\nimport { renderBundlePreferenceInstructions } from \"../memory/preferences.ts\";\nimport type { EvoPaths } from \"../paths.ts\";\nimport { attachProposalArtifact, proposalApproval, stageProposal } from \"../proposal.ts\";\nimport {\n\tadvanceEvidenceReviewCursor,\n\tcollectEvidenceCorpus,\n\ttype EvidenceCorpus,\n\ttype EvidenceReviewCursor,\n\tMATERIALIZED_CORPUS_BYTES,\n\treadEvidenceReviewCursor,\n\tvalidateDraftGrounding,\n} from \"../reflect/evidence.ts\";\nimport type { ModelRunner } from \"../reflect/model-runner.ts\";\nimport { type CounterfactualReplayResult, runCounterfactualReplay } from \"../reflect/replay.ts\";\nimport { EvoService } from \"../service.ts\";\nimport { atomicWriteFile, atomicWriteJson, canonicalJson, sha256, withFileLock } from \"../storage.ts\";\nimport type { EvoControlConfig, EvolutionResearchPlan, EvolutionRun, Proposal } from \"../types.ts\";\nimport { runEvolutionBuilder, runUnknownAbiBuilder } from \"./builder.ts\";\nimport { readProfileReceipts, writeProfileReceipt } from \"./check-profiles.ts\";\nimport { readEvoControlConfig } from \"./config.ts\";\nimport { type EvolutionEvaluationResult, runEvolutionEvaluator } from \"./evaluator.ts\";\nimport {\n\tapplyEvolutionReleasePolicy,\n\tchangesComponentSelection,\n\ttype EvolutionReleaseResult,\n\tRELEASE_ACTION_RUN_STATUS,\n} from \"./release.ts\";\nimport { type MaterializedCorpus, materializeEvidenceCorpus, readMaterializedCorpus } from \"./research-corpus.ts\";\nimport { persistEvolutionResearchPlan, runEvolutionResearchPlan } from \"./research-plan.ts\";\nimport { validateComponentCandidate } from \"./retry.ts\";\nimport { createEvolutionRun, evolutionRunDirectory, readEvolutionRun, updateEvolutionRun } from \"./run.ts\";\nimport {\n\tassertUnknownAbiCodePatch,\n\tparseUnknownAbiBuilderRequest,\n\ttype UnknownAbiBuilderRequest,\n} from \"./unknown-abi.ts\";\nimport { writePendingVerification } from \"./verification-decision.ts\";\n\nexport interface RunEvolutionCycleOptions {\n\tpaths: EvoPaths;\n\trunner: ModelRunner;\n\tservice?: EvoService;\n\tcwd?: string;\n\t/** Repository that code candidates patch; defaults to Evo-Pi's own source repository. */\n\trepositoryCwd?: string;\n\tagentDir?: string;\n\trequest?: string;\n\ttrigger?: EvolutionRun[\"trigger\"];\n\tconfig?: EvoControlConfig;\n\tmaxCorpusBytes?: number;\n\tcodeValidationExecutor?: CodeValidationExecutor;\n\t/** Test/development escape hatch for component process validation. */\n\tcomponentSandbox?: boolean;\n\tsignal?: AbortSignal;\n\t/** Existing queued run created by the background launcher. */\n\trunId?: string;\n}\n\nexport interface EvolutionCycleResult {\n\trun: EvolutionRun;\n\tproposals: Proposal[];\n\tevaluation?: EvolutionEvaluationResult;\n\trelease?: EvolutionReleaseResult;\n}\n\nexport interface RunUnknownAbiBuilderCycleOptions {\n\tpaths: EvoPaths;\n\trequest: unknown;\n\trunner: ModelRunner;\n\tservice?: EvoService;\n\t/** Stable bundle observed by pack import; drift fails before a run is created. */\n\tparentDigest?: string;\n\tcwd?: string;\n\t/** Repository that the infrastructure patch is staged against; defaults to Evo-Pi's own source repository. */\n\trepositoryCwd?: string;\n\tagentDir?: string;\n\tconfig?: EvoControlConfig;\n\tcodeValidationExecutor?: CodeValidationExecutor;\n\tsignal?: AbortSignal;\n}\n\nexport interface UnknownAbiBuilderCycleResult {\n\trun: EvolutionRun;\n\tproposal: Proposal;\n\trequest: UnknownAbiBuilderRequest;\n\tplan: EvolutionResearchPlan;\n\tnextAction: string;\n}\n\nasync function assertFrozenExperiment(paths: EvoPaths, run: EvolutionRun, experiment: unknown): Promise<void> {\n\tif (run.experimentFile !== \"experiment.json\" || !run.experimentDigest) {\n\t\tthrow new Error(`Evolution task ${run.id} has no frozen experiment`);\n\t}\n\tconst stored = JSON.parse(\n\t\tawait readFile(join(evolutionRunDirectory(paths, run.id), run.experimentFile), \"utf8\"),\n\t) as unknown;\n\tconst digest = sha256(canonicalJson(stored));\n\tif (digest !== run.experimentDigest || canonicalJson(stored) !== canonicalJson(experiment)) {\n\t\tthrow new Error(`Evolution task ${run.id} frozen experiment changed`);\n\t}\n}\n\nasync function assertFrozenUnknownAbiRequest(\n\tpaths: EvoPaths,\n\trun: EvolutionRun,\n\trequest: UnknownAbiBuilderRequest,\n): Promise<void> {\n\tconst stored = parseUnknownAbiBuilderRequest(\n\t\tJSON.parse(\n\t\t\tawait readFile(join(evolutionRunDirectory(paths, run.id), \"unknown-abi-request.json\"), \"utf8\"),\n\t\t) as unknown,\n\t);\n\tconst digest = sha256(canonicalJson(stored));\n\tif (digest !== run.evidenceDigest || canonicalJson(stored) !== canonicalJson(request)) {\n\t\tthrow new Error(`Evolution task ${run.id} frozen unknown ABI request changed`);\n\t}\n}\n\nasync function firstCorpusRequest(paths: EvoPaths, files: readonly string[]): Promise<string | undefined> {\n\tconst states = await readInboxLifecycleStates(paths);\n\tfor (const file of files) {\n\t\tconst entry = await readInboxEntry(paths, file);\n\t\tconst text = entry.text.trimStart();\n\t\tif (entry.kind === \"request\" || states.get(file)?.kind === \"request\" || text.startsWith(\"REQUEST:\")) {\n\t\t\treturn text.startsWith(\"REQUEST:\") ? text.slice(\"REQUEST:\".length).trim() : text;\n\t\t}\n\t}\n\treturn undefined;\n}\n\nasync function runEvolutionCycleUnlocked(options: RunEvolutionCycleOptions): Promise<EvolutionCycleResult> {\n\tconst service = options.service ?? new EvoService(options.paths);\n\tif (await service.registry.isPaused()) throw new Error(\"Evo-Pi is paused\");\n\tconst stable = await service.registry.readStableDigest();\n\tif (!stable) throw new Error(\"Evo-Pi registry is not initialized\");\n\tconst stableBundle = await loadCompiledBundle(options.paths, stable);\n\tconst activePreferences = await renderBundlePreferenceInstructions(stableBundle);\n\t// A run created by `retry --from building` already carries its frozen research plan\n\t// and evidence corpus; the cycle skips research and resumes at the builder.\n\tconst priorRun = options.runId ? await readEvolutionRun(options.paths, options.runId) : undefined;\n\tconst resumeFromBuilding = priorRun?.resumeFrom === \"building\";\n\tlet collected: EvidenceCorpus | undefined;\n\tlet run: EvolutionRun;\n\tif (priorRun && resumeFromBuilding) {\n\t\trun = priorRun;\n\t} else {\n\t\tconst reviewCursor = await readEvidenceReviewCursor(options.paths);\n\t\tcollected = await collectEvidenceCorpus(options.paths, {\n\t\t\tmode: \"incremental\",\n\t\t\t// The cycle materializes the corpus to disk, so it can afford the large budget.\n\t\t\tmaxBytes: options.maxCorpusBytes ?? MATERIALIZED_CORPUS_BYTES,\n\t\t\t...(reviewCursor ? { reviewCursor } : {}),\n\t\t});\n\t\tconst request = options.request ?? (await firstCorpusRequest(options.paths, collected.inboxFiles));\n\t\trun = priorRun\n\t\t\t? await updateEvolutionRun(options.paths, priorRun.id, {\n\t\t\t\t\tstatus: \"researching\",\n\t\t\t\t\t...(request ? { request } : {}),\n\t\t\t\t\tevidenceDigest: collected.evidenceDigest,\n\t\t\t\t\terror: undefined,\n\t\t\t\t})\n\t\t\t: await createEvolutionRun({\n\t\t\t\t\tpaths: options.paths,\n\t\t\t\t\ttrigger: options.trigger ?? (request ? \"request\" : \"scheduled\"),\n\t\t\t\t\t...(request ? { request } : {}),\n\t\t\t\t\tevidenceDigest: collected.evidenceDigest,\n\t\t\t\t});\n\t}\n\ttry {\n\t\tconst config = options.config ?? (await readEvoControlConfig(options.paths));\n\t\tconst cwd = options.cwd ?? process.cwd();\n\t\tconst directory = evolutionRunDirectory(options.paths, run.id);\n\t\tlet plan: EvolutionResearchPlan;\n\t\tlet corpus: Pick<EvidenceCorpus, \"text\" | \"truncated\">;\n\t\tlet materializedCorpus: MaterializedCorpus;\n\t\t// Set only by a run that collected fresh evidence; resume runs leave the review\n\t\t// cursor untouched because their corpus was frozen by the source run.\n\t\tlet reviewCursorToAdvance: EvidenceReviewCursor | undefined;\n\t\tif (resumeFromBuilding) {\n\t\t\tplan = JSON.parse(await readFile(join(directory, \"plan.json\"), \"utf8\")) as EvolutionResearchPlan;\n\t\t\tawait assertFrozenExperiment(options.paths, run, plan.experiment);\n\t\t\tconst frozenCorpus = await readMaterializedCorpus(directory);\n\t\t\tif (!frozenCorpus) throw new Error(`Evolution task ${run.id} has no frozen evidence corpus to resume`);\n\t\t\tmaterializedCorpus = frozenCorpus;\n\t\t\tcorpus = { text: \"\", truncated: false };\n\t\t} else if (collected) {\n\t\t\t// The corpus lives on disk as an on-demand file tree; prompts carry only its index\n\t\t\t// so large evidence never crowds the context window.\n\t\t\tmaterializedCorpus = await materializeEvidenceCorpus(collected, directory);\n\t\t\tconst research = await runEvolutionResearchPlan({\n\t\t\t\tpaths: options.paths,\n\t\t\t\trun,\n\t\t\t\tcorpus: collected,\n\t\t\t\tmaterializedCorpus,\n\t\t\t\trunner: options.runner,\n\t\t\t\tcwd,\n\t\t\t\t...(options.agentDir ? { agentDir: options.agentDir } : {}),\n\t\t\t\tmodel: config.models.researchPlanner.model,\n\t\t\t\t...(config.models.researchPlanner.thinkingLevel\n\t\t\t\t\t? { thinkingLevel: config.models.researchPlanner.thinkingLevel }\n\t\t\t\t\t: {}),\n\t\t\t\t...(activePreferences ? { activePreferences } : {}),\n\t\t\t\t...(options.signal ? { signal: options.signal } : {}),\n\t\t\t});\n\t\t\tawait assertFrozenExperiment(options.paths, research.state, research.plan.experiment);\n\t\t\tawait applyInboxDecisions(options.paths, research.plan.inboxDecisions, new Set(collected.inboxFiles));\n\t\t\tplan = research.plan;\n\t\t\tcorpus = collected;\n\t\t\treviewCursorToAdvance = collected.nextReviewCursor;\n\t\t} else {\n\t\t\tthrow new Error(\"Evolution cycle cannot research without an evidence corpus\");\n\t\t}\n\t\tif (plan.candidateKind === \"none\") {\n\t\t\tif (reviewCursorToAdvance) await advanceEvidenceReviewCursor(options.paths, reviewCursorToAdvance);\n\t\t\tawait garbageCollectInbox(options.paths);\n\t\t\tconst completed = await updateEvolutionRun(options.paths, run.id, { status: \"completed\" });\n\t\t\treturn { run: completed, proposals: [] };\n\t\t}\n\t\t// Code candidates always patch Evo-Pi's own source repository, never the\n\t\t// incidental launch directory of the triggering session.\n\t\tconst codeRepositoryCwd =\n\t\t\tplan.candidateKind === \"code\"\n\t\t\t\t? (options.repositoryCwd ?? (await resolveEvoSourceRepositoryRoot()))\n\t\t\t\t: undefined;\n\t\tawait updateEvolutionRun(options.paths, run.id, { status: \"building\" });\n\t\tconst built = await runEvolutionBuilder({\n\t\t\tpaths: options.paths,\n\t\t\trunId: run.id,\n\t\t\tplan,\n\t\t\tparentDigest: stable,\n\t\t\tcorpus,\n\t\t\tmaterializedCorpus,\n\t\t\tsessionIdentity: `evo-builder-${run.id}`,\n\t\t\trunner: options.runner,\n\t\t\tcwd: codeRepositoryCwd ?? cwd,\n\t\t\t...(options.agentDir ? { agentDir: options.agentDir } : {}),\n\t\t\tmodel: config.models.builder.model,\n\t\t\t...(config.models.builder.thinkingLevel ? { thinkingLevel: config.models.builder.thinkingLevel } : {}),\n\t\t\t...(activePreferences ? { activePreferences } : {}),\n\t\t\t...(options.signal ? { signal: options.signal } : {}),\n\t\t});\n\t\tawait updateEvolutionRun(options.paths, run.id, { status: \"validating\" });\n\t\tconst groundedDraft = {\n\t\t\t...built.draft,\n\t\t\tevidence: await validateDraftGrounding(options.paths, built.draft),\n\t\t};\n\t\tlet proposal = await stageProposal({\n\t\t\tpaths: options.paths,\n\t\t\tparentDigest: stable,\n\t\t\tdraft: groundedDraft,\n\t\t\tobservationsMarkdown: built.observationsMarkdown,\n\t\t\trepositoryCwd: codeRepositoryCwd ?? cwd,\n\t\t\t...(built.codeBase ? { expectedCodeBase: built.codeBase } : {}),\n\t\t\t...(options.codeValidationExecutor ? { codeValidationExecutor: options.codeValidationExecutor } : {}),\n\t\t\t...(options.signal ? { signal: options.signal } : {}),\n\t\t});\n\t\tawait updateEvolutionRun(options.paths, run.id, { status: \"validating\", proposalId: proposal.id });\n\t\tif (!proposal.l1.passed) throw new Error(`Evolution proposal ${proposal.id} failed L1`);\n\t\t// Receipts are written by the code that actually executed each check; the\n\t\t// release gate reads receipts and nothing else.\n\t\tawait writeProfileReceipt(options.paths, run.id, {\n\t\t\tprofile: \"bundle-compile\",\n\t\t\tpassed: proposal.l1.passed,\n\t\t\tsummary: \"Candidate bundle compiled during L1 staging\",\n\t\t\tartifact: proposal.candidateDigest,\n\t\t});\n\t\tif (proposal.kind === \"code\") {\n\t\t\t// Sandboxed code staging runs the repository check and related tests as part of L1.\n\t\t\tawait writeProfileReceipt(options.paths, run.id, {\n\t\t\t\tprofile: \"repo-check\",\n\t\t\t\tpassed: proposal.l1.passed,\n\t\t\t\tsummary: \"npm run check executed in the sandboxed L1 worktree\",\n\t\t\t});\n\t\t\tawait writeProfileReceipt(options.paths, run.id, {\n\t\t\t\tprofile: \"related-tests\",\n\t\t\t\tpassed: proposal.l1.passed,\n\t\t\t\tsummary: \"Changed-path vitest suites executed in the sandboxed L1 worktree\",\n\t\t\t});\n\t\t}\n\t\tif (changesComponentSelection(proposal)) {\n\t\t\tconst validation = await validateComponentCandidate(options.paths, proposal, {\n\t\t\t\tsandbox: options.componentSandbox,\n\t\t\t});\n\t\t\tawait atomicWriteFile(join(evolutionRunDirectory(options.paths, run.id), \"validation.md\"), validation);\n\t\t\tawait writeProfileReceipt(options.paths, run.id, {\n\t\t\t\tprofile: \"related-tests\",\n\t\t\t\tpassed: true,\n\t\t\t\tsummary: \"Component ABI fixture validation executed\",\n\t\t\t});\n\t\t\tproposal = await attachProposalArtifact({\n\t\t\t\tpaths: options.paths,\n\t\t\t\tproposalId: proposal.id,\n\t\t\t\texpected: proposalApproval(proposal),\n\t\t\t\tkind: \"validation\",\n\t\t\t\tcontent: validation,\n\t\t\t\tallowedStatuses: [\"pending\"],\n\t\t\t});\n\t\t}\n\t\tawait linkProposalInbox(options.paths, proposal);\n\t\t// Replay execution is contract-driven: the frozen plan requires it, or it\n\t\t// recommends it and this run auto-executes recommendations (scheduled runs\n\t\t// always do; request runs only when configured). A recommendation that is\n\t\t// not auto-executed is deferred to an explicit user decision after\n\t\t// evaluation instead of silently blocking or silently skipping.\n\t\tconst historicalReplay = plan.experiment.evidenceStrategy.historicalReplay;\n\t\tconst autoExecuteRecommended = run.trigger === \"scheduled\" || config.verification.approval === \"auto\";\n\t\tconst replayDeclared =\n\t\t\t(historicalReplay.mode === \"required\" ||\n\t\t\t\t(historicalReplay.mode === \"recommended\" && autoExecuteRecommended)) &&\n\t\t\thistoricalReplay.profiles.includes(\"paired-replay\");\n\t\tlet replay: CounterfactualReplayResult | undefined;\n\t\tif (proposal.tier === \"T2\" || (replayDeclared && proposal.replayScenarios.length > 0)) {\n\t\t\tawait updateEvolutionRun(options.paths, run.id, { status: \"replaying\", proposalId: proposal.id });\n\t\t\treplay = await runCounterfactualReplay({\n\t\t\t\tpaths: options.paths,\n\t\t\t\trunner: options.runner,\n\t\t\t\tproposal,\n\t\t\t\t...(options.agentDir ? { agentDir: options.agentDir } : {}),\n\t\t\t\tmodel: config.models.evaluator.model,\n\t\t\t\t...(config.models.evaluator.thinkingLevel ? { thinkingLevel: config.models.evaluator.thinkingLevel } : {}),\n\t\t\t\t...(options.signal ? { signal: options.signal } : {}),\n\t\t\t});\n\t\t\tproposal = replay.proposal;\n\t\t\tawait writeProfileReceipt(options.paths, run.id, {\n\t\t\t\tprofile: \"paired-replay\",\n\t\t\t\tpassed: true,\n\t\t\t\tsummary: \"Counterfactual paired replay executed against the recorded scenario\",\n\t\t\t\tartifact: sha256(replay.markdown),\n\t\t\t});\n\t\t}\n\t\tawait assertFrozenExperiment(options.paths, await readEvolutionRun(options.paths, run.id), plan.experiment);\n\t\tconst deferredProfiles =\n\t\t\thistoricalReplay.mode === \"recommended\" && !autoExecuteRecommended\n\t\t\t\t? await (async () => {\n\t\t\t\t\t\tconst receipts = await readProfileReceipts(options.paths, run.id);\n\t\t\t\t\t\treturn historicalReplay.profiles.filter((profile) => receipts.get(profile)?.passed !== true);\n\t\t\t\t\t})()\n\t\t\t\t: [];\n\t\tawait updateEvolutionRun(options.paths, run.id, { status: \"evaluating\", proposalId: proposal.id });\n\t\tconst evaluation = await runEvolutionEvaluator({\n\t\t\tpaths: options.paths,\n\t\t\tplan,\n\t\t\tproposal,\n\t\t\tcorpus,\n\t\t\tmaterializedCorpus,\n\t\t\t...(replay ? { replay } : {}),\n\t\t\t...(deferredProfiles.length > 0 ? { deferredProfiles } : {}),\n\t\t\trunner: options.runner,\n\t\t\tcwd,\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...(activePreferences ? { activePreferences } : {}),\n\t\t\t...(options.signal ? { signal: options.signal } : {}),\n\t\t});\n\t\tproposal = evaluation.proposal;\n\t\tawait atomicWriteFile(join(evolutionRunDirectory(options.paths, run.id), \"evaluation.md\"), evaluation.markdown);\n\t\tif (\n\t\t\tdeferredProfiles.length > 0 &&\n\t\t\thistoricalReplay.mode === \"recommended\" &&\n\t\t\tevaluation.verdict !== \"invalid\" &&\n\t\t\tevaluation.verdict !== \"unsupported\"\n\t\t) {\n\t\t\t// Park the run for an explicit execute/skip/reject decision. No release\n\t\t\t// intent is written: the registry has not been touched yet, and both the\n\t\t\t// skip and execute paths apply the release policy themselves later.\n\t\t\tawait writePendingVerification(options.paths, {\n\t\t\t\tschemaVersion: 1,\n\t\t\t\trunId: run.id,\n\t\t\t\tproposalId: proposal.id,\n\t\t\t\tprofiles: deferredProfiles,\n\t\t\t\tdatasets: historicalReplay.datasets,\n\t\t\t\tminimumSamples: historicalReplay.minimumSamples,\n\t\t\t\treason: historicalReplay.reason,\n\t\t\t\tverdict: evaluation.verdict,\n\t\t\t\tevaluatedAt: new Date().toISOString(),\n\t\t\t});\n\t\t\tif (reviewCursorToAdvance) await advanceEvidenceReviewCursor(options.paths, reviewCursorToAdvance);\n\t\t\tawait garbageCollectInbox(options.paths);\n\t\t\tconst parked = await updateEvolutionRun(options.paths, run.id, {\n\t\t\t\tstatus: \"awaiting-evidence\",\n\t\t\t\tproposalId: proposal.id,\n\t\t\t});\n\t\t\treturn { run: parked, proposals: [proposal], evaluation };\n\t\t}\n\t\t// The intent file marks the non-atomic window between \"release policy may have\n\t\t// mutated the registry\" and \"the final run status is on disk\"; crash reconcile\n\t\t// derives the truth from the proposal whenever this file exists.\n\t\tawait atomicWriteJson(join(evolutionRunDirectory(options.paths, run.id), \"release-intent.json\"), {\n\t\t\tschemaVersion: 1,\n\t\t\tproposalId: proposal.id,\n\t\t\tverdict: evaluation.verdict,\n\t\t\tintendedAt: new Date().toISOString(),\n\t\t});\n\t\tconst release = await applyEvolutionReleasePolicy({\n\t\t\tservice,\n\t\t\tconfig,\n\t\t\tproposal,\n\t\t\tverdict: evaluation.verdict,\n\t\t\tevidenceStrategy: plan.experiment.evidenceStrategy,\n\t\t\treceipts: await readProfileReceipts(options.paths, run.id),\n\t\t});\n\t\tif (reviewCursorToAdvance) await advanceEvidenceReviewCursor(options.paths, reviewCursorToAdvance);\n\t\tawait garbageCollectInbox(options.paths);\n\t\tconst completed = await updateEvolutionRun(options.paths, run.id, {\n\t\t\tstatus: RELEASE_ACTION_RUN_STATUS[release.action],\n\t\t\tproposalId: release.proposal.id,\n\t\t\t...(release.action === \"awaiting-canary-approval\"\n\t\t\t\t? {\n\t\t\t\t\t\tcanaryCandidateDigest: release.proposal.candidateDigest,\n\t\t\t\t\t\tcanaryParentDigest: release.proposal.parentBundleDigest,\n\t\t\t\t\t\tcanaryTargetAbi: release.proposal.targetAbi,\n\t\t\t\t\t}\n\t\t\t\t: {}),\n\t\t});\n\t\tawait rm(join(evolutionRunDirectory(options.paths, run.id), \"release-intent.json\"), { force: true });\n\t\treturn { run: completed, proposals: [release.proposal], evaluation, release };\n\t} catch (error) {\n\t\tconst message = error instanceof Error ? error.message : String(error);\n\t\tawait updateEvolutionRun(options.paths, run.id, {\n\t\t\tstatus: options.signal?.aborted ? \"cancelled\" : \"failed\",\n\t\t\terror: message,\n\t\t}).catch(() => undefined);\n\t\tthrow error;\n\t}\n}\n\nexport async function runEvolutionCycle(options: RunEvolutionCycleOptions): Promise<EvolutionCycleResult> {\n\treturn withFileLock(options.paths, \"evolution-cycle\", () => runEvolutionCycleUnlocked(options), {\n\t\ttimeoutMs: 24 * 60 * 60_000,\n\t\tstaleAfterMs: 24 * 60 * 60_000,\n\t});\n}\n\nfunction unknownAbiResearchPlan(request: UnknownAbiBuilderRequest, parentDigest: string): EvolutionResearchPlan {\n\tconst selections = request.parts.map((part) => {\n\t\tconst capabilities = part.manifest.capabilities.join(\", \") || \"(none)\";\n\t\treturn `- ${part.selection.id}: artifact ${part.selection.artifactDigest}; capabilities ${capabilities}; explicit grants ${canonicalJson(part.selection.grants)}`;\n\t});\n\tconst trialPlan =\n\t\t\"Do not activate a component in this run. Await explicit T2 code review; after approval, commit the staged patch, rebuild and restart the host, then retry the same integrity-pinned pack import to create the component-selection proposal.\";\n\treturn {\n\t\ttopic: `Add host ABI ${request.targetAbi} for optimization pack ${request.source.name}`,\n\t\treason: `Pack ${request.source.name}@${request.source.version} declares ${request.targetAbi}, which is not registered in this host.`,\n\t\tplanMarkdown: [\n\t\t\t`# Add unregistered ABI ${request.targetAbi}`,\n\t\t\t\"\",\n\t\t\t`Pack: ${request.source.name}@${request.source.version}`,\n\t\t\t`Integrity: ${request.source.integrity}`,\n\t\t\t`Surface: ${request.surface}`,\n\t\t\t`Activation boundary: ${request.activationBoundary}`,\n\t\t\t`Capability ceiling: ${request.capabilityCeiling.join(\", \") || \"(empty)\"}`,\n\t\t\t\"\",\n\t\t\t\"## Frozen future selections\",\n\t\t\t\"\",\n\t\t\t...selections,\n\t\t\t\"\",\n\t\t\t\"These selections are audit input only. This plan must not write policy or broker state. After the ABI patch is approved and the host is rebuilt, retry import to stage them through the registered-ABI path.\",\n\t\t].join(\"\\n\"),\n\t\texperiment: {\n\t\t\tbaseline: `Stable bundle ${parentDigest}; ${request.targetAbi} absent from the default ABI registry.`,\n\t\t\thypothesis:\n\t\t\t\t\"A narrowly scoped ABI definition, strict validators, host wiring, and focused tests can safely expose the requested surface without widening component authority.\",\n\t\t\tcheckProfiles: [\"repo-check\", \"related-tests\"],\n\t\t\tevidenceStrategy: {\n\t\t\t\tpatchClass: \"infrastructure\",\n\t\t\t\toffline: { mode: \"required\", profiles: [\"repo-check\", \"related-tests\"] },\n\t\t\t\thistoricalReplay: {\n\t\t\t\t\tmode: \"not-applicable\",\n\t\t\t\t\treason: \"A previously unavailable host ABI has no valid historical runtime baseline to replay.\",\n\t\t\t\t},\n\t\t\t\tonline: { mode: \"none\" },\n\t\t\t\trollout: \"direct\",\n\t\t\t},\n\t\t\tmetrics: [],\n\t\t\tminimumEffect: {},\n\t\t\ttrialPlan,\n\t\t\trollbackConditions: [\n\t\t\t\t\"Generated capability ceiling differs from the exact declared union.\",\n\t\t\t\t\"Host wiring grants ambient authority or mutates broker state.\",\n\t\t\t\t\"Repository check or focused tests fail.\",\n\t\t\t],\n\t\t},\n\t\ttargetAbi: request.targetAbi,\n\t\trequiresNewAbi: true,\n\t\tcandidateKind: \"code\",\n\t\tbuilderInstructions:\n\t\t\t\"Define and register the exact EvoAbiDefinition, make strict config/input/output validators compatible with the pack's default empty config, wire only the declared host surface, and add focused tests. Do not activate or grant the future component selection.\",\n\t\tinboxDecisions: [],\n\t};\n}\n\nasync function runUnknownAbiBuilderCycleUnlocked(\n\toptions: RunUnknownAbiBuilderCycleOptions,\n): Promise<UnknownAbiBuilderCycleResult> {\n\tconst request = parseUnknownAbiBuilderRequest(options.request);\n\tif (createDefaultEvoAbiRegistry().get(request.targetAbi)) {\n\t\tthrow new Error(`ABI is already registered; retry normal pack import instead: ${request.targetAbi}`);\n\t}\n\tconst service = options.service ?? new EvoService(options.paths);\n\tif (await service.registry.isPaused()) throw new Error(\"Evo-Pi is paused\");\n\tconst existing = (await service.listProposals()).find(\n\t\t(proposal) =>\n\t\t\tproposal.requiresNewAbi === true &&\n\t\t\tproposal.targetAbi === request.targetAbi &&\n\t\t\tproposal.status !== \"rejected\" &&\n\t\t\tproposal.status !== \"rolled-back\",\n\t);\n\tif (existing) {\n\t\tthrow new Error(\n\t\t\t`ABI ${request.targetAbi} already has proposal ${existing.id} in status ${existing.status}; review it or rebuild before retrying import`,\n\t\t);\n\t}\n\tconst stable = await service.registry.readStableDigest();\n\tif (!stable) throw new Error(\"Evo-Pi registry is not initialized\");\n\tif (options.parentDigest && options.parentDigest !== stable) {\n\t\tthrow new Error(\"Stable bundle changed after pack inspection; inspect the pack again before building its ABI\");\n\t}\n\tawait loadCompiledBundle(options.paths, stable);\n\tconst requestDigest = sha256(canonicalJson(request));\n\tconst run = await createEvolutionRun({\n\t\tpaths: options.paths,\n\t\ttrigger: \"request\",\n\t\trequest: `Install ${request.source.name}@${request.source.version}: add unregistered ABI ${request.targetAbi}`,\n\t\tevidenceDigest: requestDigest,\n\t});\n\ttry {\n\t\tconst directory = evolutionRunDirectory(options.paths, run.id);\n\t\tawait atomicWriteJson(join(directory, \"unknown-abi-request.json\"), request);\n\t\tawait assertFrozenUnknownAbiRequest(options.paths, run, request);\n\t\tconst persisted = await persistEvolutionResearchPlan({\n\t\t\tpaths: options.paths,\n\t\t\trun,\n\t\t\tplan: unknownAbiResearchPlan(request, stable),\n\t\t});\n\t\tawait assertFrozenExperiment(options.paths, persisted.state, persisted.plan.experiment);\n\t\tconst config = options.config ?? (await readEvoControlConfig(options.paths));\n\t\tconst repositoryCwd = options.repositoryCwd ?? (await resolveEvoSourceRepositoryRoot());\n\t\tawait updateEvolutionRun(options.paths, run.id, { status: \"building\" });\n\t\tconst built = await runUnknownAbiBuilder({\n\t\t\tpaths: options.paths,\n\t\t\tplan: persisted.plan,\n\t\t\trequest,\n\t\t\trunner: options.runner,\n\t\t\tcwd: repositoryCwd,\n\t\t\t...(options.agentDir ? { agentDir: options.agentDir } : {}),\n\t\t\tmodel: config.models.builder.model,\n\t\t\t...(config.models.builder.thinkingLevel ? { thinkingLevel: config.models.builder.thinkingLevel } : {}),\n\t\t\tsessionIdentity: `evo-unknown-abi-builder-${run.id}`,\n\t\t\t...(options.signal ? { signal: options.signal } : {}),\n\t\t});\n\t\tawait updateEvolutionRun(options.paths, run.id, { status: \"validating\" });\n\t\tconst expectedPaths = assertUnknownAbiCodePatch(request, built.draft.codePatch ?? \"\");\n\t\tawait assertFrozenUnknownAbiRequest(options.paths, await readEvolutionRun(options.paths, run.id), request);\n\t\tconst proposal = await stageProposal({\n\t\t\tpaths: options.paths,\n\t\t\tparentDigest: stable,\n\t\t\tdraft: built.draft,\n\t\t\tobservationsMarkdown: built.observationsMarkdown,\n\t\t\trepositoryCwd,\n\t\t\t...(options.codeValidationExecutor ? { codeValidationExecutor: options.codeValidationExecutor } : {}),\n\t\t\t...(options.signal ? { signal: options.signal } : {}),\n\t\t});\n\t\tawait updateEvolutionRun(options.paths, run.id, {\n\t\t\tstatus: \"validating\",\n\t\t\tproposalId: proposal.id,\n\t\t});\n\t\tif (canonicalJson(proposal.changedPaths) !== canonicalJson(expectedPaths)) {\n\t\t\tthrow new Error(`Unknown ABI proposal ${proposal.id} staged a path set different from its reviewed patch`);\n\t\t}\n\t\tif (\n\t\t\tproposal.kind !== \"code\" ||\n\t\t\tproposal.tier !== \"T2\" ||\n\t\t\tproposal.targetAbi !== request.targetAbi ||\n\t\t\tproposal.requiresNewAbi !== true\n\t\t) {\n\t\t\tthrow new Error(`Unknown ABI proposal ${proposal.id} lost its mandatory T2 infrastructure classification`);\n\t\t}\n\t\tawait writeProfileReceipt(options.paths, run.id, {\n\t\t\tprofile: \"repo-check\",\n\t\t\tpassed: proposal.l1.passed,\n\t\t\tsummary: \"npm run check executed in the sandboxed unknown-ABI worktree\",\n\t\t});\n\t\tawait writeProfileReceipt(options.paths, run.id, {\n\t\t\tprofile: \"related-tests\",\n\t\t\tpassed: proposal.l1.passed,\n\t\t\tsummary: \"Focused Evo tests executed in the sandboxed unknown-ABI worktree\",\n\t\t});\n\t\tif (!proposal.l1.passed) throw new Error(`Unknown ABI proposal ${proposal.id} failed L1`);\n\t\tawait assertFrozenExperiment(\n\t\t\toptions.paths,\n\t\t\tawait readEvolutionRun(options.paths, run.id),\n\t\t\tpersisted.plan.experiment,\n\t\t);\n\t\tawait assertFrozenUnknownAbiRequest(options.paths, await readEvolutionRun(options.paths, run.id), request);\n\t\tconst awaitingDecision = await updateEvolutionRun(options.paths, run.id, {\n\t\t\tstatus: \"awaiting-decision\",\n\t\t\tproposalId: proposal.id,\n\t\t});\n\t\treturn {\n\t\t\trun: awaitingDecision,\n\t\t\tproposal,\n\t\t\trequest,\n\t\t\tplan: persisted.plan,\n\t\t\tnextAction:\n\t\t\t\t\"Review and explicitly confirm the T2 patch; commit, rebuild, and restart the host, then retry the same pack import.\",\n\t\t};\n\t} catch (error) {\n\t\tconst message = error instanceof Error ? error.message : String(error);\n\t\tawait updateEvolutionRun(options.paths, run.id, {\n\t\t\tstatus: options.signal?.aborted ? \"cancelled\" : \"failed\",\n\t\t\terror: message,\n\t\t}).catch(() => undefined);\n\t\tthrow error;\n\t}\n}\n\nexport async function runUnknownAbiBuilderCycle(\n\toptions: RunUnknownAbiBuilderCycleOptions,\n): Promise<UnknownAbiBuilderCycleResult> {\n\treturn withFileLock(options.paths, \"evolution-cycle\", () => runUnknownAbiBuilderCycleUnlocked(options), {\n\t\ttimeoutMs: 24 * 60 * 60_000,\n\t\tstaleAfterMs: 24 * 60 * 60_000,\n\t});\n}\n"]}