voidmachine 5.0.4 → 5.0.6

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.4",
3
+ "version": "5.0.6",
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",
@@ -5,7 +5,7 @@
5
5
  "source": {
6
6
  "kind": "adapter",
7
7
  "version": "3.0.0-alpha.1",
8
- "rootHash": "sha256:56012433234f5e7d537066868543df205e92f817003384489c7e698778a49ab2"
8
+ "rootHash": "sha256:e38546e5aa7fcb09bddd3c33954b340026d7d92ae9d74b0272191043b98cd706"
9
9
  },
10
10
  "nodes": [
11
11
  {
@@ -4677,7 +4677,7 @@
4677
4677
  }
4678
4678
  ],
4679
4679
  "id": "skill:void-orchestrate",
4680
- "lines": 327,
4680
+ "lines": 328,
4681
4681
  "name": "void-orchestrate",
4682
4682
  "owner": "folpe",
4683
4683
  "pack": null,
@@ -4686,7 +4686,7 @@
4686
4686
  "codex"
4687
4687
  ],
4688
4688
  "source": "packages/core/skills/void-orchestrate/SKILL.md",
4689
- "staticTokens": 4673,
4689
+ "staticTokens": 4684,
4690
4690
  "type": "skill"
4691
4691
  }
4692
4692
  },
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "schemaVersion": 1,
3
- "harnessVersion": "5.0.4",
3
+ "harnessVersion": "5.0.6",
4
4
  "capabilities": [
5
5
  {
6
6
  "id": "skill:pack-mobile/void-eas-build-profile",
@@ -2415,7 +2415,7 @@
2415
2415
  }
2416
2416
  ],
2417
2417
  "id": "skill:void-orchestrate",
2418
- "lines": 327,
2418
+ "lines": 328,
2419
2419
  "name": "void-orchestrate",
2420
2420
  "owner": "folpe",
2421
2421
  "pack": null,
@@ -2424,7 +2424,7 @@
2424
2424
  "codex"
2425
2425
  ],
2426
2426
  "source": "packages/core/skills/void-orchestrate/SKILL.md",
2427
- "staticTokens": 4673,
2427
+ "staticTokens": 4684,
2428
2428
  "type": "skill"
2429
2429
  },
2430
2430
  {
@@ -13035,7 +13035,7 @@ var selectionSchema = strictObject({
13035
13035
  mission: identifier,
13036
13036
  coordinator: strictObject({ label: identifier, worktree: path })
13037
13037
  });
13038
- var instant = iso_exports.datetime();
13038
+ var instant = iso_exports.datetime({ offset: true });
13039
13039
  var status = _enum(["planned", "running", "blocked", "done", "failed"]);
13040
13040
  var workerSchema = strictObject({
13041
13041
  label: identifier,
@@ -161,7 +161,8 @@ mission bytes; exceeding a bound is incomplete, never a unique match.
161
161
  See the shipped technical contract `docs/ORCHESTRA.md` in the harness source for
162
162
  schemas; the operational fields are listed here so an installed skill is sufficient.
163
163
 
164
- - mission.md: schema, mission, project, status, updated_at (UTC ISO), repository
164
+ - mission.md: schema, mission, project, status, updated_at
165
+ (ISO with `Z` or `+HH:MM`/`-HH:MM`; prefer UTC `Z`), repository
165
166
  (canonical Git common path), coordinator {label, worktree, workspace?}, workers.
166
167
  - Worker entries: label, agent (codex or claude), worktree, branch, ticket, status,
167
168
  attempt; optional pane_id and session are hints only. At most four workers.
package/dist/main.js CHANGED
@@ -35954,7 +35954,7 @@ function collectCompletions(events, stage, expectedSource, stageStartSeqExclusiv
35954
35954
  }
35955
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));
35956
35956
  const highestRound = Math.max(historicalHighestRound, activeRound ?? 0);
35957
- 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);
35958
35958
  const nextRound = Math.max(firstCurrentRound, highestRound, ...pendingFailureRounds);
35959
35959
  return { accepted: [...retained, ...validAccepted], history, issues, highestRound, nextRound };
35960
35960
  }
