voidmachine 5.0.3 → 5.0.5

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/main.js CHANGED
@@ -35768,6 +35768,7 @@ init_esm_shims();
35768
35768
 
35769
35769
  // ../mission-engine/dist/orchestration/review-loop.js
35770
35770
  init_esm_shims();
35771
+ var BOUNDED_REVIEW_BUDGET = { maxRounds: 3, maxCorrectionBatches: 2 };
35771
35772
  var SHA256 = /^sha256:[a-f0-9]{64}$/;
35772
35773
  function validNativeContextId(value) {
35773
35774
  return typeof value === "string" && value.trim().length > 0 && value.length <= 160 && [...value].every((char) => char.charCodeAt(0) >= 32 && (char.charCodeAt(0) < 127 || char.charCodeAt(0) > 159));
@@ -35953,7 +35954,7 @@ function collectCompletions(events, stage, expectedSource, stageStartSeqExclusiv
35953
35954
  }
35954
35955
  const validAccepted = accepted.filter((envelope) => !invalidRoundEvents.has(envelope.event.eventId) && !(contractMigration !== void 0 && stage === "post-implementation" && envelope.completion.specialistId === contractMigration.specialistId && envelope.event.seq < contractMigration.afterSeq));
35955
35956
  const highestRound = Math.max(historicalHighestRound, activeRound ?? 0);
35956
- const pendingFailureRounds = [...failedRoundBySpecialist.entries()].filter(([id3]) => !completedInWindow.has(id3)).map(([, round]) => round + 1);
35957
+ const pendingFailureRounds = [...failedRoundBySpecialist.entries()].filter(([id3]) => !validAccepted.some((item) => item.completion.specialistId === id3)).map(([, round]) => round + 1);
35957
35958
  const nextRound = Math.max(firstCurrentRound, highestRound, ...pendingFailureRounds);
35958
35959
  return { accepted: [...retained, ...validAccepted], history, issues, highestRound, nextRound };
35959
35960
  }
