audit-tools 0.52.5 → 0.54.0

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 (384) hide show
  1. package/dist/audit/cli/auditStep.d.ts +1 -1
  2. package/dist/audit/cli/conceptualDispatch.d.ts +2 -2
  3. package/dist/audit/cli/conceptualDispatch.d.ts.map +1 -1
  4. package/dist/audit/cli/conceptualDispatch.js +6 -5
  5. package/dist/audit/cli/conceptualDispatch.js.map +1 -1
  6. package/dist/audit/cli/confirmIntentStep.d.ts.map +1 -1
  7. package/dist/audit/cli/confirmIntentStep.js +3 -16
  8. package/dist/audit/cli/confirmIntentStep.js.map +1 -1
  9. package/dist/audit/cli/dispatch/hostHandoff.d.ts +2 -0
  10. package/dist/audit/cli/dispatch/hostHandoff.d.ts.map +1 -1
  11. package/dist/audit/cli/dispatch/hostHandoff.js +22 -1
  12. package/dist/audit/cli/dispatch/hostHandoff.js.map +1 -1
  13. package/dist/audit/cli/dispatch/packetFilter.d.ts +1 -1
  14. package/dist/audit/cli/dispatch/packetFilter.d.ts.map +1 -1
  15. package/dist/audit/cli/dispatch/packetFilter.js +1 -1
  16. package/dist/audit/cli/dispatch/packetFilter.js.map +1 -1
  17. package/dist/audit/cli/functionalPreflight.d.ts.map +1 -1
  18. package/dist/audit/cli/functionalPreflight.js +6 -3
  19. package/dist/audit/cli/functionalPreflight.js.map +1 -1
  20. package/dist/audit/cli/nextStepCommand.d.ts.map +1 -1
  21. package/dist/audit/cli/nextStepCommand.js +57 -34
  22. package/dist/audit/cli/nextStepCommand.js.map +1 -1
  23. package/dist/audit/cli/nextStepHelpers.d.ts +3 -3
  24. package/dist/audit/cli/nextStepHelpers.d.ts.map +1 -1
  25. package/dist/audit/cli/nextStepHelpers.js +52 -77
  26. package/dist/audit/cli/nextStepHelpers.js.map +1 -1
  27. package/dist/audit/cli/prompts.d.ts +1 -2
  28. package/dist/audit/cli/prompts.d.ts.map +1 -1
  29. package/dist/audit/cli/prompts.js +2 -12
  30. package/dist/audit/cli/prompts.js.map +1 -1
  31. package/dist/audit/cli/sampleRunCommand.d.ts +2 -2
  32. package/dist/audit/cli/semanticReviewStep.d.ts +11 -2
  33. package/dist/audit/cli/semanticReviewStep.d.ts.map +1 -1
  34. package/dist/audit/cli/semanticReviewStep.js +14 -10
  35. package/dist/audit/cli/semanticReviewStep.js.map +1 -1
  36. package/dist/audit/contracts/workerSchemas.d.ts +14 -14
  37. package/dist/audit/contracts/wrapperResponse.d.ts +6 -6
  38. package/dist/audit/extractors/graphRoutes.d.ts.map +1 -1
  39. package/dist/audit/extractors/graphRoutes.js +1 -3
  40. package/dist/audit/extractors/graphRoutes.js.map +1 -1
  41. package/dist/audit/io/artifacts.d.ts.map +1 -1
  42. package/dist/audit/io/artifacts.js +2 -3
  43. package/dist/audit/io/artifacts.js.map +1 -1
  44. package/dist/audit/orchestrator/advance.d.ts.map +1 -1
  45. package/dist/audit/orchestrator/advance.js +21 -1
  46. package/dist/audit/orchestrator/advance.js.map +1 -1
  47. package/dist/audit/orchestrator/architectureDiscovery.d.ts +25 -0
  48. package/dist/audit/orchestrator/architectureDiscovery.d.ts.map +1 -0
  49. package/dist/audit/orchestrator/architectureDiscovery.js +193 -0
  50. package/dist/audit/orchestrator/architectureDiscovery.js.map +1 -0
  51. package/dist/audit/orchestrator/dependencyMap.d.ts +20 -0
  52. package/dist/audit/orchestrator/dependencyMap.d.ts.map +1 -1
  53. package/dist/audit/orchestrator/dependencyMap.js +47 -0
  54. package/dist/audit/orchestrator/dependencyMap.js.map +1 -1
  55. package/dist/audit/orchestrator/dependencySlices.d.ts.map +1 -1
  56. package/dist/audit/orchestrator/dependencySlices.js +37 -3
  57. package/dist/audit/orchestrator/dependencySlices.js.map +1 -1
  58. package/dist/audit/orchestrator/designReviewProjection.d.ts +2 -6
  59. package/dist/audit/orchestrator/designReviewProjection.d.ts.map +1 -1
  60. package/dist/audit/orchestrator/designReviewProjection.js +2 -6
  61. package/dist/audit/orchestrator/designReviewProjection.js.map +1 -1
  62. package/dist/audit/orchestrator/designReviewPrompt.d.ts +2 -2
  63. package/dist/audit/orchestrator/designReviewPrompt.js +2 -2
  64. package/dist/audit/orchestrator/designReviewSnapshot.d.ts +2 -3
  65. package/dist/audit/orchestrator/designReviewSnapshot.d.ts.map +1 -1
  66. package/dist/audit/orchestrator/designReviewSnapshot.js +2 -3
  67. package/dist/audit/orchestrator/designReviewSnapshot.js.map +1 -1
  68. package/dist/audit/orchestrator/executorRunners.d.ts.map +1 -1
  69. package/dist/audit/orchestrator/executorRunners.js +1 -22
  70. package/dist/audit/orchestrator/executorRunners.js.map +1 -1
  71. package/dist/audit/orchestrator/executors.d.ts.map +1 -1
  72. package/dist/audit/orchestrator/executors.js +0 -12
  73. package/dist/audit/orchestrator/executors.js.map +1 -1
  74. package/dist/audit/orchestrator/ingestionExecutors.d.ts.map +1 -1
  75. package/dist/audit/orchestrator/ingestionExecutors.js +38 -37
  76. package/dist/audit/orchestrator/ingestionExecutors.js.map +1 -1
  77. package/dist/audit/orchestrator/nextStep.d.ts +2 -28
  78. package/dist/audit/orchestrator/nextStep.d.ts.map +1 -1
  79. package/dist/audit/orchestrator/nextStep.js +4 -37
  80. package/dist/audit/orchestrator/nextStep.js.map +1 -1
  81. package/dist/audit/orchestrator/obligationDerive.d.ts +2 -3
  82. package/dist/audit/orchestrator/obligationDerive.d.ts.map +1 -1
  83. package/dist/audit/orchestrator/obligationDerive.js +2 -3
  84. package/dist/audit/orchestrator/obligationDerive.js.map +1 -1
  85. package/dist/audit/orchestrator/pendingTasks.d.ts +2 -0
  86. package/dist/audit/orchestrator/pendingTasks.d.ts.map +1 -1
  87. package/dist/audit/orchestrator/pendingTasks.js +5 -1
  88. package/dist/audit/orchestrator/pendingTasks.js.map +1 -1
  89. package/dist/audit/orchestrator/planningExecutors.d.ts.map +1 -1
  90. package/dist/audit/orchestrator/planningExecutors.js +9 -0
  91. package/dist/audit/orchestrator/planningExecutors.js.map +1 -1
  92. package/dist/audit/orchestrator/requeueCommand.d.ts +1 -1
  93. package/dist/audit/orchestrator/resultBaseline.d.ts +5 -5
  94. package/dist/audit/orchestrator/resultBaseline.d.ts.map +1 -1
  95. package/dist/audit/orchestrator/resultBaseline.js +24 -47
  96. package/dist/audit/orchestrator/resultBaseline.js.map +1 -1
  97. package/dist/audit/orchestrator/selectiveDeepening/findingFollowup.d.ts.map +1 -1
  98. package/dist/audit/orchestrator/selectiveDeepening/findingFollowup.js +2 -0
  99. package/dist/audit/orchestrator/selectiveDeepening/findingFollowup.js.map +1 -1
  100. package/dist/audit/orchestrator/staleness.d.ts.map +1 -1
  101. package/dist/audit/orchestrator/staleness.js +14 -10
  102. package/dist/audit/orchestrator/staleness.js.map +1 -1
  103. package/dist/audit/orchestrator/state.js +1 -1
  104. package/dist/audit/orchestrator/state.js.map +1 -1
  105. package/dist/audit/orchestrator/synthesisExecutors.d.ts.map +1 -1
  106. package/dist/audit/orchestrator/synthesisExecutors.js +3 -0
  107. package/dist/audit/orchestrator/synthesisExecutors.js.map +1 -1
  108. package/dist/audit/types/auditState.d.ts +6 -6
  109. package/dist/audit/types/flowCoverage.d.ts +6 -6
  110. package/dist/audit/types.d.ts +24 -13
  111. package/dist/audit/types.d.ts.map +1 -1
  112. package/dist/audit/types.js +92 -11
  113. package/dist/audit/types.js.map +1 -1
  114. package/dist/remediate/contractPipeline/executionPlan.d.ts +1908 -0
  115. package/dist/remediate/contractPipeline/executionPlan.d.ts.map +1 -0
  116. package/dist/remediate/contractPipeline/executionPlan.js +467 -0
  117. package/dist/remediate/contractPipeline/executionPlan.js.map +1 -0
  118. package/dist/remediate/contractPipeline/runtimePlanAuthority.d.ts +12 -0
  119. package/dist/remediate/contractPipeline/runtimePlanAuthority.d.ts.map +1 -0
  120. package/dist/remediate/contractPipeline/runtimePlanAuthority.js +41 -0
  121. package/dist/remediate/contractPipeline/runtimePlanAuthority.js.map +1 -0
  122. package/dist/remediate/dedup/crossLensDedup.d.ts +3 -16
  123. package/dist/remediate/dedup/crossLensDedup.d.ts.map +1 -1
  124. package/dist/remediate/dedup/crossLensDedup.js +2 -32
  125. package/dist/remediate/dedup/crossLensDedup.js.map +1 -1
  126. package/dist/remediate/index.d.ts +2 -19
  127. package/dist/remediate/index.d.ts.map +1 -1
  128. package/dist/remediate/index.js +22 -138
  129. package/dist/remediate/index.js.map +1 -1
  130. package/dist/remediate/intake.d.ts +10 -10
  131. package/dist/remediate/intent/intentOrdering.d.ts +6 -8
  132. package/dist/remediate/intent/intentOrdering.d.ts.map +1 -1
  133. package/dist/remediate/intent/intentOrdering.js +24 -47
  134. package/dist/remediate/intent/intentOrdering.js.map +1 -1
  135. package/dist/remediate/phases/close.d.ts +25 -17
  136. package/dist/remediate/phases/close.d.ts.map +1 -1
  137. package/dist/remediate/phases/close.js +278 -218
  138. package/dist/remediate/phases/close.js.map +1 -1
  139. package/dist/remediate/phases/closeAcceptance.d.ts +42 -0
  140. package/dist/remediate/phases/closeAcceptance.d.ts.map +1 -0
  141. package/dist/remediate/phases/closeAcceptance.js +66 -0
  142. package/dist/remediate/phases/closeAcceptance.js.map +1 -0
  143. package/dist/remediate/phases/closeReviewProvenance.d.ts +1 -1
  144. package/dist/remediate/phases/closeReviewProvenance.d.ts.map +1 -1
  145. package/dist/remediate/phases/closeReviewProvenance.js +9 -8
  146. package/dist/remediate/phases/closeReviewProvenance.js.map +1 -1
  147. package/dist/remediate/phases/closeVerifyAnalyzerLeads.d.ts.map +1 -1
  148. package/dist/remediate/phases/closeVerifyAnalyzerLeads.js +11 -9
  149. package/dist/remediate/phases/closeVerifyAnalyzerLeads.js.map +1 -1
  150. package/dist/remediate/phases/closeVerifyHeadEvidence.d.ts +21 -86
  151. package/dist/remediate/phases/closeVerifyHeadEvidence.d.ts.map +1 -1
  152. package/dist/remediate/phases/closeVerifyHeadEvidence.js +30 -19
  153. package/dist/remediate/phases/closeVerifyHeadEvidence.js.map +1 -1
  154. package/dist/remediate/phases/plan.d.ts +3 -60
  155. package/dist/remediate/phases/plan.d.ts.map +1 -1
  156. package/dist/remediate/phases/plan.js +3 -227
  157. package/dist/remediate/phases/plan.js.map +1 -1
  158. package/dist/remediate/phases/triage.d.ts.map +1 -1
  159. package/dist/remediate/phases/triage.js +19 -30
  160. package/dist/remediate/phases/triage.js.map +1 -1
  161. package/dist/remediate/riskSignal.d.ts +9 -43
  162. package/dist/remediate/riskSignal.d.ts.map +1 -1
  163. package/dist/remediate/riskSignal.js +7 -28
  164. package/dist/remediate/riskSignal.js.map +1 -1
  165. package/dist/remediate/state/disposition.d.ts +2 -1
  166. package/dist/remediate/state/disposition.d.ts.map +1 -1
  167. package/dist/remediate/state/disposition.js +37 -1
  168. package/dist/remediate/state/disposition.js.map +1 -1
  169. package/dist/remediate/state/runIdentity.d.ts +2 -13
  170. package/dist/remediate/state/runIdentity.d.ts.map +1 -1
  171. package/dist/remediate/state/runIdentity.js +3 -13
  172. package/dist/remediate/state/runIdentity.js.map +1 -1
  173. package/dist/remediate/state/store.d.ts +12 -36
  174. package/dist/remediate/state/store.d.ts.map +1 -1
  175. package/dist/remediate/state/store.js +58 -150
  176. package/dist/remediate/state/store.js.map +1 -1
  177. package/dist/remediate/state/types.d.ts +406 -504
  178. package/dist/remediate/state/types.d.ts.map +1 -1
  179. package/dist/remediate/state/types.js +24 -104
  180. package/dist/remediate/state/types.js.map +1 -1
  181. package/dist/remediate/steps/contractPipeline.d.ts +7 -661
  182. package/dist/remediate/steps/contractPipeline.d.ts.map +1 -1
  183. package/dist/remediate/steps/contractPipeline.js +264 -3851
  184. package/dist/remediate/steps/contractPipeline.js.map +1 -1
  185. package/dist/remediate/steps/contractPipelinePrompts.d.ts +20 -142
  186. package/dist/remediate/steps/contractPipelinePrompts.d.ts.map +1 -1
  187. package/dist/remediate/steps/contractPipelinePrompts.js +48 -581
  188. package/dist/remediate/steps/contractPipelinePrompts.js.map +1 -1
  189. package/dist/remediate/steps/dispatch/contractConformanceReview.d.ts +4 -1
  190. package/dist/remediate/steps/dispatch/contractConformanceReview.d.ts.map +1 -1
  191. package/dist/remediate/steps/dispatch/contractConformanceReview.js +6 -3
  192. package/dist/remediate/steps/dispatch/contractConformanceReview.js.map +1 -1
  193. package/dist/remediate/steps/dispatch/hostContracts.d.ts +1 -1
  194. package/dist/remediate/steps/dispatch/hostContracts.d.ts.map +1 -1
  195. package/dist/remediate/steps/dispatch/hostHandoff.d.ts +4 -21
  196. package/dist/remediate/steps/dispatch/hostHandoff.d.ts.map +1 -1
  197. package/dist/remediate/steps/dispatch/hostHandoff.js +161 -383
  198. package/dist/remediate/steps/dispatch/hostHandoff.js.map +1 -1
  199. package/dist/remediate/steps/finalGate.d.ts +34 -0
  200. package/dist/remediate/steps/finalGate.d.ts.map +1 -1
  201. package/dist/remediate/steps/finalGate.js +44 -4
  202. package/dist/remediate/steps/finalGate.js.map +1 -1
  203. package/dist/remediate/steps/frictionCloseout.d.ts +1 -30
  204. package/dist/remediate/steps/frictionCloseout.d.ts.map +1 -1
  205. package/dist/remediate/steps/frictionCloseout.js +6 -99
  206. package/dist/remediate/steps/frictionCloseout.js.map +1 -1
  207. package/dist/remediate/steps/nextStep.d.ts +2 -15
  208. package/dist/remediate/steps/nextStep.d.ts.map +1 -1
  209. package/dist/remediate/steps/nextStep.js +254 -1042
  210. package/dist/remediate/steps/nextStep.js.map +1 -1
  211. package/dist/remediate/steps/prompts.js +16 -16
  212. package/dist/remediate/steps/prompts.js.map +1 -1
  213. package/dist/remediate/steps/stepUtils.d.ts +1 -1
  214. package/dist/remediate/steps/stepUtils.d.ts.map +1 -1
  215. package/dist/remediate/steps/types.d.ts +1 -1
  216. package/dist/remediate/steps/types.d.ts.map +1 -1
  217. package/dist/remediate/steps/types.js +1 -1
  218. package/dist/remediate/steps/types.js.map +1 -1
  219. package/dist/remediate/types/options.d.ts +9 -0
  220. package/dist/remediate/types/options.d.ts.map +1 -1
  221. package/dist/remediate/validation/artifacts.d.ts.map +1 -1
  222. package/dist/remediate/validation/artifacts.js +10 -56
  223. package/dist/remediate/validation/artifacts.js.map +1 -1
  224. package/dist/remediate/validation/remediationState.d.ts +2 -2
  225. package/dist/remediate/validation/remediationState.d.ts.map +1 -1
  226. package/dist/remediate/validation/remediationState.js +17 -77
  227. package/dist/remediate/validation/remediationState.js.map +1 -1
  228. package/dist/remediate/validation/verificationReport.d.ts +3 -0
  229. package/dist/remediate/validation/verificationReport.d.ts.map +1 -0
  230. package/dist/remediate/validation/verificationReport.js +28 -0
  231. package/dist/remediate/validation/verificationReport.js.map +1 -0
  232. package/dist/shared/analyzers/types.d.ts +10 -10
  233. package/dist/shared/engine/obligationEngine.d.ts +2 -2
  234. package/dist/shared/friction/triage.d.ts +7 -28
  235. package/dist/shared/friction/triage.d.ts.map +1 -1
  236. package/dist/shared/friction/triage.js +7 -28
  237. package/dist/shared/friction/triage.js.map +1 -1
  238. package/dist/shared/graph/orderedReachability.js +1 -1
  239. package/dist/shared/graph/orderedReachability.js.map +1 -1
  240. package/dist/shared/index.d.ts +5 -6
  241. package/dist/shared/index.d.ts.map +1 -1
  242. package/dist/shared/index.js +3 -4
  243. package/dist/shared/index.js.map +1 -1
  244. package/dist/shared/loopCorePaths.d.ts.map +1 -1
  245. package/dist/shared/loopCorePaths.js +37 -5
  246. package/dist/shared/loopCorePaths.js.map +1 -1
  247. package/dist/shared/prompts.d.ts +24 -10
  248. package/dist/shared/prompts.d.ts.map +1 -1
  249. package/dist/shared/prompts.js +27 -13
  250. package/dist/shared/prompts.js.map +1 -1
  251. package/dist/shared/reReview/projectionDiff.d.ts.map +1 -1
  252. package/dist/shared/reReview/projectionDiff.js +6 -14
  253. package/dist/shared/reReview/projectionDiff.js.map +1 -1
  254. package/dist/shared/types/executionPlan.d.ts +416 -0
  255. package/dist/shared/types/executionPlan.d.ts.map +1 -0
  256. package/dist/shared/types/executionPlan.js +132 -0
  257. package/dist/shared/types/executionPlan.js.map +1 -0
  258. package/dist/shared/types/remediationOutcome.d.ts +1378 -12
  259. package/dist/shared/types/remediationOutcome.d.ts.map +1 -1
  260. package/dist/shared/types/remediationOutcome.js +29 -1
  261. package/dist/shared/types/remediationOutcome.js.map +1 -1
  262. package/dist/shared/types/{contractPipeline/verification.d.ts → verificationReport.d.ts} +9 -37
  263. package/dist/shared/types/verificationReport.d.ts.map +1 -0
  264. package/dist/shared/types/verificationReport.js +4 -0
  265. package/dist/shared/types/verificationReport.js.map +1 -0
  266. package/dist/shared/validation/findingGrounding.d.ts +3 -5
  267. package/dist/shared/validation/findingGrounding.d.ts.map +1 -1
  268. package/dist/shared/validation/findingGrounding.js +4 -7
  269. package/dist/shared/validation/findingGrounding.js.map +1 -1
  270. package/package.json +2 -1
  271. package/scripts/shared/primitives.mjs +20 -0
  272. package/skills/audit-code/SKILL.md +5 -12
  273. package/skills/audit-code/audit-code.prompt.md +5 -8
  274. package/skills/audit-code/opencode-command-template.txt +2 -2
  275. package/skills/remediate-code/SKILL.md +5 -7
  276. package/skills/remediate-code/remediate-code.prompt.md +5 -5
  277. package/dispatch/lens-definitions.json +0 -46
  278. package/dist/remediate/contractPipeline/artifactNames.d.ts +0 -8
  279. package/dist/remediate/contractPipeline/artifactNames.d.ts.map +0 -1
  280. package/dist/remediate/contractPipeline/artifactNames.js +0 -28
  281. package/dist/remediate/contractPipeline/artifactNames.js.map +0 -1
  282. package/dist/remediate/contractPipeline/artifactStore.d.ts +0 -219
  283. package/dist/remediate/contractPipeline/artifactStore.d.ts.map +0 -1
  284. package/dist/remediate/contractPipeline/artifactStore.js +0 -436
  285. package/dist/remediate/contractPipeline/artifactStore.js.map +0 -1
  286. package/dist/remediate/contractPipeline/changeClassification.d.ts +0 -130
  287. package/dist/remediate/contractPipeline/changeClassification.d.ts.map +0 -1
  288. package/dist/remediate/contractPipeline/changeClassification.js +0 -446
  289. package/dist/remediate/contractPipeline/changeClassification.js.map +0 -1
  290. package/dist/remediate/contractPipeline/contractReviewBinding.d.ts +0 -28
  291. package/dist/remediate/contractPipeline/contractReviewBinding.d.ts.map +0 -1
  292. package/dist/remediate/contractPipeline/contractReviewBinding.js +0 -75
  293. package/dist/remediate/contractPipeline/contractReviewBinding.js.map +0 -1
  294. package/dist/remediate/contractPipeline/counterexampleFingerprint.d.ts +0 -27
  295. package/dist/remediate/contractPipeline/counterexampleFingerprint.d.ts.map +0 -1
  296. package/dist/remediate/contractPipeline/counterexampleFingerprint.js +0 -37
  297. package/dist/remediate/contractPipeline/counterexampleFingerprint.js.map +0 -1
  298. package/dist/remediate/contractPipeline/cyclicSeamResolution.d.ts +0 -131
  299. package/dist/remediate/contractPipeline/cyclicSeamResolution.d.ts.map +0 -1
  300. package/dist/remediate/contractPipeline/cyclicSeamResolution.js +0 -257
  301. package/dist/remediate/contractPipeline/cyclicSeamResolution.js.map +0 -1
  302. package/dist/remediate/contractPipeline/derive.d.ts +0 -173
  303. package/dist/remediate/contractPipeline/derive.d.ts.map +0 -1
  304. package/dist/remediate/contractPipeline/derive.js +0 -477
  305. package/dist/remediate/contractPipeline/derive.js.map +0 -1
  306. package/dist/remediate/contractPipeline/finalizedContractFields.d.ts +0 -50
  307. package/dist/remediate/contractPipeline/finalizedContractFields.d.ts.map +0 -1
  308. package/dist/remediate/contractPipeline/finalizedContractFields.js +0 -77
  309. package/dist/remediate/contractPipeline/finalizedContractFields.js.map +0 -1
  310. package/dist/remediate/contractPipeline/idRegistry.d.ts +0 -144
  311. package/dist/remediate/contractPipeline/idRegistry.d.ts.map +0 -1
  312. package/dist/remediate/contractPipeline/idRegistry.js +0 -214
  313. package/dist/remediate/contractPipeline/idRegistry.js.map +0 -1
  314. package/dist/remediate/contractPipeline/obligationKinds.d.ts +0 -27
  315. package/dist/remediate/contractPipeline/obligationKinds.d.ts.map +0 -1
  316. package/dist/remediate/contractPipeline/obligationKinds.js +0 -18
  317. package/dist/remediate/contractPipeline/obligationKinds.js.map +0 -1
  318. package/dist/remediate/contractPipeline/phaseCut.d.ts +0 -125
  319. package/dist/remediate/contractPipeline/phaseCut.d.ts.map +0 -1
  320. package/dist/remediate/contractPipeline/phaseCut.js +0 -339
  321. package/dist/remediate/contractPipeline/phaseCut.js.map +0 -1
  322. package/dist/remediate/contractPipeline/phaseCutArtifact.d.ts +0 -31
  323. package/dist/remediate/contractPipeline/phaseCutArtifact.d.ts.map +0 -1
  324. package/dist/remediate/contractPipeline/phaseCutArtifact.js +0 -48
  325. package/dist/remediate/contractPipeline/phaseCutArtifact.js.map +0 -1
  326. package/dist/remediate/contractPipeline/repairState.d.ts +0 -110
  327. package/dist/remediate/contractPipeline/repairState.d.ts.map +0 -1
  328. package/dist/remediate/contractPipeline/repairState.js +0 -185
  329. package/dist/remediate/contractPipeline/repairState.js.map +0 -1
  330. package/dist/remediate/contractPipeline/reviewSnapshot.d.ts +0 -79
  331. package/dist/remediate/contractPipeline/reviewSnapshot.d.ts.map +0 -1
  332. package/dist/remediate/contractPipeline/reviewSnapshot.js +0 -140
  333. package/dist/remediate/contractPipeline/reviewSnapshot.js.map +0 -1
  334. package/dist/remediate/contractPipeline/semanticProjection.d.ts +0 -64
  335. package/dist/remediate/contractPipeline/semanticProjection.d.ts.map +0 -1
  336. package/dist/remediate/contractPipeline/semanticProjection.js +0 -136
  337. package/dist/remediate/contractPipeline/semanticProjection.js.map +0 -1
  338. package/dist/remediate/contractPipeline/sketchSource.d.ts +0 -194
  339. package/dist/remediate/contractPipeline/sketchSource.d.ts.map +0 -1
  340. package/dist/remediate/contractPipeline/sketchSource.js +0 -262
  341. package/dist/remediate/contractPipeline/sketchSource.js.map +0 -1
  342. package/dist/remediate/contractPipeline/testPlanCarry.d.ts +0 -17
  343. package/dist/remediate/contractPipeline/testPlanCarry.d.ts.map +0 -1
  344. package/dist/remediate/contractPipeline/testPlanCarry.js +0 -72
  345. package/dist/remediate/contractPipeline/testPlanCarry.js.map +0 -1
  346. package/dist/remediate/steps/dispatch/marshal.d.ts +0 -7
  347. package/dist/remediate/steps/dispatch/marshal.d.ts.map +0 -1
  348. package/dist/remediate/steps/dispatch/marshal.js +0 -11
  349. package/dist/remediate/steps/dispatch/marshal.js.map +0 -1
  350. package/dist/remediate/validation/contractPipeline.d.ts +0 -35
  351. package/dist/remediate/validation/contractPipeline.d.ts.map +0 -1
  352. package/dist/remediate/validation/contractPipeline.js +0 -613
  353. package/dist/remediate/validation/contractPipeline.js.map +0 -1
  354. package/dist/remediate/validation/contractPipelineGates.d.ts +0 -272
  355. package/dist/remediate/validation/contractPipelineGates.d.ts.map +0 -1
  356. package/dist/remediate/validation/contractPipelineGates.js +0 -1397
  357. package/dist/remediate/validation/contractPipelineGates.js.map +0 -1
  358. package/dist/shared/types/contractPipeline/design.d.ts +0 -79
  359. package/dist/shared/types/contractPipeline/design.d.ts.map +0 -1
  360. package/dist/shared/types/contractPipeline/design.js +0 -16
  361. package/dist/shared/types/contractPipeline/design.js.map +0 -1
  362. package/dist/shared/types/contractPipeline/goal.d.ts +0 -41
  363. package/dist/shared/types/contractPipeline/goal.d.ts.map +0 -1
  364. package/dist/shared/types/contractPipeline/goal.js +0 -10
  365. package/dist/shared/types/contractPipeline/goal.js.map +0 -1
  366. package/dist/shared/types/contractPipeline/implementation.d.ts +0 -118
  367. package/dist/shared/types/contractPipeline/implementation.d.ts.map +0 -1
  368. package/dist/shared/types/contractPipeline/implementation.js +0 -13
  369. package/dist/shared/types/contractPipeline/implementation.js.map +0 -1
  370. package/dist/shared/types/contractPipeline/obligations.d.ts +0 -283
  371. package/dist/shared/types/contractPipeline/obligations.d.ts.map +0 -1
  372. package/dist/shared/types/contractPipeline/obligations.js +0 -84
  373. package/dist/shared/types/contractPipeline/obligations.js.map +0 -1
  374. package/dist/shared/types/contractPipeline/verification.d.ts.map +0 -1
  375. package/dist/shared/types/contractPipeline/verification.js +0 -10
  376. package/dist/shared/types/contractPipeline/verification.js.map +0 -1
  377. package/dist/shared/types/contractPipeline.d.ts +0 -48
  378. package/dist/shared/types/contractPipeline.d.ts.map +0 -1
  379. package/dist/shared/types/contractPipeline.js +0 -32
  380. package/dist/shared/types/contractPipeline.js.map +0 -1
  381. package/dist/shared/types/obligationLedger.d.ts +0 -39
  382. package/dist/shared/types/obligationLedger.d.ts.map +0 -1
  383. package/dist/shared/types/obligationLedger.js +0 -49
  384. package/dist/shared/types/obligationLedger.js.map +0 -1
