audit-tools 0.52.3 → 0.52.4
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/README.md +31 -1
- package/dist/audit/cli/args.d.ts.map +1 -1
- package/dist/audit/cli/args.js +6 -2
- package/dist/audit/cli/args.js.map +1 -1
- package/dist/audit/cli/argumentContract.d.ts +11 -0
- package/dist/audit/cli/argumentContract.d.ts.map +1 -0
- package/dist/audit/cli/argumentContract.js +45 -0
- package/dist/audit/cli/argumentContract.js.map +1 -0
- package/dist/audit/cli/auditReviewBindings.d.ts +49 -0
- package/dist/audit/cli/auditReviewBindings.d.ts.map +1 -0
- package/dist/audit/cli/auditReviewBindings.js +61 -0
- package/dist/audit/cli/auditReviewBindings.js.map +1 -0
- package/dist/audit/cli/auditStep.d.ts +1 -27
- package/dist/audit/cli/auditStep.d.ts.map +1 -1
- package/dist/audit/cli/auditStep.js +7 -32
- package/dist/audit/cli/auditStep.js.map +1 -1
- package/dist/audit/cli/charterComparisonPrompt.d.ts +2 -0
- package/dist/audit/cli/charterComparisonPrompt.d.ts.map +1 -1
- package/dist/audit/cli/charterComparisonPrompt.js +9 -0
- package/dist/audit/cli/charterComparisonPrompt.js.map +1 -1
- package/dist/audit/cli/charterExtractionPrompt.d.ts.map +1 -1
- package/dist/audit/cli/charterExtractionPrompt.js +17 -3
- package/dist/audit/cli/charterExtractionPrompt.js.map +1 -1
- package/dist/audit/cli/conceptualDispatch.d.ts +1 -1
- package/dist/audit/cli/conceptualDispatch.d.ts.map +1 -1
- package/dist/audit/cli/conceptualDispatch.js +15 -2
- package/dist/audit/cli/conceptualDispatch.js.map +1 -1
- package/dist/audit/cli/confirmIntentStep.d.ts.map +1 -1
- package/dist/audit/cli/confirmIntentStep.js +10 -17
- package/dist/audit/cli/confirmIntentStep.js.map +1 -1
- package/dist/audit/cli/dispatch/hostHandoff.d.ts.map +1 -1
- package/dist/audit/cli/dispatch/hostHandoff.js +231 -229
- package/dist/audit/cli/dispatch/hostHandoff.js.map +1 -1
- package/dist/audit/cli/explainTaskCommand.d.ts.map +1 -1
- package/dist/audit/cli/explainTaskCommand.js +3 -1
- package/dist/audit/cli/explainTaskCommand.js.map +1 -1
- package/dist/audit/cli/fanoutLanes.d.ts +10 -14
- package/dist/audit/cli/fanoutLanes.d.ts.map +1 -1
- package/dist/audit/cli/fanoutLanes.js +50 -16
- package/dist/audit/cli/fanoutLanes.js.map +1 -1
- package/dist/audit/cli/functionalPreflight.d.ts +96 -0
- package/dist/audit/cli/functionalPreflight.d.ts.map +1 -0
- package/dist/audit/cli/functionalPreflight.js +103 -0
- package/dist/audit/cli/functionalPreflight.js.map +1 -0
- package/dist/audit/cli/laneSubmissions.d.ts +5 -1
- package/dist/audit/cli/laneSubmissions.d.ts.map +1 -1
- package/dist/audit/cli/laneSubmissions.js +45 -34
- package/dist/audit/cli/laneSubmissions.js.map +1 -1
- package/dist/audit/cli/nextStepCommand.d.ts +1 -9
- package/dist/audit/cli/nextStepCommand.d.ts.map +1 -1
- package/dist/audit/cli/nextStepCommand.js +101 -38
- package/dist/audit/cli/nextStepCommand.js.map +1 -1
- package/dist/audit/cli/nextStepHelpers.d.ts +10 -25
- package/dist/audit/cli/nextStepHelpers.d.ts.map +1 -1
- package/dist/audit/cli/nextStepHelpers.js +57 -67
- package/dist/audit/cli/nextStepHelpers.js.map +1 -1
- package/dist/audit/cli/prompts.d.ts.map +1 -1
- package/dist/audit/cli/prompts.js +9 -4
- package/dist/audit/cli/prompts.js.map +1 -1
- package/dist/audit/cli/recoverSubmissionCommand.d.ts.map +1 -1
- package/dist/audit/cli/recoverSubmissionCommand.js +8 -1
- package/dist/audit/cli/recoverSubmissionCommand.js.map +1 -1
- package/dist/audit/cli/reviewRun.js +1 -1
- package/dist/audit/cli/reviewRun.js.map +1 -1
- package/dist/audit/cli/reviewSubmission.d.ts +24 -0
- package/dist/audit/cli/reviewSubmission.d.ts.map +1 -0
- package/dist/audit/cli/reviewSubmission.js +97 -0
- package/dist/audit/cli/reviewSubmission.js.map +1 -0
- package/dist/audit/cli/sampleRunCommand.d.ts +3 -3
- package/dist/audit/cli/steps.d.ts +18 -18
- package/dist/audit/cli/steps.d.ts.map +1 -1
- package/dist/audit/cli/steps.js +1 -0
- package/dist/audit/cli/steps.js.map +1 -1
- package/dist/audit/cli/unacceptResultsCommand.d.ts.map +1 -1
- package/dist/audit/cli/unacceptResultsCommand.js +2 -1
- package/dist/audit/cli/unacceptResultsCommand.js.map +1 -1
- package/dist/audit/cli.d.ts +2 -1
- package/dist/audit/cli.d.ts.map +1 -1
- package/dist/audit/cli.js +27 -19
- package/dist/audit/cli.js.map +1 -1
- package/dist/audit/contracts/workerSchemas.d.ts +10 -10
- package/dist/audit/contracts/wrapperResponse.d.ts +34 -34
- package/dist/audit/extractors/commentDecomposition.d.ts +14 -0
- package/dist/audit/extractors/commentDecomposition.d.ts.map +1 -1
- package/dist/audit/extractors/commentDecomposition.js +2 -1
- package/dist/audit/extractors/commentDecomposition.js.map +1 -1
- package/dist/audit/extractors/graph.d.ts +1 -1
- package/dist/audit/extractors/graph.d.ts.map +1 -1
- package/dist/audit/extractors/graph.js +12 -7
- package/dist/audit/extractors/graph.js.map +1 -1
- package/dist/audit/extractors/graphPythonImports.d.ts +3 -0
- package/dist/audit/extractors/graphPythonImports.d.ts.map +1 -1
- package/dist/audit/extractors/graphPythonImports.js +24 -5
- package/dist/audit/extractors/graphPythonImports.js.map +1 -1
- package/dist/audit/extractors/graphRouteSources.d.ts +7 -0
- package/dist/audit/extractors/graphRouteSources.d.ts.map +1 -0
- package/dist/audit/extractors/graphRouteSources.js +208 -0
- package/dist/audit/extractors/graphRouteSources.js.map +1 -0
- package/dist/audit/extractors/graphRoutes.d.ts +1 -1
- package/dist/audit/extractors/graphRoutes.d.ts.map +1 -1
- package/dist/audit/extractors/graphRoutes.js +65 -68
- package/dist/audit/extractors/graphRoutes.js.map +1 -1
- package/dist/audit/io/artifacts.d.ts +1 -0
- package/dist/audit/io/artifacts.d.ts.map +1 -1
- package/dist/audit/io/artifacts.js +29 -10
- package/dist/audit/io/artifacts.js.map +1 -1
- package/dist/audit/io/runArtifacts.d.ts +1 -1
- package/dist/audit/io/runArtifacts.d.ts.map +1 -1
- package/dist/audit/io/runArtifacts.js +3 -0
- package/dist/audit/io/runArtifacts.js.map +1 -1
- package/dist/audit/io/toolingManifest.d.ts.map +1 -1
- package/dist/audit/io/toolingManifest.js +11 -32
- package/dist/audit/io/toolingManifest.js.map +1 -1
- package/dist/audit/orchestrator/acquisitionExecutor.d.ts +2 -3
- package/dist/audit/orchestrator/acquisitionExecutor.d.ts.map +1 -1
- package/dist/audit/orchestrator/acquisitionExecutor.js.map +1 -1
- package/dist/audit/orchestrator/advanceTypes.d.ts +1 -2
- package/dist/audit/orchestrator/advanceTypes.d.ts.map +1 -1
- package/dist/audit/orchestrator/autoFixExecutor.d.ts +2 -2
- package/dist/audit/orchestrator/autoFixExecutor.d.ts.map +1 -1
- package/dist/audit/orchestrator/autoFixExecutor.js +2 -2
- package/dist/audit/orchestrator/autoFixExecutor.js.map +1 -1
- package/dist/audit/orchestrator/charterFidelityPacket.d.ts +2 -0
- package/dist/audit/orchestrator/charterFidelityPacket.d.ts.map +1 -1
- package/dist/audit/orchestrator/charterFidelityPacket.js +6 -1
- package/dist/audit/orchestrator/charterFidelityPacket.js.map +1 -1
- package/dist/audit/orchestrator/charterPackets.d.ts +2 -0
- package/dist/audit/orchestrator/charterPackets.d.ts.map +1 -1
- package/dist/audit/orchestrator/charterPackets.js +6 -0
- package/dist/audit/orchestrator/charterPackets.js.map +1 -1
- package/dist/audit/orchestrator/designReviewProjection.d.ts +5 -8
- package/dist/audit/orchestrator/designReviewProjection.d.ts.map +1 -1
- package/dist/audit/orchestrator/designReviewProjection.js +7 -1
- package/dist/audit/orchestrator/designReviewProjection.js.map +1 -1
- package/dist/audit/orchestrator/designReviewPrompt.d.ts +3 -47
- package/dist/audit/orchestrator/designReviewPrompt.d.ts.map +1 -1
- package/dist/audit/orchestrator/designReviewPrompt.js +28 -135
- package/dist/audit/orchestrator/designReviewPrompt.js.map +1 -1
- package/dist/audit/orchestrator/designReviewSnapshot.d.ts +24 -1
- package/dist/audit/orchestrator/designReviewSnapshot.d.ts.map +1 -1
- package/dist/audit/orchestrator/designReviewSnapshot.js +8 -2
- package/dist/audit/orchestrator/designReviewSnapshot.js.map +1 -1
- package/dist/audit/orchestrator/designReviewTask.d.ts +46 -0
- package/dist/audit/orchestrator/designReviewTask.d.ts.map +1 -0
- package/dist/audit/orchestrator/designReviewTask.js +44 -0
- package/dist/audit/orchestrator/designReviewTask.js.map +1 -0
- package/dist/audit/orchestrator/intentCheckpointGate.d.ts +1 -1
- package/dist/audit/orchestrator/intentCheckpointGate.d.ts.map +1 -1
- package/dist/audit/orchestrator/intentCheckpointGate.js +4 -2
- package/dist/audit/orchestrator/intentCheckpointGate.js.map +1 -1
- package/dist/audit/orchestrator/intentEquivalenceExecutor.d.ts +3 -0
- package/dist/audit/orchestrator/intentEquivalenceExecutor.d.ts.map +1 -1
- package/dist/audit/orchestrator/intentEquivalenceExecutor.js +2 -0
- package/dist/audit/orchestrator/intentEquivalenceExecutor.js.map +1 -1
- package/dist/audit/orchestrator/intentInterpreter.d.ts +1 -9
- package/dist/audit/orchestrator/intentInterpreter.d.ts.map +1 -1
- package/dist/audit/orchestrator/intentInterpreter.js +6 -11
- package/dist/audit/orchestrator/intentInterpreter.js.map +1 -1
- package/dist/audit/orchestrator/selectiveDeepening/shared.d.ts +2 -2
- package/dist/audit/orchestrator/selectiveDeepening/shared.d.ts.map +1 -1
- package/dist/audit/orchestrator/staleness.d.ts.map +1 -1
- package/dist/audit/orchestrator/staleness.js +32 -40
- package/dist/audit/orchestrator/staleness.js.map +1 -1
- package/dist/audit/orchestrator/state.d.ts.map +1 -1
- package/dist/audit/orchestrator/state.js +8 -14
- package/dist/audit/orchestrator/state.js.map +1 -1
- package/dist/audit/orchestrator/systemicChallengeExecutor.d.ts +19 -0
- package/dist/audit/orchestrator/systemicChallengeExecutor.d.ts.map +1 -1
- package/dist/audit/orchestrator/systemicChallengeExecutor.js +15 -1
- package/dist/audit/orchestrator/systemicChallengeExecutor.js.map +1 -1
- package/dist/audit/reporting/criticalFlowFallbackPrompt.d.ts +1 -1
- package/dist/audit/reporting/criticalFlowFallbackPrompt.d.ts.map +1 -1
- package/dist/audit/reporting/criticalFlowFallbackPrompt.js +4 -2
- package/dist/audit/reporting/criticalFlowFallbackPrompt.js.map +1 -1
- package/dist/audit/reporting/mergeFindings.d.ts.map +1 -1
- package/dist/audit/reporting/mergeFindings.js +43 -2
- package/dist/audit/reporting/mergeFindings.js.map +1 -1
- package/dist/audit/types/auditScope.d.ts +2 -2
- package/dist/audit/types/auditState.d.ts +6 -6
- package/dist/audit/types/conceptualAdjudication.d.ts +19 -19
- package/dist/audit/types/conceptualAdjudication.d.ts.map +1 -1
- package/dist/audit/types/conceptualAdjudication.js +3 -3
- package/dist/audit/types/conceptualAdjudication.js.map +1 -1
- package/dist/audit/types.d.ts +17 -19
- package/dist/audit/types.d.ts.map +1 -1
- package/dist/audit/types.js +19 -20
- package/dist/audit/types.js.map +1 -1
- package/dist/remediate/contractPipeline/artifactStore.d.ts +3 -1
- package/dist/remediate/contractPipeline/artifactStore.d.ts.map +1 -1
- package/dist/remediate/contractPipeline/artifactStore.js +26 -31
- package/dist/remediate/contractPipeline/artifactStore.js.map +1 -1
- package/dist/remediate/contractPipeline/changeClassification.d.ts.map +1 -1
- package/dist/remediate/contractPipeline/changeClassification.js +20 -8
- package/dist/remediate/contractPipeline/changeClassification.js.map +1 -1
- package/dist/remediate/contractPipeline/contractReviewBinding.d.ts +28 -0
- package/dist/remediate/contractPipeline/contractReviewBinding.d.ts.map +1 -0
- package/dist/remediate/contractPipeline/contractReviewBinding.js +75 -0
- package/dist/remediate/contractPipeline/contractReviewBinding.js.map +1 -0
- package/dist/remediate/contractPipeline/repairState.d.ts +3 -1
- package/dist/remediate/contractPipeline/repairState.d.ts.map +1 -1
- package/dist/remediate/contractPipeline/repairState.js +1 -0
- package/dist/remediate/contractPipeline/repairState.js.map +1 -1
- package/dist/remediate/contractPipeline/sketchSource.d.ts +1 -0
- package/dist/remediate/contractPipeline/sketchSource.d.ts.map +1 -1
- package/dist/remediate/contractPipeline/sketchSource.js +1 -0
- package/dist/remediate/contractPipeline/sketchSource.js.map +1 -1
- package/dist/remediate/dedup/crossLensDedup.d.ts +1 -2
- package/dist/remediate/dedup/crossLensDedup.d.ts.map +1 -1
- package/dist/remediate/dedup/crossLensDedup.js +1 -0
- package/dist/remediate/dedup/crossLensDedup.js.map +1 -1
- package/dist/remediate/index.d.ts.map +1 -1
- package/dist/remediate/index.js +92 -52
- package/dist/remediate/index.js.map +1 -1
- package/dist/remediate/intake.d.ts +54 -0
- package/dist/remediate/intake.d.ts.map +1 -1
- package/dist/remediate/intake.js +15 -1
- package/dist/remediate/intake.js.map +1 -1
- package/dist/remediate/intakeSelection.d.ts +27 -0
- package/dist/remediate/intakeSelection.d.ts.map +1 -0
- package/dist/remediate/intakeSelection.js +37 -0
- package/dist/remediate/intakeSelection.js.map +1 -0
- package/dist/remediate/intent/intentPersistence.d.ts +41 -0
- package/dist/remediate/intent/intentPersistence.d.ts.map +1 -0
- package/dist/remediate/intent/intentPersistence.js +129 -0
- package/dist/remediate/intent/intentPersistence.js.map +1 -0
- package/dist/remediate/phases/close.d.ts +2 -1
- package/dist/remediate/phases/close.d.ts.map +1 -1
- package/dist/remediate/phases/close.js +19 -2
- package/dist/remediate/phases/close.js.map +1 -1
- package/dist/remediate/phases/closeReviewProvenance.d.ts +4 -0
- package/dist/remediate/phases/closeReviewProvenance.d.ts.map +1 -0
- package/dist/remediate/phases/closeReviewProvenance.js +16 -0
- package/dist/remediate/phases/closeReviewProvenance.js.map +1 -0
- package/dist/remediate/phases/triage.d.ts.map +1 -1
- package/dist/remediate/phases/triage.js +3 -1
- package/dist/remediate/phases/triage.js.map +1 -1
- package/dist/remediate/review/filterDispositions.d.ts +13 -0
- package/dist/remediate/review/filterDispositions.d.ts.map +1 -0
- package/dist/remediate/review/filterDispositions.js +32 -0
- package/dist/remediate/review/filterDispositions.js.map +1 -0
- package/dist/remediate/state/runIdentity.d.ts +27 -0
- package/dist/remediate/state/runIdentity.d.ts.map +1 -0
- package/dist/remediate/state/runIdentity.js +38 -0
- package/dist/remediate/state/runIdentity.js.map +1 -0
- package/dist/remediate/state/store.d.ts +80 -1
- package/dist/remediate/state/store.d.ts.map +1 -1
- package/dist/remediate/state/store.js +82 -1
- package/dist/remediate/state/store.js.map +1 -1
- package/dist/remediate/state/types.d.ts +215 -42
- package/dist/remediate/state/types.d.ts.map +1 -1
- package/dist/remediate/state/types.js +11 -3
- package/dist/remediate/state/types.js.map +1 -1
- package/dist/remediate/steps/contractPipeline.d.ts +61 -3
- package/dist/remediate/steps/contractPipeline.d.ts.map +1 -1
- package/dist/remediate/steps/contractPipeline.js +121 -47
- package/dist/remediate/steps/contractPipeline.js.map +1 -1
- package/dist/remediate/steps/contractPipelinePrompts.d.ts +27 -4
- package/dist/remediate/steps/contractPipelinePrompts.d.ts.map +1 -1
- package/dist/remediate/steps/contractPipelinePrompts.js +34 -32
- package/dist/remediate/steps/contractPipelinePrompts.js.map +1 -1
- package/dist/remediate/steps/dispatch/contractConformanceReview.d.ts +31 -0
- package/dist/remediate/steps/dispatch/contractConformanceReview.d.ts.map +1 -0
- package/dist/remediate/steps/dispatch/contractConformanceReview.js +98 -0
- package/dist/remediate/steps/dispatch/contractConformanceReview.js.map +1 -0
- package/dist/remediate/steps/dispatch/hostContracts.d.ts +133 -0
- package/dist/remediate/steps/dispatch/hostContracts.d.ts.map +1 -0
- package/dist/remediate/steps/dispatch/hostContracts.js +104 -0
- package/dist/remediate/steps/dispatch/hostContracts.js.map +1 -0
- package/dist/remediate/steps/dispatch/hostCorroboration.d.ts +70 -0
- package/dist/remediate/steps/dispatch/hostCorroboration.d.ts.map +1 -0
- package/dist/remediate/steps/dispatch/hostCorroboration.js +350 -0
- package/dist/remediate/steps/dispatch/hostCorroboration.js.map +1 -0
- package/dist/remediate/steps/dispatch/hostHandoff.d.ts +8 -162
- package/dist/remediate/steps/dispatch/hostHandoff.d.ts.map +1 -1
- package/dist/remediate/steps/dispatch/hostHandoff.js +137 -690
- package/dist/remediate/steps/dispatch/hostHandoff.js.map +1 -1
- package/dist/remediate/steps/dispatch/requiredTests.d.ts +83 -0
- package/dist/remediate/steps/dispatch/requiredTests.d.ts.map +1 -0
- package/dist/remediate/steps/dispatch/requiredTests.js +262 -0
- package/dist/remediate/steps/dispatch/requiredTests.js.map +1 -0
- package/dist/remediate/steps/finalGate.d.ts +8 -7
- package/dist/remediate/steps/finalGate.d.ts.map +1 -1
- package/dist/remediate/steps/finalGate.js +14 -11
- package/dist/remediate/steps/finalGate.js.map +1 -1
- package/dist/remediate/steps/frictionCloseout.d.ts +34 -0
- package/dist/remediate/steps/frictionCloseout.d.ts.map +1 -0
- package/dist/remediate/steps/frictionCloseout.js +113 -0
- package/dist/remediate/steps/frictionCloseout.js.map +1 -0
- package/dist/remediate/steps/gateCommands.d.ts +7 -9
- package/dist/remediate/steps/gateCommands.d.ts.map +1 -1
- package/dist/remediate/steps/gateCommands.js +37 -11
- package/dist/remediate/steps/gateCommands.js.map +1 -1
- package/dist/remediate/steps/intakeResolver.d.ts +2 -0
- package/dist/remediate/steps/intakeResolver.d.ts.map +1 -1
- package/dist/remediate/steps/intakeResolver.js +36 -11
- package/dist/remediate/steps/intakeResolver.js.map +1 -1
- package/dist/remediate/steps/nextStep.d.ts +23 -110
- package/dist/remediate/steps/nextStep.d.ts.map +1 -1
- package/dist/remediate/steps/nextStep.js +232 -508
- package/dist/remediate/steps/nextStep.js.map +1 -1
- package/dist/remediate/steps/prompts.d.ts +1 -1
- package/dist/remediate/steps/prompts.d.ts.map +1 -1
- package/dist/remediate/steps/prompts.js +7 -2
- package/dist/remediate/steps/prompts.js.map +1 -1
- package/dist/remediate/steps/recoverIngest.d.ts +57 -0
- package/dist/remediate/steps/recoverIngest.d.ts.map +1 -0
- package/dist/remediate/steps/recoverIngest.js +171 -0
- package/dist/remediate/steps/recoverIngest.js.map +1 -0
- package/dist/remediate/steps/stepWriter.d.ts +5 -4
- package/dist/remediate/steps/stepWriter.d.ts.map +1 -1
- package/dist/remediate/steps/stepWriter.js +2 -1
- package/dist/remediate/steps/stepWriter.js.map +1 -1
- package/dist/remediate/steps/types.d.ts +2 -3
- package/dist/remediate/steps/types.d.ts.map +1 -1
- 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 -12
- 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 +10 -7
- package/dist/remediate/validation/contractPipeline.js.map +1 -1
- package/dist/remediate/validation/contractPipelineGates.d.ts +13 -67
- package/dist/remediate/validation/contractPipelineGates.d.ts.map +1 -1
- package/dist/remediate/validation/contractPipelineGates.js +65 -139
- package/dist/remediate/validation/contractPipelineGates.js.map +1 -1
- package/dist/remediate/validation/remediationState.d.ts.map +1 -1
- package/dist/remediate/validation/remediationState.js +9 -3
- package/dist/remediate/validation/remediationState.js.map +1 -1
- package/dist/shared/agentReflections.d.ts +2 -2
- package/dist/shared/analyzerPolicy.d.ts +2 -16
- package/dist/shared/analyzerPolicy.d.ts.map +1 -1
- package/dist/shared/analyzerPolicy.js +22 -24
- package/dist/shared/analyzerPolicy.js.map +1 -1
- package/dist/shared/analyzerRunConsent.d.ts +33 -0
- package/dist/shared/analyzerRunConsent.d.ts.map +1 -0
- package/dist/shared/analyzerRunConsent.js +42 -0
- package/dist/shared/analyzerRunConsent.js.map +1 -0
- package/dist/shared/analyzers/acquisitionEngine.d.ts +3 -8
- package/dist/shared/analyzers/acquisitionEngine.d.ts.map +1 -1
- package/dist/shared/analyzers/acquisitionEngine.js +1 -0
- package/dist/shared/analyzers/acquisitionEngine.js.map +1 -1
- package/dist/shared/decompose/charterExtraction.d.ts +8 -8
- package/dist/shared/decompose/systemicChallenge.d.ts +12 -12
- package/dist/shared/engine/obligationEngine.d.ts +2 -2
- package/dist/shared/findings/dedupe.d.ts.map +1 -1
- package/dist/shared/findings/dedupe.js +16 -0
- package/dist/shared/findings/dedupe.js.map +1 -1
- package/dist/shared/graph/orderedReachability.d.ts +9 -0
- package/dist/shared/graph/orderedReachability.d.ts.map +1 -0
- package/dist/shared/graph/orderedReachability.js +21 -0
- package/dist/shared/graph/orderedReachability.js.map +1 -0
- package/dist/shared/index.d.ts +9 -3
- package/dist/shared/index.d.ts.map +1 -1
- package/dist/shared/index.js +6 -3
- package/dist/shared/index.js.map +1 -1
- package/dist/shared/intake/guidanceBootstrap.d.ts +2 -0
- package/dist/shared/intake/guidanceBootstrap.d.ts.map +1 -1
- package/dist/shared/intake/guidanceBootstrap.js +10 -1
- package/dist/shared/intake/guidanceBootstrap.js.map +1 -1
- package/dist/shared/intent/constraintClauses.d.ts +3 -0
- package/dist/shared/intent/constraintClauses.d.ts.map +1 -1
- package/dist/shared/intent/constraintClauses.js +9 -0
- package/dist/shared/intent/constraintClauses.js.map +1 -1
- package/dist/shared/io/artifactTreeHold.d.ts +5 -0
- package/dist/shared/io/artifactTreeHold.d.ts.map +1 -0
- package/dist/shared/io/artifactTreeHold.js +13 -0
- package/dist/shared/io/artifactTreeHold.js.map +1 -0
- package/dist/shared/io/auditToolsPaths.d.ts +3 -1
- package/dist/shared/io/auditToolsPaths.d.ts.map +1 -1
- package/dist/shared/io/auditToolsPaths.js +6 -1
- package/dist/shared/io/auditToolsPaths.js.map +1 -1
- package/dist/shared/io/collectFilesSorted.d.ts +10 -0
- package/dist/shared/io/collectFilesSorted.d.ts.map +1 -0
- package/dist/shared/io/collectFilesSorted.js +38 -0
- package/dist/shared/io/collectFilesSorted.js.map +1 -0
- package/dist/shared/io/json.d.ts.map +1 -1
- package/dist/shared/io/json.js +11 -4
- package/dist/shared/io/json.js.map +1 -1
- package/dist/shared/io/stepContractWriter.d.ts.map +1 -1
- package/dist/shared/io/stepContractWriter.js +8 -0
- package/dist/shared/io/stepContractWriter.js.map +1 -1
- package/dist/shared/loopCorePaths.d.ts.map +1 -1
- package/dist/shared/loopCorePaths.js +9 -0
- package/dist/shared/loopCorePaths.js.map +1 -1
- package/dist/shared/observability/rootLogObservations.d.ts +10 -0
- package/dist/shared/observability/rootLogObservations.d.ts.map +1 -0
- package/dist/shared/observability/rootLogObservations.js +147 -0
- package/dist/shared/observability/rootLogObservations.js.map +1 -0
- package/dist/shared/prompts.d.ts +7 -7
- package/dist/shared/prompts.d.ts.map +1 -1
- package/dist/shared/prompts.js +11 -11
- package/dist/shared/prompts.js.map +1 -1
- package/dist/shared/submission/submissionLedger.d.ts +7 -1
- package/dist/shared/submission/submissionLedger.d.ts.map +1 -1
- package/dist/shared/submission/submissionLedger.js +8 -0
- package/dist/shared/submission/submissionLedger.js.map +1 -1
- package/dist/shared/tooling/testCommand.d.ts +1 -0
- package/dist/shared/tooling/testCommand.d.ts.map +1 -1
- package/dist/shared/tooling/testCommand.js +4 -0
- package/dist/shared/tooling/testCommand.js.map +1 -1
- package/dist/shared/types/charter.d.ts +8 -8
- package/dist/shared/types/conceptualPerspective.d.ts +66 -0
- package/dist/shared/types/conceptualPerspective.d.ts.map +1 -0
- package/dist/shared/types/conceptualPerspective.js +135 -0
- package/dist/shared/types/conceptualPerspective.js.map +1 -0
- package/dist/shared/types/contractPipeline/design.d.ts +5 -0
- package/dist/shared/types/contractPipeline/design.d.ts.map +1 -1
- package/dist/shared/types/contractPipeline/design.js +6 -0
- package/dist/shared/types/contractPipeline/design.js.map +1 -1
- package/dist/shared/types/contractPipeline/implementation.d.ts +54 -0
- package/dist/shared/types/contractPipeline/implementation.d.ts.map +1 -1
- package/dist/shared/types/contractPipeline/implementation.js +10 -5
- package/dist/shared/types/contractPipeline/implementation.js.map +1 -1
- package/dist/shared/types/contractPipeline/obligations.d.ts +21 -10
- package/dist/shared/types/contractPipeline/obligations.d.ts.map +1 -1
- package/dist/shared/types/contractPipeline/obligations.js +13 -1
- package/dist/shared/types/contractPipeline/obligations.js.map +1 -1
- package/dist/shared/types/disposition.d.ts +6 -6
- package/dist/shared/types/finding.d.ts +50 -50
- package/dist/shared/types/intentCheckpoint.d.ts +66 -11
- package/dist/shared/types/intentCheckpoint.d.ts.map +1 -1
- package/dist/shared/types/intentCheckpoint.js +10 -2
- package/dist/shared/types/intentCheckpoint.js.map +1 -1
- package/dist/shared/types/remediationOutcome.d.ts +252 -24
- package/dist/shared/types/remediationOutcome.d.ts.map +1 -1
- package/dist/shared/types/remediationOutcome.js +4 -0
- package/dist/shared/types/remediationOutcome.js.map +1 -1
- package/dist/shared/types/reviewIndependence.d.ts +225 -0
- package/dist/shared/types/reviewIndependence.d.ts.map +1 -0
- package/dist/shared/types/reviewIndependence.js +67 -0
- package/dist/shared/types/reviewIndependence.js.map +1 -0
- package/dist/shared/types/stepContract.d.ts +8 -2
- package/dist/shared/types/stepContract.d.ts.map +1 -1
- package/dist/shared/types/stepContract.js +4 -1
- package/dist/shared/types/stepContract.js.map +1 -1
- package/dist/shared/validation/findingsReport.d.ts.map +1 -1
- package/dist/shared/validation/findingsReport.js +13 -0
- package/dist/shared/validation/findingsReport.js.map +1 -1
- package/docs/audit-pkg/contracts.md +38 -2
- package/docs/audit-pkg/operator-guide.md +59 -4
- package/package.json +5 -5
- package/skills/audit-code/audit-code.prompt.md +1 -31
- package/skills/remediate-code/remediate-code.prompt.md +12 -6
|
@@ -1,28 +1,34 @@
|
|
|
1
|
+
import { stateRunId, requireStateRunId, currentHostBoundaryState } from "../state/runIdentity.js";
|
|
2
|
+
import { buildFrictionWalkStep, presentReportStep, decideRemediateFrictionCloseout } from "./frictionCloseout.js";
|
|
3
|
+
import { reviewFilterDispositionsPath, persistReviewFilterDispositions } from "../review/filterDispositions.js";
|
|
4
|
+
import { INTENT_INTERPRETATION_FILENAME, readPersistedIntentInterpretationSync, readOrRepairIntentInterpretation } from "../intent/intentPersistence.js";
|
|
5
|
+
import { requestedFindingSelection, renderFindingSelection } from "../intakeSelection.js";
|
|
6
|
+
import { parseCommandString } from "../../shared/tooling/commandShape.js";
|
|
1
7
|
// sites-pinned: tests/remediate/friction-capture-closeout.test.ts, tests/remediate/next-step-lifecycle.test.ts, tests/remediate/next-step-pipeline-dispatch.test.ts, tests/remediate/next-step-outcomes-contract.test.ts, tests/remediate/integration-pipeline.test.ts, tests/remediate/outcomes-roundtrip.test.ts, tests/remediate/phase-close.test.ts, tests/remediate/grounding.test.ts, tests/remediate/clarification-round-contract.test.ts, tests/remediate/next-step-review-gate.test.ts, tests/remediate/n-r04-intent-checkpoint.test.ts, tests/remediate/final-gate-red-pause.test.ts
|
|
2
8
|
// (the free-form branch's write
|
|
3
9
|
// scope is normalized — a backslash-spelled citation no longer wedges prepare)
|
|
4
10
|
import { AUDIT_TOOLS_DIRNAME } from "../../shared/io/auditToolsPaths.js";
|
|
5
11
|
import { loadRemediateSessionConfig } from "./sessionConfigLoad.js";
|
|
6
12
|
import { z } from "zod";
|
|
7
|
-
import { existsSync,
|
|
13
|
+
import { existsSync, statSync } from "node:fs";
|
|
8
14
|
import { mkdir, readFile, rename, writeFile } from "node:fs/promises";
|
|
9
15
|
import { dirname, join, resolve } from "node:path";
|
|
10
|
-
import { StateStore } from "../state/store.js";
|
|
16
|
+
import { StateStore, OPERATOR_LIFECYCLE_FILENAME } from "../state/store.js";
|
|
11
17
|
// IO / validation / rendering helpers
|
|
12
|
-
import { discardOnSchemaVersionMismatch, readOptionalJsonFile, stagedAndUntracked, writeJsonFile, writeTextFile, buildAuditDeliverablePair, auditReadOf, formatValidationIssues, isRecord, renderIngestReportLines, withFsRetry, RunLogger, coerceJsonObjectArg, renderPromptCommand, headCommit, projectAuditFindingsReportSubset,
|
|
18
|
+
import { discardOnSchemaVersionMismatch, applyGuidanceFile, applyGuidanceText, readOptionalJsonFile, stagedAndUntracked, writeJsonFile, writeTextFile, buildAuditDeliverablePair, auditReadOf, formatValidationIssues, isRecord, renderIngestReportLines, withFsRetry, RunLogger, coerceJsonObjectArg, renderPromptCommand, headCommit, hashContent, projectAuditFindingsReportSubset,
|
|
13
19
|
// obligation engine + intent
|
|
14
|
-
interpretFreeFormIntent,
|
|
20
|
+
interpretFreeFormIntent, unresolvedFromClauses, advance, describeStoppedFold, linkFrictionRunIds,
|
|
15
21
|
// domain constants
|
|
16
|
-
LENSES, SEVERITIES, CLOSING_ACTIONS,
|
|
22
|
+
LENSES, SEVERITIES, CLOSING_ACTIONS, detectProjectFacts, isClosingAction, neutralProjectFacts, repoRelativePath, } from "audit-tools/shared";
|
|
17
23
|
import { applyPlanPipeline, buildCoverageLedger } from "../phases/plan.js";
|
|
18
24
|
import { groundExtractedFindings, } from "../phases/grounding.js";
|
|
19
25
|
import { runTriagePhase } from "../phases/triage.js";
|
|
20
26
|
import { runClosePhase } from "../phases/close.js";
|
|
21
27
|
import { validateRemediationPlan } from "../validation/remediationState.js";
|
|
22
28
|
import { readExtractedPlanIfPresent, } from "./dispatch/marshal.js";
|
|
23
|
-
import { ingestRemediationHostResults, hostDependencyLevels, permanentlyDeadPendingBlocks,
|
|
29
|
+
import { ingestRemediationHostResults, hostDependencyLevels, permanentlyDeadPendingBlocks, prepareRemediationHostHandoff, RemediationHostPreparationError, remediationIssueRemedy } from "./dispatch/hostHandoff.js";
|
|
24
30
|
import { FileLockTimeoutError, withFileLock, } from "../../shared/io/fileLock.js";
|
|
25
|
-
import { AUDIT_FINDINGS_FILENAME, AUDIT_REPORT_FILENAME, auditArtifactsDir, auditFindingsPath, auditReportPath, promotedAuditFindingsPath, promotedAuditReportPath, remediationArtifactsDir, } from "../../shared/io/auditToolsPaths.js";
|
|
31
|
+
import { AUDIT_FINDINGS_FILENAME, AUDIT_REPORT_FILENAME, auditArtifactsDir, auditFindingsPath, auditReportPath, promotedAuditFindingsPath, promotedAuditReportPath, remediationArtifactsDir, remediationRequiredTestLogsDir, } from "../../shared/io/auditToolsPaths.js";
|
|
26
32
|
import { callerWorkingDirectory, discoverRepoRoot, resolveRepoRoot, } from "../../shared/io/repoRoot.js";
|
|
27
33
|
import { writeCurrentStep } from "./stepWriter.js";
|
|
28
34
|
import { isTerminalStatus, isVerifiedCompleteStatus, } from "../state/itemStatus.js";
|
|
@@ -32,14 +38,13 @@ import { applyIntentOrdering } from "../intent/intentOrdering.js";
|
|
|
32
38
|
import { resolveIntakeStep } from "./intakeResolver.js";
|
|
33
39
|
import { carriesGateVerdict } from "../../shared/types/remediationOutcome.js";
|
|
34
40
|
import { RUNTIME_RESIDUAL_DECLARATION, readFinalGateVerdict, runToolOwnedFinalGate, writeFinalGateRedRecord, writeFinalGateOutcomeRecord, writeFinalGateVerdict, } from "./finalGate.js";
|
|
35
|
-
import { renderGateAttribution, worktreeContentId, } from "./gateCommands.js";
|
|
41
|
+
import { renderGateAttribution, worktreeContentId, finalGateBinding, } from "./gateCommands.js";
|
|
36
42
|
import { buildNextContractPipelineStep, shouldEnterContractPipeline, readSeedAuditRead, writePathASeedFromFindings, normalizeBlockTouchedFiles, checkWriteScopePathsAgainstTrackedTree, } from "./contractPipeline.js";
|
|
37
43
|
import { compareCodeUnits } from "../../shared/compareCodeUnits.js";
|
|
38
44
|
import { contractArtifactExists, contractPipelineDir, } from "../contractPipeline/artifactStore.js";
|
|
39
45
|
import { buildReviewRequest, applyReviewResolution, parseReviewResolution, REVIEW_REQUEST_SCHEMA_VERSION, } from "../review/reviewGate.js";
|
|
40
46
|
import { buildAutonomousReviewDecision } from "../review/autonomousGate.js";
|
|
41
47
|
import { runFindingFilterPass } from "../findingFilter.js";
|
|
42
|
-
import { droppedFindingsRecordPath, renderDroppedFindingsRecord, } from "../droppedFindingsRecord.js";
|
|
43
48
|
import { intakePaths, isIntakeReady, manifestIsInputBound, readIntakeArtifacts, readProjectFacts, readSourceManifest, writeProjectFacts, resolveManifestSources, writeRemediationBrief, } from "../intake.js";
|
|
44
49
|
import { ensureIntakeRiskSignal, readIntakeRiskSignal, writeIntakeRiskSignal, escalateRiskSignal, findingRiskEvidence, distinctAffectedFiles, } from "../riskSignal.js";
|
|
45
50
|
import { isLegacyDraftCheckpoint, readIntentCheckpoint, readIntentCheckpointLenient, } from "audit-tools/shared";
|
|
@@ -82,37 +87,6 @@ function resolveArtifactsDir(root, artifactsDir) {
|
|
|
82
87
|
// honored verbatim.
|
|
83
88
|
return artifactsDir ? resolve(artifactsDir) : remediationArtifactsDir(root);
|
|
84
89
|
}
|
|
85
|
-
/**
|
|
86
|
-
* The run's friction-record key: the plan id, or `null` when there is no plan.
|
|
87
|
-
*
|
|
88
|
-
* There is DELIBERATELY no fallback key. A fallback ("run") is a path every
|
|
89
|
-
* planless state shares, and `decideFrictionTriage` MATERIALIZES the record it
|
|
90
|
-
* is handed — so a planless caller could MINT a fresh empty record under it,
|
|
91
|
-
* which the close gate then blocks on. That is the 2026-08-24 defect exactly.
|
|
92
|
-
*
|
|
93
|
-
* The key is therefore the plan id or nothing, and a caller handed `null` has
|
|
94
|
-
* no walk to key: the walk ran before the close archived the plan-keyed record,
|
|
95
|
-
* and the plan is where its identity came from.
|
|
96
|
-
*/
|
|
97
|
-
function stateRunId(state) {
|
|
98
|
-
return state?.plan?.plan_id ?? null;
|
|
99
|
-
}
|
|
100
|
-
/**
|
|
101
|
-
* The run key for a caller holding a LOADED state that must carry a plan id —
|
|
102
|
-
* the host-handoff/ingest paths, whose contracts declare `run_id: string`.
|
|
103
|
-
*
|
|
104
|
-
* It throws rather than inventing a key: an absent plan id there is a
|
|
105
|
-
* reachable-but-unexpected state, and silently falling back would mint a
|
|
106
|
-
* shared path exactly as the fallback key did. Callers that can legitimately
|
|
107
|
-
* hold a planless state use {@link stateRunId} and handle the `null`.
|
|
108
|
-
*/
|
|
109
|
-
function requireStateRunId(state) {
|
|
110
|
-
const runId = stateRunId(state);
|
|
111
|
-
if (!runId) {
|
|
112
|
-
throw new Error("remediate: a loaded state with no plan id cannot name a run");
|
|
113
|
-
}
|
|
114
|
-
return runId;
|
|
115
|
-
}
|
|
116
90
|
/**
|
|
117
91
|
* Where an autonomous run's LEFTOVER deliverable pair lands.
|
|
118
92
|
*
|
|
@@ -547,7 +521,7 @@ function customCommandOf(checkpoint) {
|
|
|
547
521
|
: null;
|
|
548
522
|
}
|
|
549
523
|
async function saveStateForPlan(artifactsDir, existing, plan, planCoverage) {
|
|
550
|
-
const { host_handoff: _staleHostHandoff, ...carryForwardState } = existing;
|
|
524
|
+
const { host_handoff: _staleHostHandoff, conformance_review: priorConformance, ...carryForwardState } = existing;
|
|
551
525
|
const items = {};
|
|
552
526
|
const blockIds = blockIdsByFinding(plan);
|
|
553
527
|
for (const finding of plan.findings) {
|
|
@@ -559,6 +533,7 @@ async function saveStateForPlan(artifactsDir, existing, plan, planCoverage) {
|
|
|
559
533
|
}
|
|
560
534
|
const state = {
|
|
561
535
|
...carryForwardState,
|
|
536
|
+
...(priorConformance ? { conformance_review: { ...priorConformance, run_id: plan.plan_id } } : {}),
|
|
562
537
|
status: "planning",
|
|
563
538
|
plan,
|
|
564
539
|
items,
|
|
@@ -668,6 +643,7 @@ async function forceReplanFromExistingIntake(root, artifactsDir, previous, store
|
|
|
668
643
|
status: "pending",
|
|
669
644
|
started_at: previous.started_at,
|
|
670
645
|
step_count: previous.step_count,
|
|
646
|
+
...(previous.conformance_review ? { conformance_review: previous.conformance_review } : {}),
|
|
671
647
|
// Carry the run-lifetime staging-manifest fields across a force-replan.
|
|
672
648
|
// Dropping run_start_dirty here would make handlePendingExtractedPlan's
|
|
673
649
|
// capture-once guard re-capture AFTER edits have landed, misclassifying
|
|
@@ -700,256 +676,6 @@ async function forceReplanFromExistingIntake(root, artifactsDir, previous, store
|
|
|
700
676
|
await store.saveState(carried);
|
|
701
677
|
return carried;
|
|
702
678
|
}
|
|
703
|
-
/**
|
|
704
|
-
* The BLOCKING friction close-out step for a run whose walk is still owed.
|
|
705
|
-
*
|
|
706
|
-
* Emitted from `handleClosing` BEFORE the close touches disk, so the record the
|
|
707
|
-
* host is told to write (`triage.recordPath`) is the plan-keyed one this very
|
|
708
|
-
* decision just materialized — and so the run's identity in that path is the
|
|
709
|
-
* plan id, not a fallback. The step is a `closing`-phase gate: `next-step`
|
|
710
|
-
* re-decides the same walk on the next call, and only a disposed walk lets the
|
|
711
|
-
* close proceed.
|
|
712
|
-
*/
|
|
713
|
-
async function buildFrictionWalkStep(root, artifactsDir, state, triage) {
|
|
714
|
-
return writeCurrentStep({
|
|
715
|
-
stepKind: "close_run",
|
|
716
|
-
status: "ready",
|
|
717
|
-
runId: requireStateRunId(state),
|
|
718
|
-
repoRoot: root,
|
|
719
|
-
artifactsDir,
|
|
720
|
-
prompt: `# Remediation Run Friction Triage\n\nComplete the friction close-out walk before the run may close.\n${buildFrictionTriageBlock(triage)}`,
|
|
721
|
-
allowedCommands: [],
|
|
722
|
-
stopCondition: "Complete friction triage (write dispositions and open_observations), then call next-step again.",
|
|
723
|
-
artifactPaths: { friction_record: triage.recordPath },
|
|
724
|
-
});
|
|
725
|
-
}
|
|
726
|
-
async function presentReportStep(root, artifactsDir, state) {
|
|
727
|
-
const reportPath = join(dirname(artifactsDir), "remediation-report.md");
|
|
728
|
-
// Terminal friction-TRIAGE close-out, folded into present_report (single-sourced in
|
|
729
|
-
// `audit-tools/shared`). MANDATORY + BLOCKING: stays "dispose" until every mechanical
|
|
730
|
-
// event + reflection is disposed AND ≥1 open observation written. Never trivially
|
|
731
|
-
// satisfied by an empty event set — the host must actively confirm the friction state.
|
|
732
|
-
//
|
|
733
|
-
// NO FRICTION RECORD IS EVER MINTED HERE. The decision was made — and the
|
|
734
|
-
// record, under the run's plan-keyed path, was walked — before the close
|
|
735
|
-
// archived it: `handleClosing` consults `decideRemediateFrictionCloseout`
|
|
736
|
-
// against the LIVE state and short-circuits the close until the walk is
|
|
737
|
-
// disposed. So ON THE GREEN PATH — the close ran to `complete`, archived the
|
|
738
|
-
// run and removed the artifacts dir — the walk was already completed when the
|
|
739
|
-
// close started, the plan-keyed record left with the other deliverables, and
|
|
740
|
-
// all that is left is to RENDER the already-recorded walk for the host.
|
|
741
|
-
//
|
|
742
|
-
// NOT-GREEN IS THE OTHER HALF, and it is not "mint nothing": a close that
|
|
743
|
-
// returned a non-`complete` state PRESERVES the artifacts dir, so the record
|
|
744
|
-
// this step decides remains where the run wrote it — the caller reaches here
|
|
745
|
-
// with the saved state still carrying its plan id, `decideFrictionTriage`
|
|
746
|
-
// materializes (or re-reads) that same plan-keyed file, and the walk is
|
|
747
|
-
// rendered from the record that is still in place.
|
|
748
|
-
//
|
|
749
|
-
// It is decided — and so materialized — ONLY when the state names a run. The
|
|
750
|
-
// guard is the plan id, not the artifacts dir: the dir is not evidence of
|
|
751
|
-
// anything here, since `decideNextStepLoop` mkdirs it unconditionally on entry,
|
|
752
|
-
// `RunLogger.event` mkdirs it again for `run.log.jsonl`, and `writeJsonFile`
|
|
753
|
-
// mkdirs any parent it is handed. So a dir-existence test is true on every
|
|
754
|
-
// path, including a run whose state the close deleted. Guarding on the dir was
|
|
755
|
-
// the 2026-08-24 defect: the decider ran with a null state, took a fallback
|
|
756
|
-
// key, and because `decideFrictionTriage` MATERIALIZES the record it is given,
|
|
757
|
-
// minted a fresh EMPTY record inside the directory the close had just deleted —
|
|
758
|
-
// re-blocking the run on the record it had created. `stateRunId` now answers
|
|
759
|
-
// `null` for a planless state and this call therefore decides nothing.
|
|
760
|
-
//
|
|
761
|
-
// A `complete` state reaching here is a run that never ran the close walk (a
|
|
762
|
-
// re-delivery of an already-complete run, whose plan the state still carries),
|
|
763
|
-
// so the walk is genuinely still owed and deciding it here is correct — it
|
|
764
|
-
// renders as the blocking step. Only a planless state renders nothing.
|
|
765
|
-
const triage = await decideRemediateFrictionCloseout(artifactsDir, state);
|
|
766
|
-
const frictionBlock = triage ? buildFrictionTriageBlock(triage) : "";
|
|
767
|
-
const isBlocked = triage?.action === "dispose";
|
|
768
|
-
return writeCurrentStep({
|
|
769
|
-
stepKind: "present_report",
|
|
770
|
-
status: isBlocked ? "ready" : "complete",
|
|
771
|
-
runId: stateRunId(state),
|
|
772
|
-
repoRoot: root,
|
|
773
|
-
artifactsDir,
|
|
774
|
-
prompt: isBlocked
|
|
775
|
-
? `# Remediation Run Friction Triage\n\nComplete friction triage before presenting the report.\n${frictionBlock}`
|
|
776
|
-
: `# Present Remediation Report\n\nRead \`${reportPath}\` and summarize the remediation outcome.\nMention resolved, ignored, and deemed-inappropriate counts plus the closing action.\n${frictionBlock}`,
|
|
777
|
-
allowedCommands: [],
|
|
778
|
-
stopCondition: isBlocked
|
|
779
|
-
? "Complete friction triage (write dispositions and open_observations), then call next-step again."
|
|
780
|
-
: "Present the remediation report summary and stop.",
|
|
781
|
-
artifactPaths: {
|
|
782
|
-
final_report: reportPath,
|
|
783
|
-
...(triage ? { friction_record: triage.recordPath } : {}),
|
|
784
|
-
},
|
|
785
|
-
});
|
|
786
|
-
}
|
|
787
|
-
function currentHostBoundaryState(state) {
|
|
788
|
-
return {
|
|
789
|
-
contract_version: "remediate-code-state/v1alpha1",
|
|
790
|
-
...state,
|
|
791
|
-
};
|
|
792
|
-
}
|
|
793
|
-
/**
|
|
794
|
-
* The `recover-ingest` verb's whole body: ingest the host's landed results in
|
|
795
|
-
* RECOVERY mode and persist through the same file-locked, atomically-writing
|
|
796
|
-
* store, with the same `contract_version` strip.
|
|
797
|
-
*
|
|
798
|
-
* It is a separate verb rather than a flag on `next-step` because the
|
|
799
|
-
* relaxation it enables must be an operator's explicit act — see
|
|
800
|
-
* `ingestRemediationHostResults`, which states what is waived and the residual
|
|
801
|
-
* risk. Nothing else here differs from the normal ingestion: the same workload,
|
|
802
|
-
* the same contract gates, the same eligibility frontier.
|
|
803
|
-
*
|
|
804
|
-
* ## Why this runs in two phases
|
|
805
|
-
*
|
|
806
|
-
* A required-test rerun is `spawnSync`, which blocks the event loop for its
|
|
807
|
-
* whole duration. Run inside the state lock, it would starve the lock's own
|
|
808
|
-
* heartbeat timer (`setInterval` in the shared fileLock) — the held lock's mtime
|
|
809
|
-
* would stop being refreshed, a second acquirer would classify it as stale at
|
|
810
|
-
* ~30s and steal it, and mutual exclusion would be gone precisely during the
|
|
811
|
-
* longest critical section in the codebase. Holding a lock across a blocking
|
|
812
|
-
* spawn is therefore not merely slow; it is unsound.
|
|
813
|
-
*
|
|
814
|
-
* So:
|
|
815
|
-
*
|
|
816
|
-
* - **Phase 1, UNLOCKED.** Snapshot the state, capture HEAD and the workload
|
|
817
|
-
* binding, and run every distinct required-test command exactly once
|
|
818
|
-
* (`precomputeRecoveryTestVerdicts`). Both identities are captured BEFORE the
|
|
819
|
-
* spawns, not after, because a host-authored command that MOVES HEAD would
|
|
820
|
-
* otherwise produce verdicts of mixed provenance and go undetected. (The HEAD
|
|
821
|
-
* guard compares commit shas: it sees HEAD movement, not worktree dirt — a
|
|
822
|
-
* command that only dirties files is invisible to it, which is acceptable
|
|
823
|
-
* because phase 2's corroboration is commit-based.)
|
|
824
|
-
* - **Phase 2, LOCKED.** Re-read both identities and abort the whole recovery if
|
|
825
|
-
* either moved — `tree_moved_between_phases` for HEAD,
|
|
826
|
-
* `state_moved_between_phases` for the binding — because the phase-1 verdicts
|
|
827
|
-
* would describe a tree or a frontier that no longer exists, and nothing is
|
|
828
|
-
* accepted or appended. Otherwise ingest with the pre-computed verdicts, which
|
|
829
|
-
* the ingest only READS: in recovery mode it never spawns, and a command
|
|
830
|
-
* missing from the table fails closed.
|
|
831
|
-
*
|
|
832
|
-
* What remains inside the lock is git plumbing (ancestry, ref scan, diff-tree),
|
|
833
|
-
* the ledger append, and the state write — sub-second work, comfortably inside
|
|
834
|
-
* heartbeat coverage. The two unchanged-identity guards close the gap the phase
|
|
835
|
-
* split opens; the operational protocol is still one writer at a time, now
|
|
836
|
-
* enforced by a lock that cannot be stolen mid-hold instead of by convention.
|
|
837
|
-
*
|
|
838
|
-
* A recovery that changes nothing writes nothing: phase 2 returns the locked
|
|
839
|
-
* store's `SKIP_WRITE` sentinel, which `StateStore.mutate` now honors, so the
|
|
840
|
-
* retry loop of a genuinely-empty recovery no longer replaces `state.json` with
|
|
841
|
-
* byte-identical content on every pass.
|
|
842
|
-
*/
|
|
843
|
-
export async function recoverIngestHostResults(options) {
|
|
844
|
-
const root = resolveRoot(options.root);
|
|
845
|
-
const artifactsDir = resolveArtifactsDir(root, options.artifactsDir);
|
|
846
|
-
const store = new StateStore(artifactsDir);
|
|
847
|
-
// ── Phase 1: unlocked ────────────────────────────────────────────────────
|
|
848
|
-
const snapshot = await store.loadState();
|
|
849
|
-
if (!snapshot) {
|
|
850
|
-
throw new Error(`No remediation state at ${artifactsDir} — there is nothing to ingest.`);
|
|
851
|
-
}
|
|
852
|
-
const headBeforeTests = await headCommit(root);
|
|
853
|
-
const bindingBeforeTests = workloadBindingIdentity(currentHostBoundaryState(snapshot));
|
|
854
|
-
const requiredTestVerdicts = await precomputeRecoveryTestVerdicts({
|
|
855
|
-
root,
|
|
856
|
-
artifactsDir,
|
|
857
|
-
runId: options.runId,
|
|
858
|
-
state: currentHostBoundaryState(snapshot),
|
|
859
|
-
});
|
|
860
|
-
if (requiredTestVerdicts === "unsupported_retired_state") {
|
|
861
|
-
throw new Error("Remediation state uses a retired dispatch shape and cannot cross the host handoff boundary.");
|
|
862
|
-
}
|
|
863
|
-
// ── Phase 2: locked; children are async + deadline-bounded only ──────────
|
|
864
|
-
// The long-running required tests were precomputed in Phase 1, outside the
|
|
865
|
-
// lock. What still spawns under the hold is the corroboration git probes —
|
|
866
|
-
// async on the tracked twin with the shared deadline (INV-SSF), so the
|
|
867
|
-
// hold's heartbeat keeps beating through every probe.
|
|
868
|
-
let ingested;
|
|
869
|
-
await store.mutate(async (state) => {
|
|
870
|
-
if (!state) {
|
|
871
|
-
throw new Error(`No remediation state at ${artifactsDir} — there is nothing to ingest.`);
|
|
872
|
-
}
|
|
873
|
-
const headNow = await headCommit(root);
|
|
874
|
-
const bindingNow = workloadBindingIdentity(currentHostBoundaryState(state));
|
|
875
|
-
// TWO identities, because they catch different writers. HEAD moves when the
|
|
876
|
-
// tree does; the workload binding moves when a concurrent state writer
|
|
877
|
-
// settles items or re-mints the workload without committing anything. The
|
|
878
|
-
// verdict table describes the frontier as it was at phase 1, so either
|
|
879
|
-
// change invalidates it — and a stale table is not merely imprecise: the
|
|
880
|
-
// commands it no longer covers read as `required_test_failed`, which
|
|
881
|
-
// attributes a bookkeeping race to the host's work.
|
|
882
|
-
const moved = headNow !== headBeforeTests
|
|
883
|
-
? "tree"
|
|
884
|
-
: bindingNow !== bindingBeforeTests
|
|
885
|
-
? "state"
|
|
886
|
-
: null;
|
|
887
|
-
if (moved !== null) {
|
|
888
|
-
ingested = {
|
|
889
|
-
accepted_count: 0,
|
|
890
|
-
completed_work_item_ids: [],
|
|
891
|
-
pending_work_item_ids: state.host_handoff?.work_item_ids ?? [],
|
|
892
|
-
// This verdict aborts the whole recovery BEFORE any item is read, so
|
|
893
|
-
// there is no per-item observation to report — an empty map is the
|
|
894
|
-
// honest statement of that, not a missing field.
|
|
895
|
-
work_item_outcomes: new Map(),
|
|
896
|
-
issues: [
|
|
897
|
-
moved === "tree"
|
|
898
|
-
? {
|
|
899
|
-
code: "tree_moved_between_phases",
|
|
900
|
-
message: `HEAD moved from ${headBeforeTests ?? "(none)"} to ${headNow ?? "(none)"} ` +
|
|
901
|
-
"while the required tests were running, so their verdicts no longer describe " +
|
|
902
|
-
"this tree. Nothing was accepted; re-run recover-ingest on a settled tree.",
|
|
903
|
-
}
|
|
904
|
-
: {
|
|
905
|
-
code: "state_moved_between_phases",
|
|
906
|
-
message: "the run's workload binding changed while the required tests were running, " +
|
|
907
|
-
"so their verdicts no longer describe the pending frontier. Nothing was " +
|
|
908
|
-
"accepted; re-run recover-ingest once no other writer is advancing this run.",
|
|
909
|
-
},
|
|
910
|
-
],
|
|
911
|
-
state_changed: false,
|
|
912
|
-
state: currentHostBoundaryState(state),
|
|
913
|
-
};
|
|
914
|
-
// The lock was taken to WRITE. Nothing changed, so write nothing: the
|
|
915
|
-
// no-op sentinel is what keeps a settled recovery from replacing
|
|
916
|
-
// state.json with byte-identical content on every retry.
|
|
917
|
-
return SKIP_WRITE;
|
|
918
|
-
}
|
|
919
|
-
const outcome = await ingestRemediationHostResults({
|
|
920
|
-
root,
|
|
921
|
-
artifactsDir,
|
|
922
|
-
runId: options.runId,
|
|
923
|
-
state: currentHostBoundaryState(state),
|
|
924
|
-
recovery: { requiredTestVerdicts },
|
|
925
|
-
});
|
|
926
|
-
if (outcome === "unsupported_retired_state") {
|
|
927
|
-
throw new Error("Remediation state uses a retired dispatch shape and cannot cross the host handoff boundary.");
|
|
928
|
-
}
|
|
929
|
-
ingested = outcome;
|
|
930
|
-
// A nothing-to-recover pass returns the sentinel, not the state it read: the
|
|
931
|
-
// mutation is a no-op and must not rewrite the file (see StateStore.mutate).
|
|
932
|
-
if (!outcome.state_changed)
|
|
933
|
-
return SKIP_WRITE;
|
|
934
|
-
// No `contract_version` strip. The boundary helper below stamps the version
|
|
935
|
-
// onto the state it hands the host handoff, and this used to peel it back
|
|
936
|
-
// off on the way to disk so `state.json` matched what was written before.
|
|
937
|
-
// The STORE now owns that field end to end — it stamps it on read and the
|
|
938
|
-
// write hook validates it — so peeling it here would persist a state
|
|
939
|
-
// without the identity the store just established, and the next read would
|
|
940
|
-
// have to re-invent it.
|
|
941
|
-
return outcome.state;
|
|
942
|
-
});
|
|
943
|
-
// The run moved, so the persisted step contract no longer describes it: it
|
|
944
|
-
// names work this ingest just resolved, against a workload binding this ingest
|
|
945
|
-
// may have cleared. Invalidate it — but only when something actually changed,
|
|
946
|
-
// because a no-op recovery must leave the tree byte-identical (which is the
|
|
947
|
-
// same reason the mutation above writes nothing).
|
|
948
|
-
if (ingested.state_changed) {
|
|
949
|
-
await invalidateStepContracts(artifactsDir);
|
|
950
|
-
}
|
|
951
|
-
return ingested;
|
|
952
|
-
}
|
|
953
679
|
/**
|
|
954
680
|
* The ingest report for the implement-dispatch prompt, as prompt lines.
|
|
955
681
|
*
|
|
@@ -1014,25 +740,81 @@ async function buildImplementDispatchStep(ctx) {
|
|
|
1014
740
|
await store.saveState(ingested.state);
|
|
1015
741
|
return { kind: "transition", state: ingested.state };
|
|
1016
742
|
}
|
|
743
|
+
const conformance = ingested.issues.filter(issue => issue.review_request_path !== undefined);
|
|
744
|
+
if (conformance.length > 0) {
|
|
745
|
+
const nextCommand = loaderCommand("next-step");
|
|
746
|
+
const unavailable = conformance.some(issue => issue.code === "conformance_review_unavailable");
|
|
747
|
+
const repair = conformance.some(issue => issue.code === "conformance_review_insufficient");
|
|
748
|
+
return {
|
|
749
|
+
kind: "emit",
|
|
750
|
+
step: await writeCurrentStep({
|
|
751
|
+
stepKind: "review_contract_conformance", status: unavailable ? "blocked" : "ready",
|
|
752
|
+
runId, repoRoot: root, artifactsDir,
|
|
753
|
+
prompt: [
|
|
754
|
+
"# Independent contract conformance review",
|
|
755
|
+
"",
|
|
756
|
+
"The results named below passed mechanical validation. They are not accepted until their bound independent reviews pass.",
|
|
757
|
+
"Read each request file and give it to a context that did not author the implementation and cannot see the author's reasoning. The request contains the exact result, obligations, contracts, response schema and response path.",
|
|
758
|
+
"",
|
|
759
|
+
...conformance.flatMap(issue => [
|
|
760
|
+
`- ${issue.work_item_id}: ${issue.message}`,
|
|
761
|
+
` Request: ${issue.review_request_path}`,
|
|
762
|
+
]),
|
|
763
|
+
"",
|
|
764
|
+
...(repair ? ["Repair the implementation or its obligation evidence as explained by the review, then obtain a fresh review bound to the corrected result. Do not change contracts merely to make the review pass."] : []),
|
|
765
|
+
...(unavailable ? ["Stop until an independent context is available. Degraded/self review cannot replace the required review."] : []),
|
|
766
|
+
"Independence is a host declaration; the tool validates the declaration and content binding, not the identity of the reviewer.",
|
|
767
|
+
`When the responses are ready, run \`${nextCommand}\`.`,
|
|
768
|
+
].join("\n"),
|
|
769
|
+
allowedCommands: [nextCommand],
|
|
770
|
+
stopCondition: "Stop after the independent review responses are written and next-step is run; if independent review is unavailable, remain paused.",
|
|
771
|
+
artifactPaths: Object.fromEntries(conformance.map(issue => [`review_${issue.work_item_id}`, issue.review_request_path])),
|
|
772
|
+
}),
|
|
773
|
+
};
|
|
774
|
+
}
|
|
1017
775
|
const baselineCommit = await headCommit(root);
|
|
1018
776
|
if (!baselineCommit) {
|
|
1019
777
|
throw new Error("Cannot prepare remediation host work without a repository HEAD commit.");
|
|
1020
778
|
}
|
|
1021
|
-
|
|
1022
|
-
|
|
1023
|
-
|
|
1024
|
-
|
|
1025
|
-
|
|
1026
|
-
|
|
1027
|
-
|
|
779
|
+
let handoff;
|
|
780
|
+
try {
|
|
781
|
+
handoff = await prepareRemediationHostHandoff({
|
|
782
|
+
root, artifactsDir, runId, baselineCommit, state: boundaryState,
|
|
783
|
+
});
|
|
784
|
+
}
|
|
785
|
+
catch (error) {
|
|
786
|
+
if (!(error instanceof RemediationHostPreparationError))
|
|
787
|
+
throw error;
|
|
788
|
+
const sourcePath = join(artifactsDir, "state.json");
|
|
789
|
+
const diagnosticsPath = join(artifactsDir, "steps", "handoff-repair.json");
|
|
790
|
+
await writeJsonFile(diagnosticsPath, { code: error.code, message: error.message, source: sourcePath });
|
|
791
|
+
const excerpt = error.message.length <= 3_000 ? error.message : `${error.message.slice(0, 3_000)}… [excerpt truncated]`;
|
|
792
|
+
return {
|
|
793
|
+
kind: "emit",
|
|
794
|
+
step: await writeCurrentStep({
|
|
795
|
+
stepKind: "repair_handoff", status: "blocked", runId, repoRoot: root, artifactsDir,
|
|
796
|
+
prompt: `# Repair the planning output before dispatch\n\n${excerpt}\n\nFull validation details: ${diagnosticsPath}\n\n` +
|
|
797
|
+
`Owning source: ${sourcePath} (plan.blocks and its generated handoff binding).\n` +
|
|
798
|
+
`Upstream planning input: ${join(artifactsDir, "extracted-plan.json")}.\n\n` +
|
|
799
|
+
"The current run and completed work are preserved. This dispatch cannot safely repair its upstream producer. " +
|
|
800
|
+
"Return the named validation errors to the planning producer; do not edit generated state, discard accepted work, or submit results under invalid bindings. " +
|
|
801
|
+
"After the upstream producer has corrected the plan, run next-step again.\n",
|
|
802
|
+
allowedCommands: [],
|
|
803
|
+
stopCondition: "Stop and report the named upstream planning defect; do not execute or reset this handoff.",
|
|
804
|
+
artifactPaths: { source_plan: sourcePath, repair_diagnostics: diagnosticsPath },
|
|
805
|
+
access: { read_paths: [sourcePath, diagnosticsPath], write_paths: [] },
|
|
806
|
+
}),
|
|
807
|
+
};
|
|
808
|
+
}
|
|
1028
809
|
if (handoff === "unsupported_retired_state") {
|
|
1029
810
|
throw new Error("Remediation state uses a retired dispatch shape and cannot cross the host handoff boundary.");
|
|
1030
811
|
}
|
|
1031
|
-
if (state.host_handoff?.workload_sha256 !==
|
|
812
|
+
if (!state.conformance_review || state.host_handoff?.workload_sha256 !==
|
|
1032
813
|
handoff.handoff_record.workload_sha256) {
|
|
1033
814
|
await store.saveState({
|
|
1034
815
|
...state,
|
|
1035
816
|
host_handoff: handoff.handoff_record,
|
|
817
|
+
conformance_review: handoff.conformance_review,
|
|
1036
818
|
});
|
|
1037
819
|
}
|
|
1038
820
|
// Name the runs this dispatch round relates to on the friction record (semantics:
|
|
@@ -1081,7 +863,13 @@ async function buildImplementDispatchStep(ctx) {
|
|
|
1081
863
|
nextCommand,
|
|
1082
864
|
],
|
|
1083
865
|
stopCondition: "Stop after every emitted work item has a complete result and next-step has been run.",
|
|
1084
|
-
artifactPaths: { host_workload: handoff.workload_path },
|
|
866
|
+
artifactPaths: { host_workload: handoff.workload_path, required_test_logs: remediationRequiredTestLogsDir(root) },
|
|
867
|
+
access: {
|
|
868
|
+
read_paths: [root, handoff.workload_path, remediationRequiredTestLogsDir(root)],
|
|
869
|
+
write_paths: [...new Set(handoff.workload.work_items.flatMap((item) => [
|
|
870
|
+
...item.allowed_files.map((path) => resolve(root, path)), resolve(root, item.result_path),
|
|
871
|
+
]))],
|
|
872
|
+
},
|
|
1085
873
|
}),
|
|
1086
874
|
};
|
|
1087
875
|
}
|
|
@@ -1127,29 +915,6 @@ next-step joins in and takes on unclaimed work.
|
|
|
1127
915
|
async function handleComplete(root, artifactsDir, state) {
|
|
1128
916
|
return presentReportStep(root, artifactsDir, state);
|
|
1129
917
|
}
|
|
1130
|
-
/**
|
|
1131
|
-
* The terminal friction-TRIAGE close-out for the remediate half. Thin delegation to
|
|
1132
|
-
* the single-sourced `decideFrictionTriage` (`audit-tools/shared`) — the exact analog
|
|
1133
|
-
* of audit-code's `decideAuditFrictionCloseout`, so the triage shape, disposition
|
|
1134
|
-
* vocabulary, blocking semantics, and close-out logic cannot drift between the two
|
|
1135
|
-
* halves. Drops the former false-green (an empty up-front record no longer satisfies):
|
|
1136
|
-
* the blocking triage stays unsatisfied ("dispose") until every captured mechanical
|
|
1137
|
-
* event AND every surfaced agent-feedback reflection carries a disposition; an empty
|
|
1138
|
-
* set (zero events AND zero reflections) is trivially "disposed". Keyed only off
|
|
1139
|
-
* `(artifactsDir, runId)`; never coupled to any repo's backlog doc.
|
|
1140
|
-
*
|
|
1141
|
-
* A state with NO plan id can name no run, and since the decision MATERIALIZES
|
|
1142
|
-
* the record it keys, guessing a key there would mint one. It returns `null`
|
|
1143
|
-
* instead — "there is no run here to close out", which is the truthful answer
|
|
1144
|
-
* for a state whose plan is gone. Callers that hold the run's plan get a
|
|
1145
|
-
* decision; callers past the run boundary get nothing to render.
|
|
1146
|
-
*/
|
|
1147
|
-
export async function decideRemediateFrictionCloseout(artifactsDir, state) {
|
|
1148
|
-
const runId = stateRunId(state);
|
|
1149
|
-
if (!runId)
|
|
1150
|
-
return null;
|
|
1151
|
-
return decideFrictionTriage(artifactsDir, runId, "remediate-code");
|
|
1152
|
-
}
|
|
1153
918
|
/**
|
|
1154
919
|
* Copy an unusable extracted plan somewhere recoverable and PROVE the copy
|
|
1155
920
|
* landed, returning the archive path. Read back and compared byte-for-byte:
|
|
@@ -1650,32 +1415,6 @@ sourceAuditRead) {
|
|
|
1650
1415
|
`path=${findingsPath}`,
|
|
1651
1416
|
});
|
|
1652
1417
|
}
|
|
1653
|
-
// ── Path-A filter dispositions (persisted for the coverage ledger) ──────────────
|
|
1654
|
-
// The single filter pass runs at intake over the ORIGINAL findings; its
|
|
1655
|
-
// dispositions are persisted here so handlePendingExtractedPlan can build the
|
|
1656
|
-
// coverage ledger over the originals (every audit finding → exactly one
|
|
1657
|
-
// disposition), even though it runs after the pipeline has collapsed the approved
|
|
1658
|
-
// survivors into DAG nodes. Maps are serialized as entry arrays for JSON.
|
|
1659
|
-
const REVIEW_FILTER_DISPOSITIONS_FILENAME = "review_filter_dispositions.json";
|
|
1660
|
-
function reviewFilterDispositionsPath(artifactsDir) {
|
|
1661
|
-
return join(artifactsDir, REVIEW_FILTER_DISPOSITIONS_FILENAME);
|
|
1662
|
-
}
|
|
1663
|
-
async function persistReviewFilterDispositions(artifactsDir, originals, filter) {
|
|
1664
|
-
const payload = {
|
|
1665
|
-
originals,
|
|
1666
|
-
mergeMap: [...filter.mergeMap.entries()],
|
|
1667
|
-
droppedNoEvidence: filter.droppedNoEvidence,
|
|
1668
|
-
droppedPhantomPaths: [...filter.droppedPhantomPaths.entries()],
|
|
1669
|
-
phantomPathsRemoved: [...filter.phantomPathsRemoved.entries()],
|
|
1670
|
-
droppedByCheckpoint: filter.droppedByCheckpoint,
|
|
1671
|
-
};
|
|
1672
|
-
await writeJsonFile(reviewFilterDispositionsPath(artifactsDir), payload);
|
|
1673
|
-
// The human half of the same fact. The JSON above keeps only IDS, which told a
|
|
1674
|
-
// reader an id and nothing else; this states what each removed finding WAS and
|
|
1675
|
-
// why it went. Written beside it, on every pass, so its absence means the pass
|
|
1676
|
-
// did not run rather than "nothing was dropped".
|
|
1677
|
-
await writeFile(droppedFindingsRecordPath(artifactsDir), renderDroppedFindingsRecord(originals, filter), "utf8");
|
|
1678
|
-
}
|
|
1679
1418
|
async function handleReadyIntakeContractPipeline(root, artifactsDir, options, runLogger) {
|
|
1680
1419
|
// Fast path: if an extracted-plan.json already exists (pipeline complete or
|
|
1681
1420
|
// promoted from a previous contract pipeline run), consume it directly without
|
|
@@ -1850,6 +1589,7 @@ async function handlePendingIntake(root, artifactsDir, options, runLogger) {
|
|
|
1850
1589
|
root,
|
|
1851
1590
|
artifactsDir,
|
|
1852
1591
|
input: options.input,
|
|
1592
|
+
findingSelection: options,
|
|
1853
1593
|
inputResolution,
|
|
1854
1594
|
loaderCommand,
|
|
1855
1595
|
randomRunId,
|
|
@@ -1880,19 +1620,28 @@ async function handleNoState(root, artifactsDir) {
|
|
|
1880
1620
|
},
|
|
1881
1621
|
});
|
|
1882
1622
|
}
|
|
1883
|
-
async function handleInputConflict(root, artifactsDir, state, inputResolution) {
|
|
1623
|
+
async function handleInputConflict(root, artifactsDir, state, inputResolution, selectionConflict) {
|
|
1884
1624
|
const planId = state.plan?.plan_id ?? "(none)";
|
|
1885
1625
|
const itemCount = state.items ? Object.keys(state.items).length : 0;
|
|
1886
1626
|
const suppliedInline = inputResolution.checked.length > 0
|
|
1887
1627
|
? inputResolution.checked.map((p) => `\`${p}\``).join(", ")
|
|
1888
1628
|
: "(new intake source via `--guidance-file`)";
|
|
1629
|
+
const selectionPrompt = selectionConflict ? [
|
|
1630
|
+
"# Finding selection changed during an existing intake", "",
|
|
1631
|
+
"The earlier selected report and its scope have been preserved. No new selection has been published.",
|
|
1632
|
+
selectionConflict.sourceChanged
|
|
1633
|
+
? "The original audit report changed. Restore the original source bytes to resume the existing intake, or explicitly start a fresh run from the changed report. A bare resume cannot accept changed source evidence."
|
|
1634
|
+
: "The requested severity/ID selectors differ from this intake's selection. Drop the new selector flags to resume the existing selection, or explicitly start a fresh run with the new selectors.",
|
|
1635
|
+
`To start fresh, first move aside the existing ${artifactsDir} directory, then rerun next-step with --input and the intended --severity/--finding-id flags.`,
|
|
1636
|
+
"Stop and present this choice to the user before continuing.",
|
|
1637
|
+
].join("\n") : undefined;
|
|
1889
1638
|
return writeCurrentStep({
|
|
1890
1639
|
stepKind: "input_conflict",
|
|
1891
1640
|
status: "blocked",
|
|
1892
1641
|
runId: stateRunId(state),
|
|
1893
1642
|
repoRoot: root,
|
|
1894
1643
|
artifactsDir,
|
|
1895
|
-
prompt: `
|
|
1644
|
+
prompt: selectionPrompt ?? `
|
|
1896
1645
|
# New intake source given, but a remediation run is already in progress
|
|
1897
1646
|
|
|
1898
1647
|
A remediation run already exists in \`${artifactsDir}\` and has advanced past intake,
|
|
@@ -2372,14 +2121,15 @@ async function runPlanAmbiguityGate(root, artifactsDir, state, store) {
|
|
|
2372
2121
|
const candidates = detectPlanAmbiguities(findings, state.items);
|
|
2373
2122
|
if (candidates.length === 0)
|
|
2374
2123
|
return null;
|
|
2375
|
-
await writeJsonFile(requestPath, candidates);
|
|
2124
|
+
await writeJsonFile(requestPath, { candidates, findings });
|
|
2376
2125
|
return writeCurrentStep({
|
|
2377
2126
|
stepKind: "collect_clarifications",
|
|
2378
2127
|
status: "blocked",
|
|
2379
2128
|
runId: stateRunId(state),
|
|
2380
2129
|
repoRoot: root,
|
|
2381
2130
|
artifactsDir,
|
|
2382
|
-
prompt: ambiguityReviewPrompt(candidates, resolutionPath, findings.map((f) => f.id)),
|
|
2131
|
+
prompt: ambiguityReviewPrompt(candidates, resolutionPath, findings.map((f) => f.id), undefined, requestPath),
|
|
2132
|
+
access: { read_paths: [requestPath, root], write_paths: [resolutionPath] },
|
|
2383
2133
|
allowedCommands: [loaderCommand("next-step")],
|
|
2384
2134
|
stopCondition: "Stop after reviewing the candidate ambiguities (and asking the user any genuine ones), unless the resolution is already written and the prompt told you to continue.",
|
|
2385
2135
|
artifactPaths: {
|
|
@@ -2401,15 +2151,16 @@ async function runPlanAmbiguityGate(root, artifactsDir, state, store) {
|
|
|
2401
2151
|
const refusal = await planClarificationRefusal(root, state, parsed, new Set(findings.map((f) => f.id)), "not in the plan");
|
|
2402
2152
|
if (refusal !== null || !parsed.ok) {
|
|
2403
2153
|
await withFsRetry(() => rename(resolutionPath, `${resolutionPath}.refused-${Date.now()}`));
|
|
2404
|
-
const candidates = (
|
|
2405
|
-
|
|
2154
|
+
const candidates = detectPlanAmbiguities(findings, state.items);
|
|
2155
|
+
await writeJsonFile(requestPath, { candidates, findings });
|
|
2406
2156
|
return writeCurrentStep({
|
|
2407
2157
|
stepKind: "collect_clarifications",
|
|
2408
2158
|
status: "blocked",
|
|
2409
2159
|
runId: stateRunId(state),
|
|
2410
2160
|
repoRoot: root,
|
|
2411
2161
|
artifactsDir,
|
|
2412
|
-
prompt: ambiguityReviewPrompt(candidates, resolutionPath, findings.map((f) => f.id), refusal ?? "the resolution could not be read"),
|
|
2162
|
+
prompt: ambiguityReviewPrompt(candidates, resolutionPath, findings.map((f) => f.id), refusal ?? "the resolution could not be read", requestPath),
|
|
2163
|
+
access: { read_paths: [requestPath, root], write_paths: [resolutionPath] },
|
|
2413
2164
|
allowedCommands: [loaderCommand("next-step")],
|
|
2414
2165
|
stopCondition: "Stop after re-submitting a corrected ambiguity resolution, unless it is already written and the prompt told you to continue.",
|
|
2415
2166
|
artifactPaths: {
|
|
@@ -2674,6 +2425,7 @@ async function emitFinalGateRedStep(ctx) {
|
|
|
2674
2425
|
"minutes. With no edit, next-step shows this same red at once and runs\n" +
|
|
2675
2426
|
"nothing. So fix something before you run next-step again.";
|
|
2676
2427
|
const nextCommand = loaderCommand("next-step");
|
|
2428
|
+
const recoveryCommand = `${nextCommand} --verification-command "<operator-approved command>"`;
|
|
2677
2429
|
return {
|
|
2678
2430
|
kind: "emit",
|
|
2679
2431
|
step: await writeCurrentStep({
|
|
@@ -2682,10 +2434,23 @@ async function emitFinalGateRedStep(ctx) {
|
|
|
2682
2434
|
runId: stateRunId(state),
|
|
2683
2435
|
repoRoot: root,
|
|
2684
2436
|
artifactsDir,
|
|
2685
|
-
prompt: `
|
|
2437
|
+
prompt: gate.results.length === 0 ? `
|
|
2438
|
+
# Verification command required
|
|
2439
|
+
|
|
2440
|
+
This repository declares no executable build, typecheck, lint, or test command.
|
|
2441
|
+
No verification ran, and this run cannot advance or close as verified.
|
|
2442
|
+
|
|
2443
|
+
Ask the operator to supply a verification command, then run:
|
|
2444
|
+
|
|
2445
|
+
\`${recoveryCommand}\`
|
|
2446
|
+
|
|
2447
|
+
The command replaces only the test role for this run. Any declared build,
|
|
2448
|
+
typecheck, and lint checks still run. Alternatively, stop here with work preserved.
|
|
2449
|
+
Do not invent a passing command or waive the gate.
|
|
2450
|
+
` : `
|
|
2686
2451
|
# Remediation paused — the repository suite is red
|
|
2687
2452
|
|
|
2688
|
-
${ctx.where}, the tool ran the repository's
|
|
2453
|
+
${ctx.where}, the tool ran the repository's declared verification commands.
|
|
2689
2454
|
One command failed:
|
|
2690
2455
|
|
|
2691
2456
|
\`${failingCommand}\`
|
|
@@ -2707,8 +2472,10 @@ When the suite is green, the run continues from where it stopped.
|
|
|
2707
2472
|
|
|
2708
2473
|
${bindingBlock}
|
|
2709
2474
|
`,
|
|
2710
|
-
allowedCommands: [nextCommand],
|
|
2711
|
-
stopCondition:
|
|
2475
|
+
allowedCommands: gate.results.length === 0 ? [nextCommand, recoveryCommand] : [nextCommand],
|
|
2476
|
+
stopCondition: gate.results.length === 0
|
|
2477
|
+
? "Stop. Obtain an explicit verification command from the operator or leave this run paused."
|
|
2478
|
+
: "Stop. Make the repository suite green, then re-run next-step to resume the run.",
|
|
2712
2479
|
artifactPaths: { final_gate_record: recordPath },
|
|
2713
2480
|
}),
|
|
2714
2481
|
};
|
|
@@ -2730,6 +2497,10 @@ ${bindingBlock}
|
|
|
2730
2497
|
* pass OR the gate is GREEN — in which case the caller proceeds to dispatch the
|
|
2731
2498
|
* phase.
|
|
2732
2499
|
*/
|
|
2500
|
+
function explicitFinalGateTestCommand(state) {
|
|
2501
|
+
return state.plan?.test_command && state.plan.test_command_source !== "project_facts"
|
|
2502
|
+
? parseCommandString(state.plan.test_command) : undefined;
|
|
2503
|
+
}
|
|
2733
2504
|
async function runPhaseBoundaryGate(ctx) {
|
|
2734
2505
|
const { root, artifactsDir, state, options, runLogger } = ctx;
|
|
2735
2506
|
// Whether a gate is DUE is decided BEFORE whether it is suppressed. The old
|
|
@@ -2764,6 +2535,8 @@ async function runPhaseBoundaryGate(ctx) {
|
|
|
2764
2535
|
// A boundary that re-dispatches work will get a different id on the next call
|
|
2765
2536
|
// and the floor re-runs — which is correct: the tree it certified is gone.
|
|
2766
2537
|
const tree = await worktreeContentId(root);
|
|
2538
|
+
const testCommand = explicitFinalGateTestCommand(state);
|
|
2539
|
+
const binding = finalGateBinding(root, testCommand);
|
|
2767
2540
|
const gateKey = `phase_boundary_gate phase=${phase}`;
|
|
2768
2541
|
// THE CACHE. The fold re-enters this boundary on every next-step taken before
|
|
2769
2542
|
// the next phase dispatches, and the floor is build + typecheck + the whole
|
|
@@ -2772,7 +2545,7 @@ async function runPhaseBoundaryGate(ctx) {
|
|
|
2772
2545
|
// known and was already recorded. A HIT is therefore a full substitute,
|
|
2773
2546
|
// including for a RED one (see {@link readFinalGateVerdict} — the pause is
|
|
2774
2547
|
// rebuilt from the cached results rather than re-derived).
|
|
2775
|
-
const cached = await readFinalGateVerdict(artifactsDir, scope, tree);
|
|
2548
|
+
const cached = await readFinalGateVerdict(artifactsDir, scope, tree, binding);
|
|
2776
2549
|
if (cached !== undefined) {
|
|
2777
2550
|
// Recorded as a distinct outcome from a real run: the judge is `history`,
|
|
2778
2551
|
// not a spawned command, and `commands_run` counts what HISTORY held, never
|
|
@@ -2824,10 +2597,11 @@ async function runPhaseBoundaryGate(ctx) {
|
|
|
2824
2597
|
obligation: state.status,
|
|
2825
2598
|
note: gateKey,
|
|
2826
2599
|
});
|
|
2827
|
-
const gate = await runToolOwnedFinalGate(root, { runner: options.finalGateRunner });
|
|
2600
|
+
const gate = await runToolOwnedFinalGate(root, { runner: options.finalGateRunner, testCommand: explicitFinalGateTestCommand(state) });
|
|
2828
2601
|
await writeFinalGateVerdict(artifactsDir, {
|
|
2829
2602
|
scope,
|
|
2830
2603
|
tree,
|
|
2604
|
+
binding,
|
|
2831
2605
|
passed: gate.passed,
|
|
2832
2606
|
scoped_out: gate.scoped_out,
|
|
2833
2607
|
outcome: gate.outcome,
|
|
@@ -2843,7 +2617,7 @@ async function runPhaseBoundaryGate(ctx) {
|
|
|
2843
2617
|
passed: gate.passed,
|
|
2844
2618
|
commandsRun: gate.results.length,
|
|
2845
2619
|
...(gate.outcome === "scoped_out"
|
|
2846
|
-
? { reason: "
|
|
2620
|
+
? { reason: "no executable verification command declared; operator command required" }
|
|
2847
2621
|
: {}),
|
|
2848
2622
|
durationMs: Date.now() - gateStart,
|
|
2849
2623
|
});
|
|
@@ -2885,7 +2659,7 @@ async function handleAllTerminalTransition(root, artifactsDir, state, store, opt
|
|
|
2885
2659
|
obligation: state.status,
|
|
2886
2660
|
note: "tool_owned_final_gate",
|
|
2887
2661
|
});
|
|
2888
|
-
const gate = await runToolOwnedFinalGate(root, { runner: options.finalGateRunner });
|
|
2662
|
+
const gate = await runToolOwnedFinalGate(root, { runner: options.finalGateRunner, testCommand: explicitFinalGateTestCommand(state) });
|
|
2889
2663
|
await recordFinalGateOutcome({
|
|
2890
2664
|
artifactsDir,
|
|
2891
2665
|
state,
|
|
@@ -2896,7 +2670,7 @@ async function handleAllTerminalTransition(root, artifactsDir, state, store, opt
|
|
|
2896
2670
|
passed: gate.passed,
|
|
2897
2671
|
commandsRun: gate.results.length,
|
|
2898
2672
|
...(gate.outcome === "scoped_out"
|
|
2899
|
-
? { reason: "
|
|
2673
|
+
? { reason: "no executable verification command declared; operator command required" }
|
|
2900
2674
|
: {}),
|
|
2901
2675
|
durationMs: Date.now() - gateStart,
|
|
2902
2676
|
});
|
|
@@ -3092,6 +2866,39 @@ Report this diagnostic to the user and stop. Do not attempt to advance the run.
|
|
|
3092
2866
|
stopCondition: "Stop after reporting the diagnostic to the user.",
|
|
3093
2867
|
});
|
|
3094
2868
|
}
|
|
2869
|
+
function isPlanOnlyBoundary(state) {
|
|
2870
|
+
return state?.status === "implementing" || state?.status === "closing";
|
|
2871
|
+
}
|
|
2872
|
+
async function buildOperatorControlStep(root, artifactsDir, state, control) {
|
|
2873
|
+
const cancelled = control.mode === "cancelled";
|
|
2874
|
+
const active = control.mode === "active";
|
|
2875
|
+
const next = loaderCommand(["next-step", "--root", root, "--artifacts-dir", artifactsDir]);
|
|
2876
|
+
const resume = loaderCommand(["resume", "--root", root, "--artifacts-dir", artifactsDir]);
|
|
2877
|
+
return writeCurrentStep({
|
|
2878
|
+
stepKind: cancelled ? "operator_cancelled" : active ? "operator_resumed" : "operator_paused",
|
|
2879
|
+
status: cancelled ? "complete" : active ? "ready" : "blocked",
|
|
2880
|
+
runId: stateRunId(state), repoRoot: root, artifactsDir,
|
|
2881
|
+
prompt: active
|
|
2882
|
+
? `# Remediation resumed\n\nAccepted work and the live workload binding are unchanged. Run \`${next}\` to derive the next continuation.`
|
|
2883
|
+
: cancelled
|
|
2884
|
+
? "# Remediation cancelled\n\nThe operator cancelled this run. No further work will be accepted or dispatched. All run artifacts and accepted work are preserved."
|
|
2885
|
+
: `# Remediation paused\n\n${control.reason === "plan-only" ? "Planning finished; implementation has not been dispatched by this plan-only continuation." : "The operator paused this run."} The live phase, accepted items and workload binding are preserved.\n\nRun \`${resume}\`, then next-step, to continue from the live state.`,
|
|
2886
|
+
allowedCommands: cancelled ? [] : [active ? next : resume],
|
|
2887
|
+
stopCondition: cancelled ? "This run is terminally cancelled. Stop." : active ? "Run next-step to continue from the live state." : "Stop until the operator explicitly resumes this run.",
|
|
2888
|
+
artifactPaths: { operator_lifecycle: join(artifactsDir, OPERATOR_LIFECYCLE_FILENAME) },
|
|
2889
|
+
});
|
|
2890
|
+
}
|
|
2891
|
+
/** Operator commands serialize with every advancing or recovery writer. */
|
|
2892
|
+
export async function changeOperatorLifecycle(options) {
|
|
2893
|
+
const root = resolveRoot(options.root);
|
|
2894
|
+
const artifactsDir = resolveArtifactsDir(root, options.artifactsDir);
|
|
2895
|
+
await mkdir(artifactsDir, { recursive: true });
|
|
2896
|
+
const store = new StateStore(artifactsDir);
|
|
2897
|
+
return withFileLock(join(artifactsDir, "phase.lock"), async () => {
|
|
2898
|
+
const control = await store.setOperatorLifecycleUnderPhaseLock(options.action, options.hostReport);
|
|
2899
|
+
return buildOperatorControlStep(root, artifactsDir, await store.loadState(), control);
|
|
2900
|
+
});
|
|
2901
|
+
}
|
|
3095
2902
|
export async function decideNextStep(options = {}) {
|
|
3096
2903
|
const normalizedOptions = coerceJsonObjectArg(options, "decideNextStep options");
|
|
3097
2904
|
const root = resolveRoot(normalizedOptions.root);
|
|
@@ -3233,6 +3040,7 @@ async function buildConfirmIntentStep(ctx) {
|
|
|
3233
3040
|
// The proposal comes from the intake summary — the one file the host wrote at
|
|
3234
3041
|
// synthesis. There is no draft checkpoint: the facts live in one place.
|
|
3235
3042
|
const summary = (await readIntakeArtifacts(artifactsDir)).summary;
|
|
3043
|
+
const selection = (await readSourceManifest(intakePaths(artifactsDir).sourceManifest))?.finding_selection;
|
|
3236
3044
|
// Closing action: DETECTED candidates, presented for the host to choose
|
|
3237
3045
|
// from; the tool never selects one (owner decision 92b0e2dd7cfdc06d).
|
|
3238
3046
|
const facts = await detectProjectFacts(root);
|
|
@@ -3263,7 +3071,7 @@ async function buildConfirmIntentStep(ctx) {
|
|
|
3263
3071
|
].join("\n") || "- None";
|
|
3264
3072
|
const filtersBlock = Object.keys(summary.filters).length > 0
|
|
3265
3073
|
? `\`\`\`json\n${JSON.stringify(summary.filters, null, 2)}\n\`\`\``
|
|
3266
|
-
: "(none — remediating all findings)";
|
|
3074
|
+
: selection ? "(none — keeping every finding in the selected source)" : "(none — remediating all findings)";
|
|
3267
3075
|
prompt = `
|
|
3268
3076
|
${refusal}# Confirm Remediation Scope and Intent
|
|
3269
3077
|
|
|
@@ -3314,6 +3122,8 @@ Adjust \`filters\`, \`excluded_scope\`, \`must_not_touch\`, or \`free_form_inten
|
|
|
3314
3122
|
narrow scope. Valid severities: ${VALID_SEVERITIES_PROSE}.
|
|
3315
3123
|
Valid lenses: ${VALID_LENSES_PROSE}.
|
|
3316
3124
|
|
|
3125
|
+
If the operator requests independent contract conformance review, add \`"conformance_review": true\` before the first implementation handoff. It is off by default and the tool keeps the confirmed choice for this run.
|
|
3126
|
+
|
|
3317
3127
|
Once written with \`"confirmed_by": "host"\`, run:
|
|
3318
3128
|
|
|
3319
3129
|
\`${nextCommand}\`
|
|
@@ -3363,6 +3173,7 @@ Once the file is written, run:
|
|
|
3363
3173
|
\`${nextCommand}\`
|
|
3364
3174
|
`;
|
|
3365
3175
|
}
|
|
3176
|
+
prompt += renderFindingSelection(selection);
|
|
3366
3177
|
return writeCurrentStep({
|
|
3367
3178
|
stepKind: "confirm_intent",
|
|
3368
3179
|
status: "ready",
|
|
@@ -3377,138 +3188,6 @@ Once the file is written, run:
|
|
|
3377
3188
|
},
|
|
3378
3189
|
});
|
|
3379
3190
|
}
|
|
3380
|
-
// ---------------------------------------------------------------------------
|
|
3381
|
-
// Deterministic free_form_intent interpretation at the call site (INV-S04)
|
|
3382
|
-
// ---------------------------------------------------------------------------
|
|
3383
|
-
//
|
|
3384
|
-
// The IntentCheckpoint contract states `free_form_intent` is "interpreted into
|
|
3385
|
-
// priority/lens/scope signals at planning time via freeFormIntentInterpreter.
|
|
3386
|
-
// Never threaded verbatim into worker or dispatch prompts (INV-S04)." This is
|
|
3387
|
-
// the call site that honours that: when a CONFIRMED checkpoint carries a
|
|
3388
|
-
// free_form_intent, we run the shared deterministic interpreter HERE — never
|
|
3389
|
-
// pass the raw string downstream — and persist the structured signals so
|
|
3390
|
-
// planning consumes the encoded lens-weights/priority/scope, and so the
|
|
3391
|
-
// unencodable clauses are surfaced (never silently dropped) rather than relying
|
|
3392
|
-
// on an LLM-authored free-text `intent_interpretation`.
|
|
3393
|
-
/** Sidecar artifact recording the deterministic interpretation of free_form_intent. */
|
|
3394
|
-
export const INTENT_INTERPRETATION_FILENAME = "intent-interpretation.json";
|
|
3395
|
-
// v1alpha2: unencodable_clauses carries identity-keyed records (clause_id +
|
|
3396
|
-
// checkpoint_question), not bare strings — the shape the blocking consumer
|
|
3397
|
-
// reads. A v1alpha1 sidecar (string[]) is stale and is repaired by
|
|
3398
|
-
// re-derivation; it had no readers, so no migration path is owed.
|
|
3399
|
-
export const INTENT_INTERPRETATION_SCHEMA_VERSION = "remediate-code-intent-interpretation/v1alpha2";
|
|
3400
|
-
/**
|
|
3401
|
-
* Interpret a confirmed checkpoint's `free_form_intent` via the shared
|
|
3402
|
-
* deterministic interpreter and persist the structured signals to a sidecar
|
|
3403
|
-
* artifact. Idempotent and best-effort: returns the persisted interpretation (or
|
|
3404
|
-
* null when there is nothing to interpret / no confirmed checkpoint) and never
|
|
3405
|
-
* throws into the decide loop. The raw `free_form_intent` string is NOT returned
|
|
3406
|
-
* or threaded anywhere — only the structured `InterpretedIntent` is (INV-S04).
|
|
3407
|
-
*/
|
|
3408
|
-
export async function interpretConfirmedCheckpointIntent(artifactsDir, checkpoint,
|
|
3409
|
-
// Optional so the exported helper stays callable standalone; the decide loop
|
|
3410
|
-
// always supplies it, because an unencodable clause is an operator-visible
|
|
3411
|
-
// loss of intent and belongs in the durable log, not only on stderr.
|
|
3412
|
-
runLogger) {
|
|
3413
|
-
if (!checkpoint || checkpoint.confirmed_by !== "host")
|
|
3414
|
-
return null;
|
|
3415
|
-
const raw = checkpoint.free_form_intent;
|
|
3416
|
-
if (typeof raw !== "string" || raw.trim().length === 0)
|
|
3417
|
-
return null;
|
|
3418
|
-
const interpreted = interpretFreeFormIntent(raw);
|
|
3419
|
-
// The clause pipeline (interpretIntent) owns identity + blocking questions;
|
|
3420
|
-
// the hint interpreter above owns lens/priority/scope signals. Both are
|
|
3421
|
-
// deterministic draws over the same input.
|
|
3422
|
-
const clauseResult = interpretIntent(raw);
|
|
3423
|
-
const unencodable_clauses = [];
|
|
3424
|
-
for (const clause of clauseResult.clauses) {
|
|
3425
|
-
if (clause.encodable || !clause.checkpoint_question)
|
|
3426
|
-
continue;
|
|
3427
|
-
unencodable_clauses.push({
|
|
3428
|
-
clause_id: clause.clause_id,
|
|
3429
|
-
text: clause.text,
|
|
3430
|
-
checkpoint_question: clause.checkpoint_question,
|
|
3431
|
-
});
|
|
3432
|
-
}
|
|
3433
|
-
const persisted = {
|
|
3434
|
-
schema_version: INTENT_INTERPRETATION_SCHEMA_VERSION,
|
|
3435
|
-
interpreted,
|
|
3436
|
-
unencodable_clauses,
|
|
3437
|
-
created_at: new Date().toISOString(),
|
|
3438
|
-
};
|
|
3439
|
-
try {
|
|
3440
|
-
await writeJsonFile(join(artifactsDir, INTENT_INTERPRETATION_FILENAME), persisted);
|
|
3441
|
-
}
|
|
3442
|
-
catch {
|
|
3443
|
-
// Best-effort WRITE: a write failure must never crash the decide loop.
|
|
3444
|
-
// Enforcement does not depend on it — the consumer re-derives when the
|
|
3445
|
-
// sidecar is missing (readOrRepairIntentInterpretation).
|
|
3446
|
-
}
|
|
3447
|
-
if (unencodable_clauses.length > 0) {
|
|
3448
|
-
const clauseTexts = unencodable_clauses.map((c) => c.text);
|
|
3449
|
-
runLogger?.event({
|
|
3450
|
-
phase: "next-step",
|
|
3451
|
-
kind: "outcome",
|
|
3452
|
-
obligation: "interpret_intent",
|
|
3453
|
-
note: `intent_unencodable_clauses count=${String(unencodable_clauses.length)} ` +
|
|
3454
|
-
`clauses=${clauseTexts.join("; ")}`,
|
|
3455
|
-
});
|
|
3456
|
-
process.stderr.write(`[remediate-code] free_form_intent: ${unencodable_clauses.length} ` +
|
|
3457
|
-
`clause(s) could not be encoded as lens/priority/scope signals and ` +
|
|
3458
|
-
`block planning until answered via constraint_clauses: ` +
|
|
3459
|
-
`${clauseTexts.join("; ")}\n`);
|
|
3460
|
-
}
|
|
3461
|
-
return persisted;
|
|
3462
|
-
}
|
|
3463
|
-
/**
|
|
3464
|
-
* Read the persisted intent interpretation — the LOAD-BEARING input to the
|
|
3465
|
-
* constraint-clause gate — repairing it by re-derivation when it is missing,
|
|
3466
|
-
* unparseable, or carries a stale schema_version. Returns null only when
|
|
3467
|
-
* there is nothing to interpret (no confirmed checkpoint / empty intent).
|
|
3468
|
-
*/
|
|
3469
|
-
export async function readOrRepairIntentInterpretation(artifactsDir, checkpoint, runLogger) {
|
|
3470
|
-
if (!checkpoint || checkpoint.confirmed_by !== "host")
|
|
3471
|
-
return null;
|
|
3472
|
-
const raw = checkpoint.free_form_intent;
|
|
3473
|
-
if (typeof raw !== "string" || raw.trim().length === 0)
|
|
3474
|
-
return null;
|
|
3475
|
-
const sidecarPath = join(artifactsDir, INTENT_INTERPRETATION_FILENAME);
|
|
3476
|
-
try {
|
|
3477
|
-
const parsed = parsePersistedIntentInterpretation(JSON.parse(await readFile(sidecarPath, "utf8")));
|
|
3478
|
-
if (parsed)
|
|
3479
|
-
return parsed;
|
|
3480
|
-
}
|
|
3481
|
-
catch {
|
|
3482
|
-
// Missing or unreadable — fall through to repair.
|
|
3483
|
-
}
|
|
3484
|
-
return interpretConfirmedCheckpointIntent(artifactsDir, checkpoint, runLogger);
|
|
3485
|
-
}
|
|
3486
|
-
/** Pure shape gate for the sidecar: current version + record-shaped clauses, else null. */
|
|
3487
|
-
function parsePersistedIntentInterpretation(parsed) {
|
|
3488
|
-
if (isRecord(parsed) &&
|
|
3489
|
-
parsed.schema_version === INTENT_INTERPRETATION_SCHEMA_VERSION &&
|
|
3490
|
-
Array.isArray(parsed.unencodable_clauses) &&
|
|
3491
|
-
parsed.unencodable_clauses.every((c) => isRecord(c) &&
|
|
3492
|
-
typeof c.clause_id === "string" &&
|
|
3493
|
-
typeof c.text === "string" &&
|
|
3494
|
-
typeof c.checkpoint_question === "string")) {
|
|
3495
|
-
return parsed;
|
|
3496
|
-
}
|
|
3497
|
-
return null;
|
|
3498
|
-
}
|
|
3499
|
-
/**
|
|
3500
|
-
* Sync sidecar read for the obligation's derive scan. Returns the persisted
|
|
3501
|
-
* interpretation, or null when the sidecar is missing, unreadable, or stale —
|
|
3502
|
-
* the execute path repairs via {@link readOrRepairIntentInterpretation}.
|
|
3503
|
-
*/
|
|
3504
|
-
function readPersistedIntentInterpretationSync(sidecarPath) {
|
|
3505
|
-
try {
|
|
3506
|
-
return parsePersistedIntentInterpretation(JSON.parse(readFileSync(sidecarPath, "utf8")));
|
|
3507
|
-
}
|
|
3508
|
-
catch {
|
|
3509
|
-
return null;
|
|
3510
|
-
}
|
|
3511
|
-
}
|
|
3512
3191
|
/**
|
|
3513
3192
|
* Narrow a nullable engine state to non-null inside an executor whose `derive`
|
|
3514
3193
|
* only marks it actionable when the state is present — a violation is an engine
|
|
@@ -3542,7 +3221,7 @@ export const PRE_INTAKE_PRIORITY = [
|
|
|
3542
3221
|
*/
|
|
3543
3222
|
export function buildPreIntakeObligations(ctx, snapshot) {
|
|
3544
3223
|
const { artifactsDir, inputResolution } = ctx;
|
|
3545
|
-
const { existingCheckpoint, rejectedCheckpointFields, resumeAck, entryState, suppliedInputUnchanged, guidanceFileSupplied } = snapshot;
|
|
3224
|
+
const { existingCheckpoint, rejectedCheckpointFields, resumeAck, entryState, suppliedInputUnchanged, inputSelectionChanged, selectionAlreadyBound, selectedSourceChanged, guidanceFileSupplied } = snapshot;
|
|
3546
3225
|
const ip = intakePaths(artifactsDir);
|
|
3547
3226
|
const checkpointPath = join(artifactsDir, "intent_checkpoint.json");
|
|
3548
3227
|
const ackPath = join(artifactsDir, "confirm_resume_ack.json");
|
|
@@ -3561,18 +3240,18 @@ export function buildPreIntakeObligations(ctx, snapshot) {
|
|
|
3561
3240
|
// "unchanged" notion, so any guidance file against an advanced run conflicts;
|
|
3562
3241
|
// bare follow-ups don't set the flag). Derives from the frozen entry state.
|
|
3563
3242
|
id: "input_conflict",
|
|
3564
|
-
derive: () => (
|
|
3565
|
-
|
|
3566
|
-
|
|
3567
|
-
|
|
3243
|
+
derive: () => (selectionAlreadyBound && inputSelectionChanged) ||
|
|
3244
|
+
(((inputResolution.supplied && !suppliedInputUnchanged) ||
|
|
3245
|
+
inputSelectionChanged || guidanceFileSupplied) &&
|
|
3246
|
+
entryState != null && entryState.status !== "pending")
|
|
3568
3247
|
? "missing"
|
|
3569
3248
|
: "satisfied",
|
|
3570
3249
|
execute: async (_state, c) => {
|
|
3571
|
-
const s =
|
|
3572
|
-
await c.countStep(
|
|
3250
|
+
const s = entryState ?? { status: "pending" };
|
|
3251
|
+
await c.countStep(entryState);
|
|
3573
3252
|
return {
|
|
3574
3253
|
kind: "emit",
|
|
3575
|
-
step: await handleInputConflict(c.root, c.artifactsDir, s, c.inputResolution),
|
|
3254
|
+
step: await handleInputConflict(c.root, c.artifactsDir, s, c.inputResolution, selectionAlreadyBound && inputSelectionChanged ? { sourceChanged: selectedSourceChanged === true } : undefined),
|
|
3576
3255
|
};
|
|
3577
3256
|
},
|
|
3578
3257
|
},
|
|
@@ -3799,6 +3478,7 @@ Then run:
|
|
|
3799
3478
|
* cascade tail's guard order exactly so selection cannot drift.
|
|
3800
3479
|
*/
|
|
3801
3480
|
export const MAIN_PRIORITY = [
|
|
3481
|
+
"operator_plan_only",
|
|
3802
3482
|
"waiting_for_clarification",
|
|
3803
3483
|
"waiting_for_triage",
|
|
3804
3484
|
"planning_documentable",
|
|
@@ -3825,6 +3505,14 @@ export function buildMainObligations(ctx) {
|
|
|
3825
3505
|
const clarificationResolutionPath = join(artifactsDir, "clarification_resolution.json");
|
|
3826
3506
|
const triageResolutionPath = join(artifactsDir, "triage_resolution.json");
|
|
3827
3507
|
return [
|
|
3508
|
+
{
|
|
3509
|
+
id: "operator_plan_only",
|
|
3510
|
+
derive: (state) => options.planOnly === true && isPlanOnlyBoundary(state) ? "missing" : "satisfied",
|
|
3511
|
+
execute: async (state) => {
|
|
3512
|
+
const control = await store.setOperatorLifecycleUnderPhaseLock("pause", undefined, "plan-only");
|
|
3513
|
+
return { kind: "emit", step: await buildOperatorControlStep(root, artifactsDir, state, control) };
|
|
3514
|
+
},
|
|
3515
|
+
},
|
|
3828
3516
|
{
|
|
3829
3517
|
// Plan-phase clarification wait: apply a resolution if present (transition →
|
|
3830
3518
|
// re-scan), else surface the wait step.
|
|
@@ -4052,6 +3740,32 @@ async function advanceUnderPhaseLock(deps) {
|
|
|
4052
3740
|
// Loaded FRESH under the mutex: a peer may have advanced and persisted state
|
|
4053
3741
|
// between the entry read and this process winning the lock.
|
|
4054
3742
|
let state = await store.loadState();
|
|
3743
|
+
let control = await store.loadOperatorLifecycle();
|
|
3744
|
+
if (control && control.mode !== "active") {
|
|
3745
|
+
return buildOperatorControlStep(root, artifactsDir, state, control);
|
|
3746
|
+
}
|
|
3747
|
+
if (options.planOnly === true)
|
|
3748
|
+
control = await store.setOperatorLifecycleUnderPhaseLock("plan-only");
|
|
3749
|
+
// Covers a crash after planning saved its phase but before its pause was
|
|
3750
|
+
// persisted. No override, guidance, ingestion or dispatch may slip through.
|
|
3751
|
+
if (control?.plan_only && isPlanOnlyBoundary(state) && !options.forceReplan) {
|
|
3752
|
+
control = await store.setOperatorLifecycleUnderPhaseLock("pause", undefined, "plan-only");
|
|
3753
|
+
return buildOperatorControlStep(root, artifactsDir, state, control);
|
|
3754
|
+
}
|
|
3755
|
+
if (options.guidanceFile && options.guidanceText !== undefined)
|
|
3756
|
+
throw new Error("Choose --guidance or --guidance-file, not both.");
|
|
3757
|
+
if (options.guidanceFile)
|
|
3758
|
+
applyGuidanceFile(artifactsDir, options.guidanceFile);
|
|
3759
|
+
if (options.guidanceText !== undefined)
|
|
3760
|
+
applyGuidanceText(artifactsDir, options.guidanceText);
|
|
3761
|
+
if (options.verificationCommand !== undefined && state?.plan) {
|
|
3762
|
+
if (parseCommandString(options.verificationCommand).length === 0) {
|
|
3763
|
+
throw new Error("--verification-command must name an executable command");
|
|
3764
|
+
}
|
|
3765
|
+
state.plan.test_command = options.verificationCommand;
|
|
3766
|
+
state.plan.test_command_source = "explicit";
|
|
3767
|
+
await store.saveState(state);
|
|
3768
|
+
}
|
|
4055
3769
|
// step_count is incremented once per host invocation. The `counted` flag guards
|
|
4056
3770
|
// the shared `countStep` closure so the forceReplan preamble, the pre-intake
|
|
4057
3771
|
// obligation executors, and the post-intake count point can never double-count
|
|
@@ -4120,7 +3834,14 @@ async function advanceUnderPhaseLock(deps) {
|
|
|
4120
3834
|
// Whether a supplied `--input` matches the input the existing run was built
|
|
4121
3835
|
// from — so re-passing the same `--input` (the loader does this each next-step)
|
|
4122
3836
|
// resumes rather than tripping the input_conflict gate.
|
|
4123
|
-
const
|
|
3837
|
+
const sourceManifest = await readSourceManifest(intakePaths(artifactsDir).sourceManifest);
|
|
3838
|
+
const requestedSelection = requestedFindingSelection(options);
|
|
3839
|
+
const recordedSelection = sourceManifest?.finding_selection;
|
|
3840
|
+
const selectedSourceChanged = recordedSelection !== undefined && (!existsSync(recordedSelection.source_path) ||
|
|
3841
|
+
hashContent(await readFile(recordedSelection.source_path, "utf8")) !== recordedSelection.source_hash);
|
|
3842
|
+
const inputSelectionChanged = selectedSourceChanged ||
|
|
3843
|
+
(requestedSelection !== undefined && JSON.stringify(requestedSelection) !== JSON.stringify(recordedSelection?.criteria));
|
|
3844
|
+
const suppliedInputUnchanged = !inputSelectionChanged && suppliedInputMatchesRun(inputResolution, sourceManifest);
|
|
4124
3845
|
// The linear pre-intake gates run as obligations through the shared advance
|
|
4125
3846
|
// loop. An emit returns to the host; a transition re-scans within this call;
|
|
4126
3847
|
// exhausting them (step === null) means the run is past intake and falls
|
|
@@ -4128,7 +3849,7 @@ async function advanceUnderPhaseLock(deps) {
|
|
|
4128
3849
|
const ctx = {
|
|
4129
3850
|
root,
|
|
4130
3851
|
artifactsDir,
|
|
4131
|
-
options,
|
|
3852
|
+
options: { ...options, planOnly: control?.plan_only === true },
|
|
4132
3853
|
runLogger,
|
|
4133
3854
|
store,
|
|
4134
3855
|
inputResolution,
|
|
@@ -4142,6 +3863,9 @@ async function advanceUnderPhaseLock(deps) {
|
|
|
4142
3863
|
resumeAck,
|
|
4143
3864
|
entryState: state,
|
|
4144
3865
|
suppliedInputUnchanged,
|
|
3866
|
+
inputSelectionChanged,
|
|
3867
|
+
selectionAlreadyBound: recordedSelection !== undefined,
|
|
3868
|
+
selectedSourceChanged,
|
|
4145
3869
|
guidanceFileSupplied: Boolean(options.guidanceFileSupplied),
|
|
4146
3870
|
}),
|
|
4147
3871
|
}, state, ctx);
|