@@ -36778,6 +36778,55 @@ function pendingPreparationClarification(input2, previousRecovery) {
36778
36778
  const completionEventIds = request.disposition.completionEventIds;
36779
36779
  return obligations.issues.length === 0 && obligations.obligations.some((item) => blocking.has(item.obligationId) && completionEventIds.includes(item.completionEventId));
36780
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
+ }
36781
36830
  function admitStoppedMission(input2, projectedHistory = input2.stream.events) {
36782
36831
  const { stream: stream2, request, observation: observation2 } = input2;
36783
36832
  const events = stream2.events;
@@ -36796,9 +36845,12 @@ function admitStoppedMission(input2, projectedHistory = input2.stream.events) {
36796
36845
  return refuse2("stale-journal", "Re-observe the journal before requesting recovery");
36797
36846
  if (ambiguousEffects(events))
36798
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
+ }
36799
36851
  const existing = completions(events).filter((item) => field4(item.event, "stage") === observation2.stage);
36800
36852
  const previousRecovery = events.filter((event) => event.kind === "mission.recovered").at(-1);
36801
- if (previousRecovery !== void 0 && !events.some((event) => event.seq > previousRecovery.seq && event.kind === "lead-writer.completed") && !pendingPreparationClarification(input2, previousRecovery) && !validProvenanceRecovery(input2, existing, previousRecovery.seq)) {
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)) {
36802
36854
  return refuse2("no-recovery-progress", "Record actual corrective progress before reopening another stopped episode");
36803
36855
  }
36804
36856
  const disposition = request.disposition;
@@ -36921,7 +36973,7 @@ function admitStoppedMission(input2, projectedHistory = input2.stream.events) {
36921
36973
  consumedCorrectionBatches,
36922
36974
  remainingCorrectionBatches: 2 - consumedCorrectionBatches
36923
36975
  },
36924
- 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"
36925
36977
  };
36926
36978
  if (new TextEncoder().encode(canonicalJson(receipt2)).length > 16384) {
36927
36979
  return refuse2("recovery-receipt-too-large", "Narrow the recovery scope to the supported bounded incident");
@@ -36938,6 +36990,10 @@ function recoveryRequest(value) {
36938
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"]))
36939
36991
  return false;
36940
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
+ }
36941
36997
  if (disposition["kind"] === "preparation-invocation") {
36942
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;
36943
36999
  }
@@ -38882,7 +38938,7 @@ function evidenceObligations(input2, expectedSource, phase) {
38882
38938
  evidenceContext: input2.evidenceContext
38883
38939
  });
38884
38940
  }
38885
- function obligationStop(state, review, verdict, preparation = false) {
38941
+ function obligationStop(state, review, verdict) {
38886
38942
  if (state.issues.length > 0) {
38887
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.");
38888
38944
  }
@@ -38890,7 +38946,7 @@ function obligationStop(state, review, verdict, preparation = false) {
38890
38946
  if (blocking.size === 0)
38891
38947
  return void 0;
38892
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.");
38893
- return preparation ? { ...decision, action: { kind: "await-evidence", reasons: decision.reasons } } : decision;
38949
+ return { ...decision, action: { kind: "await-evidence", reasons: decision.reasons } };
38894
38950
  }
38895
38951
  function orchestrateMissionTeam(originalInput) {
38896
38952
  const migrated = validatedSpecialistContractMigrations(originalInput.stream.events, originalInput.plan);
@@ -38986,7 +39042,10 @@ function orchestrateMissionTeam(originalInput) {
38986
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])) {
38987
39043
  return stopped("blocked", preReview, baseVerdict, ["Preparation replacement inputs or contracts changed; reconcile the admitted subject before dispatch"]);
38988
39044
  }
38989
- 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);
38990
39049
  const migration = migrated.migration;
38991
39050
  if (migration !== void 0) {
38992
39051
  const receipt2 = migration.receipt;
@@ -39012,6 +39071,33 @@ function orchestrateMissionTeam(originalInput) {
39012
39071
  }, input2.specialistRuntime);
39013
39072
  }
39014
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
+ }
39015
39101
  const correctiveFindingIds = pendingRecoveredCorrectionFindingIds(input2.stream.events);
39016
39102
  const correctiveContinuation = pendingEvidence !== void 0 && obligations.issues.length === 0 && firstImplementationSeq !== void 0 && correctiveFindingIds.length > 0;
39017
39103
  if (pendingEvidence !== void 0 && !correctiveContinuation) {
@@ -39060,32 +39146,9 @@ function orchestrateMissionTeam(originalInput) {
39060
39146
  reasons
39061
39147
  }, input2.specialistRuntime);
39062
39148
  }
