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.
- package/dist/audit/cli/auditStep.d.ts.map +1 -1
- package/dist/audit/cli/auditStep.js +2 -2
- package/dist/audit/cli/auditStep.js.map +1 -1
- package/dist/audit/cli/charterClarificationPrompt.d.ts +16 -8
- package/dist/audit/cli/charterClarificationPrompt.d.ts.map +1 -1
- package/dist/audit/cli/charterClarificationPrompt.js +95 -42
- package/dist/audit/cli/charterClarificationPrompt.js.map +1 -1
- package/dist/audit/cli/charterComparisonPrompt.d.ts +16 -6
- package/dist/audit/cli/charterComparisonPrompt.d.ts.map +1 -1
- package/dist/audit/cli/charterComparisonPrompt.js +76 -29
- package/dist/audit/cli/charterComparisonPrompt.js.map +1 -1
- package/dist/audit/cli/charterExtractionPrompt.d.ts +5 -4
- package/dist/audit/cli/charterExtractionPrompt.d.ts.map +1 -1
- package/dist/audit/cli/charterExtractionPrompt.js +64 -42
- package/dist/audit/cli/charterExtractionPrompt.js.map +1 -1
- package/dist/audit/cli/charterFidelityPrompt.d.ts +17 -4
- package/dist/audit/cli/charterFidelityPrompt.d.ts.map +1 -1
- package/dist/audit/cli/charterFidelityPrompt.js +72 -21
- package/dist/audit/cli/charterFidelityPrompt.js.map +1 -1
- package/dist/audit/cli/dispatch/hostHandoff.d.ts +23 -1
- package/dist/audit/cli/dispatch/hostHandoff.d.ts.map +1 -1
- package/dist/audit/cli/dispatch/hostHandoff.js +204 -41
- package/dist/audit/cli/dispatch/hostHandoff.js.map +1 -1
- package/dist/audit/cli/dispatch/packetFilter.d.ts +8 -0
- package/dist/audit/cli/dispatch/packetFilter.d.ts.map +1 -1
- package/dist/audit/cli/fanoutLanes.d.ts.map +1 -1
- package/dist/audit/cli/fanoutLanes.js +20 -4
- package/dist/audit/cli/fanoutLanes.js.map +1 -1
- package/dist/audit/cli/laneValidators.d.ts +19 -7
- package/dist/audit/cli/laneValidators.d.ts.map +1 -1
- package/dist/audit/cli/laneValidators.js +73 -18
- package/dist/audit/cli/laneValidators.js.map +1 -1
- package/dist/audit/cli/nextStepCommand.d.ts.map +1 -1
- package/dist/audit/cli/nextStepCommand.js +13 -6
- package/dist/audit/cli/nextStepCommand.js.map +1 -1
- package/dist/audit/cli/nextStepHelpers.d.ts +25 -2
- package/dist/audit/cli/nextStepHelpers.d.ts.map +1 -1
- package/dist/audit/cli/nextStepHelpers.js +105 -35
- package/dist/audit/cli/nextStepHelpers.js.map +1 -1
- package/dist/audit/cli/recoverSubmissionCommand.d.ts.map +1 -1
- package/dist/audit/cli/recoverSubmissionCommand.js +89 -4
- package/dist/audit/cli/recoverSubmissionCommand.js.map +1 -1
- package/dist/audit/cli/sampleRunCommand.d.ts +10 -0
- package/dist/audit/cli/sampleRunCommand.d.ts.map +1 -1
- package/dist/audit/cli/semanticReviewStep.d.ts +4 -4
- package/dist/audit/cli/semanticReviewStep.d.ts.map +1 -1
- package/dist/audit/cli/semanticReviewStep.js +35 -71
- package/dist/audit/cli/semanticReviewStep.js.map +1 -1
- package/dist/audit/contracts/findingContractPrompt.d.ts.map +1 -1
- package/dist/audit/contracts/findingContractPrompt.js +7 -1
- package/dist/audit/contracts/findingContractPrompt.js.map +1 -1
- package/dist/audit/contracts/workerSchemas.d.ts +280 -4
- package/dist/audit/contracts/workerSchemas.d.ts.map +1 -1
- package/dist/audit/contracts/workerSchemas.js +24 -2
- package/dist/audit/contracts/workerSchemas.js.map +1 -1
- package/dist/audit/orchestrator/charterClarificationExecutor.d.ts.map +1 -1
- package/dist/audit/orchestrator/charterClarificationExecutor.js +39 -0
- package/dist/audit/orchestrator/charterClarificationExecutor.js.map +1 -1
- package/dist/audit/orchestrator/charterExtractionExecutor.d.ts +12 -5
- package/dist/audit/orchestrator/charterExtractionExecutor.d.ts.map +1 -1
- package/dist/audit/orchestrator/charterExtractionExecutor.js +39 -9
- package/dist/audit/orchestrator/charterExtractionExecutor.js.map +1 -1
- package/dist/audit/orchestrator/designReviewPrompt.d.ts +7 -0
- package/dist/audit/orchestrator/designReviewPrompt.d.ts.map +1 -1
- package/dist/audit/orchestrator/designReviewPrompt.js +174 -19
- package/dist/audit/orchestrator/designReviewPrompt.js.map +1 -1
- package/dist/audit/orchestrator/executorRunners.d.ts.map +1 -1
- package/dist/audit/orchestrator/executorRunners.js +14 -3
- package/dist/audit/orchestrator/executorRunners.js.map +1 -1
- package/dist/audit/orchestrator/intentCheckpointGate.d.ts.map +1 -1
- package/dist/audit/orchestrator/intentCheckpointGate.js +1 -5
- package/dist/audit/orchestrator/intentCheckpointGate.js.map +1 -1
- package/dist/audit/orchestrator/requeueCommand.d.ts +8 -0
- package/dist/audit/orchestrator/requeueCommand.d.ts.map +1 -1
- package/dist/audit/orchestrator/selectiveDeepening/lensVerification.d.ts.map +1 -1
- package/dist/audit/orchestrator/selectiveDeepening/lensVerification.js +94 -71
- package/dist/audit/orchestrator/selectiveDeepening/lensVerification.js.map +1 -1
- package/dist/audit/orchestrator/selectiveDeepening/shared.d.ts +0 -2
- package/dist/audit/orchestrator/selectiveDeepening/shared.d.ts.map +1 -1
- package/dist/audit/orchestrator/selectiveDeepening/shared.js +9 -2
- package/dist/audit/orchestrator/selectiveDeepening/shared.js.map +1 -1
- package/dist/audit/orchestrator/selectiveDeepening/stewardFollowup.d.ts.map +1 -1
- package/dist/audit/orchestrator/selectiveDeepening/stewardFollowup.js +28 -4
- package/dist/audit/orchestrator/selectiveDeepening/stewardFollowup.js.map +1 -1
- package/dist/audit/orchestrator/synthesisExecutors.d.ts +10 -3
- package/dist/audit/orchestrator/synthesisExecutors.d.ts.map +1 -1
- package/dist/audit/orchestrator/synthesisExecutors.js +5 -4
- package/dist/audit/orchestrator/synthesisExecutors.js.map +1 -1
- package/dist/audit/reporting/synthesis.d.ts +23 -6
- package/dist/audit/reporting/synthesis.d.ts.map +1 -1
- package/dist/audit/reporting/synthesis.js +53 -13
- package/dist/audit/reporting/synthesis.js.map +1 -1
- package/dist/audit/reporting/synthesisNarrativePrompt.d.ts +26 -1
- package/dist/audit/reporting/synthesisNarrativePrompt.d.ts.map +1 -1
- package/dist/audit/reporting/synthesisNarrativePrompt.js +53 -13
- package/dist/audit/reporting/synthesisNarrativePrompt.js.map +1 -1
- package/dist/audit/systemic/secondOrderAdversaryPrompt.d.ts.map +1 -1
- package/dist/audit/systemic/secondOrderAdversaryPrompt.js +67 -40
- package/dist/audit/systemic/secondOrderAdversaryPrompt.js.map +1 -1
- package/dist/audit/systemic/systemicChallengeLoop.d.ts.map +1 -1
- package/dist/audit/systemic/systemicChallengeLoop.js +13 -1
- package/dist/audit/systemic/systemicChallengeLoop.js.map +1 -1
- package/dist/audit/types/conceptualAdjudication.d.ts +26 -2
- package/dist/audit/types/conceptualAdjudication.d.ts.map +1 -1
- package/dist/audit/types/conceptualAdjudication.js +26 -7
- package/dist/audit/types/conceptualAdjudication.js.map +1 -1
- package/dist/audit/types.d.ts +279 -0
- package/dist/audit/types.d.ts.map +1 -1
- package/dist/audit/types.js +52 -0
- package/dist/audit/types.js.map +1 -1
- package/dist/audit/validation/auditResults.d.ts.map +1 -1
- package/dist/audit/validation/auditResults.js +51 -6
- package/dist/audit/validation/auditResults.js.map +1 -1
- package/dist/audit/validation/ingestIssueCodes.d.ts +3 -1
- package/dist/audit/validation/ingestIssueCodes.d.ts.map +1 -1
- package/dist/audit/validation/ingestIssueCodes.js +27 -15
- package/dist/audit/validation/ingestIssueCodes.js.map +1 -1
- package/dist/remediate/contractPipeline/sketchSource.d.ts +13 -3
- package/dist/remediate/contractPipeline/sketchSource.d.ts.map +1 -1
- package/dist/remediate/contractPipeline/sketchSource.js +14 -8
- package/dist/remediate/contractPipeline/sketchSource.js.map +1 -1
- package/dist/remediate/intake.d.ts +140 -36
- package/dist/remediate/intake.d.ts.map +1 -1
- package/dist/remediate/intake.js +159 -38
- package/dist/remediate/intake.js.map +1 -1
- package/dist/remediate/intent/checkpointFilter.d.ts +1 -2
- package/dist/remediate/intent/checkpointFilter.d.ts.map +1 -1
- package/dist/remediate/intent/checkpointFilter.js +1 -5
- package/dist/remediate/intent/checkpointFilter.js.map +1 -1
- package/dist/remediate/phases/close.d.ts.map +1 -1
- package/dist/remediate/phases/close.js +8 -8
- package/dist/remediate/phases/close.js.map +1 -1
- package/dist/remediate/phases/closeVerifyHeadEvidence.d.ts +19 -35
- package/dist/remediate/phases/closeVerifyHeadEvidence.d.ts.map +1 -1
- package/dist/remediate/phases/closeVerifyHeadEvidence.js +38 -12
- package/dist/remediate/phases/closeVerifyHeadEvidence.js.map +1 -1
- package/dist/remediate/phases/plan.d.ts +1 -1
- package/dist/remediate/review/reviewGate.d.ts +68 -39
- package/dist/remediate/review/reviewGate.d.ts.map +1 -1
- package/dist/remediate/review/reviewGate.js +133 -50
- package/dist/remediate/review/reviewGate.js.map +1 -1
- package/dist/remediate/state/store.d.ts +1 -2
- package/dist/remediate/state/store.d.ts.map +1 -1
- package/dist/remediate/state/store.js +124 -3
- package/dist/remediate/state/store.js.map +1 -1
- package/dist/remediate/state/types.d.ts +59 -8
- package/dist/remediate/state/types.d.ts.map +1 -1
- package/dist/remediate/state/types.js +12 -1
- package/dist/remediate/state/types.js.map +1 -1
- package/dist/remediate/steps/contractPipeline.d.ts +40 -2
- package/dist/remediate/steps/contractPipeline.d.ts.map +1 -1
- package/dist/remediate/steps/contractPipeline.js +147 -53
- package/dist/remediate/steps/contractPipeline.js.map +1 -1
- package/dist/remediate/steps/contractPipelinePrompts.d.ts +32 -8
- package/dist/remediate/steps/contractPipelinePrompts.d.ts.map +1 -1
- package/dist/remediate/steps/contractPipelinePrompts.js +145 -131
- package/dist/remediate/steps/contractPipelinePrompts.js.map +1 -1
- package/dist/remediate/steps/dispatch/hostHandoff.d.ts +6 -4
- package/dist/remediate/steps/dispatch/hostHandoff.d.ts.map +1 -1
- package/dist/remediate/steps/dispatch/hostHandoff.js +291 -155
- package/dist/remediate/steps/dispatch/hostHandoff.js.map +1 -1
- package/dist/remediate/steps/intakeResolver.d.ts +1 -1
- package/dist/remediate/steps/intakeResolver.d.ts.map +1 -1
- package/dist/remediate/steps/intakeResolver.js +50 -49
- package/dist/remediate/steps/intakeResolver.js.map +1 -1
- package/dist/remediate/steps/nextStep.d.ts.map +1 -1
- package/dist/remediate/steps/nextStep.js +337 -294
- package/dist/remediate/steps/nextStep.js.map +1 -1
- package/dist/remediate/steps/prompts.d.ts +10 -3
- package/dist/remediate/steps/prompts.d.ts.map +1 -1
- package/dist/remediate/steps/prompts.js +191 -228
- package/dist/remediate/steps/prompts.js.map +1 -1
- package/dist/remediate/steps/types.d.ts +2 -2
- package/dist/remediate/steps/types.d.ts.map +1 -1
- package/dist/remediate/steps/types.js +10 -3
- package/dist/remediate/steps/types.js.map +1 -1
- package/dist/remediate/validation/artifacts.d.ts.map +1 -1
- package/dist/remediate/validation/artifacts.js +5 -22
- package/dist/remediate/validation/artifacts.js.map +1 -1
- package/dist/remediate/validation/contractPipeline.d.ts.map +1 -1
- package/dist/remediate/validation/contractPipeline.js +25 -8
- package/dist/remediate/validation/contractPipeline.js.map +1 -1
- package/dist/remediate/validation/remediationState.d.ts +0 -1
- package/dist/remediate/validation/remediationState.d.ts.map +1 -1
- package/dist/remediate/validation/remediationState.js +1 -20
- package/dist/remediate/validation/remediationState.js.map +1 -1
- package/dist/shared/decompose/charterClarification.d.ts +7 -0
- package/dist/shared/decompose/charterClarification.d.ts.map +1 -1
- package/dist/shared/decompose/charterClarification.js +8 -1
- package/dist/shared/decompose/charterClarification.js.map +1 -1
- package/dist/shared/decompose/charterExtraction.d.ts +204 -16
- package/dist/shared/decompose/charterExtraction.d.ts.map +1 -1
- package/dist/shared/decompose/charterExtraction.js +70 -13
- package/dist/shared/decompose/charterExtraction.js.map +1 -1
- package/dist/shared/decompose/systemicChallenge.d.ts +42 -1
- package/dist/shared/decompose/systemicChallenge.d.ts.map +1 -1
- package/dist/shared/decompose/systemicChallenge.js +51 -0
- package/dist/shared/decompose/systemicChallenge.js.map +1 -1
- package/dist/shared/git.d.ts +31 -0
- package/dist/shared/git.d.ts.map +1 -1
- package/dist/shared/git.js +105 -0
- package/dist/shared/git.js.map +1 -1
- package/dist/shared/index.d.ts +17 -14
- package/dist/shared/index.d.ts.map +1 -1
- package/dist/shared/index.js +18 -11
- package/dist/shared/index.js.map +1 -1
- package/dist/shared/io/stepContractWriter.d.ts.map +1 -1
- package/dist/shared/io/stepContractWriter.js +13 -4
- package/dist/shared/io/stepContractWriter.js.map +1 -1
- package/dist/shared/reporting/auditDeliverable.d.ts +8 -3
- package/dist/shared/reporting/auditDeliverable.d.ts.map +1 -1
- package/dist/shared/reporting/auditDeliverable.js +10 -3
- package/dist/shared/reporting/auditDeliverable.js.map +1 -1
- package/dist/shared/submission/hostHandoffCore.d.ts +7 -0
- package/dist/shared/submission/hostHandoffCore.d.ts.map +1 -1
- package/dist/shared/submission/hostHandoffCore.js +15 -2
- package/dist/shared/submission/hostHandoffCore.js.map +1 -1
- package/dist/shared/submission/hostResultOutcomes.d.ts +60 -10
- package/dist/shared/submission/hostResultOutcomes.d.ts.map +1 -1
- package/dist/shared/submission/hostResultOutcomes.js +42 -10
- package/dist/shared/submission/hostResultOutcomes.js.map +1 -1
- package/dist/shared/submission/ingestReport.d.ts +54 -0
- package/dist/shared/submission/ingestReport.d.ts.map +1 -0
- package/dist/shared/submission/ingestReport.js +44 -0
- package/dist/shared/submission/ingestReport.js.map +1 -0
- package/dist/shared/submission/ingestionChecks.d.ts +8 -13
- package/dist/shared/submission/ingestionChecks.d.ts.map +1 -1
- package/dist/shared/submission/ingestionChecks.js +9 -14
- package/dist/shared/submission/ingestionChecks.js.map +1 -1
- package/dist/shared/submission/submissionClassifier.d.ts +6 -0
- package/dist/shared/submission/submissionClassifier.d.ts.map +1 -1
- package/dist/shared/submission/submissionClassifier.js +16 -0
- package/dist/shared/submission/submissionClassifier.js.map +1 -1
- package/dist/shared/submission/workerPromptBinding.d.ts +18 -0
- package/dist/shared/submission/workerPromptBinding.d.ts.map +1 -0
- package/dist/shared/submission/workerPromptBinding.js +38 -0
- package/dist/shared/submission/workerPromptBinding.js.map +1 -0
- package/dist/shared/tooling/exec.d.ts +24 -0
- package/dist/shared/tooling/exec.d.ts.map +1 -1
- package/dist/shared/tooling/exec.js +30 -0
- package/dist/shared/tooling/exec.js.map +1 -1
- package/dist/shared/types/charter.d.ts +143 -7
- package/dist/shared/types/charter.d.ts.map +1 -1
- package/dist/shared/types/charter.js +63 -7
- package/dist/shared/types/charter.js.map +1 -1
- package/dist/shared/types/charterPacket.d.ts +13 -0
- package/dist/shared/types/charterPacket.d.ts.map +1 -1
- package/dist/shared/types/charterPacket.js.map +1 -1
- package/dist/shared/types/contractPipeline/obligations.d.ts +30 -11
- package/dist/shared/types/contractPipeline/obligations.d.ts.map +1 -1
- package/dist/shared/types/contractPipeline/obligations.js +6 -1
- package/dist/shared/types/contractPipeline/obligations.js.map +1 -1
- package/dist/shared/types/contractPropertyShapes.d.ts.map +1 -1
- package/dist/shared/types/contractPropertyShapes.js +13 -0
- package/dist/shared/types/contractPropertyShapes.js.map +1 -1
- package/dist/shared/types/finding.d.ts +210 -15
- package/dist/shared/types/finding.d.ts.map +1 -1
- package/dist/shared/types/finding.js +68 -5
- package/dist/shared/types/finding.js.map +1 -1
- package/dist/shared/types/intentCheckpoint.d.ts +28 -42
- package/dist/shared/types/intentCheckpoint.d.ts.map +1 -1
- package/dist/shared/types/intentCheckpoint.js +30 -33
- package/dist/shared/types/intentCheckpoint.js.map +1 -1
- package/dist/shared/types/remediationOutcome.d.ts +8 -8
- package/dist/shared/validation/citationGrounding.d.ts +29 -5
- package/dist/shared/validation/citationGrounding.d.ts.map +1 -1
- package/dist/shared/validation/citationGrounding.js +56 -9
- package/dist/shared/validation/citationGrounding.js.map +1 -1
- package/dist/shared/validation/designFindingGrounding.d.ts +30 -9
- package/dist/shared/validation/designFindingGrounding.d.ts.map +1 -1
- package/dist/shared/validation/designFindingGrounding.js +32 -12
- package/dist/shared/validation/designFindingGrounding.js.map +1 -1
- package/dist/shared/validation/findingsReport.d.ts +13 -2
- package/dist/shared/validation/findingsReport.d.ts.map +1 -1
- package/dist/shared/validation/findingsReport.js +19 -2
- package/dist/shared/validation/findingsReport.js.map +1 -1
- package/docs/audit-pkg/contracts.md +4 -5
- package/package.json +1 -1
- package/schemas/audit_result.schema.json +51 -0
- package/schemas/audit_results.schema.json +51 -0
- package/schemas/audit_task.schema.json +44 -0
- 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
|
-
/**
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
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
|
-
/**
|
|
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
|
-
*
|
|
60
|
-
*
|
|
61
|
-
*
|
|
62
|
-
*
|
|
63
|
-
*
|
|
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
|
|
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 `
|
|
84
|
-
* `
|
|
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
|
-
*
|
|
89
|
-
*
|
|
90
|
-
* (INV-RSM-RESOLUTION-CORRELATE, COR-0b906e37)
|
|
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
|
|
95
|
-
*
|
|
96
|
-
* them
|
|
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 |
|
|
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":"
|
|
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
|
|
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.
|
|
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
|
-
|
|
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
|
-
*
|
|
87
|
-
* contract): every `
|
|
88
|
-
*
|
|
89
|
-
*
|
|
90
|
-
*
|
|
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
|
-
|
|
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
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
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 `
|
|
108
|
-
* `
|
|
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
|
-
*
|
|
113
|
-
*
|
|
114
|
-
* (INV-RSM-RESOLUTION-CORRELATE, COR-0b906e37)
|
|
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
|
|
119
|
-
*
|
|
120
|
-
* them
|
|
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
|
|
127
|
-
if (
|
|
128
|
-
|
|
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
|
|
145
|
-
const
|
|
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
|
|
225
|
+
const tierDeclined = declinedTiers.has(tier.necessity);
|
|
150
226
|
for (const item of tier.items) {
|
|
151
|
-
|
|
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: `
|
|
237
|
+
reason: `Declined by the user at the review gate — declined the entire "${item.necessity}" tier.`,
|
|
155
238
|
});
|
|
156
239
|
}
|
|
157
|
-
else if (
|
|
240
|
+
else if (userReasons.has(item.finding_id)) {
|
|
158
241
|
declined.push({
|
|
159
242
|
finding_id: item.finding_id,
|
|
160
|
-
reason: `
|
|
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":"AAAA,oEAAoE;AACpE,oDAAoD;AACpD,EAAE;AACF,4EAA4E;AAC5E,iFAAiF;AACjF,yEAAyE;AACzE,kFAAkF;AAClF,gFAAgF;AAChF,+EAA+E;AAC/E,8EAA8E;AAC9E,+EAA+E;AAC/E,iFAAiF;AACjF,6EAA6E;AAC7E,gDAAgD;AAChD,EAAE;AACF,iFAAiF;AACjF,
|
|
1
|
+
{"version":3,"file":"reviewGate.js","sourceRoot":"","sources":["../../../src/remediate/review/reviewGate.ts"],"names":[],"mappings":"AAAA,kGAAkG;AAClG,oEAAoE;AACpE,oDAAoD;AACpD,EAAE;AACF,4EAA4E;AAC5E,iFAAiF;AACjF,yEAAyE;AACzE,kFAAkF;AAClF,gFAAgF;AAChF,+EAA+E;AAC/E,8EAA8E;AAC9E,+EAA+E;AAC/E,iFAAiF;AACjF,6EAA6E;AAC7E,gDAAgD;AAChD,EAAE;AACF,iFAAiF;AACjF,6EAA6E;AAC7E,sEAAsE;AAEtE,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AAExB,OAAO,EAGL,uBAAuB,EACvB,0BAA0B,EAC1B,sBAAsB,EACtB,uBAAuB,GACxB,MAAM,sBAAsB,CAAC;AAE9B,MAAM,CAAC,MAAM,6BAA6B,GAAG,kCAA2C,CAAC;AAmCzF;;;;;GAKG;AACH,MAAM,sBAAsB,GAAG,CAAC;KAC7B,MAAM,CAAC;IACN,OAAO,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,QAAQ,EAAE;IAC9B,wDAAwD;IACxD,iBAAiB,EAAE,CAAC;SACjB,KAAK,CACJ,CAAC;SACE,MAAM,CAAC;QACN,UAAU,EAAE,CAAC,CAAC,MAAM,EAAE;QACtB,gEAAgE;QAChE,MAAM,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,QAAQ,EAAE;KAC9B,CAAC;SACD,MAAM,EAAE,CACZ;SACA,QAAQ,EAAE;IACb,2EAA2E;IAC3E,cAAc,EAAE,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,MAAM,EAAE,CAAC,CAAC,QAAQ,EAAE;CAC/C,CAAC;KACD,MAAM,EAAE,CAAC;AAIZ,qEAAqE;AACrE,MAAM,yBAAyB,GAA2B;IACxD,oBAAoB,EAClB,2FAA2F;IAC7F,iBAAiB,EAAE,kBAAkB;CACtC,CAAC;AAeF,SAAS,SAAS,CAAC,OAAgB;IACjC,OAAO,CAAC,OAAO,CAAC,cAAc,IAAI,EAAE,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,MAAM,CAAC,OAAO,CAAC,CAAC;AAC3E,CAAC;AAED,SAAS,OAAO,CAAC,OAAgB;IAC/B,MAAM,cAAc,GAAG,uBAAuB,CAAC,OAAO,CAAC,CAAC;IACxD,OAAO;QACL,UAAU,EAAE,OAAO,CAAC,EAAE;QACtB,KAAK,EAAE,OAAO,CAAC,KAAK;QACpB,QAAQ,EAAE,OAAO,CAAC,QAAQ;QAC1B,UAAU,EAAE,OAAO,CAAC,UAAU;QAC9B,IAAI,EAAE,OAAO,CAAC,IAAI;QAClB,OAAO,EAAE,OAAO,CAAC,OAAO;QACxB,cAAc,EAAE,SAAS,CAAC,OAAO,CAAC;QAClC,SAAS,EAAE,cAAc,CAAC,SAAS;QACnC,SAAS,EAAE,cAAc,CAAC,SAAS;QACnC,mBAAmB,EAAE,cAAc,CAAC,mBAAmB;KACxD,CAAC;AACJ,CAAC;AAED;;;;;GAKG;AACH,MAAM,UAAU,kBAAkB,CAChC,QAA4B,EAC5B,MAAc;IAEd,MAAM,OAAO,GAAG,0BAA0B,CAAC,QAAQ,CAAC,CAAC;IACrD,MAAM,MAAM,GAAoC;QAC9C,SAAS,EAAE,OAAO,CAAC,SAAS,CAAC,MAAM;QACnC,QAAQ,EAAE,OAAO,CAAC,QAAQ,CAAC,MAAM;QACjC,UAAU,EAAE,OAAO,CAAC,UAAU,CAAC,MAAM;KACtC,CAAC;IACF,MAAM,KAAK,GAAsB,EAAE,CAAC;IACpC,KAAK,MAAM,SAAS,IAAI,sBAAsB,EAAE,CAAC;QAC/C,MAAM,UAAU,GAAG,OAAO,CAAC,SAAS,CAAC,CAAC;QACtC,IAAI,UAAU,CAAC,MAAM,KAAK,CAAC;YAAE,SAAS;QACtC,KAAK,CAAC,IAAI,CAAC;YACT,SAAS;YACT,KAAK,EAAE,uBAAuB,CAAC,SAAS,CAAC,CAAC,KAAK;YAC/C,WAAW,EAAE,uBAAuB,CAAC,SAAS,CAAC,CAAC,WAAW;YAC3D,KAAK,EAAE,UAAU,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,OAAO,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC;SACjD,CAAC,CAAC;IACL,CAAC;IACD,OAAO;QACL,cAAc,EAAE,6BAA6B;QAC7C,OAAO,EAAE,MAAM;QACf,KAAK,EAAE,QAAQ,CAAC,MAAM;QACtB,MAAM;QACN,KAAK;KACN,CAAC;AACJ,CAAC;AAED;;;;;;GAMG;AACH,SAAS,sBAAsB,CAC7B,OAAsB,EACtB,UAAoD;IAEpD,MAAM,gBAAgB,GAAG,UAAU,EAAE,OAAO,CAAC;IAC7C,IAAI,gBAAgB,KAAK,SAAS;QAAE,OAAO,IAAI,CAAC;IAChD,OAAO,gBAAgB,KAAK,OAAO,CAAC,OAAO,CAAC;AAC9C,CAAC;AAED;;;;;;GAMG;AACH,SAAS,oBAAoB,CAC3B,OAAsB,EACtB,UAA4B;IAE5B,MAAM,QAAQ,GAAG,OAAO,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,UAAU,CAAC,CAAC,CAAC;IAChF,MAAM,UAAU,GAAG,IAAI,GAAG,CAAC,QAAQ,CAAC,CAAC;IACrC,MAAM,UAAU,GAAG,IAAI,GAAG,CAAS,sBAAsB,CAAC,CAAC;IAC3D,MAAM,QAAQ,GAAa,EAAE,CAAC;IAC9B,CAAC,UAAU,CAAC,iBAAiB,IAAI,EAAE,CAAC,CAAC,OAAO,CAAC,CAAC,KAAK,EAAE,KAAK,EAAE,EAAE;QAC5D,IAAI,CAAC,UAAU,CAAC,GAAG,CAAC,KAAK,CAAC,UAAU,CAAC,EAAE,CAAC;YACtC,QAAQ,CAAC,IAAI,CACX,uBAAuB,KAAK,qBAAqB,KAAK,CAAC,UAAU,oCAAoC,QAAQ,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,GAAG,CACnJ,CAAC;QACJ,CAAC;IACH,CAAC,CAAC,CAAC;IACH,CAAC,UAAU,CAAC,cAAc,IAAI,EAAE,CAAC,CAAC,OAAO,CAAC,CAAC,IAAI,EAAE,KAAK,EAAE,EAAE;QACxD,IAAI,CAAC,UAAU,CAAC,GAAG,CAAC,IAAI,CAAC,EAAE,CAAC;YAC1B,QAAQ,CAAC,IAAI,CACX,oBAAoB,KAAK,UAAU,IAAI,4BAA4B,sBAAsB,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,GAAG,CAC/H,CAAC;QACJ,CAAC;IACH,CAAC,CAAC,CAAC;IACH,OAAO,QAAQ,CAAC;AAClB,CAAC;AAED;;;;;;;GAOG;AACH,MAAM,UAAU,qBAAqB,CACnC,IAAY,EACZ,OAAsB;IAEtB,IAAI,KAAc,CAAC;IACnB,IAAI,CAAC;QACH,KAAK,GAAG,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC;IAC3B,CAAC;IAAC,OAAO,KAAK,EAAE,CAAC;QACf,OAAO;YACL,IAAI,EAAE,SAAS;YACf,MAAM,EAAE,+BAA+B,KAAK,YAAY,KAAK,CAAC,CAAC,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC,GAAG;SACjG,CAAC;IACJ,CAAC;IACD,IAAI,OAAO,KAAK,KAAK,QAAQ,IAAI,KAAK,KAAK,IAAI,IAAI,KAAK,CAAC,OAAO,CAAC,KAAK,CAAC,EAAE,CAAC;QACxE,OAAO;YACL,IAAI,EAAE,SAAS;YACf,MAAM,EAAE,6FAA6F;SACtG,CAAC;IACJ,CAAC;IACD,MAAM,MAAM,GAAI,KAA+B,CAAC,OAAO,CAAC;IACxD,IAAI,OAAO,MAAM,KAAK,QAAQ,IAAI,MAAM,KAAK,OAAO,CAAC,OAAO;QAAE,OAAO,EAAE,IAAI,EAAE,OAAO,EAAE,CAAC;IACvF,MAAM,QAAQ,GAAa,EAAE,CAAC;IAC9B,KAAK,MAAM,CAAC,OAAO,EAAE,WAAW,CAAC,IAAI,MAAM,CAAC,OAAO,CAAC,yBAAyB,CAAC,EAAE,CAAC;QAC/E,IAAI,OAAO,IAAI,KAAK,EAAE,CAAC;YACrB,QAAQ,CAAC,IAAI,CAAC,KAAK,OAAO,6BAA6B,WAAW,EAAE,CAAC,CAAC;QACxE,CAAC;IACH,CAAC;IACD,MAAM,MAAM,GAAG,sBAAsB,CAAC,SAAS,CAAC,KAAK,CAAC,CAAC;IACvD,IAAI,CAAC,MAAM,CAAC,OAAO,EAAE,CAAC;QACpB,KAAK,MAAM,KAAK,IAAI,MAAM,CAAC,KAAK,CAAC,MAAM,EAAE,CAAC;YACxC,IAAI,KAAK,CAAC,IAAI,KAAK,CAAC,CAAC,YAAY,CAAC,iBAAiB,EAAE,CAAC;gBACpD,MAAM,OAAO,GAAG,KAAK,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,CAAC,IAAI,yBAAyB,CAAC,CAAC,CAAC;gBAC5E,MAAM,KAAK,GAAG,KAAK,CAAC,IAAI,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC,CAAC,SAAS,KAAK,CAAC,IAAI,CAAC,IAAI,CAAC,GAAG,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC;gBAC7E,IAAI,OAAO,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;oBACvB,QAAQ,CAAC,IAAI,CACX,mBAAmB,KAAK,KAAK,OAAO,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,EAAE,CACzE,CAAC;gBACJ,CAAC;gBACD,SAAS;YACX,CAAC;YACD,MAAM,KAAK,GAAG,KAAK,CAAC,IAAI,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC,CAAC,KAAK,KAAK,CAAC,IAAI,CAAC,IAAI,CAAC,GAAG,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,CAAC;YAC3E,QAAQ,CAAC,IAAI,CAAC,GAAG,KAAK,GAAG,KAAK,CAAC,OAAO,EAAE,CAAC,CAAC;QAC5C,CAAC;IACH,CAAC;SAAM,CAAC;QACN,QAAQ,CAAC,IAAI,CAAC,GAAG,oBAAoB,CAAC,OAAO,EAAE,MAAM,CAAC,IAAI,CAAC,CAAC,CAAC;IAC/D,CAAC;IACD,OAAO,QAAQ,CAAC,MAAM,GAAG,CAAC,IAAI,CAAC,MAAM,CAAC,OAAO;QAC3C,CAAC,CAAC,EAAE,IAAI,EAAE,SAAS,EAAE,MAAM,EAAE,QAAQ,CAAC,IAAI,CAAC,IAAI,CAAC,EAAE;QAClD,CAAC,CAAC,EAAE,IAAI,EAAE,IAAI,EAAE,UAAU,EAAE,MAAM,CAAC,IAAI,EAAE,CAAC;AAC9C,CAAC;AAED;;;;;;;;;;;;;;;;GAgBG;AACH,MAAM,UAAU,qBAAqB,CACnC,OAAsB,EACtB,UAAwC;IAExC,IAAI,CAAC,sBAAsB,CAAC,OAAO,EAAE,UAAU,CAAC,EAAE,CAAC;QACjD,MAAM,IAAI,KAAK,CACb,8BAA8B,UAAU,EAAE,OAAO,6CAA6C,OAAO,CAAC,OAAO,yEAAyE,CACvL,CAAC;IACJ,CAAC;IACD,MAAM,OAAO,GAAG,UAAU,CAAC,CAAC,CAAC,oBAAoB,CAAC,OAAO,EAAE,UAAU,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC;IAC5E,IAAI,OAAO,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;QACvB,MAAM,IAAI,KAAK,CAAC,+BAA+B,OAAO,CAAC,IAAI,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC;IACvE,CAAC;IACD,MAAM,WAAW,GAAG,IAAI,GAAG,CACzB,CAAC,UAAU,EAAE,iBAAiB,IAAI,EAAE,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,CAAC,UAAU,EAAE,CAAC,CAAC,MAAM,EAAE,IAAI,EAAE,IAAI,SAAS,CAAC,CAAC,CAChG,CAAC;IACF,MAAM,aAAa,GAAG,IAAI,GAAG,CAAS,UAAU,EAAE,cAAc,IAAI,EAAE,CAAC,CAAC;IACxE,MAAM,YAAY,GAAa,EAAE,CAAC;IAClC,MAAM,QAAQ,GAA+B,EAAE,CAAC;IAEhD,KAAK,MAAM,IAAI,IAAI,OAAO,CAAC,KAAK,EAAE,CAAC;QACjC,MAAM,YAAY,GAAG,aAAa,CAAC,GAAG,CAAC,IAAI,CAAC,SAAS,CAAC,CAAC;QACvD,KAAK,MAAM,IAAI,IAAI,IAAI,CAAC,KAAK,EAAE,CAAC;YAC9B,MAAM,UAAU,GAAG,WAAW,CAAC,GAAG,CAAC,IAAI,CAAC,UAAU,CAAC,CAAC;YACpD,IAAI,UAAU,KAAK,SAAS,EAAE,CAAC;gBAC7B,QAAQ,CAAC,IAAI,CAAC;oBACZ,UAAU,EAAE,IAAI,CAAC,UAAU;oBAC3B,MAAM,EAAE,4CAA4C,UAAU,EAAE;iBACjE,CAAC,CAAC;YACL,CAAC;iBAAM,IAAI,YAAY,EAAE,CAAC;gBACxB,QAAQ,CAAC,IAAI,CAAC;oBACZ,UAAU,EAAE,IAAI,CAAC,UAAU;oBAC3B,MAAM,EAAE,kEAAkE,IAAI,CAAC,SAAS,SAAS;iBAClG,CAAC,CAAC;YACL,CAAC;iBAAM,IAAI,WAAW,CAAC,GAAG,CAAC,IAAI,CAAC,UAAU,CAAC,EAAE,CAAC;gBAC5C,QAAQ,CAAC,IAAI,CAAC;oBACZ,UAAU,EAAE,IAAI,CAAC,UAAU;oBAC3B,MAAM,EAAE,8DAA8D,IAAI,CAAC,SAAS,IAAI;iBACzF,CAAC,CAAC;YACL,CAAC;iBAAM,CAAC;gBACN,YAAY,CAAC,IAAI,CAAC,IAAI,CAAC,UAAU,CAAC,CAAC;YACrC,CAAC;QACH,CAAC;IACH,CAAC;IACD,OAAO,EAAE,YAAY,EAAE,QAAQ,EAAE,CAAC;AACpC,CAAC"}
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { SKIP_WRITE } from "audit-tools/shared";
|
|
2
|
-
import { RemediationPlan, RemediationItemState,
|
|
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":"
|
|
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
|
|
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
|
-
|
|
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,
|