audit-tools 0.55.4 → 0.55.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.
Files changed (262) hide show
  1. package/dist/audit/cli/auditReviewBindings.d.ts +4 -4
  2. package/dist/audit/cli/auditStep.d.ts +15 -1
  3. package/dist/audit/cli/auditStep.d.ts.map +1 -1
  4. package/dist/audit/cli/auditStep.js +24 -11
  5. package/dist/audit/cli/auditStep.js.map +1 -1
  6. package/dist/audit/cli/charterExtractionPrompt.d.ts.map +1 -1
  7. package/dist/audit/cli/charterExtractionPrompt.js +3 -1
  8. package/dist/audit/cli/charterExtractionPrompt.js.map +1 -1
  9. package/dist/audit/cli/cleanup.d.ts.map +1 -1
  10. package/dist/audit/cli/cleanup.js +26 -2
  11. package/dist/audit/cli/cleanup.js.map +1 -1
  12. package/dist/audit/cli/conceptualDispatch.d.ts +2 -0
  13. package/dist/audit/cli/conceptualDispatch.d.ts.map +1 -1
  14. package/dist/audit/cli/conceptualDispatch.js +29 -8
  15. package/dist/audit/cli/conceptualDispatch.js.map +1 -1
  16. package/dist/audit/cli/dispatch/hostHandoff.d.ts +37 -3
  17. package/dist/audit/cli/dispatch/hostHandoff.d.ts.map +1 -1
  18. package/dist/audit/cli/dispatch/hostHandoff.js +185 -16
  19. package/dist/audit/cli/dispatch/hostHandoff.js.map +1 -1
  20. package/dist/audit/cli/dispatch/packetFilter.d.ts +1 -1
  21. package/dist/audit/cli/envelope.d.ts +1 -1
  22. package/dist/audit/cli/envelope.d.ts.map +1 -1
  23. package/dist/audit/cli/envelope.js +4 -1
  24. package/dist/audit/cli/envelope.js.map +1 -1
  25. package/dist/audit/cli/fanoutLanes.d.ts +30 -0
  26. package/dist/audit/cli/fanoutLanes.d.ts.map +1 -1
  27. package/dist/audit/cli/fanoutLanes.js +76 -8
  28. package/dist/audit/cli/fanoutLanes.js.map +1 -1
  29. package/dist/audit/cli/laneValidators.d.ts +9 -6
  30. package/dist/audit/cli/laneValidators.d.ts.map +1 -1
  31. package/dist/audit/cli/laneValidators.js +22 -7
  32. package/dist/audit/cli/laneValidators.js.map +1 -1
  33. package/dist/audit/cli/nextStepCommand.d.ts +3 -0
  34. package/dist/audit/cli/nextStepCommand.d.ts.map +1 -1
  35. package/dist/audit/cli/nextStepCommand.js +104 -66
  36. package/dist/audit/cli/nextStepCommand.js.map +1 -1
  37. package/dist/audit/cli/nextStepHelpers.d.ts +49 -14
  38. package/dist/audit/cli/nextStepHelpers.d.ts.map +1 -1
  39. package/dist/audit/cli/nextStepHelpers.js +169 -68
  40. package/dist/audit/cli/nextStepHelpers.js.map +1 -1
  41. package/dist/audit/cli/reviewRun.d.ts +10 -0
  42. package/dist/audit/cli/reviewRun.d.ts.map +1 -1
  43. package/dist/audit/cli/reviewRun.js +4 -3
  44. package/dist/audit/cli/reviewRun.js.map +1 -1
  45. package/dist/audit/cli/sampleRunCommand.d.ts +1 -1
  46. package/dist/audit/cli/semanticReviewStep.d.ts +2 -0
  47. package/dist/audit/cli/semanticReviewStep.d.ts.map +1 -1
  48. package/dist/audit/cli/semanticReviewStep.js +5 -50
  49. package/dist/audit/cli/semanticReviewStep.js.map +1 -1
  50. package/dist/audit/cli/validateResultsCommand.d.ts.map +1 -1
  51. package/dist/audit/cli/validateResultsCommand.js +3 -1
  52. package/dist/audit/cli/validateResultsCommand.js.map +1 -1
  53. package/dist/audit/contracts/workerSchemas.d.ts +14 -14
  54. package/dist/audit/extractors/fsIntake.d.ts +13 -0
  55. package/dist/audit/extractors/fsIntake.d.ts.map +1 -1
  56. package/dist/audit/extractors/fsIntake.js +11 -1
  57. package/dist/audit/extractors/fsIntake.js.map +1 -1
  58. package/dist/audit/io/artifacts.d.ts +35 -9
  59. package/dist/audit/io/artifacts.d.ts.map +1 -1
  60. package/dist/audit/io/artifacts.js +84 -67
  61. package/dist/audit/io/artifacts.js.map +1 -1
  62. package/dist/audit/io/rollover.d.ts +18 -0
  63. package/dist/audit/io/rollover.d.ts.map +1 -0
  64. package/dist/audit/io/rollover.js +152 -0
  65. package/dist/audit/io/rollover.js.map +1 -0
  66. package/dist/audit/io/runSnapshot.d.ts +149 -0
  67. package/dist/audit/io/runSnapshot.d.ts.map +1 -0
  68. package/dist/audit/io/runSnapshot.js +710 -0
  69. package/dist/audit/io/runSnapshot.js.map +1 -0
  70. package/dist/audit/orchestrator/advanceTypes.d.ts +11 -0
  71. package/dist/audit/orchestrator/advanceTypes.d.ts.map +1 -1
  72. package/dist/audit/orchestrator/autoFixExecutor.d.ts +9 -0
  73. package/dist/audit/orchestrator/autoFixExecutor.d.ts.map +1 -1
  74. package/dist/audit/orchestrator/autoFixExecutor.js +22 -2
  75. package/dist/audit/orchestrator/autoFixExecutor.js.map +1 -1
  76. package/dist/audit/orchestrator/charterExtractionExecutor.d.ts.map +1 -1
  77. package/dist/audit/orchestrator/charterExtractionExecutor.js +18 -3
  78. package/dist/audit/orchestrator/charterExtractionExecutor.js.map +1 -1
  79. package/dist/audit/orchestrator/charterPackets.d.ts +11 -1
  80. package/dist/audit/orchestrator/charterPackets.d.ts.map +1 -1
  81. package/dist/audit/orchestrator/charterPackets.js +13 -0
  82. package/dist/audit/orchestrator/charterPackets.js.map +1 -1
  83. package/dist/audit/orchestrator/dependencyMap.d.ts.map +1 -1
  84. package/dist/audit/orchestrator/dependencyMap.js +6 -0
  85. package/dist/audit/orchestrator/dependencyMap.js.map +1 -1
  86. package/dist/audit/orchestrator/dependencySlices.d.ts.map +1 -1
  87. package/dist/audit/orchestrator/dependencySlices.js +35 -11
  88. package/dist/audit/orchestrator/dependencySlices.js.map +1 -1
  89. package/dist/audit/orchestrator/designReviewPrompt.d.ts +9 -0
  90. package/dist/audit/orchestrator/designReviewPrompt.d.ts.map +1 -1
  91. package/dist/audit/orchestrator/designReviewPrompt.js +69 -31
  92. package/dist/audit/orchestrator/designReviewPrompt.js.map +1 -1
  93. package/dist/audit/orchestrator/designReviewSnapshot.d.ts +1 -0
  94. package/dist/audit/orchestrator/designReviewSnapshot.d.ts.map +1 -1
  95. package/dist/audit/orchestrator/designReviewSnapshot.js +2 -1
  96. package/dist/audit/orchestrator/designReviewSnapshot.js.map +1 -1
  97. package/dist/audit/orchestrator/executorRunners.d.ts.map +1 -1
  98. package/dist/audit/orchestrator/executorRunners.js +13 -4
  99. package/dist/audit/orchestrator/executorRunners.js.map +1 -1
  100. package/dist/audit/orchestrator/hostInputPause.js +1 -1
  101. package/dist/audit/orchestrator/hostInputPause.js.map +1 -1
  102. package/dist/audit/orchestrator/intakeExecutors.d.ts +8 -1
  103. package/dist/audit/orchestrator/intakeExecutors.d.ts.map +1 -1
  104. package/dist/audit/orchestrator/intakeExecutors.js +13 -4
  105. package/dist/audit/orchestrator/intakeExecutors.js.map +1 -1
  106. package/dist/audit/orchestrator/obligationPolicy.d.ts.map +1 -1
  107. package/dist/audit/orchestrator/obligationPolicy.js +4 -1
  108. package/dist/audit/orchestrator/obligationPolicy.js.map +1 -1
  109. package/dist/audit/orchestrator/planningExecutors.d.ts +1 -1
  110. package/dist/audit/orchestrator/planningExecutors.d.ts.map +1 -1
  111. package/dist/audit/orchestrator/planningExecutors.js +40 -6
  112. package/dist/audit/orchestrator/planningExecutors.js.map +1 -1
  113. package/dist/audit/orchestrator/requeueCommand.d.ts +1 -1
  114. package/dist/audit/orchestrator/systemicChallengeExecutor.d.ts.map +1 -1
  115. package/dist/audit/orchestrator/systemicChallengeExecutor.js +4 -1
  116. package/dist/audit/orchestrator/systemicChallengeExecutor.js.map +1 -1
  117. package/dist/audit/orchestrator/taskBuilder.d.ts +20 -0
  118. package/dist/audit/orchestrator/taskBuilder.d.ts.map +1 -1
  119. package/dist/audit/orchestrator/taskBuilder.js +80 -18
  120. package/dist/audit/orchestrator/taskBuilder.js.map +1 -1
  121. package/dist/audit/systemic/reviewFileMap.d.ts +72 -21
  122. package/dist/audit/systemic/reviewFileMap.d.ts.map +1 -1
  123. package/dist/audit/systemic/reviewFileMap.js +96 -45
  124. package/dist/audit/systemic/reviewFileMap.js.map +1 -1
  125. package/dist/audit/systemic/secondOrderAdversaryPrompt.d.ts +7 -0
  126. package/dist/audit/systemic/secondOrderAdversaryPrompt.d.ts.map +1 -1
  127. package/dist/audit/systemic/secondOrderAdversaryPrompt.js +39 -8
  128. package/dist/audit/systemic/secondOrderAdversaryPrompt.js.map +1 -1
  129. package/dist/audit/types.d.ts +12 -12
  130. package/dist/audit/validation/auditResults.d.ts +1 -0
  131. package/dist/audit/validation/auditResults.d.ts.map +1 -1
  132. package/dist/audit/validation/auditResults.js +1 -0
  133. package/dist/audit/validation/auditResults.js.map +1 -1
  134. package/dist/remediate/findingFilter.js +1 -1
  135. package/dist/remediate/findingFilter.js.map +1 -1
  136. package/dist/remediate/phases/close.d.ts +1 -0
  137. package/dist/remediate/phases/close.d.ts.map +1 -1
  138. package/dist/remediate/phases/close.js +29 -5
  139. package/dist/remediate/phases/close.js.map +1 -1
  140. package/dist/remediate/phases/closeVerifyAnalyzerLeads.d.ts +1 -1
  141. package/dist/remediate/phases/triage.d.ts +2 -1
  142. package/dist/remediate/phases/triage.d.ts.map +1 -1
  143. package/dist/remediate/phases/triage.js +46 -25
  144. package/dist/remediate/phases/triage.js.map +1 -1
  145. package/dist/remediate/phases/triageConformance.d.ts +14 -0
  146. package/dist/remediate/phases/triageConformance.d.ts.map +1 -0
  147. package/dist/remediate/phases/triageConformance.js +189 -0
  148. package/dist/remediate/phases/triageConformance.js.map +1 -0
  149. package/dist/remediate/state/decisionConsumption.d.ts +110 -0
  150. package/dist/remediate/state/decisionConsumption.d.ts.map +1 -0
  151. package/dist/remediate/state/decisionConsumption.js +122 -0
  152. package/dist/remediate/state/decisionConsumption.js.map +1 -0
  153. package/dist/remediate/state/store.d.ts +4 -0
  154. package/dist/remediate/state/store.d.ts.map +1 -1
  155. package/dist/remediate/state/store.js +16 -0
  156. package/dist/remediate/state/store.js.map +1 -1
  157. package/dist/remediate/steps/contractPipeline.d.ts +0 -6
  158. package/dist/remediate/steps/contractPipeline.d.ts.map +1 -1
  159. package/dist/remediate/steps/contractPipeline.js +3 -32
  160. package/dist/remediate/steps/contractPipeline.js.map +1 -1
  161. package/dist/remediate/steps/dispatch/contractConformanceReview.d.ts +9 -0
  162. package/dist/remediate/steps/dispatch/contractConformanceReview.d.ts.map +1 -1
  163. package/dist/remediate/steps/dispatch/contractConformanceReview.js +32 -14
  164. package/dist/remediate/steps/dispatch/contractConformanceReview.js.map +1 -1
  165. package/dist/remediate/steps/dispatch/hostContracts.js +1 -1
  166. package/dist/remediate/steps/dispatch/hostContracts.js.map +1 -1
  167. package/dist/remediate/steps/dispatch/hostHandoff.d.ts.map +1 -1
  168. package/dist/remediate/steps/dispatch/hostHandoff.js +6 -2
  169. package/dist/remediate/steps/dispatch/hostHandoff.js.map +1 -1
  170. package/dist/remediate/steps/dispatch/requiredTests.js +1 -1
  171. package/dist/remediate/steps/dispatch/requiredTests.js.map +1 -1
  172. package/dist/remediate/steps/gateCommands.d.ts +1 -0
  173. package/dist/remediate/steps/gateCommands.d.ts.map +1 -1
  174. package/dist/remediate/steps/gateCommands.js +8 -52
  175. package/dist/remediate/steps/gateCommands.js.map +1 -1
  176. package/dist/remediate/steps/nextStep.d.ts +7 -0
  177. package/dist/remediate/steps/nextStep.d.ts.map +1 -1
  178. package/dist/remediate/steps/nextStep.js +69 -75
  179. package/dist/remediate/steps/nextStep.js.map +1 -1
  180. package/dist/remediate/steps/prompts.d.ts.map +1 -1
  181. package/dist/remediate/steps/prompts.js +6 -9
  182. package/dist/remediate/steps/prompts.js.map +1 -1
  183. package/dist/remediate/steps/recoverIngest.js +1 -1
  184. package/dist/remediate/steps/recoverIngest.js.map +1 -1
  185. package/dist/shared/analyzers/provenance.d.ts +1 -1
  186. package/dist/shared/analyzers/provenance.js +1 -1
  187. package/dist/shared/contentKey.d.ts +3 -2
  188. package/dist/shared/contentKey.d.ts.map +1 -1
  189. package/dist/shared/contentKey.js +3 -2
  190. package/dist/shared/contentKey.js.map +1 -1
  191. package/dist/shared/decompose/charterExtraction.d.ts +200 -13
  192. package/dist/shared/decompose/charterExtraction.d.ts.map +1 -1
  193. package/dist/shared/decompose/charterExtraction.js +34 -6
  194. package/dist/shared/decompose/charterExtraction.js.map +1 -1
  195. package/dist/shared/decompose/systemicChallenge.js +1 -1
  196. package/dist/shared/decompose/systemicChallenge.js.map +1 -1
  197. package/dist/shared/engine/obligationEngine.d.ts +1 -1
  198. package/dist/shared/engine/obligationEngine.js +1 -1
  199. package/dist/shared/findings/dedupe.js +1 -1
  200. package/dist/shared/findings/dedupe.js.map +1 -1
  201. package/dist/shared/git.d.ts +30 -1
  202. package/dist/shared/git.d.ts.map +1 -1
  203. package/dist/shared/git.js +65 -6
  204. package/dist/shared/git.js.map +1 -1
  205. package/dist/shared/index.d.ts +1 -0
  206. package/dist/shared/index.d.ts.map +1 -1
  207. package/dist/shared/index.js +1 -0
  208. package/dist/shared/index.js.map +1 -1
  209. package/dist/shared/intent/clauseBoundaries.d.ts +1 -0
  210. package/dist/shared/intent/clauseBoundaries.d.ts.map +1 -1
  211. package/dist/shared/intent/clauseBoundaries.js +1 -0
  212. package/dist/shared/intent/clauseBoundaries.js.map +1 -1
  213. package/dist/shared/intent/pathScope.d.ts +1 -0
  214. package/dist/shared/intent/pathScope.d.ts.map +1 -1
  215. package/dist/shared/intent/pathScope.js +1 -0
  216. package/dist/shared/intent/pathScope.js.map +1 -1
  217. package/dist/shared/io/nodeWorktreeGuard.d.ts +1 -1
  218. package/dist/shared/io/nodeWorktreeGuard.js +1 -1
  219. package/dist/shared/io/pathIdentity.d.ts.map +1 -1
  220. package/dist/shared/io/pathIdentity.js +1 -0
  221. package/dist/shared/io/pathIdentity.js.map +1 -1
  222. package/dist/shared/io/stepContractWriter.d.ts +1 -0
  223. package/dist/shared/io/stepContractWriter.d.ts.map +1 -1
  224. package/dist/shared/io/stepContractWriter.js +1 -0
  225. package/dist/shared/io/stepContractWriter.js.map +1 -1
  226. package/dist/shared/loopCorePaths.d.ts.map +1 -1
  227. package/dist/shared/loopCorePaths.js +11 -0
  228. package/dist/shared/loopCorePaths.js.map +1 -1
  229. package/dist/shared/prompts.d.ts +2 -0
  230. package/dist/shared/prompts.d.ts.map +1 -1
  231. package/dist/shared/prompts.js +8 -2
  232. package/dist/shared/prompts.js.map +1 -1
  233. package/dist/shared/submission/ingestReport.d.ts.map +1 -1
  234. package/dist/shared/submission/ingestReport.js +19 -1
  235. package/dist/shared/submission/ingestReport.js.map +1 -1
  236. package/dist/shared/submission/submissionLedger.js +2 -2
  237. package/dist/shared/submission/submissionLedger.js.map +1 -1
  238. package/dist/shared/submission/submissionScan.js +1 -1
  239. package/dist/shared/submission/submissionScan.js.map +1 -1
  240. package/dist/shared/tooling/exec.js +2 -2
  241. package/dist/shared/tooling/exec.js.map +1 -1
  242. package/dist/shared/types/charter.d.ts +9 -0
  243. package/dist/shared/types/charter.d.ts.map +1 -1
  244. package/dist/shared/types/charter.js +7 -0
  245. package/dist/shared/types/charter.js.map +1 -1
  246. package/dist/shared/types/closingActions.js +1 -1
  247. package/dist/shared/types/closingActions.js.map +1 -1
  248. package/dist/shared/types/finding.js +2 -2
  249. package/dist/shared/types/finding.js.map +1 -1
  250. package/dist/shared/types/stepContract.js +1 -1
  251. package/dist/shared/types/stepContract.js.map +1 -1
  252. package/dist/shared/validation/findingGrounding.d.ts +1 -0
  253. package/dist/shared/validation/findingGrounding.d.ts.map +1 -1
  254. package/dist/shared/validation/findingGrounding.js +1 -0
  255. package/dist/shared/validation/findingGrounding.js.map +1 -1
  256. package/dist/shared/validation/producerBoundary.d.ts +10 -0
  257. package/dist/shared/validation/producerBoundary.d.ts.map +1 -0
  258. package/dist/shared/validation/producerBoundary.js +26 -0
  259. package/dist/shared/validation/producerBoundary.js.map +1 -0
  260. package/docs/audit-pkg/contracts.md +6 -2
  261. package/docs/audit-pkg/operator-guide.md +13 -4
  262. package/package.json +5 -1