39063
- if (firstWriterSeq === void 0 || firstImplementationSeq === void 0 || lastWriterSeq === void 0) {
39149
+ if (firstWriterSeq === void 0 || firstImplementationSeq === void 0 || lastWriterSeq === void 0 || postReview === void 0) {
39064
39150
  throw new Error("MISSION_TEAM_INVARIANT: writer completion boundary is missing");
39065
39151
  }
39066
- const postReview = reduceReviewLoop({
39067
- stage: "post-implementation",
39068
- expectedSource,
39069
- stageStartSeqExclusive: firstImplementationSeq,
39070
- afterSeqExclusive: lastWriterSeq,
39071
- events: reviewEvents,
39072
- requiredSpecialists: requiredSpecialists(input2.plan, "post-implementation", start.boundedReview),
39073
- contractVersions: contractVersions(input2.plan),
39074
- currentInputHashes: input2.currentInputHashesByStage["post-implementation"],
39075
- ...start.boundedReview ? BOUNDED_REVIEW_BUDGET : { maxRounds: input2.maxReviewRounds },
39076
- evidenceObligations: obligations,
39077
- validProofIds: input2.stream.events.flatMap((event) => {
39078
- if (event.kind !== "evidence.recorded")
39079
- return [];
39080
- const parsed = parseEvidence(lifecycleField(event, "evidence"));
39081
- 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] : [];
39082
- }),
39083
- ...migration === void 0 ? {} : { contractMigration: {
39084
- specialistId: migration.receipt.specialistId,
39085
- afterSeq: migration.seq,
39086
- reviewRound: migration.receipt.reviewRound
39087
- } }
39088
- });
39089
39152
  if (start.boundedReview && postReview.missingSpecialists.length === 0) {
39090
39153
  const receiptReasons = boundedReceiptReasons(input2, start, reviewEvents);
39091
39154
  if (receiptReasons.length > 0)
@@ -67163,6 +67226,11 @@ function parseMissionRecoveryRequest(value) {
67163
67226
  if (!exact2(disposition, ["kind", "defect"]) || defect !== "partial-fanout-round" && defect !== "stale-input-dispatch" && defect !== "opaque-native-context") invalid11();
67164
67227
  return { ...base, disposition: { kind: "controller-defect", defect } };
67165
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
+ }
67166
67234
  const commandCorrection = disposition["kind"] === "command-correction";
67167
67235
  const ids = disposition["completionEventIds"];
67168
67236
  if (disposition["kind"] === "preparation-invocation") {
@@ -67444,13 +67512,22 @@ async function validateCompletion(events, candidate, roots, expectedSource) {
67444
67512
  if (discharge2) {
67445
67513
  const items = field7(candidate.payload, "items");
67446
67514
  if (!Array.isArray(items) || items.some((item) => !record25(item) || !state.obligations.some((value) => value.obligationId === item["obligationId"] && value.discharged))) {
67447
- 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`);
67448
67516
  }
67449
67517
  }
67450
67518
  }
67451
- async function recordSpecialistEvidence(roots, missionId, status3, input2) {
67452
- 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;
67453
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);
67454
67531
  const source2 = identity(events);
67455
67532
  const request = exactRequest(events, input2);
67456
67533
  const kind2 = status3 === "started" ? "specialist.evidence-started" : request.kind === "specialist.evidence-classification-requested" ? "specialist.evidence-classified" : "specialist.evidence-discharged";
@@ -67474,7 +67551,7 @@ async function recordSpecialistEvidence(roots, missionId, status3, input2) {
67474
67551
  eventId: id3,
67475
67552
  draft,
67476
67553
  validate: async (current) => {
67477
- requireOpenMission(current);
67554
+ requireResponseAdmission(current, status3, input2);
67478
67555
  exactRequest(current, input2);
67479
67556
  if (status3 === "completed") {
67480
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);
@@ -67515,6 +67592,9 @@ function parseSpecialistEvidenceRequest(value) {
67515
67592
  return { ...base, operation: "discharge", obligationIds: ids };
67516
67593
  }
67517
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
+ }
67518
67598
  if (!record25(value) || !text5(value["reason"])) invalid13("an author reason is required for each disposition");
67519
67599
  if ("requestIndex" in value) {
67520
67600
  const due2 = value["due"];
@@ -67946,7 +68026,7 @@ async function recordSpecialistRequests(root, missionId, envelopes, planHash) {
67946
68026
  init_esm_shims();
67947
68027
 
67948
68028
  // package.json
67949
- var version2 = "5.0.4";
68029
+ var version2 = "5.0.6";
67950
68030
 
67951
68031
  // src/lib/runs/verify.ts
67952
68032
  var MAX_CAPTURE_BYTES = 64 * 1024;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "voidmachine",
3
- "version": "5.0.4",
3
+ "version": "5.0.6",
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,10 +58,10 @@
58
58
  "ws": "8.21.0",
59
59
  "yaml": "^2.5.0",
60
60
  "zod": "^4.6.5",
61
- "@voidcorp/harness-graph": "5.0.4",
62
- "@voidcorp/hook-runner": "0.0.0",
63
61
  "@voidcorp/mission-engine": "0.0.0",
64
- "@voidcorp/void-machine": "0.0.0"
62
+ "@voidcorp/void-machine": "0.0.0",
63
+ "@voidcorp/harness-graph": "5.0.6",
64
+ "@voidcorp/hook-runner": "0.0.0"
65
65
  },
66
66
  "scripts": {
67
67
  "build": "tsup",