@@ -1,8 +1,11 @@
1
+ import { assertApprovedRuntimePlan, RemediationPlanAuthorityError } from "../contractPipeline/runtimePlanAuthority.js";
2
+ import { executionPlanContextIssues } from "../contractPipeline/executionPlan.js";
3
+ import { contentSha256 } from "../../shared/submission/hostHandoffCore.js";
1
4
  import { AcceptedConformanceReviewSchema, ContractReviewOutcomeSchema } from "../../shared/types/reviewIndependence.js";
2
5
  import { readContractReviewOutcomes } from "./closeReviewProvenance.js";
3
6
  import { dirname, extname, isAbsolute, join, relative } from "node:path";
4
7
  import { readFileSync, existsSync } from "node:fs";
5
- import { AGENT_FEEDBACK_FILENAME, normalizeRepoPath, parseReflectionsNdjson, readOptionalJsonFile, readOptionalTextFile, renderProcessFeedbackSection, reportDiscardedReflections, stagedAndUntracked, writeJsonFile, writeTextFile, RemediationOutcomeStatusSchema, countBy, commandLeavesDeclaredShape, parseCommandString, runTrackedAsync, compareCodeUnits, runAdmittedProjectTestCommand, runAdmittedProjectE2eCommand, readSubmissionLedger, } from "audit-tools/shared";
8
+ import { AGENT_FEEDBACK_FILENAME, normalizeRepoPath, parseReflectionsNdjson, readOptionalJsonFile, readOptionalTextFile, renderProcessFeedbackSection, reportDiscardedReflections, stagedAndUntracked, writeJsonFile, writeTextFile, RemediationOutcomeStatusSchema, REMEDIATION_OUTCOMES_CONTRACT_VERSION, countBy, commandLeavesDeclaredShape, parseCommandString, runTrackedAsync, compareCodeUnits, runAdmittedProjectTestCommand, runAdmittedProjectE2eCommand, readSubmissionLedger, } from "audit-tools/shared";
6
9
  import { CONTRACT_PIPELINE_VERIFICATION_REPORT_VERSION } from "audit-tools/shared";
