audit-tools 0.51.11 → 0.52.1

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 (282) hide show
  1. package/dist/audit/cli/auditStep.d.ts.map +1 -1
  2. package/dist/audit/cli/auditStep.js +2 -2
  3. package/dist/audit/cli/auditStep.js.map +1 -1
  4. package/dist/audit/cli/charterClarificationPrompt.d.ts +16 -8
  5. package/dist/audit/cli/charterClarificationPrompt.d.ts.map +1 -1
  6. package/dist/audit/cli/charterClarificationPrompt.js +95 -42
  7. package/dist/audit/cli/charterClarificationPrompt.js.map +1 -1
  8. package/dist/audit/cli/charterComparisonPrompt.d.ts +16 -6
  9. package/dist/audit/cli/charterComparisonPrompt.d.ts.map +1 -1
  10. package/dist/audit/cli/charterComparisonPrompt.js +76 -29
  11. package/dist/audit/cli/charterComparisonPrompt.js.map +1 -1
  12. package/dist/audit/cli/charterExtractionPrompt.d.ts +5 -4
  13. package/dist/audit/cli/charterExtractionPrompt.d.ts.map +1 -1
  14. package/dist/audit/cli/charterExtractionPrompt.js +64 -42
  15. package/dist/audit/cli/charterExtractionPrompt.js.map +1 -1
  16. package/dist/audit/cli/charterFidelityPrompt.d.ts +17 -4
  17. package/dist/audit/cli/charterFidelityPrompt.d.ts.map +1 -1
  18. package/dist/audit/cli/charterFidelityPrompt.js +72 -21
  19. package/dist/audit/cli/charterFidelityPrompt.js.map +1 -1
  20. package/dist/audit/cli/dispatch/hostHandoff.d.ts +23 -1
  21. package/dist/audit/cli/dispatch/hostHandoff.d.ts.map +1 -1
  22. package/dist/audit/cli/dispatch/hostHandoff.js +204 -41
  23. package/dist/audit/cli/dispatch/hostHandoff.js.map +1 -1
  24. package/dist/audit/cli/dispatch/packetFilter.d.ts +8 -0
  25. package/dist/audit/cli/dispatch/packetFilter.d.ts.map +1 -1
  26. package/dist/audit/cli/fanoutLanes.d.ts.map +1 -1
  27. package/dist/audit/cli/fanoutLanes.js +20 -4
  28. package/dist/audit/cli/fanoutLanes.js.map +1 -1
  29. package/dist/audit/cli/laneValidators.d.ts +19 -7
  30. package/dist/audit/cli/laneValidators.d.ts.map +1 -1
  31. package/dist/audit/cli/laneValidators.js +73 -18
  32. package/dist/audit/cli/laneValidators.js.map +1 -1
  33. package/dist/audit/cli/nextStepCommand.d.ts.map +1 -1
  34. package/dist/audit/cli/nextStepCommand.js +13 -6
  35. package/dist/audit/cli/nextStepCommand.js.map +1 -1
  36. package/dist/audit/cli/nextStepHelpers.d.ts +25 -2
  37. package/dist/audit/cli/nextStepHelpers.d.ts.map +1 -1
  38. package/dist/audit/cli/nextStepHelpers.js +105 -35
  39. package/dist/audit/cli/nextStepHelpers.js.map +1 -1
  40. package/dist/audit/cli/recoverSubmissionCommand.d.ts.map +1 -1
  41. package/dist/audit/cli/recoverSubmissionCommand.js +89 -4
  42. package/dist/audit/cli/recoverSubmissionCommand.js.map +1 -1
  43. package/dist/audit/cli/sampleRunCommand.d.ts +10 -0
  44. package/dist/audit/cli/sampleRunCommand.d.ts.map +1 -1
  45. package/dist/audit/cli/semanticReviewStep.d.ts +4 -4
  46. package/dist/audit/cli/semanticReviewStep.d.ts.map +1 -1
  47. package/dist/audit/cli/semanticReviewStep.js +35 -71
  48. package/dist/audit/cli/semanticReviewStep.js.map +1 -1
  49. package/dist/audit/contracts/findingContractPrompt.d.ts.map +1 -1
  50. package/dist/audit/contracts/findingContractPrompt.js +7 -1
  51. package/dist/audit/contracts/findingContractPrompt.js.map +1 -1
  52. package/dist/audit/contracts/workerSchemas.d.ts +280 -4
  53. package/dist/audit/contracts/workerSchemas.d.ts.map +1 -1
  54. package/dist/audit/contracts/workerSchemas.js +24 -2
  55. package/dist/audit/contracts/workerSchemas.js.map +1 -1
  56. package/dist/audit/orchestrator/charterClarificationExecutor.d.ts.map +1 -1
  57. package/dist/audit/orchestrator/charterClarificationExecutor.js +39 -0
  58. package/dist/audit/orchestrator/charterClarificationExecutor.js.map +1 -1
  59. package/dist/audit/orchestrator/charterExtractionExecutor.d.ts +12 -5
  60. package/dist/audit/orchestrator/charterExtractionExecutor.d.ts.map +1 -1
  61. package/dist/audit/orchestrator/charterExtractionExecutor.js +39 -9
  62. package/dist/audit/orchestrator/charterExtractionExecutor.js.map +1 -1
  63. package/dist/audit/orchestrator/designReviewPrompt.d.ts +7 -0
  64. package/dist/audit/orchestrator/designReviewPrompt.d.ts.map +1 -1
  65. package/dist/audit/orchestrator/designReviewPrompt.js +174 -19
  66. package/dist/audit/orchestrator/designReviewPrompt.js.map +1 -1
  67. package/dist/audit/orchestrator/executorRunners.d.ts.map +1 -1
  68. package/dist/audit/orchestrator/executorRunners.js +14 -3
  69. package/dist/audit/orchestrator/executorRunners.js.map +1 -1
  70. package/dist/audit/orchestrator/intentCheckpointGate.d.ts.map +1 -1
  71. package/dist/audit/orchestrator/intentCheckpointGate.js +1 -5
  72. package/dist/audit/orchestrator/intentCheckpointGate.js.map +1 -1
  73. package/dist/audit/orchestrator/requeueCommand.d.ts +8 -0
  74. package/dist/audit/orchestrator/requeueCommand.d.ts.map +1 -1
  75. package/dist/audit/orchestrator/selectiveDeepening/lensVerification.d.ts.map +1 -1
  76. package/dist/audit/orchestrator/selectiveDeepening/lensVerification.js +94 -71
  77. package/dist/audit/orchestrator/selectiveDeepening/lensVerification.js.map +1 -1
  78. package/dist/audit/orchestrator/selectiveDeepening/shared.d.ts +0 -2
  79. package/dist/audit/orchestrator/selectiveDeepening/shared.d.ts.map +1 -1
  80. package/dist/audit/orchestrator/selectiveDeepening/shared.js +9 -2
  81. package/dist/audit/orchestrator/selectiveDeepening/shared.js.map +1 -1
  82. package/dist/audit/orchestrator/selectiveDeepening/stewardFollowup.d.ts.map +1 -1
  83. package/dist/audit/orchestrator/selectiveDeepening/stewardFollowup.js +28 -4
  84. package/dist/audit/orchestrator/selectiveDeepening/stewardFollowup.js.map +1 -1
  85. package/dist/audit/orchestrator/synthesisExecutors.d.ts +10 -3
  86. package/dist/audit/orchestrator/synthesisExecutors.d.ts.map +1 -1
  87. package/dist/audit/orchestrator/synthesisExecutors.js +5 -4
  88. package/dist/audit/orchestrator/synthesisExecutors.js.map +1 -1
  89. package/dist/audit/reporting/synthesis.d.ts +23 -6
  90. package/dist/audit/reporting/synthesis.d.ts.map +1 -1
  91. package/dist/audit/reporting/synthesis.js +53 -13
  92. package/dist/audit/reporting/synthesis.js.map +1 -1
  93. package/dist/audit/reporting/synthesisNarrativePrompt.d.ts +26 -1
  94. package/dist/audit/reporting/synthesisNarrativePrompt.d.ts.map +1 -1
  95. package/dist/audit/reporting/synthesisNarrativePrompt.js +53 -13
  96. package/dist/audit/reporting/synthesisNarrativePrompt.js.map +1 -1
  97. package/dist/audit/systemic/secondOrderAdversaryPrompt.d.ts.map +1 -1
  98. package/dist/audit/systemic/secondOrderAdversaryPrompt.js +67 -40
  99. package/dist/audit/systemic/secondOrderAdversaryPrompt.js.map +1 -1
  100. package/dist/audit/systemic/systemicChallengeLoop.d.ts.map +1 -1
  101. package/dist/audit/systemic/systemicChallengeLoop.js +13 -1
  102. package/dist/audit/systemic/systemicChallengeLoop.js.map +1 -1
  103. package/dist/audit/types/conceptualAdjudication.d.ts +26 -2
  104. package/dist/audit/types/conceptualAdjudication.d.ts.map +1 -1
  105. package/dist/audit/types/conceptualAdjudication.js +26 -7
  106. package/dist/audit/types/conceptualAdjudication.js.map +1 -1
  107. package/dist/audit/types.d.ts +279 -0
  108. package/dist/audit/types.d.ts.map +1 -1
  109. package/dist/audit/types.js +52 -0
  110. package/dist/audit/types.js.map +1 -1
  111. package/dist/audit/validation/auditResults.d.ts.map +1 -1
  112. package/dist/audit/validation/auditResults.js +51 -6
  113. package/dist/audit/validation/auditResults.js.map +1 -1
  114. package/dist/audit/validation/ingestIssueCodes.d.ts +3 -1
  115. package/dist/audit/validation/ingestIssueCodes.d.ts.map +1 -1
  116. package/dist/audit/validation/ingestIssueCodes.js +27 -15
  117. package/dist/audit/validation/ingestIssueCodes.js.map +1 -1
  118. package/dist/remediate/contractPipeline/sketchSource.d.ts +13 -3
  119. package/dist/remediate/contractPipeline/sketchSource.d.ts.map +1 -1
  120. package/dist/remediate/contractPipeline/sketchSource.js +14 -8
  121. package/dist/remediate/contractPipeline/sketchSource.js.map +1 -1
  122. package/dist/remediate/intake.d.ts +140 -36
  123. package/dist/remediate/intake.d.ts.map +1 -1
  124. package/dist/remediate/intake.js +159 -38
  125. package/dist/remediate/intake.js.map +1 -1
  126. package/dist/remediate/intent/checkpointFilter.d.ts +1 -2
  127. package/dist/remediate/intent/checkpointFilter.d.ts.map +1 -1
  128. package/dist/remediate/intent/checkpointFilter.js +1 -5
  129. package/dist/remediate/intent/checkpointFilter.js.map +1 -1
  130. package/dist/remediate/phases/close.d.ts.map +1 -1
  131. package/dist/remediate/phases/close.js +8 -8
  132. package/dist/remediate/phases/close.js.map +1 -1
  133. package/dist/remediate/phases/closeVerifyHeadEvidence.d.ts +19 -35
  134. package/dist/remediate/phases/closeVerifyHeadEvidence.d.ts.map +1 -1
  135. package/dist/remediate/phases/closeVerifyHeadEvidence.js +38 -12
  136. package/dist/remediate/phases/closeVerifyHeadEvidence.js.map +1 -1
  137. package/dist/remediate/phases/plan.d.ts +1 -1
  138. package/dist/remediate/review/reviewGate.d.ts +68 -39
  139. package/dist/remediate/review/reviewGate.d.ts.map +1 -1
  140. package/dist/remediate/review/reviewGate.js +133 -50
  141. package/dist/remediate/review/reviewGate.js.map +1 -1
  142. package/dist/remediate/state/store.d.ts +1 -2
  143. package/dist/remediate/state/store.d.ts.map +1 -1
  144. package/dist/remediate/state/store.js +124 -3
  145. package/dist/remediate/state/store.js.map +1 -1
  146. package/dist/remediate/state/types.d.ts +59 -8
  147. package/dist/remediate/state/types.d.ts.map +1 -1
  148. package/dist/remediate/state/types.js +12 -1
  149. package/dist/remediate/state/types.js.map +1 -1
  150. package/dist/remediate/steps/contractPipeline.d.ts +40 -2
  151. package/dist/remediate/steps/contractPipeline.d.ts.map +1 -1
  152. package/dist/remediate/steps/contractPipeline.js +147 -53
  153. package/dist/remediate/steps/contractPipeline.js.map +1 -1
  154. package/dist/remediate/steps/contractPipelinePrompts.d.ts +32 -8
  155. package/dist/remediate/steps/contractPipelinePrompts.d.ts.map +1 -1
  156. package/dist/remediate/steps/contractPipelinePrompts.js +145 -131
  157. package/dist/remediate/steps/contractPipelinePrompts.js.map +1 -1
  158. package/dist/remediate/steps/dispatch/hostHandoff.d.ts +6 -4
  159. package/dist/remediate/steps/dispatch/hostHandoff.d.ts.map +1 -1
  160. package/dist/remediate/steps/dispatch/hostHandoff.js +291 -155
  161. package/dist/remediate/steps/dispatch/hostHandoff.js.map +1 -1
  162. package/dist/remediate/steps/intakeResolver.d.ts +1 -1
  163. package/dist/remediate/steps/intakeResolver.d.ts.map +1 -1
  164. package/dist/remediate/steps/intakeResolver.js +50 -49
  165. package/dist/remediate/steps/intakeResolver.js.map +1 -1
  166. package/dist/remediate/steps/nextStep.d.ts.map +1 -1
  167. package/dist/remediate/steps/nextStep.js +337 -294
  168. package/dist/remediate/steps/nextStep.js.map +1 -1
  169. package/dist/remediate/steps/prompts.d.ts +10 -3
  170. package/dist/remediate/steps/prompts.d.ts.map +1 -1
  171. package/dist/remediate/steps/prompts.js +191 -228
  172. package/dist/remediate/steps/prompts.js.map +1 -1
  173. package/dist/remediate/steps/types.d.ts +2 -2
  174. package/dist/remediate/steps/types.d.ts.map +1 -1
  175. package/dist/remediate/steps/types.js +10 -3
  176. package/dist/remediate/steps/types.js.map +1 -1
  177. package/dist/remediate/validation/artifacts.d.ts.map +1 -1
  178. package/dist/remediate/validation/artifacts.js +5 -22
  179. package/dist/remediate/validation/artifacts.js.map +1 -1
  180. package/dist/remediate/validation/contractPipeline.d.ts.map +1 -1
  181. package/dist/remediate/validation/contractPipeline.js +25 -8
  182. package/dist/remediate/validation/contractPipeline.js.map +1 -1
  183. package/dist/remediate/validation/remediationState.d.ts +0 -1
  184. package/dist/remediate/validation/remediationState.d.ts.map +1 -1
  185. package/dist/remediate/validation/remediationState.js +1 -20
  186. package/dist/remediate/validation/remediationState.js.map +1 -1
  187. package/dist/shared/decompose/charterClarification.d.ts +7 -0
  188. package/dist/shared/decompose/charterClarification.d.ts.map +1 -1
  189. package/dist/shared/decompose/charterClarification.js +8 -1
  190. package/dist/shared/decompose/charterClarification.js.map +1 -1
  191. package/dist/shared/decompose/charterExtraction.d.ts +204 -16
  192. package/dist/shared/decompose/charterExtraction.d.ts.map +1 -1
  193. package/dist/shared/decompose/charterExtraction.js +70 -13
  194. package/dist/shared/decompose/charterExtraction.js.map +1 -1
  195. package/dist/shared/decompose/systemicChallenge.d.ts +42 -1
  196. package/dist/shared/decompose/systemicChallenge.d.ts.map +1 -1
  197. package/dist/shared/decompose/systemicChallenge.js +51 -0
  198. package/dist/shared/decompose/systemicChallenge.js.map +1 -1
  199. package/dist/shared/git.d.ts +31 -0
  200. package/dist/shared/git.d.ts.map +1 -1
  201. package/dist/shared/git.js +105 -0
  202. package/dist/shared/git.js.map +1 -1
  203. package/dist/shared/index.d.ts +17 -14
  204. package/dist/shared/index.d.ts.map +1 -1
  205. package/dist/shared/index.js +18 -11
  206. package/dist/shared/index.js.map +1 -1
  207. package/dist/shared/io/stepContractWriter.d.ts.map +1 -1
  208. package/dist/shared/io/stepContractWriter.js +13 -4
  209. package/dist/shared/io/stepContractWriter.js.map +1 -1
  210. package/dist/shared/reporting/auditDeliverable.d.ts +8 -3
  211. package/dist/shared/reporting/auditDeliverable.d.ts.map +1 -1
  212. package/dist/shared/reporting/auditDeliverable.js +10 -3
  213. package/dist/shared/reporting/auditDeliverable.js.map +1 -1
  214. package/dist/shared/submission/hostHandoffCore.d.ts +7 -0
  215. package/dist/shared/submission/hostHandoffCore.d.ts.map +1 -1
  216. package/dist/shared/submission/hostHandoffCore.js +15 -2
  217. package/dist/shared/submission/hostHandoffCore.js.map +1 -1
  218. package/dist/shared/submission/hostResultOutcomes.d.ts +60 -10
  219. package/dist/shared/submission/hostResultOutcomes.d.ts.map +1 -1
  220. package/dist/shared/submission/hostResultOutcomes.js +42 -10
  221. package/dist/shared/submission/hostResultOutcomes.js.map +1 -1
  222. package/dist/shared/submission/ingestReport.d.ts +54 -0
  223. package/dist/shared/submission/ingestReport.d.ts.map +1 -0
  224. package/dist/shared/submission/ingestReport.js +44 -0
  225. package/dist/shared/submission/ingestReport.js.map +1 -0
  226. package/dist/shared/submission/ingestionChecks.d.ts +8 -13
  227. package/dist/shared/submission/ingestionChecks.d.ts.map +1 -1
  228. package/dist/shared/submission/ingestionChecks.js +9 -14
  229. package/dist/shared/submission/ingestionChecks.js.map +1 -1
  230. package/dist/shared/submission/submissionClassifier.d.ts +6 -0
  231. package/dist/shared/submission/submissionClassifier.d.ts.map +1 -1
  232. package/dist/shared/submission/submissionClassifier.js +16 -0
  233. package/dist/shared/submission/submissionClassifier.js.map +1 -1
  234. package/dist/shared/submission/workerPromptBinding.d.ts +18 -0
  235. package/dist/shared/submission/workerPromptBinding.d.ts.map +1 -0
  236. package/dist/shared/submission/workerPromptBinding.js +38 -0
  237. package/dist/shared/submission/workerPromptBinding.js.map +1 -0
  238. package/dist/shared/tooling/exec.d.ts +24 -0
  239. package/dist/shared/tooling/exec.d.ts.map +1 -1
  240. package/dist/shared/tooling/exec.js +30 -0
  241. package/dist/shared/tooling/exec.js.map +1 -1
  242. package/dist/shared/types/charter.d.ts +143 -7
  243. package/dist/shared/types/charter.d.ts.map +1 -1
  244. package/dist/shared/types/charter.js +63 -7
  245. package/dist/shared/types/charter.js.map +1 -1
  246. package/dist/shared/types/charterPacket.d.ts +13 -0
  247. package/dist/shared/types/charterPacket.d.ts.map +1 -1
  248. package/dist/shared/types/charterPacket.js.map +1 -1
  249. package/dist/shared/types/contractPipeline/obligations.d.ts +30 -11
  250. package/dist/shared/types/contractPipeline/obligations.d.ts.map +1 -1
  251. package/dist/shared/types/contractPipeline/obligations.js +6 -1
  252. package/dist/shared/types/contractPipeline/obligations.js.map +1 -1
  253. package/dist/shared/types/contractPropertyShapes.d.ts.map +1 -1
  254. package/dist/shared/types/contractPropertyShapes.js +13 -0
  255. package/dist/shared/types/contractPropertyShapes.js.map +1 -1
  256. package/dist/shared/types/finding.d.ts +210 -15
  257. package/dist/shared/types/finding.d.ts.map +1 -1
  258. package/dist/shared/types/finding.js +68 -5
  259. package/dist/shared/types/finding.js.map +1 -1
  260. package/dist/shared/types/intentCheckpoint.d.ts +28 -42
  261. package/dist/shared/types/intentCheckpoint.d.ts.map +1 -1
  262. package/dist/shared/types/intentCheckpoint.js +30 -33
  263. package/dist/shared/types/intentCheckpoint.js.map +1 -1
  264. package/dist/shared/types/remediationOutcome.d.ts +8 -8
  265. package/dist/shared/validation/citationGrounding.d.ts +29 -5
  266. package/dist/shared/validation/citationGrounding.d.ts.map +1 -1
  267. package/dist/shared/validation/citationGrounding.js +56 -9
  268. package/dist/shared/validation/citationGrounding.js.map +1 -1
  269. package/dist/shared/validation/designFindingGrounding.d.ts +30 -9
  270. package/dist/shared/validation/designFindingGrounding.d.ts.map +1 -1
  271. package/dist/shared/validation/designFindingGrounding.js +32 -12
  272. package/dist/shared/validation/designFindingGrounding.js.map +1 -1
  273. package/dist/shared/validation/findingsReport.d.ts +13 -2
  274. package/dist/shared/validation/findingsReport.d.ts.map +1 -1
  275. package/dist/shared/validation/findingsReport.js +19 -2
  276. package/dist/shared/validation/findingsReport.js.map +1 -1
  277. package/docs/audit-pkg/contracts.md +4 -5
  278. package/package.json +1 -1
  279. package/schemas/audit_result.schema.json +51 -0
  280. package/schemas/audit_results.schema.json +51 -0
  281. package/schemas/audit_task.schema.json +44 -0
  282. package/schemas/finding.schema.json +4 -0
