voidmachine 5.0.7 → 5.0.8

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.
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "harness",
3
- "version": "5.0.7",
3
+ "version": "5.0.8",
4
4
  "description": "VoidCorp universal craftsman skills (void-tdd, void-typescript-strict, void-hexagonal-architecture, void-domain-driven-design, void-functional, void-code-review, void-api-and-interface-design, void-source-driven-development, void-context, void-learn, void-decide, void-claude-md, void-observability, void-security-guidance, ...) plus 12 PreToolUse enforcement hooks (tdd-guard, no-any, protect-sensitive-files, block-dangerous-bash, no-ai-design-slop, ...) and PostToolUse/SessionStart lifecycle hooks (incl. trim-large-output token frugality), five read-only critics including doctrine-critic, native architecture/security/QA specialists, and the void-graph, void-doctor and void-audit gestures, which are skills like everything else.",
5
5
  "author": {
6
6
  "name": "VoidCorp",
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "schemaVersion": 1,
3
- "harnessVersion": "5.0.7",
3
+ "harnessVersion": "5.0.8",
4
4
  "capabilities": [
5
5
  {
6
6
  "id": "skill:pack-mobile/void-eas-build-profile",
package/dist/main.js CHANGED
@@ -35804,6 +35804,89 @@ function parseEnvelope(event) {
35804
35804
  completion
35805
35805
  };
35806
35806
  }
35807
+ function freshReviewProofIds(events, context2) {
35808
+ return events.flatMap((event) => {
35809
+ if (event.kind !== "evidence.recorded")
35810
+ return [];
35811
+ const parsed = parseEvidence(record7(event.payload)?.["evidence"]);
35812
+ return parsed.ok && parsed.value.evidenceId === event.subject && parsed.value.missionId === event.missionId && parsed.value.status === "passed" && assessEvidence(parsed.value, context2).status === "fresh" ? [parsed.value.evidenceId] : [];
35813
+ });
35814
+ }
35815
+ function reviewField(event, key) {
35816
+ return record7(event?.payload)?.[key];
35817
+ }
35818
+ function sameNativeDispatch(left, right) {
35819
+ return left.missionId === right.missionId && left.subject === right.subject && ["stage", "reviewRound", "inputHash", "contractVersion", "reviewSubject", "reviewScope"].every((key) => reviewField(left, key) !== void 0 && reviewField(right, key) !== void 0 && canonicalJsonHash(reviewField(left, key)) === canonicalJsonHash(reviewField(right, key)));
35820
+ }
35821
+ function boundNativeRequest(events, envelope) {
35822
+ const { event, completion, contextId } = envelope;
35823
+ const receipt2 = completion.review;
35824
+ const subject = reviewField(event, "reviewSubject");
35825
+ const scope = reviewField(event, "reviewScope");
35826
+ const start = events[0];
35827
+ if (!start || !receipt2 || !isReviewSubject(subject) || !isReviewScope(scope) || !sameReviewSubject(receipt2, subject) || canonicalJsonHash(receipt2.scope) !== canonicalJsonHash(scope) || receipt2.taskId !== event.missionId || receipt2.writerId !== reviewField(start, "leadWriterId") || receipt2.provenance.kind !== "native-context" || receipt2.provenance.contextId !== contextId || contextId === receipt2.writerId || event.source !== `runtime:${String(reviewField(start, "runtime"))}` || event.subject !== completion.specialistId || reviewField(event, "contractVersion") !== completion.contractVersion)
35828
+ return void 0;
35829
+ const requests = events.filter((candidate) => candidate.kind === "specialist.requested" && candidate.seq < event.seq && candidate.source === "void-harness:mission.dispatch" && sameNativeDispatch(candidate, event) && reviewField(candidate, "planHash") === reviewField(start, "planHash") && reviewField(candidate, "runtime") === reviewField(start, "runtime"));
35830
+ const request = requests[0];
35831
+ if (requests.length !== 1 || !request)
35832
+ return void 0;
35833
+ const started = events.filter((candidate) => candidate.kind === "specialist.started" && candidate.seq > request.seq && candidate.seq < event.seq && candidate.source === event.source && sameNativeDispatch(candidate, event) && reviewField(candidate, "contextId") === contextId && (candidate.causationId === void 0 || candidate.causationId === request.eventId));
35834
+ return started.length === 1 && (event.causationId === void 0 || event.causationId === request.eventId) && (reviewField(event, "reviewInvocationEventId") === void 0 || reviewField(event, "reviewInvocationEventId") === started[0]?.eventId) ? request : void 0;
35835
+ }
35836
+ function sameMembers(actual, expected) {
35837
+ return Array.isArray(actual) && actual.length === new Set(expected).size && new Set(actual).size === actual.length && actual.every((value) => expected.includes(value));
35838
+ }
35839
+ function authorizedTargetedRequest(events, request, history) {
35840
+ const original = history[0]?.completion.review;
35841
+ const previous = history.at(-1);
35842
+ const subject = reviewField(request, "reviewSubject");
35843
+ const scope = reviewField(request, "reviewScope");
35844
+ if (!original || !previous || !isReviewSubject(subject) || !isReviewScope(scope) || scope.kind !== "targeted" || subject.taskId !== original.taskId || subject.baseCommit !== original.baseCommit || subject.acceptanceCriteriaHash !== original.acceptanceCriteriaHash || reviewField(request, "contractVersion") !== previous.completion.contractVersion)
35845
+ return false;
35846
+ const blockers = history.flatMap((item) => item.completion.findings).filter((item) => item.classification === "blocking");
35847
+ if (blockers.length === 0 || !sameMembers(scope.findingIds, blockers.map((item) => item.id)) || !sameMembers(scope.affectedPaths, blockers.flatMap((item) => item.evidence.map((proof4) => proof4.path))))
35848
+ return false;
35849
+ const writers = events.filter((event) => event.kind === "lead-writer.completed" && event.seq > previous.event.seq && event.seq < request.seq);
35850
+ const writer = writers[0];
35851
+ const correction = events.find((event) => event.eventId === reviewField(writer ?? request, "requestEventId"));
35852
+ const count = events.filter((event) => event.kind === "lead-writer.completed" && event.seq < request.seq && reviewField(event, "actionKind") === "run-correction").length;
35853
+ if (writers.length !== 1 || !writer || !correction || count < 1 || count > 2 || reviewField(request, "reviewRound") !== count + 1 || correction.kind !== "lead-writer.requested" || correction.source !== "void-harness:mission.dispatch" || correction.seq <= previous.event.seq || correction.seq >= writer.seq || correction.subject !== original.writerId || writer.subject !== original.writerId || writer.source !== original.writerId || writer.causationId !== correction.eventId || reviewField(writer, "actionKind") !== "run-correction" || reviewField(writer, "planHash") !== reviewField(events[0], "planHash") || reviewField(writer, "implementationRound") !== count + 1 || !["actionKind", "planHash", "writerId", "implementationRound"].every((key) => reviewField(writer, key) === reviewField(correction, key)))
35854
+ return false;
35855
+ const open11 = reconcileReviewFindings(history, freshReviewProofIds(events.filter((event) => event.seq < correction.seq), { dependencies: {} })).filter((finding) => finding.classification === "blocking");
35856
+ return open11.length > 0 && sameMembers(reviewField(correction, "findingIds"), open11.map((finding) => finding.findingId));
35857
+ }
35858
+ function canContinueNativeReview(events, request, contextId) {
35859
+ const start = events[0];
35860
+ if (!start || start.kind !== "mission.started" || reviewField(request, "planHash") !== reviewField(start, "planHash") || reviewField(request, "runtime") !== reviewField(start, "runtime") || request.kind !== "specialist.requested" || request.source !== "void-harness:mission.dispatch" || request.subject !== "core:independent-code-reviewer" || reviewField(request, "stage") !== "post-implementation" || !validNativeContextId(contextId) || events.some((event) => event.missionId !== request.missionId))
35861
+ return false;
35862
+ const history = events.filter((event) => event.kind === "specialist.completed" && event.seq < request.seq && reviewField(event, "contextId") === contextId).map(parseEnvelope);
35863
+ if (history.length === 0 || history.some((item) => !item))
35864
+ return false;
35865
+ const prior = history.filter((item) => item !== void 0);
35866
+ const first = prior[0];
35867
+ if (!first || first.completion.review?.scope.kind !== "general" || first.completion.verdict !== "changes-requested" || new Set(prior.map((item) => item.completion.completionId)).size !== prior.length || events.some((event) => event.seq < request.seq && ["specialist.started", "specialist.completed"].includes(event.kind) && reviewField(event, "contextId") === contextId && (event.subject !== request.subject || reviewField(event, "stage") !== "post-implementation")) || events.filter((event) => event.kind === "specialist.completed" && event.subject === request.subject && reviewField(event, "stage") === "post-implementation" && event.seq < request.seq).at(-1)?.eventId !== prior.at(-1)?.event.eventId)
35868
+ return false;
35869
+ for (const [index, item] of prior.entries()) {
35870
+ const bound = boundNativeRequest(events, item);
35871
+ if (!bound || item.completion.review?.reviewerId !== first.completion.review.reviewerId || index > 0 && !authorizedTargetedRequest(events, bound, prior.slice(0, index)))
35872
+ return false;
35873
+ }
35874
+ return authorizedTargetedRequest(events, request, prior);
35875
+ }
35876
+ function validNativeReviewContinuation(events, completion) {
35877
+ const envelope = parseEnvelope(completion);
35878
+ if (!envelope || !envelope.contextId)
35879
+ return false;
35880
+ const request = boundNativeRequest(events, envelope);
35881
+ const receipt2 = envelope.completion.review;
35882
+ if (!request || !receipt2 || !canContinueNativeReview(events, request, envelope.contextId) || receipt2.scope.kind !== "targeted")
35883
+ return false;
35884
+ const previous = events.filter((event) => event.kind === "specialist.completed" && event.seq < request.seq && reviewField(event, "contextId") === envelope.contextId).at(-1);
35885
+ const previousReceipt = previous && parseEnvelope(previous)?.completion.review;
35886
+ const correction = events.filter((event) => event.kind === "lead-writer.requested" && event.seq < request.seq && reviewField(event, "actionKind") === "run-correction").at(-1);
35887
+ const historicalProofIds = freshReviewProofIds(events.filter((event) => event.seq < completion.seq), { dependencies: {} });
35888
+ return receipt2.reviewerId === previousReceipt?.reviewerId && correction !== void 0 && receipt2.resolutions.every((resolution) => receipt2.scope.kind === "targeted" && receipt2.scope.findingIds.includes(resolution.findingId) && (resolution.status !== "resolved" || resolution.proofIds.every((id3) => historicalProofIds.includes(id3) && receipt2.proofIds.includes(id3) && events.some((event) => event.kind === "evidence.recorded" && event.subject === id3 && event.seq > correction.seq && event.seq < completion.seq))));
35889
+ }
35807
35890
  function collectCompletions(events, stage, expectedSource, stageStartSeqExclusive, afterSeqExclusive, beforeSeqExclusive, maxRounds, retainedSpecialists, contractMigration, correctionBatches) {
35808
35891
  const accepted = [];
35809
35892
  const retained = [];
@@ -35873,7 +35956,7 @@ function collectCompletions(events, stage, expectedSource, stageStartSeqExclusiv
35873
35956
  }
35874
35957
  const duplicateCompletion = completionIds.has(envelope.completion.completionId);
35875
35958
  const contextKey = envelope.contextId ?? (envelope.completion.review?.provenance.kind === "review-artifact" ? `artifact:${envelope.completion.review.provenance.sha256}` : void 0);
35876
- const reusedContext = contextKey !== void 0 && contextIds.has(contextKey);
35959
+ const reusedContext = contextKey !== void 0 && contextIds.has(contextKey) && !(envelope.stage === "post-implementation" && validNativeReviewContinuation(events, event));
35877
35960
  if (duplicateCompletion) {
35878
35961
  issues.push({
35879
35962
  code: "duplicate-completion",
@@ -36592,6 +36675,17 @@ function opaqueNativeContextDefect(input2) {
36592
36675
  return starts.length === 1 && started !== void 0 && started.source === event.source && started.seq > requested.seq && started.seq < event.seq && ["stage", "reviewRound", "inputHash", "contractVersion", "contextId"].every((key) => field4(started, key) === field4(event, key));
36593
36676
  });
36594
36677
  }
36678
+ function targetedReviewContextDefect(input2) {
36679
+ const { events } = input2.stream;
36680
+ const { observation: observation2 } = input2;
36681
+ const start = events[0];
36682
+ const closure = events.at(-1);
36683
+ const current = completions(events).filter((item) => field4(item.event, "stage") === "post-implementation").at(-1);
36684
+ const receipt2 = current?.completion.review;
36685
+ if (!start || closure?.kind !== "mission.closed" || closure.source !== "void-harness:mission.dispatch" || start.kind !== "mission.started" || start.source !== "void-harness:mission" || field4(start, "runtimeAttested") !== true || observation2.stage !== "post-implementation" || observation2.reviewPolicy !== "bounded-corrections-v1" || !observation2.reviewSubject || !current || !receipt2 || !sameReviewSubject(receipt2, observation2.reviewSubject) || current.completion.verdict !== "pass" || current.event.source !== observation2.expectedSource || field4(current.event, "inputHash") !== observation2.currentInputHashes[current.event.subject] || current.completion.contractVersion !== observation2.contractVersions[current.event.subject])
36686
+ return false;
36687
+ return validNativeReviewContinuation(events, current.event);
36688
+ }
36595
36689
  function preparationInvocationReplacement(input2) {
36596
36690
  const { events } = input2.stream;
36597
36691
  const { observation: observation2, request } = input2;
@@ -36867,12 +36961,13 @@ function admitStoppedMission(input2, projectedHistory = input2.stream.events) {
36867
36961
  }
36868
36962
  const disposition = request.disposition;
36869
36963
  const replacement = disposition.kind === "preparation-invocation";
36964
+ const targetedContext = disposition.kind === "controller-defect" && disposition.defect === "targeted-review-context";
36870
36965
  if (replacement && !preparationInvocationReplacement(input2)) {
36871
36966
  return refuse2("invalid-preparation-invocation", "Name exactly the reused-context preparation completions with intact invocation bindings; replacement is one-time and retains round two");
36872
36967
  }
36873
- const roundCorrections = replacement ? [] : partialFanoutCorrections(projectedHistory);
36874
- const inadmissible = replacement ? disposition.completionEventIds : staleDispatchCompletions(projectedHistory);
36875
- if (disposition.kind === "controller-defect" && (disposition.defect === "partial-fanout-round" ? roundCorrections.length === 0 : disposition.defect === "stale-input-dispatch" ? inadmissible.length === 0 : !opaqueNativeContextDefect(input2))) {
36968
+ const roundCorrections = replacement || targetedContext ? [] : partialFanoutCorrections(projectedHistory);
36969
+ const inadmissible = replacement ? disposition.completionEventIds : targetedContext ? [] : staleDispatchCompletions(projectedHistory);
36970
+ if (disposition.kind === "controller-defect" && (disposition.defect === "partial-fanout-round" ? roundCorrections.length === 0 : disposition.defect === "stale-input-dispatch" ? inadmissible.length === 0 : targetedContext ? !targetedReviewContextDefect(input2) : !opaqueNativeContextDefect(input2))) {
36876
36971
  return refuse2("unproven-controller-defect", "The journal must prove the requested controller defect; do not reset its budget");
36877
36972
  }
36878
36973
  if (disposition.kind === "review-provenance" && !validProvenanceRecovery(input2, existing)) {
@@ -36944,6 +37039,9 @@ function admitStoppedMission(input2, projectedHistory = input2.stream.events) {
36944
37039
  requiredSpecialists: required2,
36945
37040
  contractVersions: observation2.contractVersions,
36946
37041
  currentInputHashes: observation2.currentInputHashes,
37042
+ ...targetedContext ? { validProofIds: freshReviewProofIds(projected, {
37043
+ dependencies: observation2.evidenceDependencies ?? {}
37044
+ }) } : {},
36947
37045
  ...observation2.reviewPolicy === "bounded-corrections-v1" ? BOUNDED_REVIEW_BUDGET : { maxRounds: observation2.maxRounds }
36948
37046
  };
36949
37047
  const originalReview = reduceReviewLoop({ ...reviewInput, events: projectedHistory });
@@ -36957,14 +37055,17 @@ function admitStoppedMission(input2, projectedHistory = input2.stream.events) {
36957
37055
  if (disposition.kind === "controller-defect" && disposition.defect === "opaque-native-context" && (!review.readyForVerdict || existing.length !== required2.length || required2.some((id3) => !existing.some((item) => item.event.subject === id3)))) {
36958
37056
  return refuse2("incomplete-required-panel", "Every required preparation specialist must have its original matching PASS triplet before recovery");
36959
37057
  }
37058
+ if (targetedContext && !review.readyForVerdict) {
37059
+ return refuse2("unproven-controller-defect", "The retained targeted review must resolve every blocking finding with current evidence before verification");
37060
+ }
36960
37061
  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"))));
36961
- if (!replacement && disposition.kind !== "review-provenance" && consumedRounds >= reviewInput.maxRounds)
37062
+ if (!replacement && !targetedContext && disposition.kind !== "review-provenance" && consumedRounds >= reviewInput.maxRounds)
36962
37063
  return refuse2("review-budget-exhausted", "The real review budget is exhausted; recovery cannot reset it");
36963
37064
  const consumedCorrectionBatches = projected.filter((event) => event.kind === "lead-writer.completed" && field4(event, "actionKind") === "run-correction").length;
36964
- if (disposition.kind === "review-provenance" && consumedCorrectionBatches > 2) {
37065
+ if ((targetedContext || disposition.kind === "review-provenance") && consumedCorrectionBatches > 2) {
36965
37066
  return refuse2("review-budget-exhausted", "The real correction budget is exhausted; recovery cannot reset it");
36966
37067
  }
36967
- const preserved = existing.filter((item) => !inadmissible.includes(item.event.eventId) && item.completion.contractVersion === observation2.contractVersions[item.event.subject] && field4(item.event, "inputHash") === observation2.currentInputHashes[item.event.subject]);
37068
+ const preserved = targetedContext ? existing : existing.filter((item) => !inadmissible.includes(item.event.eventId) && item.completion.contractVersion === observation2.contractVersions[item.event.subject] && field4(item.event, "inputHash") === observation2.currentInputHashes[item.event.subject]);
36968
37069
  const preservedIds = new Set(preserved.map((item) => item.event.eventId));
36969
37070
  const receipt2 = {
36970
37071
  schemaVersion: 1,
@@ -36981,11 +37082,11 @@ function admitStoppedMission(input2, projectedHistory = input2.stream.events) {
36981
37082
  roundCorrections,
36982
37083
  consumedRounds,
36983
37084
  remainingRounds: Math.max(0, reviewInput.maxRounds - consumedRounds),
36984
- ...!replacement && disposition.kind !== "review-provenance" ? {} : {
37085
+ ...!replacement && !targetedContext && disposition.kind !== "review-provenance" ? {} : {
36985
37086
  consumedCorrectionBatches,
36986
37087
  remainingCorrectionBatches: 2 - consumedCorrectionBatches
36987
37088
  },
36988
- 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"
37089
+ nextAction: replacement || targetedContext || 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"
36989
37090
  };
36990
37091
  if (new TextEncoder().encode(canonicalJson(receipt2)).length > 16384) {
36991
37092
  return refuse2("recovery-receipt-too-large", "Narrow the recovery scope to the supported bounded incident");
@@ -37012,7 +37113,7 @@ function recoveryRequest(value) {
37012
37113
  if (disposition["kind"] === "command-correction") {
37013
37114
  return exactKeys2(disposition, ["kind", "pairs", "resolutionArtifact"]) && isCommandEvidenceCorrections(disposition["pairs"]) && artifact(disposition["resolutionArtifact"]);
37014
37115
  }
37015
- return disposition["kind"] === "controller-defect" ? exactKeys2(disposition, ["kind", "defect"]) && ["partial-fanout-round", "stale-input-dispatch", "opaque-native-context"].includes(String(disposition["defect"])) : (disposition["kind"] === "review-blocker" || disposition["kind"] === "review-provenance") && exactKeys2(disposition, ["kind", "completionEventIds", "resolutionArtifact"]) && Array.isArray(disposition["completionEventIds"]) && disposition["completionEventIds"].length <= 64 && disposition["completionEventIds"].every((item) => typeof item === "string") && artifact(disposition["resolutionArtifact"]);
37116
+ return disposition["kind"] === "controller-defect" ? exactKeys2(disposition, ["kind", "defect"]) && ["partial-fanout-round", "stale-input-dispatch", "opaque-native-context", "targeted-review-context"].includes(String(disposition["defect"])) : (disposition["kind"] === "review-blocker" || disposition["kind"] === "review-provenance") && exactKeys2(disposition, ["kind", "completionEventIds", "resolutionArtifact"]) && Array.isArray(disposition["completionEventIds"]) && disposition["completionEventIds"].length <= 64 && disposition["completionEventIds"].every((item) => typeof item === "string") && artifact(disposition["resolutionArtifact"]);
37016
37117
  }
37017
37118
  function reviewBinding(value) {
37018
37119
  if (typeof value !== "object" || value === null || Array.isArray(value))
@@ -38991,6 +39092,7 @@ function orchestrateMissionTeam(originalInput) {
38991
39092
  contractVersions: contractVersions(input2.plan),
38992
39093
  currentInputHashes: input2.currentInputHashesByStage["pre-implementation"],
38993
39094
  maxRounds: input2.maxReviewRounds,
39095
+ validProofIds: freshReviewProofIds(input2.stream.events, input2.evidenceContext),
38994
39096
  evidenceObligations: {
38995
39097
  ...obligations,
38996
39098
  blockingObligationIds: obligations.blockingObligationIds.filter((id3) => preparationObligationIds.has(id3))
@@ -39106,12 +39208,7 @@ function orchestrateMissionTeam(originalInput) {
39106
39208
  ...start.boundedReview ? BOUNDED_REVIEW_BUDGET : { maxRounds: input2.maxReviewRounds },
39107
39209
  // Pending proofs are handled by obligationStop; they must not mask failed reviews.
39108
39210
  evidenceObligations: { ...obligations, blockingObligationIds: [] },
39109
- validProofIds: input2.stream.events.flatMap((event) => {
39110
- if (event.kind !== "evidence.recorded")
39111
- return [];
39112
- const parsed = parseEvidence(lifecycleField(event, "evidence"));
39113
- 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] : [];
39114
- }),
39211
+ validProofIds: freshReviewProofIds(input2.stream.events, input2.evidenceContext),
39115
39212
  ...migration === void 0 ? {} : { contractMigration: {
39116
39213
  specialistId: migration.receipt.specialistId,
39117
39214
  afterSeq: migration.seq,
@@ -67259,7 +67356,7 @@ function parseMissionRecoveryRequest(value) {
67259
67356
  };
67260
67357
  if (disposition["kind"] === "controller-defect") {
67261
67358
  const defect = disposition["defect"];
67262
- if (!exact2(disposition, ["kind", "defect"]) || defect !== "partial-fanout-round" && defect !== "stale-input-dispatch" && defect !== "opaque-native-context") invalid11();
67359
+ if (!exact2(disposition, ["kind", "defect"]) || defect !== "partial-fanout-round" && defect !== "stale-input-dispatch" && defect !== "opaque-native-context" && defect !== "targeted-review-context") invalid11();
67263
67360
  return { ...base, disposition: { kind: "controller-defect", defect } };
67264
67361
  }
67265
67362
  if (disposition["kind"] === "evidence-disposition") {
@@ -67872,7 +67969,7 @@ async function recordSpecialistLifecycle(root, missionId, input2, artifactRoot =
67872
67969
  const failed2 = input2.envelope.reviewSubject !== void 0 && dispatchEvents.some((event) => event.kind === "specialist.failed" && event.seq > existing.seq && sameDispatch(event, input2.envelope) && field8(event.payload, "contextId") === field8(existing.payload, "contextId"));
67873
67970
  if (!failed2) invalid14("dispatch already started with another contextId");
67874
67971
  }
67875
- if (recovery !== void 0 && input2.contextId !== void 0 && events.some((event) => event.kind === "specialist.started" && field8(event.payload, "contextId") === input2.contextId)) {
67972
+ if (recovery !== void 0 && input2.contextId !== void 0 && events.some((event) => event.kind === "specialist.started" && field8(event.payload, "contextId") === input2.contextId) && !canContinueNativeReview(events, requested, input2.contextId)) {
67876
67973
  invalid14("contextId was already used by a previous dispatch");
67877
67974
  }
67878
67975
  } else {
@@ -67904,7 +68001,7 @@ async function recordSpecialistLifecycle(root, missionId, input2, artifactRoot =
67904
68001
  if (observedMissionLifecycle(current).episodeId !== lifecycle.episodeId) {
67905
68002
  invalid14("mission episode changed before recording lifecycle");
67906
68003
  }
67907
- if (recovery !== void 0 && input2.status === "started" && input2.contextId !== void 0 && current.some((event) => event.kind === "specialist.started" && field8(event.payload, "contextId") === input2.contextId && event.eventId !== eventId3)) {
68004
+ if (recovery !== void 0 && input2.status === "started" && input2.contextId !== void 0 && current.some((event) => event.kind === "specialist.started" && field8(event.payload, "contextId") === input2.contextId && event.eventId !== eventId3) && !canContinueNativeReview(current, requested, input2.contextId)) {
67908
68005
  invalid14("contextId was already used by a previous dispatch");
67909
68006
  }
67910
68007
  }
@@ -68062,7 +68159,7 @@ async function recordSpecialistRequests(root, missionId, envelopes, planHash) {
68062
68159
  init_esm_shims();
68063
68160
 
68064
68161
  // package.json
68065
- var version2 = "5.0.7";
68162
+ var version2 = "5.0.8";
68066
68163
 
68067
68164
  // src/lib/runs/verify.ts
68068
68165
  var MAX_CAPTURE_BYTES = 64 * 1024;
@@ -68633,6 +68730,7 @@ async function recoverStoppedMission(roots, missionId, request) {
68633
68730
  const identity2 = missionRuntimeIdentity(inspected.stream.events);
68634
68731
  const coordinator = coordinatorRuntimeIdentity(process.env);
68635
68732
  const provenanceRecovery = request.disposition.kind === "review-provenance";
68733
+ const targetedContextRecovery = request.disposition.kind === "controller-defect" && request.disposition.defect === "targeted-review-context";
68636
68734
  if (identity2 === void 0 || coordinator.attested && coordinator.runtime !== identity2.runtime || !provenanceRecovery && (!identity2.attested || !coordinator.attested)) {
68637
68735
  throw new Error("MISSION_RECOVERY_RUNTIME: observe the original native runtime before recovery");
68638
68736
  }
@@ -68686,10 +68784,10 @@ async function recoverStoppedMission(roots, missionId, request) {
68686
68784
  const provenance4 = request.disposition.kind !== "review-provenance" ? void 0 : await readRecoveryReviewBindings(workRoot, request.disposition.resolutionArtifact.path);
68687
68785
  const resolutionArtifact = provenance4?.artifact ?? (request.disposition.kind === "review-blocker" || request.disposition.kind === "command-correction" ? await recoveryResolutionArtifact(workRoot, request.disposition.resolutionArtifact.path) : void 0);
68688
68786
  const opaqueContextRecovery = request.disposition.kind === "controller-defect" && request.disposition.defect === "opaque-native-context";
68689
- const recoverySpecialists = opaqueContextRecovery || request.disposition.kind === "preparation-invocation" ? migration.plan.specialists.filter((value) => value.state === "applicable" && value.stages?.includes("pre-implementation")) : migration.plan.specialists;
68787
+ const recoverySpecialists = targetedContextRecovery ? migration.plan.specialists.filter((value) => value.specialistId === "core:independent-code-reviewer") : opaqueContextRecovery || request.disposition.kind === "preparation-invocation" ? migration.plan.specialists.filter((value) => value.state === "applicable" && value.stages?.includes("pre-implementation")) : migration.plan.specialists;
68690
68788
  const observation2 = {
68691
- ...provenance4 === void 0 || subject?.baseCommit === void 0 || subject.reviewedCommit === void 0 ? {} : {
68692
- reviewBindings: provenance4.bindings,
68789
+ ...provenance4 === void 0 && !targetedContextRecovery || subject?.baseCommit === void 0 || subject.reviewedCommit === void 0 ? {} : {
68790
+ ...provenance4 === void 0 ? {} : { reviewBindings: provenance4.bindings },
68693
68791
  reviewSubject: {
68694
68792
  taskId: missionId,
68695
68793
  baseCommit: subject.baseCommit,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "voidmachine",
3
- "version": "5.0.7",
3
+ "version": "5.0.8",
4
4
  "description": "void-machine CLI — install an opinionated engineering doctrine on any project; render deterministic project health. Run via `npx voidmachine`; command: void-machine (alias vm).",
5
5
  "license": "MIT",
6
6
  "author": "Folpe @ VoidCorp",
@@ -58,9 +58,9 @@
58
58
  "ws": "8.21.0",
59
59
  "yaml": "^2.5.0",
60
60
  "zod": "^4.6.5",
61
+ "@voidcorp/harness-graph": "5.0.8",
61
62
  "@voidcorp/hook-runner": "0.0.0",
62
63
  "@voidcorp/mission-engine": "0.0.0",
63
- "@voidcorp/harness-graph": "5.0.7",
64
64
  "@voidcorp/void-machine": "0.0.0"
65
65
  },
66
66
  "scripts": {