7
10
  import { FAILURE_OUTPUT_TAIL_CHARS } from "./constants.js";
8
11
  import { verifyAnalyzerLeads } from "./closeVerifyAnalyzerLeads.js";
@@ -21,6 +24,7 @@ import { ABSENT_FINAL_GATE_REPORT, FinalGateReportSchema, NO_RECOVERY, carriesGa
21
24
  // cycle — and the alternative, a second `"final-gate-outcome.json"` literal
22
25
  // here, is exactly the drift the layout registry exists to prevent.
23
26
  import { finalGateOutcomePath } from "../steps/finalGate.js";
27
+ import { runCloseAcceptance } from "./closeAcceptance.js";
24
28
  // Derived from the single source so the key list can never drift from the
25
29
  // RemediationOutcomeStatus contract (A6).
26
30
  const OUTCOME_KEYS = [
@@ -42,6 +46,8 @@ const FINAL_STATUS_BY_OUTCOME = {
42
46
  inappropriate: "skipped",
43
47
  ignored: "ignored",
44
48
  blocked: "failed",
49
+ pending: "pending",
50
+ deferred: "deferred",
45
51
  verified_already_fixed: "fixed",
46
52
  refuted: "skipped",
47
53
  };
@@ -223,26 +229,111 @@ finalGate = ABSENT_FINAL_GATE_REPORT,
223
229
  // empty set — never an omitted key, which a reader could not tell from a
224
230
  // release that did not record recovery at all.
225
231
  recovery = NO_RECOVERY, contractReviews = []) {
226
- const findingsById = new Map((state.plan?.findings ?? []).map((finding) => [finding.id, finding]));
227
- const blocksById = new Map((state.plan?.blocks ?? []).map((block) => [block.block_id, block]));
228
232
  const outcomes = [];
229
233
  const closeReason = closingStatusReason(closingResult);
230
- for (const item of Object.values(state.items ?? {})) {
231
- // Derive the outcome from the single status→disposition→outcome
232
- // authority. A non-terminal status — in-progress, `blocked`, or an
233
- // unanswered `needs_clarification` — means the run was force-closed while
234
- // the item was still non-terminal: record it as a failed (`blocked`)
235
- // outcome — never drop it — and preserve the original state so a retry
236
- // sees where it stood (every non-terminal status, not only the five
237
- // in-progress ones — needs_clarification included).
234
+ const executionOutcomes = (state.plan?.units ?? []).map(unit => {
235
+ const item = state.items?.[unit.id];
236
+ return {
237
+ unit_id: unit.id, title: unit.title, source_finding_ids: [...unit.source_finding_ids],
238
+ status: item?.status ?? "pending",
239
+ required_tests: [...unit.required_tests], evidence: [...(item?.host_result_evidence ?? [])],
240
+ rework_count: item?.rework_count ?? 0,
241
+ ...(item?.host_landed_commit ? { landed_commit: item.host_landed_commit } : {}),
242
+ ...(item?.host_landed_files ? { landed_files: [...item.host_landed_files] } : {}),
243
+ ...(item?.failure_reason ? { reason: item.failure_reason } : !item ? { reason: "No execution state was recorded for this reviewed unit." } : !isVerifiedCompleteStatus(item.status) && !isSkipStatus(item.status) ? { reason: `Execution unit remains ${item.status}.` } : {}),
244
+ ...(item?.conformance_review ? { conformance_review: AcceptedConformanceReviewSchema.parse(item.conformance_review) } : {}),
245
+ ...(item?.started_at ? { started_at: item.started_at } : {}),
246
+ ...(item?.completed_at ? { completed_at: item.completed_at } : {}),
247
+ };
248
+ });
249
+ let requestOutcome;
250
+ const requestDisposition = state.plan?.request_disposition;
251
+ if (state.plan?.request || requestDisposition) {
252
+ const units = state.plan?.units ?? [];
253
+ const items = units.map(unit => state.items?.[unit.id]);
254
+ const settled = items.length > 0 && items.every(item => item && (isVerifiedCompleteStatus(item.status) || isSkipStatus(item.status)));
255
+ const skipped = items.filter(item => item && isSkipStatus(item.status));
256
+ const status = requestDisposition?.status ??
257
+ (items.some(item => !item || isUnsuccessfulEndStatus(item.status)) ? "blocked" :
258
+ settled && skipped.length > 0 ? "ignored" : settled ? "resolved" : "pending");
259
+ requestOutcome = {
260
+ status,
261
+ reason: requestDisposition?.reason ?? (status === "ignored"
262
+ ? skipped.map(item => item.failure_reason ?? "Owner declined this execution unit").join("; ")
263
+ : status === "resolved" ? "All reviewed execution units were verified."
264
+ : "The request still has unfinished execution work."),
265
+ evidence: requestDisposition?.evidence ?? executionOutcomes.flatMap(unit => unit.evidence),
266
+ unit_ids: units.map(unit => unit.id),
267
+ };
268
+ }
269
+ const sourceFindings = new Map((state.plan?.findings ?? []).map(finding => [finding.id, finding]));
270
+ for (const entry of state.plan_coverage?.entries ?? []) {
271
+ const original = sourceFindings.get(entry.finding_id);
272
+ if (entry.finding && original && contentSha256(entry.finding) !== contentSha256(original)) {
273
+ throw new Error(`Conflicting original source payload for ${entry.finding_id}; refusing an ambiguous outcome.`);
274
+ }
275
+ if (entry.finding && !original)
276
+ sourceFindings.set(entry.finding_id, entry.finding);
277
+ }
278
+ for (const finding of sourceFindings.values()) {
279
+ const units = (state.plan?.units ?? []).filter(unit => unit.source_finding_ids.includes(finding.id));
280
+ const linked = units.map(unit => state.items?.[unit.id]);
281
+ const coverage = state.plan_coverage?.entries.find(entry => entry.finding_id === finding.id);
282
+ const coverageDrop = coverage && DROP_REASON_BY_DISPOSITION[coverage.disposition];
283
+ const settledWithOwnerSkip = linked.length > 0 && linked.every(item => item && (isVerifiedCompleteStatus(item.status) || isSkipStatus(item.status))) && linked.some(item => item && isSkipStatus(item.status));
284
+ const unitOwnerDisposition = settledWithOwnerSkip ? {
285
+ status: linked.every(item => item?.status === "deemed_inappropriate") ? "declined" : "ignored",
286
+ reason: linked.filter(item => item && isSkipStatus(item.status)).map(item => item.failure_reason ?? "Owner declined this execution unit").join("; "),
287
+ } : undefined;
288
+ const ownerDisposition = state.finding_dispositions?.[finding.id] ?? unitOwnerDisposition ??
289
+ (coverageDrop ? { status: "ignored", reason: coverage.rationale ?? `Source was not scheduled: ${coverageDrop}` } : undefined);
290
+ const semanticDisposition = state.plan?.source_dispositions?.find(entry => entry.finding_id === finding.id);
291
+ const recorded = state.source_verifications?.[finding.id];
292
+ const verified = recorded?.review_revision_sha256 === state.plan?.review_revision_sha256 &&
293
+ recorded?.source_sha256 === contentSha256(finding) ? recorded : undefined;
294
+ const allVerified = linked.length > 0 && linked.every(item => item && isVerifiedCompleteStatus(item.status));
295
+ const starts = linked.flatMap(item => item?.started_at ? [item.started_at] : []).sort();
296
+ const ends = linked.flatMap(item => item?.completed_at ? [item.completed_at] : []).sort();
297
+ const hasBlocked = linked.some(item => !item || isUnsuccessfulEndStatus(item.status));
298
+ const sourceDeferred = semanticDisposition?.status === "deferred";
299
+ const sourcePending = !ownerDisposition && !semanticDisposition && units.length > 0 && !hasBlocked && !sourceDeferred && !allVerified;
300
+ const sharedStatus = linked.length > 0 && linked.every(item => item?.status === linked[0]?.status) ? linked[0]?.status : undefined;
301
+ const status = ownerDisposition ? ownerDisposition.status === "declined" ? "deemed_inappropriate" : "ignored"
302
+ : semanticDisposition?.status === "deferred" ? "blocked"
303
+ : semanticDisposition ? "resolved_no_change"
304
+ : allVerified ? linked.every(item => item?.status === "resolved_no_change") ? "resolved_no_change" : "resolved"
305
+ : sourcePending ? sharedStatus ?? "pending" : sharedStatus && isUnsuccessfulEndStatus(sharedStatus) ? sharedStatus : "blocked";
306
+ // A source aggregation is not an execution item. Evidence remains keyed by
307
+ // original source identity, so shared units cannot overwrite another source.
308
+ const item = {
309
+ status,
310
+ ...verified,
311
+ disposition_override: ownerDisposition ? undefined : semanticDisposition?.status === "already_fixed" ? "verified_already_fixed"
312
+ : semanticDisposition?.status === "refuted" ? "refuted" : verified?.disposition_override,
313
+ failure_reason: ownerDisposition?.reason ?? semanticDisposition?.reason ??
314
+ (!allVerified ? units.length === 0 ? "No reviewed execution unit or source disposition covers this finding."
315
+ : "Not every required execution unit was verified: " + units.map((unit, i) => `${unit.id}=${linked[i]?.status ?? "missing"}${linked[i]?.failure_reason ? ` (${linked[i].failure_reason})` : ""}`).join(", ") : undefined),
316
+ rework_count: linked.reduce((sum, item) => sum + (item?.rework_count ?? 0), 0),
317
+ started_at: starts[0], completed_at: linked.length > 0 && ends.length === linked.length ? ends.at(-1) : undefined,
318
+ };
319
+ // Source verdicts retain pending/deferred separately from blocked. A
320
+ // successful source requires every linked unit verified, or an independently
321
+ // corroborated explicit source disposition; an empty link set never passes.
238
322
  let outcome;
239
323
  let originalState;
240
324
  let evidence;
241
325
  let recordedByModule;
242
326
  let evidenceRefusalReason;
243
- if (!isTerminalStatus(item.status)) {
327
+ if (sourceDeferred && !ownerDisposition) {
328
+ outcome = "deferred";
329
+ }
330
+ else if (sourcePending) {
331
+ outcome = "pending";
332
+ originalState = sharedStatus;
333
+ }
334
+ else if (!isTerminalStatus(item.status)) {
244
335
  outcome = "blocked";
245
- originalState = item.status;
336
+ originalState = sharedStatus;
246
337
  }
247
338
  else {
248
339
  const disposition = resolveDisposition(item.status, item.disposition_override);
@@ -284,7 +375,6 @@ recovery = NO_RECOVERY, contractReviews = []) {
284
375
  }
285
376
  }
286
377
  }
287
- const finding = findingsById.get(item.finding_id);
288
378
  const fileExts = [
289
379
  ...new Set((finding?.affected_files ?? [])
290
380
  .map((file) => extname(file.path).toLowerCase())
@@ -306,7 +396,7 @@ recovery = NO_RECOVERY, contractReviews = []) {
306
396
  reason = DEFAULT_REASON_BY_OUTCOME[outcome];
307
397
  }
308
398
  const base = {
309
- finding_id: item.finding_id,
399
+ finding_id: finding.id,
310
400
  lens: finding?.lens ?? "unknown",
311
401
  file_exts: fileExts,
312
402
  outcome,
@@ -322,22 +412,15 @@ recovery = NO_RECOVERY, contractReviews = []) {
322
412
  : {}),
323
413
  ...(evidence ? { evidence } : {}),
324
414
  ...(recordedByModule ? { recorded_by_module: recordedByModule } : {}),
325
- ...((outcome === "resolved" || outcome === "verified_no_change") && item.conformance_review
326
- ? { conformance_review: AcceptedConformanceReviewSchema.parse(item.conformance_review) } : {}),
327
415
  };
328
- if (!finding) {
329
- // Degenerate (corrupt state): without the plan finding there is no payload
330
- // to carry — emit the lean per-finding outcome rather than inventing one.
331
- outcomes.push(base);
332
- continue;
333
- }
334
416
  const enriched = {
335
417
  ...base,
336
418
  finding,
337
- block_id: item.block_id,
338
- block_dependencies: [...(blocksById.get(item.block_id)?.dependencies ?? [])],
419
+ unit_ids: units.map(unit => unit.id),
420
+ unit_dependencies: [...new Set(units.flatMap(unit => unit.dependencies))],
421
+ unit_statuses: Object.fromEntries(units.map((unit, i) => [unit.id, linked[i]?.status ?? "missing"])),
339
422
  final_status: FINAL_STATUS_BY_OUTCOME[outcome],
340
- ...(originalState ? { original_state: originalState } : {}),
423
+ original_state: originalState,
341
424
  };
342
425
  outcomes.push(enriched);
343
426
  }
@@ -387,7 +470,7 @@ recovery = NO_RECOVERY, contractReviews = []) {
387
470
  ? durationBetweenMs(aggregateStarted.value, aggregateCompleted.value)
388
471
  : undefined;
389
472
  return {
390
- contract_version: "remediate-code-outcomes/v1alpha1",
473
+ contract_version: REMEDIATION_OUTCOMES_CONTRACT_VERSION,
391
474
  total: outcomes.length,
392
475
  by_outcome: byOutcome,
393
476
  by_lens: byLens,
@@ -398,6 +481,10 @@ recovery = NO_RECOVERY, contractReviews = []) {
398
481
  recovery,
399
482
  ...(contractReviews.length > 0 ? { contract_reviews: contractReviews.map(review => ContractReviewOutcomeSchema.parse(review)) } : {}),
400
483
  outcomes,
484
+ execution_outcomes: executionOutcomes,
485
+ ...(requestDisposition ? { request_disposition: requestDisposition } : {}),
486
+ ...(requestOutcome ? { request_outcome: requestOutcome } : {}),
487
+ ...(state.plan ? { objective: state.plan.objective, ...(state.plan.request ? { request: state.plan.request } : {}) } : {}),
401
488
  };
402
489
  }
403
490
  /** Drop-reason discriminator per never-planned coverage disposition. */
@@ -723,24 +810,14 @@ function repoPathExactKey(path) {
723
810
  * Falls back to a generic message when there are no findings to summarize.
724
811
  */
725
812
  function generateCommitMessage(state) {
726
- const findings = state.plan?.findings ?? [];
727
- const items = Object.values(state.items ?? {});
728
- const resolvedFindingIds = new Set(items
729
- .filter((i) => isVerifiedCompleteStatus(i.status))
730
- .map((i) => i.finding_id));
731
- const resolved = findings.filter((f) => resolvedFindingIds.has(f.id));
732
- if (resolved.length === 0) {
813
+ const resolved = (state.plan?.units ?? []).filter(unit => {
814
+ const item = state.items?.[unit.id];
815
+ return item && isVerifiedCompleteStatus(item.status);
816
+ });
817
+ if (resolved.length === 0)
733
818
  return "Remediation complete";
734
- }
735
- if (resolved.length === 1) {
736
- return `Fix: ${resolved[0].title ?? resolved[0].id}`;
737
- }
738
- const titles = resolved
739
- .slice(0, 3)
740
- .map((f) => f.title ?? f.id)
741
- .join(", ");
742
- const suffix = resolved.length > 3 ? ` (+${resolved.length - 3} more)` : "";
743
- return `Fix: ${titles}${suffix}`;
819
+ const titles = resolved.slice(0, 3).map(unit => unit.title).join(", ");
820
+ return `Fix: ${titles}${resolved.length > 3 ? ` (+${resolved.length - 3} more)` : ""}`;
744
821
  }
745
822
  /**
746
823
  * Actions that require user confirmation before executing. `custom` is not
@@ -764,11 +841,19 @@ async function checkClosingPreview(state, options) {
764
841
  return undefined;
765
842
  const { files, leftover } = await collectStagingFiles(options.root, resolveEditSurfaceManifest(state), toolDeliverablePaths(options));
766
843
  const commitMessage = generateCommitMessage(state);
767
- return {
844
+ const preview = {
845
+ action: closingPlan.action,
846
+ ...(closingPlan.custom_command ? { custom_command: closingPlan.custom_command } : {}),
768
847
  files,
769
848
  commit_message: commitMessage,
770
849
  ...(leftover.length > 0 ? { leftover_files: leftover } : {}),
771
850
  };
851
+ if (options.finalizeClosing && closingPlan.closing_action_preview &&
852
+ JSON.stringify(preview) === JSON.stringify(closingPlan.closing_action_preview)) {
853
+ closingPlan.pre_authorized = true;
854
+ return undefined;
855
+ }
856
+ return preview;
772
857
  }
773
858
  /**
774
859
  * Execute the run's closing action.
@@ -931,6 +1016,7 @@ export async function runCombinedTestSuite(state, options) {
931
1016
  return {
932
1017
  ran: true,
933
1018
  passed,
1019
+ exit_code: admitted.exit_code,
934
1020
  suite_name: suiteName,
935
1021
  duration_ms: Date.now() - startedAt,
936
1022
  output: passed
@@ -951,12 +1037,12 @@ export async function runCombinedTestSuite(state, options) {
951
1037
  });
952
1038
  const durationMs = Date.now() - startedAt;
953
1039
  if (result.status === 0) {
954
- return { ran: true, passed: true, suite_name: suiteName, duration_ms: durationMs, output: "" };
1040
+ return { ran: true, passed: true, exit_code: result.status, suite_name: suiteName, duration_ms: durationMs, output: "" };
955
1041
  }
956
1042
  const output = ((result.stdout?.toString() ?? "") + (result.stderr?.toString() ?? ""))
957
1043
  .trim()
958
1044
  .slice(-FAILURE_OUTPUT_TAIL_CHARS);
959
- return { ran: true, passed: false, suite_name: suiteName, duration_ms: durationMs, output };
1045
+ return { ran: true, passed: false, exit_code: result.status, suite_name: suiteName, duration_ms: durationMs, output };
960
1046
  }
961
1047
  /**
962
1048
  * Parse test output to extract implicated file paths. Looks for common test
@@ -1041,12 +1127,12 @@ export function blockResolvedItemsOnCombinedFailure(state, testOutput) {
1041
1127
  return false;
1042
1128
  const implicatedPaths = extractImplicatedPaths(testOutput);
1043
1129
  const now = new Date().toISOString();
1044
- const touchedFilesByBlock = new Map((state.plan?.blocks ?? []).map((block) => [block.block_id, block.touched_files]));
1130
+ const touchedFilesByUnit = new Map((state.plan?.units ?? []).map((unit) => [unit.id, unit.allowed_files]));
1045
1131
  // Attempt attribution: find items whose block touched an implicated path.
1046
1132
  let attributed = [];
1047
1133
  if (implicatedPaths.length > 0) {
1048
1134
  for (const item of resolvedItems) {
1049
- const touchedFiles = touchedFilesByBlock.get(item.block_id) ?? [];
1135
+ const touchedFiles = touchedFilesByUnit.get(item.unit_id) ?? [];
1050
1136
  const overlaps = touchedFiles.some((tf) => implicatedPaths.some((ip) => touchedPathMatchesImplicated(tf, ip)));
1051
1137
  if (overlaps)
1052
1138
  attributed.push(item);
@@ -1137,10 +1223,10 @@ function collectReportEntries(state, options) {
1137
1223
  };
1138
1224
  for (const item of Object.values(state.items ?? {})) {
1139
1225
  if (isVerifiedCompleteStatus(item.status)) {
1140
- const finding = state.plan?.findings.find((f) => f.id === item.finding_id);
1141
- const title = finding?.title ?? "Unknown";
1226
+ const unit = state.plan?.units.find((u) => u.id === item.unit_id);
1227
+ const title = unit?.title ?? item.unit_id;
1142
1228
  let verificationEvidence = item.host_result_evidence;
1143
- const verificationResultPath = join(options.artifactsDir, `result_${item.finding_id}_verify_code_against_documentation.json`);
1229
+ const verificationResultPath = join(options.artifactsDir, `result_${item.unit_id}_verify_code_against_documentation.json`);
1144
1230
  if (existsSync(verificationResultPath)) {
1145
1231
  try {
1146
1232
  const verRes = JSON.parse(readFileSync(verificationResultPath, "utf8"));
@@ -1153,7 +1239,7 @@ function collectReportEntries(state, options) {
1153
1239
  }
1154
1240
  }
1155
1241
  const entry = {
1156
- finding_id: item.finding_id,
1242
+ unit_id: item.unit_id,
1157
1243
  summary: title,
1158
1244
  verification_evidence: verificationEvidence,
1159
1245
  };
@@ -1166,19 +1252,19 @@ function collectReportEntries(state, options) {
1166
1252
  }
1167
1253
  else if (item.status === "deemed_inappropriate") {
1168
1254
  entries.inappropriate.push({
1169
- finding_id: item.finding_id,
1255
+ unit_id: item.unit_id,
1170
1256
  rationale: item.failure_reason ?? "Deemed inappropriate",
1171
1257
  });
1172
1258
  }
1173
1259
  else if (item.status === "ignored") {
1174
1260
  entries.ignored.push({
1175
- finding_id: item.finding_id,
1261
+ unit_id: item.unit_id,
1176
1262
  rationale: item.failure_reason ?? "Ignored by user",
1177
1263
  });
1178
1264
  }
1179
1265
  else if (item.status === "blocked") {
1180
1266
  entries.blocked.push({
1181
- finding_id: item.finding_id,
1267
+ unit_id: item.unit_id,
1182
1268
  rationale: item.failure_reason ?? "Blocked",
1183
1269
  });
1184
1270
  }
@@ -1189,15 +1275,32 @@ function collectReportEntries(state, options) {
1189
1275
  * Render `remediation-report.md` from the partitioned entries, closing action,
1190
1276
  * e2e result, and per-finding outcomes. Pure string builder (no I/O).
1191
1277
  */
1192
- function buildRemediationReportMarkdown(state, entries, closingResult, e2ePassed, outcomesReport, combinedTest, reflections = [], landingGates = { commands: [], gates: [], passed: true }) {
1278
+ export function buildRemediationReportMarkdown(state, entries, closingResult, e2ePassed, outcomesReport, combinedTest, reflections = [], landingGates = { commands: [], gates: [], passed: true }) {
1193
1279
  let reportContent = `# Remediation Report\n\n`;
1280
+ if (outcomesReport.request_outcome) {
1281
+ const request = outcomesReport.request_outcome;
1282
+ reportContent += `## Request outcome\n\n${request.status}: ${request.reason}\n`;
1283
+ for (const evidence of request.evidence)
1284
+ reportContent += `- ${evidence}\n`;
1285
+ reportContent += "\n";
1286
+ }
1287
+ const executionUnits = outcomesReport.execution_outcomes;
1288
+ const completedUnits = executionUnits.filter(unit => unit.status === "resolved" || unit.status === "resolved_no_change").length;
1289
+ const skippedUnits = executionUnits.filter(unit => unit.status === "ignored" || unit.status === "deemed_inappropriate").length;
1290
+ reportContent += `## Execution units\n\n${executionUnits.length} unit(s): ${completedUnits} verified complete, ${skippedUnits} skipped, ${executionUnits.length - completedUnits - skippedUnits} unfinished.\n\n`;
1291
+ for (const unit of outcomesReport.execution_outcomes ?? []) {
1292
+ reportContent += `- **${unit.unit_id}** ${unit.title}: ${unit.status}${unit.reason ? ` — ${unit.reason}` : ""}\n`;
1293
+ }
1294
+ if (!(outcomesReport.execution_outcomes ?? []).length)
1295
+ reportContent += "No execution units were required.\n";
1296
+ reportContent += "\n";
1194
1297
  // Host results are accepted only after the claimed commit is mechanically
1195
1298
  // corroborated as reachable from HEAD. A no-change/skip-only run has no
1196
1299
  // changed commit to review.
1197
1300
  if (entries.resolved.length > 0) {
1198
1301
  reportContent += `## Review\n\nAll code changes were accepted through the provider-neutral host handoff and corroborated as landed commits reachable from the repository HEAD. Review the resulting diff and commit history.\n\n`;
1199
1302
  }
1200
- const resultReviews = outcomesReport.outcomes.filter(outcome => outcome.conformance_review);
1303
+ const resultReviews = outcomesReport.execution_outcomes.filter(outcome => outcome.conformance_review);
1201
1304
  if (outcomesReport.contract_reviews?.length || resultReviews.length) {
1202
1305
  reportContent += `## Declared Review Context\n\nThese are host-declared review contexts bound to accepted artifacts/results, not verified reviewer identities.\n\n`;
1203
1306
  for (const review of outcomesReport.contract_reviews ?? []) {
@@ -1205,7 +1308,7 @@ function buildRemediationReportMarkdown(state, entries, closingResult, e2ePassed
1205
1308
  }
1206
1309
  for (const outcome of resultReviews) {
1207
1310
  const review = outcome.conformance_review;
1208
- reportContent += `- ${outcome.finding_id} conformance: required independent; declared ${review.review.mode}. ${review.summary}\n`;
1311
+ reportContent += `- ${outcome.unit_id} conformance: required independent; declared ${review.review.mode}. ${review.summary}\n`;
1209
1312
  }
1210
1313
  reportContent += "\n";
1211
1314
  }
@@ -1215,7 +1318,7 @@ function buildRemediationReportMarkdown(state, entries, closingResult, e2ePassed
1215
1318
  }
1216
1319
  else {
1217
1320
  for (const entry of entries.resolved) {
1218
- reportContent += `- **${entry.finding_id}**: ${entry.summary}\n`;
1321
+ reportContent += `- **${entry.unit_id}**: ${entry.summary}\n`;
1219
1322
  if (entry.verification_evidence) {
1220
1323
  for (const check of entry.verification_evidence) {
1221
1324
  reportContent += ` - *Verification*: ${check}\n`;
@@ -1226,7 +1329,7 @@ function buildRemediationReportMarkdown(state, entries, closingResult, e2ePassed
1226
1329
  if (entries.verifiedNoChange.length > 0) {
1227
1330
  reportContent += `\n## Verified Already Correct (no changes made)\n\n`;
1228
1331
  for (const entry of entries.verifiedNoChange) {
1229
- reportContent += `- **${entry.finding_id}**: ${entry.summary}\n`;
1332
+ reportContent += `- **${entry.unit_id}**: ${entry.summary}\n`;
1230
1333
  if (entry.verification_evidence) {
1231
1334
  for (const check of entry.verification_evidence) {
1232
1335
  reportContent += ` - *Verification*: ${check}\n`;
@@ -1237,13 +1340,13 @@ function buildRemediationReportMarkdown(state, entries, closingResult, e2ePassed
1237
1340
  if (entries.inappropriate.length > 0) {
1238
1341
  reportContent += `\n## Deemed Inappropriate\n\n`;
1239
1342
  for (const entry of entries.inappropriate) {
1240
- reportContent += `- **${entry.finding_id}**: ${entry.rationale}\n`;
1343
+ reportContent += `- **${entry.unit_id}**: ${entry.rationale}\n`;
1241
1344
  }
1242
1345
  }
1243
1346
  if (entries.ignored.length > 0) {
1244
1347
  reportContent += `\n## Ignored\n\n`;
1245
1348
  for (const entry of entries.ignored) {
1246
- reportContent += `- **${entry.finding_id}**: ${entry.rationale}\n`;
1349
+ reportContent += `- **${entry.unit_id}**: ${entry.rationale}\n`;
1247
1350
  }
1248
1351
  }
1249
1352
  const skippedByCheckpoint = (state.plan_coverage?.entries ?? []).filter((e) => e.disposition === "dropped_by_checkpoint");
@@ -1355,7 +1458,7 @@ function buildRemediationReportMarkdown(state, entries, closingResult, e2ePassed
1355
1458
  }
1356
1459
  const o = outcomesReport.by_outcome;
1357
1460
  reportContent += `\n## Remediation Outcomes\n\n`;
1358
- reportContent += `Of ${outcomesReport.total} finding(s): ${o.resolved} resolved, ${o.verified_no_change} verified already correct, ${o.inappropriate} deemed inappropriate, ${o.ignored} ignored, ${o.blocked} blocked.\n`;
1461
+ reportContent += `Of ${outcomesReport.total} finding(s): ${o.resolved} resolved, ${o.verified_no_change} verified already correct, ${o.inappropriate} deemed inappropriate, ${o.ignored} ignored, ${o.blocked} blocked, ${o.pending} pending, ${o.deferred} deferred.\n`;
1359
1462
  const lensNames = Object.keys(outcomesReport.by_lens).sort();
1360
1463
  if (lensNames.length > 0) {
1361
1464
  reportContent += `\nBy lens:\n`;
@@ -1421,7 +1524,7 @@ function buildRemediationReportMarkdown(state, entries, closingResult, e2ePassed
1421
1524
  * apply to — the same rule the combined-suite leg follows for a run with no
1422
1525
  * configured suite.
1423
1526
  */
1424
- export async function cleanupTempBranchesAndArtifacts(options, completeState, combinedTest, e2eResult, closingResult, runLogger, landingGates = { commands: [], gates: [], passed: true }) {
1527
+ export async function cleanupTempBranchesAndArtifacts(options, completeState, combinedTest, e2eResult, closingResult, runLogger, landingGates = { commands: [], gates: [], passed: true }, sourceOutcomes = buildRemediationOutcomesReport(completeState, closingResult)) {
1425
1528
  // Write final state before deleting the artifacts directory so the completion
1426
1529
  // is durable even if cleanup partially fails.
1427
1530
  try {
@@ -1467,18 +1570,27 @@ export async function cleanupTempBranchesAndArtifacts(options, completeState, co
1467
1570
  // classification (see closingActionCompleted): success, or the skipped
1468
1571
  // action=none no-op — never a skipped non-none close.
1469
1572
  const closingCompleted = closingActionCompleted(closingResult);
1573
+ const unresolvedSources = sourceOutcomes.outcomes.some(outcome => ["blocked", "pending", "deferred"].includes(outcome.outcome));
1574
+ const unresolvedRequest = sourceOutcomes.request_outcome && ["blocked", "pending", "deferred"].includes(sourceOutcomes.request_outcome.status);
1575
+ const unresolvedUnits = (completeState.plan?.units ?? []).some(unit => {
1576
+ const item = completeState.items?.[unit.id];
1577
+ return !item || (!isVerifiedCompleteStatus(item.status) && !isSkipStatus(item.status));
1578
+ });
1470
1579
  const fullyGreen = combinedTest.passed &&
1471
1580
  e2eResult.passed &&
1472
1581
  landingGates.passed &&
1473
1582
  closingCompleted &&
1474
- !anyBlocked;
1583
+ !anyBlocked &&
1584
+ !unresolvedSources &&
1585
+ !unresolvedRequest &&
1586
+ !unresolvedUnits;
1475
1587
  if (!fullyGreen) {
1476
1588
  runLogger?.event({
1477
1589
  phase: "close",
1478
1590
  kind: "artifact_write",
1479
1591
  obligation: "closing",
1480
1592
  artifact: options.artifactsDir,
1481
- note: `Artifacts directory preserved for diagnosis (combinedTest.passed=${combinedTest.passed}, e2e.passed=${e2eResult.passed}, landingGates.passed=${landingGates.passed}, closing=${closingResult.status}, closingAction=${closingResult.action}, anyBlocked=${anyBlocked})`,
1593
+ note: `Artifacts directory preserved for diagnosis (combinedTest.passed=${combinedTest.passed}, e2e.passed=${e2eResult.passed}, landingGates.passed=${landingGates.passed}, closing=${closingResult.status}, closingAction=${closingResult.action}, anyBlocked=${anyBlocked}, unresolvedSources=${unresolvedSources}, unresolvedUnits=${unresolvedUnits})`,
1482
1594
  });
1483
1595
  return {};
1484
1596
  }
@@ -1548,158 +1660,85 @@ export async function cleanupTempBranchesAndArtifacts(options, completeState, co
1548
1660
  * items have at least one passing trace. "failed" otherwise.
1549
1661
  */
1550
1662
  export function buildVerificationReport(state, options, closingResult, combinedTest) {
1551
- const findings = [];
1552
- const findingsById = new Map((state.plan?.findings ?? []).map((f) => [f.id, f]));
1553
- for (const item of Object.values(state.items ?? {})) {
1554
- const finding = findingsById.get(item.finding_id);
1555
- const isResolved = isVerifiedCompleteStatus(item.status);
1556
- const isSkipped = isSkipStatus(item.status);
1557
- const traces = [];
1558
- const itemPassed = isResolved && combinedTest.passed;
1559
- if (isSkipped) {
1560
- // Ignored/inappropriate items are excluded from the run verdict — they
1561
- // get a single trace recording the user's settled decision and a
1562
- // first-class overall_status of "skipped" (see FindingVerificationTrace
1563
- // doc comment in src/shared/types/contractPipeline/verification.ts).
1564
- // The trace's own `status` stays "failed" because it did not verify
1565
- // anything (there is no passing evidence); the finding-level
1566
- // "skipped" is what excludes it from the report-level verdict below.
1663
+ const units = (state.plan?.units ?? []).map(unit => {
1664
+ const item = state.items?.[unit.id];
1665
+ const passed = !!item && isVerifiedCompleteStatus(item.status) && combinedTest.passed;
1666
+ const skipped = !!item && isSkipStatus(item.status);
1667
+ const traces = [{
1668
+ trace_id: `${unit.id}:combined-tests`, kind: "task", label: combinedTest.suite_name ?? "combined test suite",
1669
+ evidence: [combinedTest.ran ? combinedTest.passed ? "Suite passed" : combinedTest.output : "No combined suite ran"],
1670
+ status: combinedTest.ran && combinedTest.passed ? "passed" : "failed",
1671
+ }];
1672
+ for (const requirementId of unit.requirement_ids)
1567
1673
  traces.push({
1568
- trace_id: `${item.finding_id}:skipped`,
1569
- kind: "task",
1570
- label: item.status === "ignored" ? "ignored by user" : "deemed inappropriate",
1571
- evidence: [item.failure_reason ?? item.status],
1572
- status: "failed",
1573
- });
1574
- findings.push({
1575
- finding_id: item.finding_id,
1576
- traces,
1577
- overall_status: "skipped",
1674
+ trace_id: `${unit.id}:requirement:${requirementId}`, kind: "requirement", label: requirementId,
1675
+ evidence: [...(item?.host_result_evidence ?? [])], status: passed ? "passed" : "failed",
1578
1676
  });
1579
- continue;
1580
- }
1581
- // Combined test suite trace. A never-configured suite (`ran:false`) is
1582
- // NEVER labelled "passed" here — `combinedTest.passed` stays `true` for
1583
- // that case only so the pre-existing fullyGreen/itemPassed formulas below
1584
- // keep their vacuously-green behavior; this trace's own evidence/status is
1585
- // what tells a reader "nothing ran" apart from "it ran and passed"
1586
- // (`status` stays the file's established "failed" for any non-affirmative
1587
- // case — see the identical choice on the skipped-item trace above — the
1588
- // finding-level verdict is driven by `itemPassed`, not by this one trace).
1589
- const suiteLabel = combinedTest.suite_name ?? "combined test suite";
1590
- traces.push({
1591
- trace_id: `${item.finding_id}:combined-tests`,
1592
- kind: "task",
1593
- label: suiteLabel,
1594
- evidence: !combinedTest.ran
1595
- ? [combinedTest.output || "no combined test suite configured for this run"]
1596
- : combinedTest.passed
1597
- ? [`${suiteLabel} passed`]
1598
- : [`${suiteLabel} failed`, ...(combinedTest.output ? [combinedTest.output.slice(-500)] : [])],
1599
- status: combinedTest.ran && combinedTest.passed ? "passed" : "failed",
1600
- });
1601
- const contractGoalId = finding?.contract_goal_id ?? state.plan?.goal_id;
1602
- if (contractGoalId) {
1677
+ for (const [i, command] of unit.required_tests.entries())
1603
1678
  traces.push({
1604
- trace_id: `${item.finding_id}:contract-goal`,
1605
- kind: "requirement",
1606
- label: "contract-pipeline goal",
1607
- evidence: [`goal_id=${contractGoalId}`],
1608
- status: itemPassed ? "passed" : "failed",
1679
+ trace_id: `${unit.id}:command:${i}`, kind: "command", label: command,
1680
+ evidence: [...(item?.host_result_evidence ?? [])], status: passed ? "passed" : "failed",
1609
1681
  });
1610
- }
1611
- if (finding?.contract_obligation_ids?.length) {
1612
- traces.push({
1613
- trace_id: `${item.finding_id}:contract-obligations`,
1614
- kind: "requirement",
1615
- label: "contract-pipeline obligations satisfied by task",
1616
- evidence: finding.contract_obligation_ids,
1617
- status: itemPassed ? "passed" : "failed",
1618
- });
1619
- }
1620
- if (finding?.verification_obligation_ids?.length) {
1621
- traces.push({
1622
- trace_id: `${item.finding_id}:verification-obligations`,
1623
- kind: "invariant",
1624
- label: "contract-pipeline verification obligations",
1625
- evidence: finding.verification_obligation_ids,
1626
- status: itemPassed ? "passed" : "failed",
1627
- });
1628
- }
1629
- for (const [index, command] of (finding?.targeted_commands ?? []).entries()) {
1630
- traces.push({
1631
- trace_id: `${item.finding_id}:targeted-command-${index + 1}`,
1632
- kind: "command",
1633
- label: "implementation DAG targeted command",
1634
- evidence: [`planned command: ${command}`],
1635
- status: itemPassed ? "passed" : "failed",
1636
- });
1637
- }
1638
- // Verification result file evidence (verify_code_against_documentation).
1639
- const verificationResultPath = join(options.artifactsDir, `result_${item.finding_id}_verify_code_against_documentation.json`);
1682
+ const verificationResultPath = join(options.artifactsDir, `result_${unit.id}_verify_code_against_documentation.json`);
1640
1683
  if (existsSync(verificationResultPath)) {
1641
1684
  try {
1642
- const verRes = JSON.parse(readFileSync(verificationResultPath, "utf8"));
1643
- const evidence = Array.isArray(verRes.reason) ? verRes.reason : [];
1644
- traces.push({
1645
- trace_id: `${item.finding_id}:verify-doc`,
1646
- kind: "file",
1647
- label: `verify_code_against_documentation for ${item.finding_id}`,
1648
- evidence,
1649
- status: evidence.length > 0 ? "passed" : "failed",
1650
- });
1651
- }
1652
- catch {
1653
- // Non-fatal: evidence file malformed
1685
+ const result = JSON.parse(readFileSync(verificationResultPath, "utf8"));
1686
+ const evidence = Array.isArray(result.reason) ? result.reason.filter((v) => typeof v === "string") : [];
1687
+ traces.push({ trace_id: `${unit.id}:verify-doc`, kind: "file", label: "verify_code_against_documentation", evidence, status: evidence.length ? "passed" : "failed" });
1654
1688
  }
1689
+ catch { /* malformed optional evidence cannot establish a passing trace */ }
1655
1690
  }
1656
- // Closing action trace (one per finding so the report is self-contained).
1657
- // Status keys on the single-sourced completion classification: a skipped
1658
- // NON-none closing action did not complete, so its trace is red — not
1659
- // "passed" merely because it didn't literally report "failed"
1660
- // (COR-fb656e3f-2).
1661
- if (closingResult.action !== "none") {
1691
+ if (closingResult.action !== "none")
1662
1692
  traces.push({
1663
- trace_id: `${item.finding_id}:closing`,
1664
- kind: "command",
1665
- label: `closing action: ${closingResult.action}`,
1666
- evidence: [`status=${closingResult.status}`],
1667
- status: closingActionCompleted(closingResult) ? "passed" : "failed",
1693
+ trace_id: `${unit.id}:closing`, kind: "command", label: `closing action: ${closingResult.action}`,
1694
+ evidence: [`status=${closingResult.status}`], status: closingActionCompleted(closingResult) ? "passed" : "failed",
1668
1695
  });
1669
- }
1670
- findings.push({
1671
- finding_id: item.finding_id,
1672
- traces,
1673
- overall_status: itemPassed ? "passed" : "failed",
1674
- });
1675
- }
1676
- // Sort by finding_id for determinism.
1677
- findings.sort((a, b) => compareCodeUnits(a.finding_id, b.finding_id));
1678
- // Overall status: ignored/inappropriate (skipped) items do NOT contribute
1679
- // to failure — only resolved/non-skipped items count. The closing action's
1680
- // completion is part of the verdict (COR-fb656e3f): a run whose closing
1681
- // action failed or silently skipped a non-none action never reports an
1682
- // overall "passed".
1683
- const overallPassed = combinedTest.passed &&
1684
- closingActionCompleted(closingResult) &&
1685
- findings
1686
- .filter((f) => f.overall_status !== "skipped")
1687
- .every((f) => f.overall_status === "passed");
1688
- // Derive goal_id from the plan if available.
1689
- const goalId = state.plan?.goal_id;
1696
+ return { unit_id: unit.id, source_finding_ids: [...unit.source_finding_ids], traces,
1697
+ overall_status: skipped ? "skipped" : passed ? "passed" : "failed" };
1698
+ });
1699
+ const outcomeReport = buildRemediationOutcomesReport(state, closingResult);
1700
+ const findings = outcomeReport.outcomes.map(outcome => ({
1701
+ finding_id: outcome.finding_id,
1702
+ traces: [
1703
+ ...units.filter(unit => unit.source_finding_ids.includes(outcome.finding_id)).flatMap(unit => unit.traces),
1704
+ ...(outcome.evidence ? [{ trace_id: `${outcome.finding_id}:source-verification`, kind: "file",
1705
+ label: outcome.evidence.mechanism, evidence: [`${outcome.evidence.file}:${outcome.evidence.line}`, outcome.evidence.mechanism_detail ?? ""],
1706
+ status: outcome.outcome === "verified_already_fixed" || outcome.outcome === "refuted" ? "passed" : "failed" }] : []),
1707
+ ...(outcome.reason ? [{ trace_id: `${outcome.finding_id}:source-disposition`, kind: "task", label: outcome.outcome, evidence: [outcome.reason], status: "failed" }] : []),
1708
+ ],
1709
+ overall_status: outcome.outcome === "ignored" || outcome.outcome === "inappropriate" ? "skipped"
1710
+ : ["blocked", "pending", "deferred"].includes(outcome.outcome) ? "failed" : "passed",
1711
+ }));
1712
+ const requestUnfinished = outcomeReport.request_outcome && ["blocked", "pending", "deferred"].includes(outcomeReport.request_outcome.status);
1713
+ const overallPassed = !requestUnfinished && combinedTest.passed && closingActionCompleted(closingResult) &&
1714
+ units.every(unit => unit.overall_status !== "failed") && findings.every(finding => finding.overall_status !== "failed");
1690
1715
  return {
1691
1716
  contract_version: CONTRACT_PIPELINE_VERIFICATION_REPORT_VERSION,
1692
- ...(goalId ? { goal_id: goalId } : {}),
1693
- findings,
1717
+ ...(state.plan?.goal_id ? { goal_id: state.plan.goal_id } : {}),
1718
+ findings, units,
1694
1719
  overall_status: overallPassed ? "passed" : "failed",
1695
1720
  created_at: new Date().toISOString(),
1696
1721
  };
1697
1722
  }
1723
+ // sites-pinned: tests/remediate/close-plan-authority.test.ts
1724
+ /** Closing observes reviewed context, allowing only changes explained by accepted landed work. */
1725
+ async function assertClosingPlanAuthority(state, options) {
1726
+ const approved = await assertApprovedRuntimePlan(options.artifactsDir, state);
1727
+ const accepted = approved.canonical.plan.units.flatMap(unit => {
1728
+ const item = state.items?.[unit.id];
1729
+ return item?.host_landed_commit ? [{ allowed_files: [...unit.allowed_files], landed_commit: item.host_landed_commit }] : [];
1730
+ });
1731
+ const issues = await executionPlanContextIssues(options.root, approved.canonical, accepted);
1732
+ if (issues.length)
1733
+ throw new RemediationPlanAuthorityError(issues.join("; "));
1734
+ }
1698
1735
  export async function runClosePhase(state, options, runLogger) {
1699
1736
  console.log("Running Close Phase...");
1700
1737
  if (!state.plan || !state.items || !state.closing_plan) {
1701
1738
  throw new Error("Cannot run close phase: missing plan, items, or closing_plan from state.");
1702
1739
  }
1740
+ // Check before preview, verification, state mutation, action execution or promotion.
1741
+ await assertClosingPlanAuthority(state, options);
1703
1742
  // CDC-19 (advisory): a planned finding with no corresponding item in
1704
1743
  // state.items would be left uncounted by every module's INV-COVERAGE join
1705
1744
  // — a module that was blocked or never dispatched must not silently vanish
@@ -1707,7 +1746,7 @@ export async function runClosePhase(state, options, runLogger) {
1707
1746
  // runLogger is actually wired, so it changes nothing for a caller that
1708
1747
  // doesn't ask for it.
1709
1748
  {
1710
- const findingIds = new Set((state.plan.findings ?? []).map((f) => f.id));
1749
+ const findingIds = new Set((state.plan.units ?? []).map((unit) => unit.id));
1711
1750
  const itemIds = new Set(Object.keys(state.items));
1712
1751
  const uncounted = [...findingIds].filter((id) => !itemIds.has(id));
1713
1752
  if (uncounted.length > 0) {
@@ -1715,7 +1754,7 @@ export async function runClosePhase(state, options, runLogger) {
1715
1754
  phase: "close",
1716
1755
  kind: "error",
1717
1756
  obligation: "closing",
1718
- note: `${uncounted.length} planned finding(s) have no corresponding item in state.items and would be left uncounted by every module's coverage join: ${uncounted.slice(0, 10).join(", ")}`,
1757
+ note: `${uncounted.length} planned execution unit(s) have no corresponding item in state.items and would be left uncounted by every module's coverage join: ${uncounted.slice(0, 10).join(", ")}`,
1719
1758
  });
1720
1759
  }
1721
1760
  }
@@ -1735,8 +1774,17 @@ export async function runClosePhase(state, options, runLogger) {
1735
1774
  const updatedClosingPlan = { ...state.closing_plan, closing_action_preview: preview };
1736
1775
  return { ...state, closing_plan: updatedClosingPlan };
1737
1776
  }
1738
- // 2. Run the full test suite; on failure re-block resolved items and triage.
1739
- const combinedTest = await runCombinedTestSuite(state, options);
1777
+ // 2. Acceptance begins only after preview/host pauses. The floor owns its
1778
+ // failure: stop before the combined-suite triage branch can re-block items.
1779
+ const acceptance = await runCloseAcceptance({
1780
+ state, options, runLogger,
1781
+ runCombined: () => runCombinedTestSuite(state, options),
1782
+ });
1783
+ if (acceptance.gate && !acceptance.gate.passed) {
1784
+ await options.onFinalGateRed?.(acceptance.gate);
1785
+ return state;
1786
+ }
1787
+ const combinedTest = acceptance.combinedTest ?? await runCombinedTestSuite(state, options);
1740
1788
  if (!combinedTest.passed) {
1741
1789
  console.log("Full test suite failed. Transitioning back to triage.");
1742
1790
  if (blockResolvedItemsOnCombinedFailure(state, combinedTest.output)) {
@@ -1765,17 +1813,21 @@ export async function runClosePhase(state, options, runLogger) {
1765
1813
  if (analyzerVerify.ran) {
1766
1814
  const now = new Date().toISOString();
1767
1815
  for (const [findingId, verdict] of Object.entries(analyzerVerify.verdicts)) {
1768
- const item = state.items[findingId];
1769
- if (!item)
1770
- continue;
1771
- item.mechanical_verification = verdict;
1816
+ state.source_verifications ??= {};
1817
+ const source = state.plan.findings.find(finding => finding.id === findingId);
1818
+ state.source_verifications[findingId] = {
1819
+ ...state.source_verifications[findingId], mechanical_verification: verdict,
1820
+ review_revision_sha256: state.plan.review_revision_sha256, source_sha256: contentSha256(source),
1821
+ };
1772
1822
  if (verdict.status === "lead_persists") {
1773
- item.status = "blocked";
1774
- item.completed_at = now;
1775
- item.failure_reason =
1776
- `Mechanical re-verify: analyzer '${verdict.analyzer_id}' still reports this ` +
1777
- `finding's content-anchored lead identity after the fix (item C). The lead is ` +
1778
- `objective evidence — rework the fix or dispose the item in triage.`;
1823
+ for (const unit of state.plan.units.filter(unit => unit.source_finding_ids.includes(findingId))) {
1824
+ const item = state.items[unit.id];
1825
+ if (!item || !isVerifiedCompleteStatus(item.status))
1826
+ continue;
1827
+ item.status = "blocked";
1828
+ item.completed_at = now;
1829
+ item.failure_reason = `Mechanical re-verify: analyzer '${verdict.analyzer_id}' still reports source ${findingId}. Rework the linked execution unit.`;
1830
+ }
1779
1831
  }
1780
1832
  }
1781
1833
  if (analyzerVerify.persisting.length > 0) {
@@ -1870,6 +1922,12 @@ export async function runClosePhase(state, options, runLogger) {
1870
1922
  }
1871
1923
  }
1872
1924
  }
1925
+ // Verification may await arbitrary repository commands. Revalidate the same
1926
+ // current authority immediately before the consequential closing boundary.
1927
+ // Once the authorized action runs, its real outcome is persisted and reported;
1928
+ // expected publish/version effects do not retroactively revoke that operation.
1929
+ const contractReviews = await readContractReviewOutcomes(options.artifactsDir);
1930
+ await assertClosingPlanAuthority(state, options);
1873
1931
  // 4. Execute the closing action and record exact command outcomes before
1874
1932
  // reporting success.
1875
1933
  console.log(`Executing closing action: ${state.closing_plan.action}`);
@@ -1889,8 +1947,14 @@ export async function runClosePhase(state, options, runLogger) {
1889
1947
  // set: the alternative makes "the ledger was unreadable" report as "this run
1890
1948
  // was clean", which is the collapse the ledger exists to prevent.
1891
1949
  const recovery = await readRunRecoveryFacts(options.artifactsDir, state);
1950
+ // Hydrate original coverage-only sources before ANY outcome or verification
1951
+ // projection. Reading them after building outcomes silently loses excluded
1952
+ // originals from the otherwise self-contained source report.
1953
+ const outcomeCoverage = await buildOutcomeCoverageLedger(state, options);
1954
+ if (outcomeCoverage)
1955
+ state.plan_coverage = outcomeCoverage;
1892
1956
  const finalGate = await readFinalGateReport(options.artifactsDir);
1893
- const outcomesReport = buildRemediationOutcomesReport(state, closingResult, finalGate, recovery, await readContractReviewOutcomes(options.artifactsDir));
1957
+ const outcomesReport = buildRemediationOutcomesReport(state, closingResult, finalGate, recovery, contractReviews);
1894
1958
  // No run-log line for the gate here ON PURPOSE: the gate already records its
1895
1959
  // own outcome at evaluation time (`recordFinalGateOutcome`), so a second line
1896
1960
  // at close would be a duplicate — and `kind: "outcome"` at close means "one
@@ -1914,10 +1978,6 @@ export async function runClosePhase(state, options, runLogger) {
1914
1978
  : { reflections: [], discarded: [] };
1915
1979
  reportDiscardedReflections(closeFeedback.discarded, "remediate-code");
1916
1980
  const reportContent = buildRemediationReportMarkdown(state, entries, closingResult, e2eResult.ran ? e2eResult.passed : undefined, outcomesReport, combinedTest, closeFeedback.reflections, landingGates);
1917
- // Enrich the coverage ledger with never-planned payloads NOW, from the live
1918
- // state and intake artifacts — both are deleted at the end of close, so this
1919
- // must happen strictly before cleanup.
1920
- const outcomeCoverage = await buildOutcomeCoverageLedger(state, options);
1921
1981
  const outcomesFile = {
1922
1982
  ...outcomesReport,
1923
1983
  ...(state.started_at ? { started_at: state.started_at } : {}),
@@ -1976,7 +2036,7 @@ export async function runClosePhase(state, options, runLogger) {
1976
2036
  });
1977
2037
  console.log("Remediation report generated.");
1978
2038
  // 6. Clean up temporary branches and artifact directory (only when fully green).
1979
- await cleanupTempBranchesAndArtifacts(options, completeState, combinedTest, e2eResult, closingResult, runLogger, landingGates);
2039
+ await cleanupTempBranchesAndArtifacts(options, completeState, combinedTest, e2eResult, closingResult, runLogger, landingGates, outcomesReport);
1980
2040
  return completeState;
1981
2041
  }
1982
2042
  //# sourceMappingURL=close.js.map