@@ -1,3 +1,4 @@
1
+ import { z } from "zod";
1
2
  import type { Finding, FindingSeverity, FindingConfidence } from "audit-tools/shared";
2
3
  import { type ReviewNecessity, type ImplementationCost } from "./reviewNecessity.js";
3
4
  export declare const REVIEW_REQUEST_SCHEMA_VERSION: "remediate-code-review-request/v1";
@@ -31,18 +32,59 @@ export interface ReviewRequest {
31
32
  /** Non-empty tiers only, ordered most-review-needed first. */
32
33
  tiers: ReviewTierGroup[];
33
34
  }
34
- /** The user's verdict (`review_resolution.json`). */
35
- export interface ReviewResolution {
36
- plan_id?: string;
37
- /** Finding ids the user disapproved — do NOT act on these. */
38
- disapproved_findings?: string[];
39
- /** Whole tiers the user disapproved (e.g. "decline everything strategic"). */
40
- disapproved_tiers?: ReviewNecessity[];
35
+ /**
36
+ * The user's verdict (`review_resolution.json`). Strict at every level: the
37
+ * gate's default is APPROVE, so a field the tool does not read (a mistyped
38
+ * name, the retired `disapproved_*` names) would turn the user's decline into
39
+ * an approval in silence. An unknown field refuses the whole file instead.
40
+ */
41
+ declare const ReviewResolutionSchema: z.ZodObject<{
42
+ plan_id: z.ZodOptional<z.ZodString>;
43
+ /** Findings the user declined — do NOT act on these. */
44
+ declined_findings: z.ZodOptional<z.ZodArray<z.ZodObject<{
45
+ finding_id: z.ZodString;
46
+ /** The user's reason, in their words. Blank reads as absent. */
47
+ reason: z.ZodOptional<z.ZodString>;
48
+ }, "strict", z.ZodTypeAny, {
49
+ finding_id: string;
50
+ reason?: string | undefined;
51
+ }, {
52
+ finding_id: string;
53
+ reason?: string | undefined;
54
+ }>, "many">>;
55
+ /** Whole tiers the user declined (e.g. "decline everything strategic"). */
56
+ declined_tiers: z.ZodOptional<z.ZodArray<z.ZodString, "many">>;
57
+ }, "strict", z.ZodTypeAny, {
58
+ plan_id?: string | undefined;
59
+ declined_findings?: {
60
+ finding_id: string;
61
+ reason?: string | undefined;
62
+ }[] | undefined;
63
+ declined_tiers?: string[] | undefined;
64
+ }, {
65
+ plan_id?: string | undefined;
66
+ declined_findings?: {
67
+ finding_id: string;
68
+ reason?: string | undefined;
69
+ }[] | undefined;
70
+ declined_tiers?: string[] | undefined;
71
+ }>;
72
+ export type ReviewResolution = z.infer<typeof ReviewResolutionSchema>;
73
+ export type ParsedReviewResolution = {
74
+ kind: "ok";
75
+ resolution: ReviewResolution;
41
76
  }
77
+ /** A resolution from another run (its `plan_id` names a different request). */
78
+ | {
79
+ kind: "stale";
80
+ } | {
81
+ kind: "refused";
82
+ reason: string;
83
+ };
42
84
  export interface ReviewDecision {
43
85
  /** Finding ids approved to proceed to implementation. */
44
86
  approved_ids: string[];
45
- /** Disapproved items, each with the recorded reason for its terminal disposition. */
87
+ /** Declined items, each with the recorded reason for its terminal disposition. */
46
88
  declined: Array<{
47
89
  finding_id: string;
48
90
  reason: string;
@@ -56,44 +98,31 @@ export interface ReviewDecision {
56
98
  */
57
99
  export declare function buildReviewRequest(findings: readonly Finding[], planId: string): ReviewRequest;
58
100
  /**
59
- * Whether `resolution` answers THIS `request` (INV-RSM-RESOLUTION-CORRELATE).
60
- * An absent resolution or an absent `plan_id` correlates (host-lenient: the
61
- * single-run common case writes no plan_id); a PRESENT plan_id that differs
62
- * from the request's marks a stale leftover from another run — the caller must
63
- * archive it and re-halt rather than apply a cross-run answer.
101
+ * Read the text of `review_resolution.json` against the request it answers.
102
+ * The WHOLE file is refused — never partly applied — when it is not valid
103
+ * JSON, has a field the schema does not name (the retired `disapproved_*`
104
+ * names get their replacement stated), has a wrong type, or names an id or a
105
+ * tier outside the request. The reason names each problem. A file whose
106
+ * `plan_id` names a different request is `stale`: it answers another run.
64
107
  */
65
- export declare function isResolutionForRequest(request: ReviewRequest, resolution: ReviewResolution | null | undefined): boolean;
66
- /**
67
- * Screen a resolution's id references against the request (uniform id-join
68
- * contract): every `disapproved_findings` entry must name an item in the
69
- * request, and every `disapproved_tiers` entry must be one of the closed
70
- * review-necessity names. A stray id here is not a no-op — the gate's default
71
- * is APPROVE, so a typo'd decline would silently become an approval. Empty
72
- * arrays = clean. A tier that is valid but empty in this request stays a
73
- * harmless no-op (it names a real vocabulary member, not a phantom item).
74
- */
75
- export declare function screenResolutionIds(request: ReviewRequest, resolution: ReviewResolution | null | undefined): {
76
- unknown_finding_ids: string[];
77
- unknown_tiers: string[];
78
- valid_finding_ids: string[];
79
- };
108
+ export declare function parseReviewResolution(text: string, request: ReviewRequest): ParsedReviewResolution;
80
109
  /**
81
110
  * Apply the user's resolution to a request: every item is either approved (act
82
111
  * on it) or declined (recorded terminal disposition with a reason). An item is
83
- * declined if its id is in `disapproved_findings` OR its tier is in
84
- * `disapproved_tiers`. Everything else is approved — the default is to act,
112
+ * declined if its id is in `declined_findings` OR its tier is in
113
+ * `declined_tiers`. Everything else is approved — the default is to act,
85
114
  * because the gate's job is to let the user REMOVE items, not to require
86
115
  * opting every item in. An absent/empty resolution approves everything.
87
116
  *
88
- * A resolution carrying a MISMATCHED `plan_id` is rejected (throws): applying a
89
- * stale cross-run answer would approve/decline the wrong finding set
90
- * (INV-RSM-RESOLUTION-CORRELATE, COR-0b906e37). Callers pre-screen with
91
- * {@link isResolutionForRequest} to archive-and-re-halt instead of crashing;
92
- * the throw here is the mechanical backstop, not the primary UX.
117
+ * The resolution must come from {@link parseReviewResolution}; a stale
118
+ * `plan_id` or an unknown id still throws here as the mechanical backstop
119
+ * (INV-RSM-RESOLUTION-CORRELATE, COR-0b906e37), never the primary UX.
93
120
  *
94
- * Crucially, declined items are returned with an explicit reason so the caller
95
- * records a terminal disposition (e.g. `ignored`) rather than silently closing
96
- * them — the exact failure this gate exists to prevent.
121
+ * Crucially, declined items are returned with an explicit reason — the user's
122
+ * own words when they gave one — so the caller records a terminal disposition
123
+ * (e.g. `ignored`) rather than silently closing them, the exact failure this
124
+ * gate exists to prevent.
97
125
  */
98
- export declare function applyReviewResolution(request: ReviewRequest, resolution: ReviewResolution | null | undefined): ReviewDecision;
126
+ export declare function applyReviewResolution(request: ReviewRequest, resolution: ReviewResolution | undefined): ReviewDecision;
127
+ export {};
99
128
  //# sourceMappingURL=reviewGate.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"reviewGate.d.ts","sourceRoot":"","sources":["../../../src/remediate/review/reviewGate.ts"],"names":[],"mappings":"AAmBA,OAAO,KAAK,EAAE,OAAO,EAAE,eAAe,EAAE,iBAAiB,EAAE,MAAM,oBAAoB,CAAC;AACtF,OAAO,EACL,KAAK,eAAe,EACpB,KAAK,kBAAkB,EAKxB,MAAM,sBAAsB,CAAC;AAE9B,eAAO,MAAM,6BAA6B,EAAG,kCAA2C,CAAC;AAEzF,+EAA+E;AAC/E,MAAM,WAAW,eAAe;IAC9B,UAAU,EAAE,MAAM,CAAC;IACnB,KAAK,EAAE,MAAM,CAAC;IACd,QAAQ,EAAE,eAAe,CAAC;IAC1B,UAAU,EAAE,iBAAiB,CAAC;IAC9B,IAAI,EAAE,MAAM,CAAC;IACb,OAAO,EAAE,MAAM,CAAC;IAChB,cAAc,EAAE,MAAM,EAAE,CAAC;IACzB,SAAS,EAAE,eAAe,CAAC;IAC3B,yDAAyD;IACzD,SAAS,EAAE,MAAM,CAAC;IAClB,mBAAmB,EAAE,kBAAkB,CAAC;CACzC;AAED,+EAA+E;AAC/E,MAAM,WAAW,eAAe;IAC9B,SAAS,EAAE,eAAe,CAAC;IAC3B,KAAK,EAAE,MAAM,CAAC;IACd,WAAW,EAAE,MAAM,CAAC;IACpB,KAAK,EAAE,eAAe,EAAE,CAAC;CAC1B;AAED,uEAAuE;AACvE,MAAM,WAAW,aAAa;IAC5B,cAAc,EAAE,OAAO,6BAA6B,CAAC;IACrD,OAAO,EAAE,MAAM,CAAC;IAChB,KAAK,EAAE,MAAM,CAAC;IACd,MAAM,EAAE,MAAM,CAAC,eAAe,EAAE,MAAM,CAAC,CAAC;IACxC,8DAA8D;IAC9D,KAAK,EAAE,eAAe,EAAE,CAAC;CAC1B;AAED,qDAAqD;AACrD,MAAM,WAAW,gBAAgB;IAC/B,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,8DAA8D;IAC9D,oBAAoB,CAAC,EAAE,MAAM,EAAE,CAAC;IAChC,8EAA8E;IAC9E,iBAAiB,CAAC,EAAE,eAAe,EAAE,CAAC;CACvC;AAED,MAAM,WAAW,cAAc;IAC7B,yDAAyD;IACzD,YAAY,EAAE,MAAM,EAAE,CAAC;IACvB,qFAAqF;IACrF,QAAQ,EAAE,KAAK,CAAC;QAAE,UAAU,EAAE,MAAM,CAAC;QAAC,MAAM,EAAE,MAAM,CAAA;KAAE,CAAC,CAAC;CACzD;AAsBD;;;;;GAKG;AACH,wBAAgB,kBAAkB,CAChC,QAAQ,EAAE,SAAS,OAAO,EAAE,EAC5B,MAAM,EAAE,MAAM,GACb,aAAa,CAyBf;AAED;;;;;;GAMG;AACH,wBAAgB,sBAAsB,CACpC,OAAO,EAAE,aAAa,EACtB,UAAU,EAAE,gBAAgB,GAAG,IAAI,GAAG,SAAS,GAC9C,OAAO,CAIT;AAED;;;;;;;;GAQG;AACH,wBAAgB,mBAAmB,CACjC,OAAO,EAAE,aAAa,EACtB,UAAU,EAAE,gBAAgB,GAAG,IAAI,GAAG,SAAS,GAC9C;IAAE,mBAAmB,EAAE,MAAM,EAAE,CAAC;IAAC,aAAa,EAAE,MAAM,EAAE,CAAC;IAAC,iBAAiB,EAAE,MAAM,EAAE,CAAA;CAAE,CAazF;AAED;;;;;;;;;;;;;;;;;GAiBG;AACH,wBAAgB,qBAAqB,CACnC,OAAO,EAAE,aAAa,EACtB,UAAU,EAAE,gBAAgB,GAAG,IAAI,GAAG,SAAS,GAC9C,cAAc,CAsDhB"}
1
+ {"version":3,"file":"reviewGate.d.ts","sourceRoot":"","sources":["../../../src/remediate/review/reviewGate.ts"],"names":[],"mappings":"AAoBA,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AACxB,OAAO,KAAK,EAAE,OAAO,EAAE,eAAe,EAAE,iBAAiB,EAAE,MAAM,oBAAoB,CAAC;AACtF,OAAO,EACL,KAAK,eAAe,EACpB,KAAK,kBAAkB,EAKxB,MAAM,sBAAsB,CAAC;AAE9B,eAAO,MAAM,6BAA6B,EAAG,kCAA2C,CAAC;AAEzF,+EAA+E;AAC/E,MAAM,WAAW,eAAe;IAC9B,UAAU,EAAE,MAAM,CAAC;IACnB,KAAK,EAAE,MAAM,CAAC;IACd,QAAQ,EAAE,eAAe,CAAC;IAC1B,UAAU,EAAE,iBAAiB,CAAC;IAC9B,IAAI,EAAE,MAAM,CAAC;IACb,OAAO,EAAE,MAAM,CAAC;IAChB,cAAc,EAAE,MAAM,EAAE,CAAC;IACzB,SAAS,EAAE,eAAe,CAAC;IAC3B,yDAAyD;IACzD,SAAS,EAAE,MAAM,CAAC;IAClB,mBAAmB,EAAE,kBAAkB,CAAC;CACzC;AAED,+EAA+E;AAC/E,MAAM,WAAW,eAAe;IAC9B,SAAS,EAAE,eAAe,CAAC;IAC3B,KAAK,EAAE,MAAM,CAAC;IACd,WAAW,EAAE,MAAM,CAAC;IACpB,KAAK,EAAE,eAAe,EAAE,CAAC;CAC1B;AAED,uEAAuE;AACvE,MAAM,WAAW,aAAa;IAC5B,cAAc,EAAE,OAAO,6BAA6B,CAAC;IACrD,OAAO,EAAE,MAAM,CAAC;IAChB,KAAK,EAAE,MAAM,CAAC;IACd,MAAM,EAAE,MAAM,CAAC,eAAe,EAAE,MAAM,CAAC,CAAC;IACxC,8DAA8D;IAC9D,KAAK,EAAE,eAAe,EAAE,CAAC;CAC1B;AAED;;;;;GAKG;AACH,QAAA,MAAM,sBAAsB;;IAGxB,wDAAwD;;;QAMhD,gEAAgE;;;;;;;;;IAMxE,2EAA2E;;;;;;;;;;;;;;;;EAGpE,CAAC;AAEZ,MAAM,MAAM,gBAAgB,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,sBAAsB,CAAC,CAAC;AAStE,MAAM,MAAM,sBAAsB,GAC9B;IAAE,IAAI,EAAE,IAAI,CAAC;IAAC,UAAU,EAAE,gBAAgB,CAAA;CAAE;AAC9C,+EAA+E;GAC7E;IAAE,IAAI,EAAE,OAAO,CAAA;CAAE,GACjB;IAAE,IAAI,EAAE,SAAS,CAAC;IAAC,MAAM,EAAE,MAAM,CAAA;CAAE,CAAC;AAExC,MAAM,WAAW,cAAc;IAC7B,yDAAyD;IACzD,YAAY,EAAE,MAAM,EAAE,CAAC;IACvB,kFAAkF;IAClF,QAAQ,EAAE,KAAK,CAAC;QAAE,UAAU,EAAE,MAAM,CAAC;QAAC,MAAM,EAAE,MAAM,CAAA;KAAE,CAAC,CAAC;CACzD;AAsBD;;;;;GAKG;AACH,wBAAgB,kBAAkB,CAChC,QAAQ,EAAE,SAAS,OAAO,EAAE,EAC5B,MAAM,EAAE,MAAM,GACb,aAAa,CAyBf;AAkDD;;;;;;;GAOG;AACH,wBAAgB,qBAAqB,CACnC,IAAI,EAAE,MAAM,EACZ,OAAO,EAAE,aAAa,GACrB,sBAAsB,CA8CxB;AAED;;;;;;;;;;;;;;;;GAgBG;AACH,wBAAgB,qBAAqB,CACnC,OAAO,EAAE,aAAa,EACtB,UAAU,EAAE,gBAAgB,GAAG,SAAS,GACvC,cAAc,CA0ChB"}
@@ -1,5 +1,6 @@
1
+ // sites-pinned: tests/remediate/reviewGate.test.ts, tests/remediate/next-step-review-gate.test.ts
1
2
  // Review-approval gate engine — builds the tiered item-set the user
2
- // approves/disapproves, and consumes their verdict.
3
+ // approves or declines, and consumes their verdict.
3
4
  //
4
5
  // This is the single review surface for both paths. It replaced the classic
5
6
  // per-block implementation-risk preview, which fired AFTER the contract pipeline
@@ -14,10 +15,39 @@
14
15
  // pipeline can mark it terminal-without-change.
15
16
  //
16
17
  // Tool owns the structure (tiering, rationale, cost, which items must be shown);
17
- // the host fills only the semantic pros/cons slots when presenting. Disapproved
18
+ // the host fills only the semantic pros/cons slots when presenting. Declined
18
19
  // items become a RECORDED terminal disposition, never a silent close.
20
+ import { z } from "zod";
19
21
  import { classifyReviewNecessity, partitionByReviewNecessity, REVIEW_NECESSITY_ORDER, REVIEW_NECESSITY_LABELS, } from "./reviewNecessity.js";
20
22
  export const REVIEW_REQUEST_SCHEMA_VERSION = "remediate-code-review-request/v1";
23
+ /**
24
+ * The user's verdict (`review_resolution.json`). Strict at every level: the
25
+ * gate's default is APPROVE, so a field the tool does not read (a mistyped
26
+ * name, the retired `disapproved_*` names) would turn the user's decline into
27
+ * an approval in silence. An unknown field refuses the whole file instead.
28
+ */
29
+ const ReviewResolutionSchema = z
30
+ .object({
31
+ plan_id: z.string().optional(),
32
+ /** Findings the user declined — do NOT act on these. */
33
+ declined_findings: z
34
+ .array(z
35
+ .object({
36
+ finding_id: z.string(),
37
+ /** The user's reason, in their words. Blank reads as absent. */
38
+ reason: z.string().optional(),
39
+ })
40
+ .strict())
41
+ .optional(),
42
+ /** Whole tiers the user declined (e.g. "decline everything strategic"). */
43
+ declined_tiers: z.array(z.string()).optional(),
44
+ })
45
+ .strict();
46
+ /** The retired field names, each with the field that replaced it. */
47
+ const RETIRED_RESOLUTION_FIELDS = {
48
+ disapproved_findings: '`declined_findings`, whose entries are `{ "finding_id": "<id>", "reason": "<optional>" }`',
49
+ disapproved_tiers: "`declined_tiers`",
50
+ };
21
51
  function firstPath(finding) {
22
52
  return (finding.affected_files ?? []).map((f) => f.path).filter(Boolean);
23
53
  }
@@ -76,88 +106,141 @@ export function buildReviewRequest(findings, planId) {
76
106
  * from the request's marks a stale leftover from another run — the caller must
77
107
  * archive it and re-halt rather than apply a cross-run answer.
78
108
  */
79
- export function isResolutionForRequest(request, resolution) {
109
+ function isResolutionForRequest(request, resolution) {
80
110
  const resolutionPlanId = resolution?.plan_id;
81
111
  if (resolutionPlanId === undefined)
82
112
  return true;
83
113
  return resolutionPlanId === request.plan_id;
84
114
  }
85
115
  /**
86
- * Screen a resolution's id references against the request (uniform id-join
87
- * contract): every `disapproved_findings` entry must name an item in the
88
- * request, and every `disapproved_tiers` entry must be one of the closed
89
- * review-necessity names. A stray id here is not a no-op — the gate's default
90
- * is APPROVE, so a typo'd decline would silently become an approval. Empty
91
- * arrays = clean. A tier that is valid but empty in this request stays a
92
- * harmless no-op (it names a real vocabulary member, not a phantom item).
116
+ * The id references a resolution names outside the request (uniform id-join
117
+ * contract): every `declined_findings` id must name an item in the request,
118
+ * and every `declined_tiers` entry must be one of the closed review-necessity
119
+ * names. A tier that is valid but empty in this request stays a harmless no-op
120
+ * (it names a real vocabulary member, not a phantom item).
93
121
  */
94
- export function screenResolutionIds(request, resolution) {
122
+ function unknownResolutionIds(request, resolution) {
95
123
  const validIds = request.tiers.flatMap((t) => t.items.map((i) => i.finding_id));
96
124
  const validIdSet = new Set(validIds);
97
125
  const validTiers = new Set(REVIEW_NECESSITY_ORDER);
98
- return {
99
- unknown_finding_ids: (resolution?.disapproved_findings ?? []).filter((id) => !validIdSet.has(id)),
100
- unknown_tiers: (resolution?.disapproved_tiers ?? []).filter((t) => !validTiers.has(t)),
101
- valid_finding_ids: validIds,
102
- };
126
+ const problems = [];
127
+ (resolution.declined_findings ?? []).forEach((entry, index) => {
128
+ if (!validIdSet.has(entry.finding_id)) {
129
+ problems.push(`\`declined_findings[${index}].finding_id\`: \`${entry.finding_id}\` is not in the request (valid: ${validIds.map((i) => `\`${i}\``).join(", ")})`);
130
+ }
131
+ });
132
+ (resolution.declined_tiers ?? []).forEach((tier, index) => {
133
+ if (!validTiers.has(tier)) {
134
+ problems.push(`\`declined_tiers[${index}]\`: \`${tier}\` is not a tier (valid: ${REVIEW_NECESSITY_ORDER.map((t) => `\`${t}\``).join(", ")})`);
135
+ }
136
+ });
137
+ return problems;
138
+ }
139
+ /**
140
+ * Read the text of `review_resolution.json` against the request it answers.
141
+ * The WHOLE file is refused — never partly applied — when it is not valid
142
+ * JSON, has a field the schema does not name (the retired `disapproved_*`
143
+ * names get their replacement stated), has a wrong type, or names an id or a
144
+ * tier outside the request. The reason names each problem. A file whose
145
+ * `plan_id` names a different request is `stale`: it answers another run.
146
+ */
147
+ export function parseReviewResolution(text, request) {
148
+ let value;
149
+ try {
150
+ value = JSON.parse(text);
151
+ }
152
+ catch (error) {
153
+ return {
154
+ kind: "refused",
155
+ reason: `the file is not valid JSON (${error instanceof Error ? error.message : String(error)})`,
156
+ };
157
+ }
158
+ if (typeof value !== "object" || value === null || Array.isArray(value)) {
159
+ return {
160
+ kind: "refused",
161
+ reason: 'the file must be one JSON object: `{ "declined_findings": [...], "declined_tiers": [...] }`',
162
+ };
163
+ }
164
+ const planId = value.plan_id;
165
+ if (typeof planId === "string" && planId !== request.plan_id)
166
+ return { kind: "stale" };
167
+ const problems = [];
168
+ for (const [retired, replacement] of Object.entries(RETIRED_RESOLUTION_FIELDS)) {
169
+ if (retired in value) {
170
+ problems.push(`\`${retired}\` is not a field — write ${replacement}`);
171
+ }
172
+ }
173
+ const parsed = ReviewResolutionSchema.safeParse(value);
174
+ if (!parsed.success) {
175
+ for (const issue of parsed.error.issues) {
176
+ if (issue.code === z.ZodIssueCode.unrecognized_keys) {
177
+ const unknown = issue.keys.filter((k) => !(k in RETIRED_RESOLUTION_FIELDS));
178
+ const where = issue.path.length > 0 ? ` in \`${issue.path.join(".")}\`` : "";
179
+ if (unknown.length > 0) {
180
+ problems.push(`unknown field(s)${where}: ${unknown.map((k) => `\`${k}\``).join(", ")}`);
181
+ }
182
+ continue;
183
+ }
184
+ const field = issue.path.length > 0 ? `\`${issue.path.join(".")}\`: ` : "";
185
+ problems.push(`${field}${issue.message}`);
186
+ }
187
+ }
188
+ else {
189
+ problems.push(...unknownResolutionIds(request, parsed.data));
190
+ }
191
+ return problems.length > 0 || !parsed.success
192
+ ? { kind: "refused", reason: problems.join("; ") }
193
+ : { kind: "ok", resolution: parsed.data };
103
194
  }
104
195
  /**
105
196
  * Apply the user's resolution to a request: every item is either approved (act
106
197
  * on it) or declined (recorded terminal disposition with a reason). An item is
107
- * declined if its id is in `disapproved_findings` OR its tier is in
108
- * `disapproved_tiers`. Everything else is approved — the default is to act,
198
+ * declined if its id is in `declined_findings` OR its tier is in
199
+ * `declined_tiers`. Everything else is approved — the default is to act,
109
200
  * because the gate's job is to let the user REMOVE items, not to require
110
201
  * opting every item in. An absent/empty resolution approves everything.
111
202
  *
112
- * A resolution carrying a MISMATCHED `plan_id` is rejected (throws): applying a
113
- * stale cross-run answer would approve/decline the wrong finding set
114
- * (INV-RSM-RESOLUTION-CORRELATE, COR-0b906e37). Callers pre-screen with
115
- * {@link isResolutionForRequest} to archive-and-re-halt instead of crashing;
116
- * the throw here is the mechanical backstop, not the primary UX.
203
+ * The resolution must come from {@link parseReviewResolution}; a stale
204
+ * `plan_id` or an unknown id still throws here as the mechanical backstop
205
+ * (INV-RSM-RESOLUTION-CORRELATE, COR-0b906e37), never the primary UX.
117
206
  *
118
- * Crucially, declined items are returned with an explicit reason so the caller
119
- * records a terminal disposition (e.g. `ignored`) rather than silently closing
120
- * them — the exact failure this gate exists to prevent.
207
+ * Crucially, declined items are returned with an explicit reason — the user's
208
+ * own words when they gave one — so the caller records a terminal disposition
209
+ * (e.g. `ignored`) rather than silently closing them, the exact failure this
210
+ * gate exists to prevent.
121
211
  */
122
212
  export function applyReviewResolution(request, resolution) {
123
213
  if (!isResolutionForRequest(request, resolution)) {
124
214
  throw new Error(`review resolution plan_id "${resolution?.plan_id}" does not answer review request plan_id "${request.plan_id}" — stale cross-run resolution rejected (INV-RSM-RESOLUTION-CORRELATE).`);
125
215
  }
126
- const screen = screenResolutionIds(request, resolution);
127
- if (screen.unknown_finding_ids.length > 0 || screen.unknown_tiers.length > 0) {
128
- // Uniform id-join contract: refuse the WHOLE resolution. Callers pre-screen
129
- // with screenResolutionIds to archive-and-re-halt; this throw is the
130
- // mechanical backstop, mirroring the plan_id-mismatch backstop above.
131
- const parts = [];
132
- if (screen.unknown_finding_ids.length > 0) {
133
- parts.push(`unknown finding id(s) ${screen.unknown_finding_ids.map((i) => `"${i}"`).join(", ")} ` +
134
- `(valid: ${screen.valid_finding_ids.join(", ")})`);
135
- }
136
- if (screen.unknown_tiers.length > 0) {
137
- parts.push(`unknown tier(s) ${screen.unknown_tiers.map((t) => `"${t}"`).join(", ")} ` +
138
- `(valid: ${REVIEW_NECESSITY_ORDER.join(", ")})`);
139
- }
140
- throw new Error(`review resolution refused — ${parts.join("; ")}. The gate's default is approve, ` +
141
- `so a mistyped decline would silently become an approval; re-submit the whole ` +
142
- `resolution with ids drawn from the request.`);
216
+ const unknown = resolution ? unknownResolutionIds(request, resolution) : [];
217
+ if (unknown.length > 0) {
218
+ throw new Error(`review resolution refused — ${unknown.join("; ")}`);
143
219
  }
144
- const disapprovedIds = new Set(resolution?.disapproved_findings ?? []);
145
- const disapprovedTiers = new Set(resolution?.disapproved_tiers ?? []);
220
+ const userReasons = new Map((resolution?.declined_findings ?? []).map((d) => [d.finding_id, d.reason?.trim() || undefined]));
221
+ const declinedTiers = new Set(resolution?.declined_tiers ?? []);
146
222
  const approved_ids = [];
147
223
  const declined = [];
148
224
  for (const tier of request.tiers) {
149
- const tierDisapproved = disapprovedTiers.has(tier.necessity);
225
+ const tierDeclined = declinedTiers.has(tier.necessity);
150
226
  for (const item of tier.items) {
151
- if (tierDisapproved) {
227
+ const userReason = userReasons.get(item.finding_id);
228
+ if (userReason !== undefined) {
229
+ declined.push({
230
+ finding_id: item.finding_id,
231
+ reason: `Declined by the user at the review gate: ${userReason}`,
232
+ });
233
+ }
234
+ else if (tierDeclined) {
152
235
  declined.push({
153
236
  finding_id: item.finding_id,
154
- reason: `Disapproved by the user at the review gate — declined the entire "${item.necessity}" tier.`,
237
+ reason: `Declined by the user at the review gate — declined the entire "${item.necessity}" tier.`,
155
238
  });
156
239
  }
157
- else if (disapprovedIds.has(item.finding_id)) {
240
+ else if (userReasons.has(item.finding_id)) {
158
241
  declined.push({
159
242
  finding_id: item.finding_id,
160
- reason: `Disapproved by the user at the review gate (review-necessity: ${item.necessity}).`,
243
+ reason: `Declined by the user at the review gate (review-necessity: ${item.necessity}).`,
161
244
  });
162
245
  }
163
246
  else {
@@ -1 +1 @@
1
- {"version":3,"file":"reviewGate.js","sourceRoot":"","sources":["../../../src/remediate/review/reviewGate.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"reviewGate.js","sourceRoot":"","sources":["../../../src/remediate/review/reviewGate.ts"],"names":[],"mappings":"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"}
@@ -1,5 +1,5 @@
1
1
  import { SKIP_WRITE } from "audit-tools/shared";
2
- import { RemediationPlan, RemediationItemState, ClarificationRequest, ClosingPlan, CoverageLedger, RemediationHostHandoffRecord } from "./types.js";
2
+ import { RemediationPlan, RemediationItemState, ClosingPlan, CoverageLedger, RemediationHostHandoffRecord } from "./types.js";
3
3
  import { type RemediationRunStatus } from "./runStatus.js";
4
4
  /**
5
5
  * The schema version stamped on every persisted `state.json`.
@@ -44,7 +44,6 @@ export interface RemediationState {
44
44
  status: RemediationRunStatus;
45
45
  plan?: RemediationPlan;
46
46
  items?: Record<string, RemediationItemState>;
47
- clarifications?: ClarificationRequest[];
48
47
  closing_plan?: ClosingPlan;
49
48
  started_at?: string;
50
49
  step_count?: number;
@@ -1 +1 @@
1
- {"version":3,"file":"store.d.ts","sourceRoot":"","sources":["../../../src/remediate/state/store.ts"],"names":[],"mappings":"AAEA,OAAO,EAKL,UAAU,EAEX,MAAM,oBAAoB,CAAC;AAC5B,OAAO,EACL,eAAe,EACf,oBAAoB,EACpB,oBAAoB,EACpB,WAAW,EACX,cAAc,EACd,4BAA4B,EAE7B,MAAM,YAAY,CAAC;AAEpB,OAAO,EAGL,KAAK,oBAAoB,EAC1B,MAAM,gBAAgB,CAAC;AAExB;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA+BG;AACH,eAAO,MAAM,kCAAkC,EAC7C,+BAAwC,CAAC;AAK3C,MAAM,WAAW,gBAAgB;IAC/B;;;;OAIG;IACH,gBAAgB,CAAC,EAAE,OAAO,kCAAkC,CAAC;IAC7D,MAAM,EAAE,oBAAoB,CAAC;IAC7B,IAAI,CAAC,EAAE,eAAe,CAAC;IACvB,KAAK,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,oBAAoB,CAAC,CAAC;IAC7C,cAAc,CAAC,EAAE,oBAAoB,EAAE,CAAC;IACxC,YAAY,CAAC,EAAE,WAAW,CAAC;IAC3B,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,aAAa,CAAC,EAAE,cAAc,CAAC;IAC/B;;;;OAIG;IACH,eAAe,CAAC,EAAE,aAAa,CAAC;IAChC;;;;;;;;;;;;;;OAcG;IACH,oBAAoB,CAAC,EAAE,MAAM,EAAE,CAAC;IAChC;;;;;;;;;;;;;;;;;;OAkBG;IACH,eAAe,CAAC,EAAE,MAAM,EAAE,CAAC;IAC3B;;;;OAIG;IACH,YAAY,CAAC,EAAE,4BAA4B,CAAC;CAC7C;AA+ID,eAAO,MAAM,eAAe,QAA+B,CAAC;AAU5D,qBAAa,UAAU;IAWnB,OAAO,CAAC,YAAY;IAEpB,OAAO,CAAC,QAAQ,CAAC,cAAc,CAAC;IAZlC;;;;;;OAMG;IACH,OAAO,CAAC,QAAQ,CAAC,KAAK,CAA2C;gBAGvD,YAAY,EAAE,MAAM,EAEX,cAAc,CAAC,EAAE,MAAM,YAAA;IA4CpC,IAAI,IAAI,OAAO,CAAC,IAAI,CAAC;IAI3B;;;;;;;;OAQG;IACG,SAAS,IAAI,OAAO,CAAC,gBAAgB,GAAG,IAAI,CAAC;IAInD;;;;;;;;;;;;;;OAcG;IACG,MAAM,CACV,EAAE,EAAE,CACF,OAAO,EAAE,gBAAgB,GAAG,IAAI,KAC7B,OAAO,CAAC,gBAAgB,GAAG,OAAO,UAAU,CAAC,GACjD,OAAO,CAAC,gBAAgB,CAAC;IA4B5B;;;;;OAKG;IACG,SAAS,CAAC,KAAK,EAAE,gBAAgB,GAAG,OAAO,CAAC,IAAI,CAAC;CAMxD"}
1
+ {"version":3,"file":"store.d.ts","sourceRoot":"","sources":["../../../src/remediate/state/store.ts"],"names":[],"mappings":"AAGA,OAAO,EAKL,UAAU,EAEX,MAAM,oBAAoB,CAAC;AAC5B,OAAO,EACL,eAAe,EACf,oBAAoB,EACpB,WAAW,EAEX,cAAc,EACd,4BAA4B,EAE7B,MAAM,YAAY,CAAC;AAEpB,OAAO,EAGL,KAAK,oBAAoB,EAC1B,MAAM,gBAAgB,CAAC;AAExB;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA+BG;AACH,eAAO,MAAM,kCAAkC,EAC7C,+BAAwC,CAAC;AAK3C,MAAM,WAAW,gBAAgB;IAC/B;;;;OAIG;IACH,gBAAgB,CAAC,EAAE,OAAO,kCAAkC,CAAC;IAC7D,MAAM,EAAE,oBAAoB,CAAC;IAC7B,IAAI,CAAC,EAAE,eAAe,CAAC;IACvB,KAAK,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,oBAAoB,CAAC,CAAC;IAC7C,YAAY,CAAC,EAAE,WAAW,CAAC;IAC3B,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,aAAa,CAAC,EAAE,cAAc,CAAC;IAC/B;;;;OAIG;IACH,eAAe,CAAC,EAAE,aAAa,CAAC;IAChC;;;;;;;;;;;;;;OAcG;IACH,oBAAoB,CAAC,EAAE,MAAM,EAAE,CAAC;IAChC;;;;;;;;;;;;;;;;;;OAkBG;IACH,eAAe,CAAC,EAAE,MAAM,EAAE,CAAC;IAC3B;;;;OAIG;IACH,YAAY,CAAC,EAAE,4BAA4B,CAAC;CAC7C;AA4QD,eAAO,MAAM,eAAe,QAA+B,CAAC;AAU5D,qBAAa,UAAU;IAWnB,OAAO,CAAC,YAAY;IAEpB,OAAO,CAAC,QAAQ,CAAC,cAAc,CAAC;IAZlC;;;;;;OAMG;IACH,OAAO,CAAC,QAAQ,CAAC,KAAK,CAA2C;gBAGvD,YAAY,EAAE,MAAM,EAEX,cAAc,CAAC,EAAE,MAAM,YAAA;IA+CpC,IAAI,IAAI,OAAO,CAAC,IAAI,CAAC;IAI3B;;;;;;;;OAQG;IACG,SAAS,IAAI,OAAO,CAAC,gBAAgB,GAAG,IAAI,CAAC;IAInD;;;;;;;;;;;;;;OAcG;IACG,MAAM,CACV,EAAE,EAAE,CACF,OAAO,EAAE,gBAAgB,GAAG,IAAI,KAC7B,OAAO,CAAC,gBAAgB,GAAG,OAAO,UAAU,CAAC,GACjD,OAAO,CAAC,gBAAgB,CAAC;IA4B5B;;;;;OAKG;IACG,SAAS,CAAC,KAAK,EAAE,gBAAgB,GAAG,OAAO,CAAC,IAAI,CAAC;CAMxD"}
@@ -1,7 +1,8 @@
1
+ // sites-pinned: tests/remediate/clarification-round-contract.test.ts
1
2
  import { mkdir } from "node:fs/promises";
2
3
  import { join } from "node:path";
3
4
  import { createLockedJsonStore, LOCKED_JSON_STORE_TIMEOUT_MS, SchemaVersionMismatchError, SKIP_WRITE, assertNotNodeWorktreeCwd, } from "audit-tools/shared";
4
- import { RemediationHostHandoffRecordSchema, } from "./types.js";
5
+ import { isClarificationCategory, RemediationHostHandoffRecordSchema, } from "./types.js";
5
6
  import { validateRemediationBlock } from "../validation/remediationState.js";
6
7
  import { REMEDIATION_RUN_STATUSES, isRemediationRunStatus, } from "./runStatus.js";
7
8
  /**
@@ -142,6 +143,7 @@ function validateState(value) {
142
143
  }
143
144
  }
144
145
  }
146
+ errors.push(...clarificationQuestionErrors(obj));
145
147
  if (status === "closing") {
146
148
  const closingPlan = obj["closing_plan"];
147
149
  if (!closingPlan || typeof closingPlan !== "object" || Array.isArray(closingPlan)) {
@@ -161,6 +163,122 @@ function validateState(value) {
161
163
  }
162
164
  return errors;
163
165
  }
166
+ /**
167
+ * The clarification invariants. A worker question lives on its item, so:
168
+ *
169
+ * - the retired run-level `clarifications` list is refused (it was a second
170
+ * copy of the questions, and clearing it after a partial answer left the
171
+ * unanswered items with nothing to show — the round then listed no question
172
+ * and the run could not progress);
173
+ * - an item paused as `needs_clarification` must carry its question, because
174
+ * the clarification round is built from those items alone.
175
+ *
176
+ * A state written before the move is translated on READ by
177
+ * {@link adoptLegacyClarifications}, so these refuse only a WRITE of the old
178
+ * shape.
179
+ */
180
+ function clarificationQuestionErrors(obj) {
181
+ const errors = [];
182
+ if (obj["clarifications"] !== undefined) {
183
+ errors.push("clarifications is retired: a worker question lives on its item as clarification_question");
184
+ }
185
+ const items = obj["items"];
186
+ if (!items || typeof items !== "object" || Array.isArray(items))
187
+ return errors;
188
+ for (const [key, item] of Object.entries(items)) {
189
+ if (!item || typeof item !== "object" || Array.isArray(item))
190
+ continue;
191
+ const it = item;
192
+ if (it["status"] !== "needs_clarification")
193
+ continue;
194
+ const question = it["clarification_question"];
195
+ if (!question ||
196
+ typeof question !== "object" ||
197
+ Array.isArray(question) ||
198
+ !isClarificationCategory(question["category"]) ||
199
+ typeof question["description"] !== "string" ||
200
+ question["description"].trim().length === 0) {
201
+ errors.push(`items["${key}"] is needs_clarification but carries no valid clarification_question ` +
202
+ "(a category and a non-empty description)");
203
+ }
204
+ }
205
+ return errors;
206
+ }
207
+ /**
208
+ * The READ rule for a state written before the worker question moved onto its
209
+ * item. Such a state holds the questions in a run-level `clarifications` list,
210
+ * and the old ingest ALSO wrote each question into the paused item's
211
+ * `failure_reason`.
212
+ *
213
+ * Every `needs_clarification` item without a `clarification_question` takes
214
+ * one: from its entry in the legacy list when there is one, else from its
215
+ * `failure_reason` (this recovers a run that the old partial-answer defect
216
+ * already wedged, where the list was cleared but the item still waits). The
217
+ * legacy list is then dropped, and the next write persists the new shape.
218
+ *
219
+ * This is the one place a read is not byte-faithful, and it touches only the
220
+ * retired shape: a current state passes through unchanged.
221
+ */
222
+ function adoptLegacyClarifications(raw) {
223
+ if (!raw || typeof raw !== "object" || Array.isArray(raw))
224
+ return raw;
225
+ const obj = raw;
226
+ const items = obj["items"];
227
+ const hasItems = Boolean(items) && typeof items === "object" && !Array.isArray(items);
228
+ const needsQuestion = hasItems
229
+ ? Object.values(items).some((item) => Boolean(item) &&
230
+ typeof item === "object" &&
231
+ item["status"] === "needs_clarification" &&
232
+ item["clarification_question"] === undefined)
233
+ : false;
234
+ if (obj["clarifications"] === undefined && !needsQuestion)
235
+ return raw;
236
+ const legacy = new Map();
237
+ if (Array.isArray(obj["clarifications"])) {
238
+ for (const entry of obj["clarifications"]) {
239
+ if (!entry || typeof entry !== "object" || Array.isArray(entry))
240
+ continue;
241
+ const e = entry;
242
+ if (typeof e["finding_id"] === "string" && !legacy.has(e["finding_id"])) {
243
+ legacy.set(e["finding_id"], e);
244
+ }
245
+ }
246
+ }
247
+ const { clarifications: _retired, ...rest } = obj;
248
+ if (!hasItems)
249
+ return rest;
250
+ const nextItems = {};
251
+ for (const [key, item] of Object.entries(items)) {
252
+ const it = item;
253
+ if (!it ||
254
+ typeof it !== "object" ||
255
+ it["status"] !== "needs_clarification" ||
256
+ it["clarification_question"] !== undefined) {
257
+ nextItems[key] = item;
258
+ continue;
259
+ }
260
+ const entry = legacy.get(key);
261
+ const description = typeof entry?.["description"] === "string" && entry["description"].trim()
262
+ ? entry["description"]
263
+ : typeof it["failure_reason"] === "string" && it["failure_reason"].trim()
264
+ ? it["failure_reason"]
265
+ : "The worker paused this finding for a clarification but recorded no question text.";
266
+ const options = Array.isArray(entry?.["options"])
267
+ ? entry["options"].filter((o) => typeof o === "string")
268
+ : [];
269
+ nextItems[key] = {
270
+ ...it,
271
+ clarification_question: {
272
+ category: isClarificationCategory(entry?.["category"])
273
+ ? entry["category"]
274
+ : "scope_of_fix",
275
+ description,
276
+ ...(options.length > 0 ? { options } : {}),
277
+ },
278
+ };
279
+ }
280
+ return { ...rest, items: nextItems };
281
+ }
164
282
  const LOCK_FILENAME = "state.lock";
165
283
  // Acquire timeout for the shared file lock — the STALE_LOCK_MS-minus-margin
166
284
  // derivation is single-sourced in the shared locked JSON store (a fresh-but-held
@@ -199,7 +317,8 @@ export class StateStore {
199
317
  // `validateState` throws SchemaVersionMismatchError for a state stamped
200
318
  // with another release's contract version; that propagates out of the
201
319
  // read exactly as the policy requires (see the constant's doc).
202
- const errors = validateState(raw);
320
+ const adopted = adoptLegacyClarifications(raw);
321
+ const errors = validateState(adopted);
203
322
  if (errors.length > 0) {
204
323
  throw new Error(`state.json failed schema validation: ${errors.join("; ")}`);
205
324
  }
@@ -210,7 +329,9 @@ export class StateStore {
210
329
  // written before the field existed has none until its next write, and
211
330
  // `validateState` admits that (see the constant's doc) — which is the
212
331
  // whole reason the field is optional on the type rather than required.
213
- return raw;
332
+ // The one exception is the retired clarification shape, translated by
333
+ // `adoptLegacyClarifications`; a current state is returned unchanged.
334
+ return adopted;
214
335
  },
215
336
  // The WRITE hook. Without it the store's own `persist` wrote whatever a
216
337
  // caller handed it — the load gate was the only validation on the path,