@@ -1,4 +1,6 @@
1
+ import { readDecisionSnapshot, decisionContextDigest } from "../state/decisionConsumption.js";
1
2
  import { RemediationPlanAuthorityError } from "../contractPipeline/runtimePlanAuthority.js";
3
+ import { reopenStaleTriageSuccesses } from "../phases/triageConformance.js";
2
4
  import { stateRunId, requireStateRunId, currentHostBoundaryState } from "../state/runIdentity.js";
3
5
  import { presentReportStep } from "./frictionCloseout.js";
4
6
  import { reviewFilterDispositionsPath, persistReviewFilterDispositions } from "../review/filterDispositions.js";
@@ -34,7 +36,7 @@ import { applyIntentOrdering } from "../intent/intentOrdering.js";
34
36
  import { resolveIntakeStep } from "./intakeResolver.js";
35
37
  import { RUNTIME_RESIDUAL_DECLARATION, finalGateDisabledReason, finalGateRecordPath, readFinalGateVerdict, runToolOwnedFinalGate, writeFinalGateRedRecord, recordFinalGateOutcome, writeFinalGateVerdict, } from "./finalGate.js";
36
38
  import { renderGateAttribution, worktreeContentId, finalGateBinding, } from "./gateCommands.js";
37
- import { buildNextContractPipelineStep, readApprovedExecutionPlan, writePathASeedFromFindings, normalizeBlockTouchedFiles, checkWriteScopePathsAgainstTrackedTree, } from "./contractPipeline.js";
39
+ import { buildNextContractPipelineStep, readApprovedExecutionPlan, writePathASeedFromFindings, } from "./contractPipeline.js";
38
40
  import { compareCodeUnits } from "../../shared/compareCodeUnits.js";