@@ -36727,7 +36728,7 @@ function staleDispatchCompletions(events) {
36727
36728
  function specialistId2(value) {
36728
36729
  return /^core:[a-z0-9]+(?:-[a-z0-9]+)*$/.test(value);
36729
36730
  }
36730
- function validProvenanceRecovery(input2, existing) {
36731
+ function validProvenanceRecovery(input2, existing, afterSeqExclusive = 0) {
36731
36732
  const disposition = input2.request.disposition;
36732
36733
  const observation2 = input2.observation;
36733
36734
  const bindings = observation2.reviewBindings;
@@ -36741,10 +36742,10 @@ function validProvenanceRecovery(input2, existing) {
36741
36742
  const observedSubject = observation2.reviewSubject;
36742
36743
  return bindings.every((binding3) => {
36743
36744
  const original = existing.find((item) => item.event.eventId === binding3.completionEventId);
36744
- if (original === void 0 || !disposition.completionEventIds.includes(binding3.completionEventId) || original.event.subject !== "core:independent-code-reviewer" || original.event.source !== observation2.expectedSource || original.completion.verdict !== "pass" || original.completion.findings.some((finding) => finding.classification !== "advisory") || original.completion.contractVersion !== observation2.contractVersions[original.event.subject] || field4(original.event, "inputHash") !== observation2.currentInputHashes[original.event.subject] || canonicalJsonHash(original.completion) !== binding3.completionHash || !sameReviewSubject(binding3.review, observedSubject) || binding3.review.taskId !== original.event.missionId || binding3.review.writerId !== writerId || binding3.review.provenance.kind !== "native-context" || binding3.review.provenance.contextId !== field4(original.event, "contextId"))
36745
+ if (parseReviewReceipt(binding3.review) === void 0 || original === void 0 || original.event.seq <= afterSeqExclusive || !disposition.completionEventIds.includes(binding3.completionEventId) || original.event.subject !== "core:independent-code-reviewer" || original.event.source !== observation2.expectedSource || original.completion.verdict !== "pass" || original.completion.findings.some((finding) => finding.classification !== "advisory") || original.completion.contractVersion !== observation2.contractVersions[original.event.subject] || field4(original.event, "inputHash") !== observation2.currentInputHashes[original.event.subject] || canonicalJsonHash(original.completion) !== binding3.completionHash || !sameReviewSubject(binding3.review, observedSubject) || binding3.review.taskId !== original.event.missionId || binding3.review.writerId !== writerId || binding3.review.provenance.kind !== "native-context" || binding3.review.provenance.contextId !== field4(original.event, "contextId"))
36745
36746
  return false;
36746
- const requested = events.find((event) => event.seq < original.event.seq && matchesRequest(original.event, event) && field4(event, "contractVersion") === original.completion.contractVersion && `runtime:${String(field4(event, "runtime"))}` === observation2.expectedSource);
36747
- return requested !== void 0 && events.some((event) => event.kind === "specialist.started" && event.source === observation2.expectedSource && event.subject === original.event.subject && event.seq > requested.seq && event.seq < original.event.seq && ["stage", "reviewRound", "inputHash", "contextId"].every((key) => field4(event, key) === field4(original.event, key)));
36747
+ const requested = events.find((event) => event.seq > afterSeqExclusive && event.seq < original.event.seq && matchesRequest(original.event, event) && field4(event, "contractVersion") === original.completion.contractVersion && `runtime:${String(field4(event, "runtime"))}` === observation2.expectedSource);
36748
+ return requested !== void 0 && events.some((event) => event.kind === "specialist.started" && event.source === observation2.expectedSource && event.subject === original.event.subject && event.seq > requested.seq && event.seq < original.event.seq && ["stage", "reviewRound", "inputHash", "contractVersion", "contextId"].every((key) => field4(event, key) === field4(original.event, key)));
36748
36749
  });
36749
36750
  }
36750
36751
  function pendingPreparationClarification(input2, previousRecovery) {
@@ -36777,6 +36778,55 @@ function pendingPreparationClarification(input2, previousRecovery) {
36777
36778
  const completionEventIds = request.disposition.completionEventIds;
36778
36779
  return obligations.issues.length === 0 && obligations.obligations.some((item) => blocking.has(item.obligationId) && completionEventIds.includes(item.completionEventId));
36779
36780
  }
36781
+ function pendingStoppedEvidenceRequest(events, requestEventId, contextId) {
36782
+ const lifecycle = projectMissionLifecycle(events);
36783
+ if (lifecycle.status !== "closed" || lifecycle.closure.source !== "void-harness:mission.dispatch" || field4(lifecycle.closure, "reason") !== "controller-stop" || ambiguousEffects(events))
36784
+ return false;
36785
+ const request = events.find((event) => event.eventId === requestEventId);
36786
+ const runtime2 = events[0] === void 0 ? void 0 : field4(events[0], "runtime");
36787
+ if (runtime2 !== "codex" && runtime2 !== "claude" || events[0] === void 0 || field4(events[0], "runtimeAttested") !== true || !request || request.kind !== "specialist.evidence-discharge-requested" || request.source !== "void-harness:mission.dispatch" || request.seq >= lifecycle.closure.seq || field4(request, "nativeContextId") !== contextId || !validNativeContextId(contextId))
36788
+ return false;
36789
+ return events.some((event) => event.kind === "specialist.evidence-started" && event.source === `runtime:${String(runtime2)}` && event.subject === request.subject && event.causationId === request.eventId && event.seq > request.seq && event.seq < lifecycle.closure.seq && [
36790
+ "completionEventId",
36791
+ "completionHash",
36792
+ "specialistId",
36793
+ "nativeContextId",
36794
+ "requestId"
36795
+ ].every((key) => field4(event, key) === field4(request, key)));
36796
+ }
36797
+ function validEvidenceDispositionRecovery(input2) {
36798
+ const { events } = input2.stream;
36799
+ const { disposition } = input2.request;
36800
+ if (disposition.kind !== "evidence-disposition" || disposition.responseEventIds.length < 1 || disposition.responseEventIds.length > 64 || new Set(disposition.responseEventIds).size !== disposition.responseEventIds.length)
36801
+ return false;
36802
+ const closure = events.find((event) => event.eventId === input2.request.closureEventId);
36803
+ if (!closure)
36804
+ return false;
36805
+ const proofs = events.flatMap((event) => {
36806
+ const parsed = event.kind === "evidence.recorded" ? parseEvidence(field4(event, "evidence")) : void 0;
36807
+ return parsed?.ok ? [{ eventId: event.eventId, evidence: parsed.value }] : [];
36808
+ });
36809
+ const reduction = {
36810
+ expectedSource: input2.observation.expectedSource,
36811
+ phase: "completion",
36812
+ evidenceContext: { missionId: closure.missionId, dependencies: input2.observation.evidenceDependencies ?? {} },
36813
+ proofs
36814
+ };
36815
+ const before = reduceEvidenceObligations({ ...reduction, events: events.filter((event) => event.seq <= closure.seq) });
36816
+ const after = reduceEvidenceObligations({ ...reduction, events });
36817
+ if (before.issues.length > 0 || after.issues.length > 0)
36818
+ return false;
36819
+ return disposition.responseEventIds.every((id3) => {
36820
+ const response = events.find((event) => event.eventId === id3);
36821
+ if (response?.kind !== "specialist.evidence-discharged" || response.seq <= closure.seq || response.source !== input2.observation.expectedSource || !response.causationId || typeof field4(response, "nativeContextId") !== "string" || !pendingStoppedEvidenceRequest(events.filter((event) => event.seq < response.seq), response.causationId, String(field4(response, "nativeContextId"))))
36822
+ return false;
36823
+ const origin = completions(events).find((item) => item.event.eventId === field4(response, "completionEventId"));
36824
+ if (!origin || origin.completion.contractVersion !== input2.observation.contractVersions[origin.event.subject])
36825
+ return false;
36826
+ const items = field4(response, "items");
36827
+ return Array.isArray(items) && items.length > 0 && items.every((item) => record10(item) && Array.isArray(item["proofEventIds"]) && item["proofEventIds"].every((id4) => proofs.some((proof4) => proof4.eventId === id4 && proof4.evidence.dependencies.some((dependency) => dependency.key === "git:working-tree" && dependency.hash === input2.observation.evidenceDependencies?.["git:working-tree"]))) && before.obligations.some((value) => value.obligationId === item["obligationId"] && !value.discharged) && after.obligations.some((value) => value.obligationId === item["obligationId"] && value.discharged));
36828
+ });
36829
+ }
36780
36830
  function admitStoppedMission(input2, projectedHistory = input2.stream.events) {
36781
36831
  const { stream: stream2, request, observation: observation2 } = input2;
36782
36832
  const events = stream2.events;
@@ -36795,11 +36845,14 @@ function admitStoppedMission(input2, projectedHistory = input2.stream.events) {
36795
36845
  return refuse2("stale-journal", "Re-observe the journal before requesting recovery");
36796
36846
  if (ambiguousEffects(events))
36797
36847
  return refuse2("ambiguous-effect", "Reconcile the unfinished external effect; recovery cannot replay it");
36848
+ if (request.disposition.kind === "evidence-disposition" && !validEvidenceDispositionRecovery(input2)) {
36849
+ return refuse2("invalid-evidence-disposition", "Complete a pending pre-stop author invocation with fresh canonical proofs; an old or unstarted response cannot reopen this episode");
36850
+ }
36851
+ const existing = completions(events).filter((item) => field4(item.event, "stage") === observation2.stage);
36798
36852
  const previousRecovery = events.filter((event) => event.kind === "mission.recovered").at(-1);
36799
- if (previousRecovery !== void 0 && !events.some((event) => event.seq > previousRecovery.seq && event.kind === "lead-writer.completed") && !pendingPreparationClarification(input2, previousRecovery)) {
36853
+ if (previousRecovery !== void 0 && !events.some((event) => event.seq > previousRecovery.seq && event.kind === "lead-writer.completed") && !pendingPreparationClarification(input2, previousRecovery) && !validEvidenceDispositionRecovery(input2) && !validProvenanceRecovery(input2, existing, previousRecovery.seq)) {
36800
36854
  return refuse2("no-recovery-progress", "Record actual corrective progress before reopening another stopped episode");
36801
36855
  }
36802
- const existing = completions(events).filter((item) => field4(item.event, "stage") === observation2.stage);
36803
36856
  const disposition = request.disposition;
36804
36857
  const replacement = disposition.kind === "preparation-invocation";
36805
36858
  if (replacement && !preparationInvocationReplacement(input2)) {
@@ -36879,7 +36932,7 @@ function admitStoppedMission(input2, projectedHistory = input2.stream.events) {
36879
36932
  requiredSpecialists: required2,
36880
36933
  contractVersions: observation2.contractVersions,
36881
36934
  currentInputHashes: observation2.currentInputHashes,
36882
- maxRounds: observation2.maxRounds
36935
+ ...observation2.reviewPolicy === "bounded-corrections-v1" ? BOUNDED_REVIEW_BUDGET : { maxRounds: observation2.maxRounds }
36883
36936
  };
36884
36937
  const originalReview = reduceReviewLoop({ ...reviewInput, events: projectedHistory });
36885
36938
  if (originalReview.issues.some((issue4) => replacement ? issue4.code !== "reused-context" || !inadmissible.includes(issue4.eventId) : issue4.code !== "wrong-review-round")) {
@@ -36893,7 +36946,7 @@ function admitStoppedMission(input2, projectedHistory = input2.stream.events) {
36893
36946
  return refuse2("incomplete-required-panel", "Every required preparation specialist must have its original matching PASS triplet before recovery");
36894
36947
  }
36895
36948
  const consumedRounds = Math.max(0, ...(replacement ? projectedHistory : projected).filter((event) => ["specialist.completed", "specialist.failed"].includes(event.kind) && field4(event, "stage") === observation2.stage).map((event) => Number(field4(event, "reviewRound"))));
36896
- if (!replacement && disposition.kind !== "review-provenance" && consumedRounds >= observation2.maxRounds)
36949
+ if (!replacement && disposition.kind !== "review-provenance" && consumedRounds >= reviewInput.maxRounds)
36897
36950
  return refuse2("review-budget-exhausted", "The real review budget is exhausted; recovery cannot reset it");
36898
36951
  const consumedCorrectionBatches = projected.filter((event) => event.kind === "lead-writer.completed" && field4(event, "actionKind") === "run-correction").length;
36899
36952
  if (disposition.kind === "review-provenance" && consumedCorrectionBatches > 2) {
@@ -36915,12 +36968,12 @@ function admitStoppedMission(input2, projectedHistory = input2.stream.events) {
36915
36968
  inadmissibleCompletionEventIds: inadmissible,
36916
36969
  roundCorrections,
36917
36970
  consumedRounds,
36918
- remainingRounds: Math.max(0, observation2.maxRounds - consumedRounds),
36971
+ remainingRounds: Math.max(0, reviewInput.maxRounds - consumedRounds),
36919
36972
  ...!replacement && disposition.kind !== "review-provenance" ? {} : {
36920
36973
  consumedCorrectionBatches,
36921
36974
  remainingCorrectionBatches: 2 - consumedCorrectionBatches
36922
36975
  },
36923
- nextAction: replacement || disposition.kind === "review-provenance" || disposition.kind === "command-correction" || disposition.kind === "controller-defect" && disposition.defect === "opaque-native-context" ? "verification" : disposition.kind === "review-blocker" ? "clarification" : "correction"
36976
+ nextAction: replacement || disposition.kind === "evidence-disposition" || disposition.kind === "review-provenance" || disposition.kind === "command-correction" || disposition.kind === "controller-defect" && disposition.defect === "opaque-native-context" ? "verification" : disposition.kind === "review-blocker" ? "clarification" : "correction"
36924
36977
  };
36925
36978
  if (new TextEncoder().encode(canonicalJson(receipt2)).length > 16384) {
36926
36979
  return refuse2("recovery-receipt-too-large", "Narrow the recovery scope to the supported bounded incident");
@@ -36937,6 +36990,10 @@ function recoveryRequest(value) {
36937
36990
  if (value === void 0 || !record10(value) || !exactKeys2(value, ["schemaVersion", "closureEventId", "expectedJournalHash", "disposition"]) || value["schemaVersion"] !== 1 || typeof value["closureEventId"] !== "string" || typeof value["expectedJournalHash"] !== "string" || !/^sha256:[a-f0-9]{64}$/.test(value["expectedJournalHash"]) || value["disposition"] === void 0 || !record10(value["disposition"]))
36938
36991
  return false;
36939
36992
  const disposition = value["disposition"];
36993
+ if (disposition["kind"] === "evidence-disposition") {
36994
+ const ids = disposition["responseEventIds"];
36995
+ return exactKeys2(disposition, ["kind", "responseEventIds"]) && Array.isArray(ids) && ids.length > 0 && ids.length <= 64 && new Set(ids).size === ids.length && ids.every((id3) => typeof id3 === "string" && /^evt_[A-Za-z0-9_-]{8,100}$/.test(id3));
36996
+ }
36940
36997
  if (disposition["kind"] === "preparation-invocation") {
36941
36998
  return exactKeys2(disposition, ["kind", "completionEventIds"]) && Array.isArray(disposition["completionEventIds"]) && disposition["completionEventIds"].length > 0 && disposition["completionEventIds"].length <= 64 && disposition["completionEventIds"].every((item) => typeof item === "string" && /^evt_[A-Za-z0-9_-]{8,100}$/.test(item)) && new Set(disposition["completionEventIds"]).size === disposition["completionEventIds"].length;
36942
36999
  }
@@ -36960,11 +37017,12 @@ function recoveryObservation(value) {
36960
37017
  "currentInputHashes",
36961
37018
  "maxRounds",
36962
37019
  "expectedSource",
37020
+ ...value["reviewPolicy"] === void 0 ? [] : ["reviewPolicy"],
36963
37021
  ...value["resolutionArtifact"] === void 0 ? [] : ["resolutionArtifact"],
36964
37022
  ...value["reviewSubject"] === void 0 ? [] : ["reviewSubject"],
36965
37023
  ...value["reviewBindings"] === void 0 ? [] : ["reviewBindings"],
36966
37024
  ...value["evidenceDependencies"] === void 0 ? [] : ["evidenceDependencies"]
36967
- ]) || !["pre-implementation", "post-implementation"].includes(String(value["stage"])) || !["runtime:codex", "runtime:claude"].includes(String(value["expectedSource"])) || typeof value["maxRounds"] !== "number" || !Number.isSafeInteger(value["maxRounds"]) || value["maxRounds"] < 1 || value["maxRounds"] > 8 || value["contractVersions"] === void 0 || !record10(value["contractVersions"]) || value["currentInputHashes"] === void 0 || !record10(value["currentInputHashes"]))
37025
+ ]) || value["reviewPolicy"] !== void 0 && value["reviewPolicy"] !== "bounded-corrections-v1" || !["pre-implementation", "post-implementation"].includes(String(value["stage"])) || !["runtime:codex", "runtime:claude"].includes(String(value["expectedSource"])) || typeof value["maxRounds"] !== "number" || !Number.isSafeInteger(value["maxRounds"]) || value["maxRounds"] < 1 || value["maxRounds"] > 8 || value["contractVersions"] === void 0 || !record10(value["contractVersions"]) || value["currentInputHashes"] === void 0 || !record10(value["currentInputHashes"]))
36968
37026
  return false;
36969
37027
  return Object.keys(value["contractVersions"]).length <= 64 && Object.keys(value["currentInputHashes"]).length <= 64 && Object.entries(value["contractVersions"]).every(([key, version3]) => specialistId2(key) && typeof version3 === "number" && Number.isSafeInteger(version3) && version3 >= 1 && version3 <= 1e4) && Object.entries(value["currentInputHashes"]).every(([key, hash7]) => specialistId2(key) && typeof hash7 === "string" && /^sha256:[a-f0-9]{64}$/.test(hash7)) && (value["reviewSubject"] === void 0 || isReviewSubject(value["reviewSubject"])) && (value["reviewBindings"] === void 0 || Array.isArray(value["reviewBindings"]) && value["reviewBindings"].length <= 64 && value["reviewBindings"].every(reviewBinding)) && (value["resolutionArtifact"] === void 0 || artifact(value["resolutionArtifact"])) && (value["evidenceDependencies"] === void 0 || record10(value["evidenceDependencies"]) && Object.keys(value["evidenceDependencies"]).length <= 64 && Object.entries(value["evidenceDependencies"]).every(([key, hash7]) => key.length > 0 && key.length <= 500 && typeof hash7 === "string" && /^sha256:[a-f0-9]{64}$/.test(hash7)));
36970
37028
  }
@@ -38880,7 +38938,7 @@ function evidenceObligations(input2, expectedSource, phase) {
38880
38938
  evidenceContext: input2.evidenceContext
38881
38939
  });
38882
38940
  }
38883
- function obligationStop(state, review, verdict, preparation = false) {
38941
+ function obligationStop(state, review, verdict) {
38884
38942
  if (state.issues.length > 0) {
38885
38943
  return stopped("degraded", review, verdict, state.issues.map((issue4) => `Evidence obligation ${issue4.eventId}: ${issue4.detail}`), "Resolve the invalid specialist evidence disposition, then classify or discharge the original obligation through its authorized specialist.");
38886
38944
  }
@@ -38888,7 +38946,7 @@ function obligationStop(state, review, verdict, preparation = false) {
38888
38946
  if (blocking.size === 0)
38889
38947
  return void 0;
38890
38948
  const decision = stopped("blocked", review, verdict, state.obligations.filter((item) => blocking.has(item.obligationId)).map((item) => `Evidence obligation ${item.obligationId} (${item.due}): ${item.requestText}`), "Use mission evidence-request, then record the authorized author response with mission evidence-event to classify or discharge the obligation; a writer completion does not replace the required proof.");
38891
- return preparation ? { ...decision, action: { kind: "await-evidence", reasons: decision.reasons } } : decision;
38949
+ return { ...decision, action: { kind: "await-evidence", reasons: decision.reasons } };
38892
38950
  }
38893
38951
  function orchestrateMissionTeam(originalInput) {
38894
38952
  const migrated = validatedSpecialistContractMigrations(originalInput.stream.events, originalInput.plan);
@@ -38984,7 +39042,10 @@ function orchestrateMissionTeam(originalInput) {
38984
39042
  if (preparationRecovery?.replacement && firstImplementationSeq === void 0 && requiredSpecialists(input2.plan, "pre-implementation").some((id3) => input2.currentInputHashesByStage["pre-implementation"][id3] !== preparationRecovery.inputHashes[id3] || contractVersions(input2.plan)[id3] !== preparationRecovery.contractVersions[id3])) {
38985
39043
  return stopped("blocked", preReview, baseVerdict, ["Preparation replacement inputs or contracts changed; reconcile the admitted subject before dispatch"]);
38986
39044
  }
38987
- const pendingEvidence = obligationStop(obligations, preReview, baseVerdict, firstImplementationSeq === void 0);
39045
+ if (firstImplementationSeq !== void 0 && input2.stream.events.some((event) => event.kind === "specialist.completed" && event.seq > firstImplementationSeq && lifecycleField(event, "stage") === "pre-implementation")) {
39046
+ return stopped("blocked", preReview, baseVerdict, ["pre-implementation review was replayed after implementation"]);
39047
+ }
39048
+ const pendingEvidence = obligationStop(obligations, preReview, baseVerdict);
38988
39049
  const migration = migrated.migration;
38989
39050
  if (migration !== void 0) {
38990
39051
  const receipt2 = migration.receipt;
@@ -39010,6 +39071,33 @@ function orchestrateMissionTeam(originalInput) {
39010
39071
  }, input2.specialistRuntime);
39011
39072
  }
39012
39073
  }
39074
+ const postReview = firstImplementationSeq === void 0 || lastWriterSeq === void 0 ? void 0 : reduceReviewLoop({
39075
+ stage: "post-implementation",
39076
+ expectedSource,
39077
+ stageStartSeqExclusive: firstImplementationSeq,
39078
+ afterSeqExclusive: lastWriterSeq,
39079
+ events: reviewEvents,
39080
+ requiredSpecialists: requiredSpecialists(input2.plan, "post-implementation", start.boundedReview),
39081
+ contractVersions: contractVersions(input2.plan),
39082
+ currentInputHashes: input2.currentInputHashesByStage["post-implementation"],
39083
+ ...start.boundedReview ? BOUNDED_REVIEW_BUDGET : { maxRounds: input2.maxReviewRounds },
39084
+ // Pending proofs are handled by obligationStop; they must not mask failed reviews.
39085
+ evidenceObligations: { ...obligations, blockingObligationIds: [] },
39086
+ validProofIds: input2.stream.events.flatMap((event) => {
39087
+ if (event.kind !== "evidence.recorded")
39088
+ return [];
39089
+ const parsed = parseEvidence(lifecycleField(event, "evidence"));
39090
+ return parsed.ok && parsed.value.evidenceId === event.subject && parsed.value.missionId === event.missionId && parsed.value.status === "passed" && assessEvidence(parsed.value, input2.evidenceContext).status === "fresh" ? [parsed.value.evidenceId] : [];
39091
+ }),
39092
+ ...migration === void 0 ? {} : { contractMigration: {
39093
+ specialistId: migration.receipt.specialistId,
39094
+ afterSeq: migration.seq,
39095
+ reviewRound: migration.receipt.reviewRound
39096
+ } }
39097
+ });
39098
+ if (postReview?.status === "blocked" || postReview?.status === "degraded") {
39099
+ return applyRuntimeCertification(decideReviewPhase(start, postReview, baseVerdict, "post-implementation"), input2.specialistRuntime);
39100
+ }
39013
39101
  const correctiveFindingIds = pendingRecoveredCorrectionFindingIds(input2.stream.events);
39014
39102
  const correctiveContinuation = pendingEvidence !== void 0 && obligations.issues.length === 0 && firstImplementationSeq !== void 0 && correctiveFindingIds.length > 0;
39015
39103
  if (pendingEvidence !== void 0 && !correctiveContinuation) {
@@ -39058,33 +39146,9 @@ function orchestrateMissionTeam(originalInput) {
39058
39146
  reasons
39059
39147
  }, input2.specialistRuntime);
39060
39148
  }
39061
- if (firstWriterSeq === void 0 || firstImplementationSeq === void 0 || lastWriterSeq === void 0) {
39149
+ if (firstWriterSeq === void 0 || firstImplementationSeq === void 0 || lastWriterSeq === void 0 || postReview === void 0) {
39062
39150
  throw new Error("MISSION_TEAM_INVARIANT: writer completion boundary is missing");
39063
39151
  }
39064
- const postReview = reduceReviewLoop({
39065
- stage: "post-implementation",
39066
- expectedSource,
39067
- stageStartSeqExclusive: firstImplementationSeq,
39068
- afterSeqExclusive: lastWriterSeq,
39069
- events: reviewEvents,
39070
- requiredSpecialists: requiredSpecialists(input2.plan, "post-implementation", start.boundedReview),
39071
- contractVersions: contractVersions(input2.plan),
39072
- currentInputHashes: input2.currentInputHashesByStage["post-implementation"],
39073
- maxRounds: start.boundedReview ? 3 : input2.maxReviewRounds,
39074
- ...start.boundedReview ? { maxCorrectionBatches: 2 } : {},
39075
- evidenceObligations: obligations,
39076
- validProofIds: input2.stream.events.flatMap((event) => {
39077
- if (event.kind !== "evidence.recorded")
39078
- return [];
39079
- const parsed = parseEvidence(lifecycleField(event, "evidence"));
39080
- return parsed.ok && parsed.value.evidenceId === event.subject && parsed.value.missionId === event.missionId && parsed.value.status === "passed" && assessEvidence(parsed.value, input2.evidenceContext).status === "fresh" ? [parsed.value.evidenceId] : [];
39081
- }),
39082
- ...migration === void 0 ? {} : { contractMigration: {
39083
- specialistId: migration.receipt.specialistId,
39084
- afterSeq: migration.seq,
39085
- reviewRound: migration.receipt.reviewRound
39086
- } }
39087
- });
39088
39152
  if (start.boundedReview && postReview.missingSpecialists.length === 0) {
39089
39153
  const receiptReasons = boundedReceiptReasons(input2, start, reviewEvents);
39090
39154
  if (receiptReasons.length > 0)
@@ -46578,7 +46642,7 @@ function dependencyEntries(pkg) {
46578
46642
  function technology(id3, version3, source2) {
46579
46643
  return Object.freeze({ id: id3, version: version3, sources: Object.freeze([source2]) });
46580
46644
  }
46581
- function packageTechnologies(workspace) {
46645
+ function packageTechnologies(workspace, recipe) {
46582
46646
  const technologies = /* @__PURE__ */ new Map();
46583
46647
  const source2 = workspace.path === "." ? "package.json" : `${workspace.path}/package.json`;
46584
46648
  for (const [dependency, version3] of dependencyEntries(workspace.packageJson)) {
@@ -46591,7 +46655,10 @@ function packageTechnologies(workspace) {
46591
46655
  }
46592
46656
  }
46593
46657
  if (workspace.path === ".") {
46594
- technologies.set("node", technology("node", exactVersion(process.versions.node), "runtime:node"));
46658
+ const declaredNode2 = workspace.packageJson.engines?.node;
46659
+ const projectNode = typeof declaredNode2 === "string" && /^\d+\.\d+\.\d+$/.test(declaredNode2) ? declaredNode2 : null;
46660
+ const projectNodeSource = projectNode === null && typeof declaredNode2 === "string" ? `package.json:engines.node:${declaredNode2}` : "package.json:engines.node";
46661
+ technologies.set("node", recipe === "runtime-v1" ? technology("node", exactVersion(process.versions.node), "runtime:node") : technology("node", projectNode, projectNodeSource));
46595
46662
  const packageManager = workspace.packageJson.packageManager;
46596
46663
  if (packageManager !== void 0) {
46597
46664
  const separator = packageManager.indexOf("@");
@@ -46617,7 +46684,7 @@ function packageTechnologies(workspace) {
46617
46684
  function validChangedFile(file2) {
46618
46685
  return file2.length > 0 && !file2.startsWith("/") && !file2.startsWith("../") && !file2.includes("/../") && !file2.includes("\\") && posix.normalize(file2) === file2;
46619
46686
  }
46620
- function detectProfileInput(root, files) {
46687
+ function detectProfileInput(root, files, recipe = "runtime-v1") {
46621
46688
  if (!files.every(validChangedFile)) {
46622
46689
  throw new Error("PROFILE_INPUT_INVALID: changed files must remain inside the project root");
46623
46690
  }
@@ -46625,7 +46692,7 @@ function detectProfileInput(root, files) {
46625
46692
  const rootWorkspace = workspaces.find((workspace) => workspace.path === ".");
46626
46693
  const projects2 = workspaces.map((workspace) => Object.freeze({
46627
46694
  path: workspace.path,
46628
- technologies: packageTechnologies(workspace)
46695
+ technologies: packageTechnologies(workspace, recipe)
46629
46696
  }));
46630
46697
  if (projects2.length > 1) {
46631
46698
  const rootProject = projects2.find((project) => project.path === ".");
@@ -55863,17 +55930,18 @@ async function createMission(root, input2) {
55863
55930
  );
55864
55931
  await writeMissionMetadata(run2, safeInput, createdAt);
55865
55932
  }
55866
- async function writeMissionControllerPlan(root, missionId, plan2, ticket, baseCommit) {
55933
+ async function writeMissionControllerPlan(root, missionId, plan2, ticket, baseCommit, inputRecipe) {
55867
55934
  const run2 = await existingRunDirectory(root, missionId);
55868
- const routingHash = missionControllerRoutingHash(plan2, ticket, baseCommit);
55935
+ const routingHash = missionControllerRoutingHash(plan2, ticket, baseCommit, inputRecipe);
55869
55936
  const handle3 = await open$1(join(run2, "controller-plan.json"), "wx", 384);
55870
55937
  try {
55871
55938
  await handle3.writeFile(`${JSON.stringify({
55872
- schemaVersion: baseCommit === void 0 ? 1 : 2,
55939
+ schemaVersion: inputRecipe === void 0 ? baseCommit === void 0 ? 1 : 2 : 3,
55873
55940
  routingHash,
55874
55941
  plan: plan2,
55875
55942
  ticket,
55876
- ...baseCommit === void 0 ? {} : { baseCommit }
55943
+ ...baseCommit === void 0 ? {} : { baseCommit },
55944
+ ...inputRecipe === void 0 ? {} : { inputRecipe }
55877
55945
  })}
55878
55946
  `, "utf8");
55879
55947
  } finally {
@@ -55902,28 +55970,38 @@ async function loadMissionControllerPlan(root, missionId) {
55902
55970
  "routingHash",
55903
55971
  "plan",
55904
55972
  "ticket",
55905
- ...parsed.schemaVersion === 2 ? ["baseCommit"] : []
55973
+ ...parsed.schemaVersion === 2 || parsed.schemaVersion === 3 ? ["baseCommit"] : [],
55974
+ ...parsed.schemaVersion === 3 ? ["inputRecipe"] : []
55906
55975
  ])) {
55907
55976
  throw new Error("MISSION_CONTROLLER_PLAN_INVALID: plan envelope is malformed");
55908
55977
  }
55909
55978
  const plan2 = parseMissionSpecialistPlan(parsed.plan);
55910
55979
  const ticket = parseMissionControllerTicket(parsed.ticket);
55911
55980
  const routingHash = typeof parsed.routingHash === "string" ? parsed.routingHash : "";
55912
- const baseCommit = parsed.schemaVersion === 2 ? parsed.baseCommit : void 0;
55913
- if (parsed.schemaVersion !== 1 && parsed.schemaVersion !== 2 || parsed.schemaVersion === 2 && (typeof baseCommit !== "string" || !/^(?:[a-f0-9]{40}|[a-f0-9]{64})$/.test(baseCommit)) || !/^sha256:[a-f0-9]{64}$/.test(routingHash)) {
55981
+ const baseCommit = parsed.schemaVersion === 2 || parsed.schemaVersion === 3 ? parsed.baseCommit : void 0;
55982
+ const inputRecipe = parsed.schemaVersion === 3 && parsed.inputRecipe === "project-v1" ? "project-v1" : void 0;
55983
+ if (parsed.schemaVersion !== 1 && parsed.schemaVersion !== 2 && parsed.schemaVersion !== 3 || parsed.schemaVersion === 3 && inputRecipe === void 0 || (parsed.schemaVersion === 2 || parsed.schemaVersion === 3) && (typeof baseCommit !== "string" || !/^(?:[a-f0-9]{40}|[a-f0-9]{64})$/.test(baseCommit)) || !/^sha256:[a-f0-9]{64}$/.test(routingHash)) {
55914
55984
  throw new Error("MISSION_CONTROLLER_PLAN_INVALID: plan integrity check failed");
55915
55985
  }
55916
55986
  const base = typeof baseCommit === "string" ? { baseCommit } : {};
55917
- if (missionControllerRoutingHash(plan2, ticket, base.baseCommit) !== routingHash) {
55987
+ if (missionControllerRoutingHash(plan2, ticket, base.baseCommit, inputRecipe) !== routingHash) {
55918
55988
  throw new Error("MISSION_CONTROLLER_PLAN_INVALID: plan integrity check failed");
55919
55989
  }
55920
- return Object.freeze({ plan: plan2, ticket, routingHash, ...base });
55990
+ return Object.freeze({ plan: plan2, ticket, routingHash, ...base, ...inputRecipe === void 0 ? {} : { inputRecipe } });
55921
55991
  }
55922
- function missionControllerRoutingHash(plan2, ticket, baseCommit) {
55992
+ function missionControllerRoutingHash(plan2, ticket, baseCommit, inputRecipe) {
55993
+ if (inputRecipe !== void 0 && (inputRecipe !== "project-v1" || baseCommit === void 0)) {
55994
+ throw new Error("MISSION_CONTROLLER_PLAN_INVALID: project input recipe requires a committed baseline");
55995
+ }
55923
55996
  if (baseCommit !== void 0 && !/^(?:[a-f0-9]{40}|[a-f0-9]{64})$/.test(baseCommit)) {
55924
55997
  throw new Error("MISSION_REVIEW_BASE_INVALID: expected a full commit object ID");
55925
55998
  }
55926
- return canonicalJsonHash({ plan: plan2, ticket, ...baseCommit === void 0 ? {} : { baseCommit } });
55999
+ return canonicalJsonHash({
56000
+ plan: plan2,
56001
+ ticket,
56002
+ ...baseCommit === void 0 ? {} : { baseCommit },
56003
+ ...inputRecipe === void 0 ? {} : { inputRecipe }
56004
+ });
55927
56005
  }
55928
56006
  function unknownRecord2(value) {
55929
56007
  return typeof value === "object" && Boolean(value) && !Array.isArray(value);
@@ -56335,6 +56413,7 @@ async function loadProfiles(root, coreProfileDirectory) {
56335
56413
  // src/lib/mission-inputs.ts
56336
56414
  var execFile2 = promisify(execFile);
56337
56415
  var MAX_TICKET_BYTES = 1e5;
56416
+ var MAX_REVIEW_PATCH_BYTES = 16e6;
56338
56417
  async function readTicket(root, ticketPath) {
56339
56418
  const canonicalRoot = await realpath(resolve(root));
56340
56419
  const loaded = await readBoundedProjectFile({
@@ -56390,7 +56469,7 @@ async function missionReviewBase(root) {
56390
56469
  throw new Error("MISSION_REVIEW_BASE_INVALID: a committed Git baseline is required");
56391
56470
  }
56392
56471
  }
56393
- async function captureMissionReviewSubject(root, baseCommit, committed = false) {
56472
+ async function captureMissionReviewSubject(root, baseCommit, committed = false, hashFormat = committed ? "committed" : "legacy") {
56394
56473
  if (baseCommit === void 0) {
56395
56474
  throw new Error(
56396
56475
  "MISSION_REVIEW_BASE_MISSING: legacy mission has no review baseline; preserve its history and start a new mission"
@@ -56430,7 +56509,17 @@ async function captureMissionReviewSubject(root, baseCommit, committed = false)
56430
56509
  "--relative",
56431
56510
  ...range,
56432
56511
  ...paths
56433
- ], options2),
56512
+ ], {
56513
+ ...options2,
56514
+ maxBuffer: MAX_REVIEW_PATCH_BYTES
56515
+ }).catch((error62) => {
56516
+ if (error62 instanceof Error && "code" in error62 && error62.code === "ERR_CHILD_PROCESS_STDIO_MAXBUFFER") {
56517
+ throw new Error(
56518
+ `MISSION_REVIEW_PATCH_TOO_LARGE: Git review patch exceeds ${MAX_REVIEW_PATCH_BYTES} bytes`
56519
+ );
56520
+ }
56521
+ throw error62;
56522
+ }),
56434
56523
  execFile2("git", [
56435
56524
  "diff",
56436
56525
  "--no-ext-diff",
@@ -56463,7 +56552,7 @@ async function captureMissionReviewSubject(root, baseCommit, committed = false)
56463
56552
  files,
56464
56553
  hash: canonicalJsonHash({
56465
56554
  baseCommit,
56466
- ...reviewedCommit === void 0 ? {} : { reviewedCommit },
56555
+ ...hashFormat === "legacy" ? {} : { reviewedCommit },
56467
56556
  diff: patch.stdout,
56468
56557
  files: files.files
56469
56558
  })
@@ -56537,10 +56626,10 @@ function detectedStack(root, profileInput) {
56537
56626
  status: "known"
56538
56627
  });
56539
56628
  }
56540
- async function planMission(root, ticketPath, generatedAt = (/* @__PURE__ */ new Date()).toISOString()) {
56541
- return (await planBoundMission(root, ticketPath, generatedAt)).plan;
56629
+ async function planMission(root, ticketPath, generatedAt = (/* @__PURE__ */ new Date()).toISOString(), inputRecipe = "runtime-v1") {
56630
+ return (await planBoundMission(root, ticketPath, generatedAt, void 0, void 0, inputRecipe)).plan;
56542
56631
  }
56543
- async function compileMission(root, ticket, generatedAt, detectedFiles, specialistCatalog) {
56632
+ async function compileMission(root, ticket, generatedAt, detectedFiles, specialistCatalog, inputRecipe = "runtime-v1") {
56544
56633
  const [coreRoot, diff] = await Promise.all([
56545
56634
  findCoreSource(),
56546
56635
  detectedFiles ?? gitFiles(root)
@@ -56550,7 +56639,7 @@ async function compileMission(root, ticket, generatedAt, detectedFiles, speciali
56550
56639
  loadProfiles(root, join(coreRoot, "profiles")),
56551
56640
  specialistCatalog ?? loadSpecialists(coreRoot)
56552
56641
  ]);
56553
- const profileInput = detectProfileInput(root, diff.files);
56642
+ const profileInput = detectProfileInput(root, diff.files, inputRecipe);
56554
56643
  const stack = detectedStack(root, profileInput);
56555
56644
  return compileMissionPlan({
56556
56645
  ticket: { id: ticket.id, title: ticket.title, body: ticket.body },
@@ -56570,9 +56659,9 @@ function controllerTicketBinding(ticket) {
56570
56659
  contentHash: `sha256:${createHash("sha256").update(ticket.body).digest("hex")}`
56571
56660
  });
56572
56661
  }
56573
- async function planBoundMission(root, ticketPath, generatedAt = (/* @__PURE__ */ new Date()).toISOString(), detectedFiles, specialistCatalog) {
56662
+ async function planBoundMission(root, ticketPath, generatedAt = (/* @__PURE__ */ new Date()).toISOString(), detectedFiles, specialistCatalog, inputRecipe = "runtime-v1") {
56574
56663
  const ticket = await readTicket(root, ticketPath);
56575
- const plan2 = await compileMission(root, ticket, generatedAt, detectedFiles, specialistCatalog);
56664
+ const plan2 = await compileMission(root, ticket, generatedAt, detectedFiles, specialistCatalog, inputRecipe);
56576
56665
  return Object.freeze({
56577
56666
  plan: plan2,
56578
56667
  ticket: controllerTicketBinding(ticket)
@@ -67137,6 +67226,11 @@ function parseMissionRecoveryRequest(value) {
67137
67226
  if (!exact2(disposition, ["kind", "defect"]) || defect !== "partial-fanout-round" && defect !== "stale-input-dispatch" && defect !== "opaque-native-context") invalid11();
67138
67227
  return { ...base, disposition: { kind: "controller-defect", defect } };
67139
67228
  }
67229
+ if (disposition["kind"] === "evidence-disposition") {
67230
+ const ids2 = disposition["responseEventIds"];
67231
+ if (!exact2(disposition, ["kind", "responseEventIds"]) || !Array.isArray(ids2) || ids2.length < 1 || ids2.length > 64 || !ids2.every(eventId) || new Set(ids2).size !== ids2.length) invalid11();
67232
+ return { ...base, disposition: { kind: "evidence-disposition", responseEventIds: ids2 } };
67233
+ }
67140
67234
  const commandCorrection = disposition["kind"] === "command-correction";
67141
67235
  const ids = disposition["completionEventIds"];
67142
67236
  if (disposition["kind"] === "preparation-invocation") {
@@ -67418,13 +67512,22 @@ async function validateCompletion(events, candidate, roots, expectedSource) {
67418
67512
  if (discharge2) {
67419
67513
  const items = field7(candidate.payload, "items");
67420
67514
  if (!Array.isArray(items) || items.some((item) => !record25(item) || !state.obligations.some((value) => value.obligationId === item["obligationId"] && value.discharged))) {
67421
- invalid13("requested obligations need fresh successful canonical proofs");
67515
+ invalid13(`requested obligations need fresh successful canonical proofs; observed git:working-tree ${dependencies["git:working-tree"]} at ${roots.workRoot}; run from the proof worktree and inspect its commit/diff binding`);
67422
67516
  }
67423
67517
  }
67424
67518
  }
67425
- async function recordSpecialistEvidence(roots, missionId, status3, input2) {
67426
- const events = (await inspectMission(roots.installRoot, missionId, { dependencies: {} })).stream.events;
67519
+ function requireResponseAdmission(events, status3, input2) {
67520
+ if (observedMissionLifecycle(events).status === "open") return;
67521
+ if (status3 === "completed" && pendingStoppedEvidenceRequest(events, input2.requestEventId, input2.contextId)) return;
67427
67522
  requireOpenMission(events);
67523
+ }
67524
+ async function recordSpecialistEvidence(roots, missionId, status3, input2) {
67525
+ const { stream: stream2 } = await inspectMission(roots.installRoot, missionId, { dependencies: {} });
67526
+ if (stream2.continuity !== "complete" || stream2.invalidLines > 0 || stream2.duplicateEventIds > 0) {
67527
+ invalid13("complete unaltered journal is required before author response admission");
67528
+ }
67529
+ const events = stream2.events;
67530
+ requireResponseAdmission(events, status3, input2);
67428
67531
  const source2 = identity(events);
67429
67532
  const request = exactRequest(events, input2);
67430
67533
  const kind2 = status3 === "started" ? "specialist.evidence-started" : request.kind === "specialist.evidence-classification-requested" ? "specialist.evidence-classified" : "specialist.evidence-discharged";
@@ -67448,7 +67551,7 @@ async function recordSpecialistEvidence(roots, missionId, status3, input2) {
67448
67551
  eventId: id3,
67449
67552
  draft,
67450
67553
  validate: async (current) => {
67451
- requireOpenMission(current);
67554
+ requireResponseAdmission(current, status3, input2);
67452
67555
  exactRequest(current, input2);
67453
67556
  if (status3 === "completed") {
67454
67557
  const started = current.find((event) => event.kind === "specialist.evidence-started" && event.source === source2 && event.subject === request.subject && event.causationId === request.eventId && field7(event.payload, "nativeContextId") === input2.contextId);
@@ -67489,6 +67592,9 @@ function parseSpecialistEvidenceRequest(value) {
67489
67592
  return { ...base, operation: "discharge", obligationIds: ids };
67490
67593
  }
67491
67594
  function responseItem(value) {
67595
+ if (record25(value) && typeof value["reason"] === "string" && value["reason"].length > 1e3) {
67596
+ invalid13(`reason has ${value["reason"].length} characters; maximum is 1000; request a corrected author response`);
67597
+ }
67492
67598
  if (!record25(value) || !text5(value["reason"])) invalid13("an author reason is required for each disposition");
67493
67599
  if ("requestIndex" in value) {
67494
67600
  const due2 = value["due"];
@@ -67920,7 +68026,7 @@ async function recordSpecialistRequests(root, missionId, envelopes, planHash) {
67920
68026
  init_esm_shims();
67921
68027
 
67922
68028
  // package.json
67923
- var version2 = "5.0.3";
68029
+ var version2 = "5.0.5";
67924
68030
 
67925
68031
  // src/lib/runs/verify.ts
67926
68032
  var MAX_CAPTURE_BYTES = 64 * 1024;
@@ -68430,8 +68536,8 @@ async function migrateMissionSpecialist(roots, missionId, request) {
68430
68536
  throw new Error(`SPECIALIST_CONTRACT_MIGRATION_NATIVE: ${capability.limitations.join("; ")}`);
68431
68537
  }
68432
68538
  const assets = await observeSpecialistMigrationAssets(coreRoot, installRoot, identity2.runtime);
68433
- const subject = await captureMissionReviewSubject(workRoot, stored.baseCommit);
68434
- const live = await planBoundMission(workRoot, stored.ticket.path, (/* @__PURE__ */ new Date()).toISOString(), subject.files);
68539
+ const subject = await captureControllerReviewSubject(workRoot, stored.baseCommit, current.inspected.stream.events);
68540
+ const live = await planBoundMission(workRoot, stored.ticket.path, (/* @__PURE__ */ new Date()).toISOString(), subject.files, void 0, stored.inputRecipe);
68435
68541
  if (live.ticket.path !== stored.ticket.path || live.ticket.contentHash !== stored.ticket.contentHash) {
68436
68542
  throw new Error("MISSION_TICKET_CHANGED: migration cannot replace the bound ticket");
68437
68543
  }
@@ -68440,7 +68546,7 @@ async function migrateMissionSpecialist(roots, missionId, request) {
68440
68546
  canonicalJsonHash({ routing: specialist.proof.inputHash, subject: subject.hash })
68441
68547
  ]));
68442
68548
  const comparisonCatalog = await loadSpecialistMigrationComparisonCatalog(coreRoot, assets.declaration, stored.plan);
68443
- const comparison = await planBoundMission(workRoot, stored.ticket.path, (/* @__PURE__ */ new Date()).toISOString(), subject.files, comparisonCatalog);
68549
+ const comparison = await planBoundMission(workRoot, stored.ticket.path, (/* @__PURE__ */ new Date()).toISOString(), subject.files, comparisonCatalog, stored.inputRecipe);
68444
68550
  const originalHashes = Object.fromEntries(comparison.plan.specialists.map((specialist) => [
68445
68551
  specialist.specialistId,
68446
68552
  canonicalJsonHash({ routing: specialist.proof.inputHash, subject: subject.hash })
@@ -68465,6 +68571,18 @@ async function migrateMissionSpecialist(roots, missionId, request) {
68465
68571
  nativeAgentSha256: assets.nativeAgentSha256
68466
68572
  } };
68467
68573
  }
68574
+ function boundedMissionReview(events) {
68575
+ return events.some((event) => event.kind === "mission.started" && objectField(event.payload, "reviewPolicy") === "bounded-corrections-v1") || events.some((event) => event.kind === "mission.recovered" && objectField(objectField(objectField(event.payload, "request"), "disposition"), "kind") === "review-provenance");
68576
+ }
68577
+ function captureControllerReviewSubject(root, baseCommit, events, requireCommitted = false) {
68578
+ const committedFormat = events.some((event) => event.kind === "mission.started" && objectField(event.payload, "reviewPolicy") === "bounded-corrections-v1");
68579
+ return captureMissionReviewSubject(
68580
+ root,
68581
+ baseCommit,
68582
+ requireCommitted || boundedMissionReview(events),
68583
+ committedFormat ? "committed" : "legacy"
68584
+ );
68585
+ }
68468
68586
  async function recoverStoppedMission(roots, missionId, request) {
68469
68587
  const { workRoot, installRoot } = roots;
68470
68588
  const [stored, current, coreRoot] = await Promise.all([
@@ -68496,8 +68614,13 @@ async function recoverStoppedMission(roots, missionId, request) {
68496
68614
  }
68497
68615
  }
68498
68616
  const implemented = inspected.stream.events.some((event) => event.kind === "lead-writer.completed" && objectField(event.payload, "actionKind") !== "run-preparation-correction");
68499
- const subject = implemented ? await captureMissionReviewSubject(workRoot, stored.baseCommit) : void 0;
68500
- const live = await planBoundMission(workRoot, stored.ticket.path, (/* @__PURE__ */ new Date()).toISOString(), subject?.files);
68617
+ const subject = implemented ? await captureControllerReviewSubject(
68618
+ workRoot,
68619
+ stored.baseCommit,
68620
+ inspected.stream.events,
68621
+ provenanceRecovery
68622
+ ) : void 0;
68623
+ const live = await planBoundMission(workRoot, stored.ticket.path, (/* @__PURE__ */ new Date()).toISOString(), subject?.files, void 0, stored.inputRecipe);
68501
68624
  if (live.ticket.path !== stored.ticket.path || live.ticket.contentHash !== stored.ticket.contentHash) {
68502
68625
  throw new Error("MISSION_TICKET_CHANGED: recovery cannot replace the original ticket");
68503
68626
  }
@@ -68514,7 +68637,7 @@ async function recoverStoppedMission(roots, missionId, request) {
68514
68637
  throw new Error("SPECIALIST_CONTRACT_MIGRATION_INVALID: declared or installed contract changed");
68515
68638
  }
68516
68639
  const comparisonCatalog = await loadSpecialistMigrationComparisonCatalog(coreRoot, assets.declaration, stored.plan);
68517
- const comparison = await planBoundMission(workRoot, stored.ticket.path, (/* @__PURE__ */ new Date()).toISOString(), subject.files, comparisonCatalog);
68640
+ const comparison = await planBoundMission(workRoot, stored.ticket.path, (/* @__PURE__ */ new Date()).toISOString(), subject.files, comparisonCatalog, stored.inputRecipe);
68518
68641
  for (const specialist of comparison.plan.specialists) {
68519
68642
  if (specialist.specialistId !== assets.declaration.specialistId) {
68520
68643
  currentInputHashes[specialist.specialistId] = canonicalJsonHash({
@@ -68526,19 +68649,19 @@ async function recoverStoppedMission(roots, missionId, request) {
68526
68649
  }
68527
68650
  const provenance4 = request.disposition.kind !== "review-provenance" ? void 0 : await readRecoveryReviewBindings(workRoot, request.disposition.resolutionArtifact.path);
68528
68651
  const resolutionArtifact = provenance4?.artifact ?? (request.disposition.kind === "review-blocker" || request.disposition.kind === "command-correction" ? await recoveryResolutionArtifact(workRoot, request.disposition.resolutionArtifact.path) : void 0);
68529
- const committedSubject = provenance4 === void 0 ? void 0 : await captureMissionReviewSubject(workRoot, stored.baseCommit, true);
68530
68652
  const opaqueContextRecovery = request.disposition.kind === "controller-defect" && request.disposition.defect === "opaque-native-context";
68531
68653
  const recoverySpecialists = opaqueContextRecovery || request.disposition.kind === "preparation-invocation" ? migration.plan.specialists.filter((value) => value.state === "applicable" && value.stages?.includes("pre-implementation")) : migration.plan.specialists;
68532
68654
  const observation2 = {
68533
- ...provenance4 === void 0 || committedSubject?.baseCommit === void 0 || committedSubject.reviewedCommit === void 0 ? {} : {
68655
+ ...provenance4 === void 0 || subject?.baseCommit === void 0 || subject.reviewedCommit === void 0 ? {} : {
68534
68656
  reviewBindings: provenance4.bindings,
68535
68657
  reviewSubject: {
68536
68658
  taskId: missionId,
68537
- baseCommit: committedSubject.baseCommit,
68538
- reviewedCommit: committedSubject.reviewedCommit,
68659
+ baseCommit: subject.baseCommit,
68660
+ reviewedCommit: subject.reviewedCommit,
68539
68661
  acceptanceCriteriaHash: stored.ticket.contentHash
68540
68662
  }
68541
68663
  },
68664
+ ...implemented && (provenanceRecovery || boundedMissionReview(inspected.stream.events)) ? { reviewPolicy: "bounded-corrections-v1" } : {},
68542
68665
  stage: implemented ? "post-implementation" : "pre-implementation",
68543
68666
  maxRounds: 2,
68544
68667
  evidenceDependencies: { "git:working-tree": current.project.diffHash },
@@ -68602,13 +68725,14 @@ async function dispatchMissionSpecialists(roots, input2, generatedAt = (/* @__PU
68602
68725
  const inspected = current?.inspected ?? initialInspection;
68603
68726
  requireOpenMission(inspected.stream.events);
68604
68727
  const implemented = inspected.stream.events.some((event) => event.kind === "lead-writer.completed" && isUnknownRecord(event.payload) && event.payload.actionKind !== "run-preparation-correction");
68605
- const boundedReview = inspected.stream.events.some((event) => event.kind === "mission.started" && objectField(event.payload, "reviewPolicy") === "bounded-corrections-v1") || inspected.stream.events.some((event) => event.kind === "mission.recovered" && objectField(objectField(objectField(event.payload, "request"), "disposition"), "kind") === "review-provenance");
68606
- const reviewSubject = implemented ? await captureMissionReviewSubject(workRoot, stored.baseCommit, boundedReview) : void 0;
68728
+ const reviewSubject = implemented ? await captureControllerReviewSubject(workRoot, stored.baseCommit, inspected.stream.events) : void 0;
68607
68729
  const live = await planBoundMission(
68608
68730
  workRoot,
68609
68731
  stored.ticket.path,
68610
68732
  generatedAt,
68611
- reviewSubject?.files
68733
+ reviewSubject?.files,
68734
+ void 0,
68735
+ stored.inputRecipe
68612
68736
  );
68613
68737
  const livePlan = live.plan;
68614
68738
  if (live.ticket.path !== stored.ticket.path || live.ticket.contentHash !== stored.ticket.contentHash) {
@@ -68639,7 +68763,7 @@ async function dispatchMissionSpecialists(roots, input2, generatedAt = (/* @__PU
68639
68763
  throw new Error("SPECIALIST_CONTRACT_MIGRATION_INVALID: declared or installed contract changed");
68640
68764
  }
68641
68765
  const catalog = await loadSpecialistMigrationComparisonCatalog(coreRoot, assets.declaration, stored.plan);
68642
- const comparison = await planBoundMission(workRoot, stored.ticket.path, generatedAt, reviewSubject.files, catalog);
68766
+ const comparison = await planBoundMission(workRoot, stored.ticket.path, generatedAt, reviewSubject.files, catalog, stored.inputRecipe);
68643
68767
  for (const specialist of comparison.plan.specialists) {
68644
68768
  if (specialist.specialistId !== assets.declaration.specialistId) {
68645
68769
  currentInputHashes[specialist.specialistId] = canonicalJsonHash({
@@ -68674,19 +68798,6 @@ async function dispatchMissionSpecialists(roots, input2, generatedAt = (/* @__PU
68674
68798
  reviewedCommit: reviewSubject.reviewedCommit,
68675
68799
  acceptanceCriteriaHash: stored.ticket.contentHash
68676
68800
  };
68677
- if (reviewBinding2 !== void 0) {
68678
- const restored = validatedRecoveredReviewEvents(inspected.stream.events);
68679
- if (!restored.ok) throw new Error(`MISSION_RECOVERY_INVALID: ${restored.reasons.join("; ")}`);
68680
- const hasProvenanceRecovery = inspected.stream.events.some((event) => event.kind === "mission.recovered" && objectField(objectField(objectField(event.payload, "request"), "disposition"), "kind") === "review-provenance");
68681
- if (hasProvenanceRecovery) for (const event of restored.events) {
68682
- if (event.kind !== "specialist.completed") continue;
68683
- const receipt2 = parseSpecialistCompletionValue(objectField(event.payload, "completion"))?.review;
68684
- const hash7 = objectField(event.payload, "inputHash");
68685
- if (receipt2 !== void 0 && sameReviewSubject(receipt2, reviewBinding2) && typeof hash7 === "string") {
68686
- currentInputHashes[event.subject] = hash7;
68687
- }
68688
- }
68689
- }
68690
68801
  const decision = orchestrateMissionTeam({
68691
68802
  ...reviewBinding2 === void 0 ? {} : { reviewSubject: reviewBinding2 },
68692
68803
  plan: stored.plan,
@@ -69010,7 +69121,7 @@ Fix: ${parsed.fix}
69010
69121
  const journal = roots.installRoot;
69011
69122
  try {
69012
69123
  if (parsed.kind === "plan") {
69013
- const plan2 = await planMission(root, parsed.ticketPath);
69124
+ const plan2 = await planMission(root, parsed.ticketPath, void 0, "project-v1");
69014
69125
  process.stdout.write(parsed.json ? `${JSON.stringify(plan2)}
69015
69126
  ` : `${renderPlan(plan2)}
69016
69127
  `);
@@ -69095,7 +69206,7 @@ ${dispatched.envelopes.map((envelope) => `${envelope.agentName} ${envelope.stage
69095
69206
  }
69096
69207
  if (parsed.kind === "start") {
69097
69208
  const baseCommit = parsed.ticketPath === void 0 ? void 0 : await missionReviewBase(root);
69098
- const bound = parsed.ticketPath === void 0 ? void 0 : await planBoundMission(root, parsed.ticketPath);
69209
+ const bound = parsed.ticketPath === void 0 ? void 0 : await planBoundMission(root, parsed.ticketPath, void 0, void 0, void 0, "project-v1");
69099
69210
  const plan2 = bound?.plan;
69100
69211
  const diff = plan2 === void 0 ? await gitFiles(root) : void 0;
69101
69212
  const stack = diff === void 0 ? void 0 : detectedStack(root, detectProfileInput(root, diff.files));
@@ -69114,7 +69225,7 @@ ${dispatched.envelopes.map((envelope) => `${envelope.agentName} ${envelope.stage
69114
69225
  const controllerPlan = plan2 === void 0 ? void 0 : missionSpecialistPlan(plan2);
69115
69226
  const ticketBinding = bound?.ticket;
69116
69227
  const runtimeIdentity = bound === void 0 ? void 0 : coordinatorRuntimeIdentity(process.env);
69117
- const routingHash = controllerPlan === void 0 ? void 0 : ticketBinding === void 0 ? void 0 : missionControllerRoutingHash(controllerPlan, ticketBinding, baseCommit);
69228
+ const routingHash = controllerPlan === void 0 ? void 0 : ticketBinding === void 0 ? void 0 : missionControllerRoutingHash(controllerPlan, ticketBinding, baseCommit, "project-v1");
69118
69229
  await createMission(journal, {
69119
69230
  missionId,
69120
69231
  title: parsed.title,
@@ -69141,7 +69252,8 @@ ${dispatched.envelopes.map((envelope) => `${envelope.agentName} ${envelope.stage
69141
69252
  missionId,
69142
69253
  controllerPlan,
69143
69254
  ticketBinding,
69144
- baseCommit
69255
+ baseCommit,
69256
+ "project-v1"
69145
69257
  );
69146
69258
  if (storedHash !== routingHash) {
69147
69259
  throw new Error("MISSION_CONTROLLER_PLAN_INVALID: routing hash changed while storing");