39
41
  import { executionPlanPaths, readCanonicalPlan, readPlanSource, readPlanReviewHistory } from "../contractPipeline/executionPlan.js";
40
42
  import { buildReviewRequest, applyReviewResolution, parseReviewResolution, REVIEW_REQUEST_SCHEMA_VERSION, } from "../review/reviewGate.js";
@@ -472,6 +474,15 @@ function remediationIngestReportLines(ingested) {
472
474
  .sort(compareCodeUnits),
473
475
  });
474
476
  }
477
+ /**
478
+ * Whether the ingest report names a result file the host must write: only the
479
+ * repair and landed-without-result sections do. Pending results render as a
480
+ * count, listed in the workload, so a report holding only those names no file.
481
+ */
482
+ export function reportNamesResultFiles(ingested) {
483
+ return (ingested.issues.some((issue) => remediationIssueRemedy(issue) === "repair") ||
484
+ [...ingested.work_item_outcomes].some(([, outcome]) => outcome === "missing_result_with_commit"));
485
+ }
475
486
  async function buildImplementDispatchStep(ctx) {
476
487
  const { root, artifactsDir, state, store, runLogger } = ctx;
477
488
  const runId = requireStateRunId(state);
@@ -618,7 +629,7 @@ async function buildImplementDispatchStep(ctx) {
618
629
  "The file lists every work item that is ready now. Give each item's prompt to a worker. You choose the order, the groups, and how many run at the same time.",
619
630
  "",
620
631
  ...report,
621
- ...(report.length > 0 ? ["Change only the result files named above.", ""] : []),
632
+ ...(reportNamesResultFiles(ingested) ? ["Change only the result files named above.", ""] : []),
622
633
  "Do not edit the workload file.",
623
634
  "",
624
635
  "When each item has its commit on HEAD and its result file, run:",
@@ -720,7 +731,10 @@ async function activateApprovedPlan(root, artifactsDir, existing, _input, _runLo
720
731
  if (!existing.run_start_dirty)
721
732
  existing = { ...existing, run_start_dirty: [...await stagedAndUntracked(root)].sort() };
722
733
  const filter = await readOptionalJsonFile(reviewFilterDispositionsPath(artifactsDir));
723
- const decision = await readOptionalJsonFile(reviewDecisionPath(artifactsDir));
734
+ // Through the schema-checked reader: with zero survivors the review gate never
735
+ // rewrites a stale-schema record, so a raw read let its declines into the
736
+ // ledger as declined-by-review although the current run never decided them.
737
+ const decision = await readReviewDecision(artifactsDir);
724
738
  const coverage = buildCoverageLedger({
725
739
  planId: plan.plan_id, sourceFindings: filter?.originals ?? source.findings,
726
740
  droppedNoEvidence: filter?.droppedNoEvidence ?? [], droppedByCheckpoint: filter?.droppedByCheckpoint ?? [],
@@ -1276,22 +1290,16 @@ const PLAN_CLARIFICATION_ACTIONS = ["clarified", "reject_finding", "defer"];
1276
1290
  * - `rationale` is REQUIRED and non-empty on `clarified`: it becomes the item's
1277
1291
  * `clarification_context`, the answer the next worker reads. A `clarified`
1278
1292
  * entry without it re-opened the item with no answer attached.
1279
- * - `scope_additions` lists the files the answer requires the fix to touch. Each
1280
- * must already be in the owning unit's reviewed `allowed_files`; a path outside
1281
- * it is refused, because new write scope needs a revised, freshly reviewed plan.
1282
- * Accepted entries never rewrite the plan, so its approved revision holds. It is allowed
1283
- * ONLY on `clarified`, the one action that re-opens the item; on
1284
- * `reject_finding` or `defer` it used to be ignored without a word. Each path
1285
- * is validated whole-file fail-closed BEFORE anything is applied
1286
- * (`validateClarificationScopeAdditions`), so the host never edits the plan
1287
- * by hand.
1293
+ * - There is no write-scope field: new write scope needs a revised, freshly
1294
+ * reviewed plan, so an answer cannot widen a unit (owner decision 2026-10-01;
1295
+ * the ignored `scope_additions` field was removed in two releases, and is now
1296
+ * refused as an unknown field).
1288
1297
  */
1289
1298
  const PlanClarificationResolutionSchema = z
1290
1299
  .object({
1291
1300
  unit_id: z.string().min(1, "must be a non-empty finding id"),
1292
1301
  action: z.enum(PLAN_CLARIFICATION_ACTIONS),
1293
1302
  rationale: z.string().optional(),
1294
- scope_additions: z.array(z.string()).optional(),
1295
1303
  })
1296
1304
  .strict()
1297
1305
  .superRefine((entry, ctx) => {
@@ -1302,13 +1310,6 @@ const PlanClarificationResolutionSchema = z
1302
1310
  message: 'is required and must be non-empty when action is "clarified" — it carries the answer to the next worker',
1303
1311
  });
1304
1312
  }
1305
- if (entry.action !== "clarified" && entry.scope_additions !== undefined) {
1306
- ctx.addIssue({
1307
- code: z.ZodIssueCode.custom,
1308
- path: ["scope_additions"],
1309
- message: `is allowed only with action "clarified" — a ${entry.action} entry widens no write scope`,
1310
- });
1311
- }
1312
1313
  });
1313
1314
  /**
1314
1315
  * Read and validate the mid-run `clarification_resolution.json` file.
@@ -1321,10 +1322,10 @@ const PlanClarificationResolutionSchema = z
1321
1322
  * user's answer, and the item then waited forever for a question the round no
1322
1323
  * longer showed.
1323
1324
  */
1324
- async function readPlanClarificationResolutions(path) {
1325
+ async function readPlanClarificationResolutions(path, snapshotBytes) {
1325
1326
  let value;
1326
1327
  try {
1327
- value = JSON.parse(await readFile(path, "utf8"));
1328
+ value = JSON.parse(snapshotBytes ?? await readFile(path, "utf8"));
1328
1329
  }
1329
1330
  catch (error) {
1330
1331
  return {
@@ -1365,10 +1366,9 @@ async function readPlanClarificationResolutions(path) {
1365
1366
  }
1366
1367
  /**
1367
1368
  * Every whole-file refusal of a parsed resolution, in check order: the parse,
1368
- * then the closed id set, then the scope additions. Null means the file may be
1369
- * applied.
1369
+ * then the closed id set. Null means the file may be applied.
1370
1370
  */
1371
- async function planClarificationRefusal(root, state, parsed, validIds, outsideSetLabel) {
1371
+ function planClarificationRefusal(parsed, validIds, outsideSetLabel) {
1372
1372
  if (!parsed.ok)
1373
1373
  return parsed.reason;
1374
1374
  const unknownIds = parsed.resolutions
@@ -1377,40 +1377,8 @@ async function planClarificationRefusal(root, state, parsed, validIds, outsideSe
1377
1377
  if (unknownIds.length > 0) {
1378
1378
  return `finding id(s) ${outsideSetLabel}: ${unknownIds.map((i) => `\`${i}\``).join(", ")}`;
1379
1379
  }
1380
- const scopeRefusals = await validateClarificationScopeAdditions(root, state, parsed.resolutions);
1381
- if (scopeRefusals.length > 0) {
1382
- return `invalid scope_additions — fix and re-submit: ${scopeRefusals.join(" | ")}`;
1383
- }
1384
1380
  return null;
1385
1381
  }
1386
- /**
1387
- * Validate every `clarified` resolution's `scope_additions` BEFORE anything is
1388
- * applied: each entry must normalize beneath the repository root against the
1389
- * finding's owning block (the one write-scope normalizer), must clear the SAME
1390
- * tracked-tree rule the promotion gate enforced (the delta lane must not
1391
- * bypass it), and a finding with no owning block has no scope to widen. ANY
1392
- * refusal refuses the WHOLE resolution file — the uniform id-join contract's
1393
- * shape: a half-applied decision record is worse than a re-ask.
1394
- */
1395
- async function validateClarificationScopeAdditions(root, state, resolutions) {
1396
- const refusals = [];
1397
- for (const res of resolutions) {
1398
- if (res.action !== "clarified" || !res.scope_additions?.length)
1399
- continue;
1400
- const block = state.plan?.units?.find((b) => b.id === res.unit_id);
1401
- if (!block) {
1402
- refusals.push(`scope_additions for \`${res.unit_id}\`: no plan block owns this finding, so ` +
1403
- `there is no write scope to widen.`);
1404
- continue;
1405
- }
1406
- const normalized = normalizeBlockTouchedFiles(root, res.scope_additions, block.id);
1407
- if (normalized.touched_files.some(path => !block.allowed_files.includes(path)))
1408
- refusals.push(`Unit ${block.id} needs a revised executable plan and fresh review before its write scope can expand.`);
1409
- refusals.push(...normalized.refusals);
1410
- refusals.push(...(await checkWriteScopePathsAgainstTrackedTree(root, normalized.touched_files, `scope_additions for \`${res.unit_id}\` (block "${block.id}")`)));
1411
- }
1412
- return refusals;
1413
- }
1414
1382
  /**
1415
1383
  * Apply one mid-run clarification resolution to its execution unit:
1416
1384
  * `clarified` re-opens it (pending) with the answer as context,
@@ -1449,14 +1417,15 @@ async function applyPlanClarificationResolution(root, artifactsDir, state, store
1449
1417
  if (!state.plan || !state.items)
1450
1418
  return { kind: "applied", state };
1451
1419
  const resolutionPath = join(artifactsDir, "clarification_resolution.json");
1452
- const parsed = await readPlanClarificationResolutions(resolutionPath);
1453
- // Uniform whole-file fail-closed contract: a malformed entry, an id outside
1454
- // the paused set, or an invalid scope addition refuses the WHOLE resolution
1455
- // (archived, nothing applied) and re-halts with the reason named. The
1456
- // silent-continue alternative drops the user's answer and leaves its item
1457
- // waiting on a question nobody is asked, so a decision record never
1458
- // half-applies (open-bugs.md:110).
1459
- const refusal = await planClarificationRefusal(root, state, parsed, new Set(pausedClarifications(state).map((q) => q.unit_id)), "not waiting for a clarification");
1420
+ const inputSnapshot = await readDecisionSnapshot(resolutionPath);
1421
+ const contextDigest = decisionContextDigest(state);
1422
+ const parsed = await readPlanClarificationResolutions(resolutionPath, inputSnapshot?.bytes);
1423
+ // Uniform whole-file fail-closed contract: a malformed entry or an id outside
1424
+ // the paused set refuses the WHOLE resolution (archived, nothing applied) and
1425
+ // re-halts with the reason named. The silent-continue alternative drops the
1426
+ // user's answer and leaves its item waiting on a question nobody is asked, so
1427
+ // a decision record never half-applies.
1428
+ const refusal = planClarificationRefusal(parsed, new Set(pausedClarifications(state).map((q) => q.unit_id)), "not waiting for a clarification");
1460
1429
  if (refusal !== null || !parsed.ok) {
1461
1430
  await withFsRetry(() => rename(resolutionPath, `${resolutionPath}.refused-${Date.now()}`));
1462
1431
  return {
@@ -1471,9 +1440,8 @@ async function applyPlanClarificationResolution(root, artifactsDir, state, store
1471
1440
  const item = state.items[res.unit_id];
1472
1441
  if (!item || isTerminalStatus(item.status))
1473
1442
  continue;
1474
- // Validation admits only paths already in the reviewed unit's allowed_files, so an
1475
- // accepted scope_additions entry changes nothing: the reviewed plan is never rewritten
1476
- // here, and its revision (which hashes author order) stays the approved one.
1443
+ // The reviewed plan is never rewritten here, and its revision (which hashes
1444
+ // author order) stays the approved one.
1477
1445
  applyClarificationActionToItem(item, res, now);
1478
1446
  appliedCount += 1;
1479
1447
  }
@@ -1484,9 +1452,11 @@ async function applyPlanClarificationResolution(root, artifactsDir, state, store
1484
1452
  // outright). Mirrors the saveStateForPlan strip.
1485
1453
  if (appliedCount > 0)
1486
1454
  delete state.host_handoff;
1487
- if (existsSync(resolutionPath)) {
1488
- await withFsRetry(() => rename(resolutionPath, `${resolutionPath}.consumed-${Date.now()}`));
1489
- }
1455
+ if (!inputSnapshot)
1456
+ throw new Error("Clarification input changed during application; retry without accepting it.");
1457
+ (state.decision_applications ??= []).push({ input: "clarification_resolution.json", input_sha256: inputSnapshot.sha256, input_identity_sha256: inputSnapshot.identity_sha256,
1458
+ context_sha256: contextDigest, run_id: state.plan.plan_id, revision_sha256: state.plan.review_revision_sha256,
1459
+ applied_at: now, retry_results: [] });
1490
1460
  const remainingPending = state.plan.units.some((f) => state.items?.[f.id]?.status === "pending");
1491
1461
  // Undecided-remainder guard, mirroring triage's still-blocked guard: a
1492
1462
  // resolution covering only SOME of the paused items must not fall through to
@@ -1595,12 +1565,24 @@ async function handlePlanning(root, artifactsDir, state, store) {
1595
1565
  async function handleImplementing(root, artifactsDir, state, runLogger, store) {
1596
1566
  const triageStart = Date.now();
1597
1567
  runLogger.event({ phase: "next-step", kind: "executor_start", obligation: state.status, note: "triage" });
1598
- const triaged = await runTriagePhase(state, { root, artifactsDir });
1568
+ // sites-pinned: tests/remediate/host-handoff-corroboration-obligations.test.ts
1569
+ const conformanceIssues = [];
1570
+ const triaged = await runTriagePhase(state, { root, artifactsDir }, issue => conformanceIssues.push(issue));
1599
1571
  runLogger.event({ phase: "next-step", kind: "executor_end", obligation: state.status, note: "triage", duration_ms: Date.now() - triageStart });
1572
+ if (conformanceIssues.length) {
1573
+ await store.saveState(triaged);
1574
+ return { kind: "emit", step: await writeCurrentStep({
1575
+ stepKind: "review_contract_conformance", status: conformanceIssues.some(issue => issue.code === "conformance_review_unavailable") ? "blocked" : "ready",
1576
+ runId: stateRunId(triaged), repoRoot: root, artifactsDir,
1577
+ prompt: ["Independent contract conformance review is required before triage can accept these tool-owned working-tree observations.", ...conformanceIssues.map(issue => `${issue.work_item_id}: ${issue.message}\nRequest: ${issue.review_request_path}`), "Read each request in an independent context and write its bound response. Self-review cannot satisfy this requirement. Resume next-step after the responses are ready."].join("\n\n"),
1578
+ stopCondition: "Obtain independent conformance review or remain paused.",
1579
+ allowedCommands: [loaderCommand("next-step")],
1580
+ }) };
1581
+ }
1600
1582
  // Triage may enter closing directly: the close itself owns mandatory final
1601
1583
  // acceptance, so a status transition cannot bypass it.
1602
1584
  if (triaged.status === "closing")
1603
- return handleAllTerminalTransition(triaged, store);
1585
+ return handleAllTerminalTransition(triaged, store, root, artifactsDir);
1604
1586
  await store.saveState(triaged);
1605
1587
  return { kind: "transition", state: triaged };
1606
1588
  }
@@ -1889,7 +1871,12 @@ async function runPhaseBoundaryGate(ctx) {
1889
1871
  runLogger,
1890
1872
  });
1891
1873
  }
1892
- async function handleAllTerminalTransition(state, store) {
1874
+ async function handleAllTerminalTransition(state, store, root, artifactsDir) {
1875
+ // sites-pinned: tests/remediate/host-handoff-corroboration-obligations.test.ts
1876
+ if (await reopenStaleTriageSuccesses(root, artifactsDir, state)) {
1877
+ await store.saveState(state);
1878
+ return { kind: "transition", state };
1879
+ }
1893
1880
  // Final acceptance belongs to the actual close execution, after preview
1894
1881
  // pauses. A transition carries no executable verification verdict.
1895
1882
  state.status = "closing";
@@ -1897,6 +1884,11 @@ async function handleAllTerminalTransition(state, store) {
1897
1884
  return { kind: "transition", state };
1898
1885
  }
1899
1886
  async function handleClosing(root, artifactsDir, state, runLogger, store, options) {
1887
+ // sites-pinned: tests/remediate/host-handoff-corroboration-obligations.test.ts
1888
+ if (await reopenStaleTriageSuccesses(root, artifactsDir, state)) {
1889
+ await store.saveState(state);
1890
+ return { kind: "transition", state };
1891
+ }
1900
1892
  const closeStart = Date.now();
1901
1893
  runLogger.event({ phase: "next-step", kind: "executor_start", obligation: state.status, note: "close" });
1902
1894
  // Target verification and closing are product obligations. Development
@@ -2885,7 +2877,7 @@ export function buildMainObligations(ctx) {
2885
2877
  derive: (state) => state != null && allItemsTerminal(state) && state.status !== "closing"
2886
2878
  ? "missing"
2887
2879
  : "satisfied",
2888
- execute: async (state) => handleAllTerminalTransition(requireState(state), store),
2880
+ execute: async (state) => handleAllTerminalTransition(requireState(state), store, root, artifactsDir),
2889
2881
  },
2890
2882
  {
2891
2883
  id: "closing",
@@ -2955,6 +2947,8 @@ async function advanceUnderPhaseLock(deps) {
2955
2947
  // Loaded FRESH under the mutex: a peer may have advanced and persisted state
2956
2948
  // between the entry read and this process winning the lock.
2957
2949
  let state = await store.loadState();
2950
+ if (state)
2951
+ await store.reconcileDecisionApplications(state);
2958
2952
  let control = await store.loadOperatorLifecycle();
2959
2953
  if (control && control.mode !== "active") {
2960
2954
  return buildOperatorControlStep(root, artifactsDir, state, control);