audit-tools 0.45.0 → 0.48.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +51 -18
- package/dispatch/merge-results.mjs +3 -3
- package/dispatch/validate-result.mjs +3 -3
- package/dist/audit/cli/charterExtractionPrompt.d.ts.map +1 -1
- package/dist/audit/cli/charterExtractionPrompt.js +11 -1
- package/dist/audit/cli/charterExtractionPrompt.js.map +1 -1
- package/dist/audit/cli/cleanupCommand.d.ts +25 -0
- package/dist/audit/cli/cleanupCommand.d.ts.map +1 -1
- package/dist/audit/cli/cleanupCommand.js +76 -1
- package/dist/audit/cli/cleanupCommand.js.map +1 -1
- package/dist/audit/cli/dispatch/hostHandoff.d.ts.map +1 -1
- package/dist/audit/cli/dispatch/hostHandoff.js +52 -76
- package/dist/audit/cli/dispatch/hostHandoff.js.map +1 -1
- package/dist/audit/cli/dispatch/packetFilter.d.ts +1 -1
- package/dist/audit/cli/fanoutLanes.d.ts.map +1 -1
- package/dist/audit/cli/fanoutLanes.js +6 -1
- package/dist/audit/cli/fanoutLanes.js.map +1 -1
- package/dist/audit/cli/laneSubmissions.d.ts +13 -0
- package/dist/audit/cli/laneSubmissions.d.ts.map +1 -1
- package/dist/audit/cli/laneSubmissions.js +38 -27
- package/dist/audit/cli/laneSubmissions.js.map +1 -1
- package/dist/audit/cli/nextStepCommand.d.ts +12 -0
- package/dist/audit/cli/nextStepCommand.d.ts.map +1 -1
- package/dist/audit/cli/nextStepCommand.js +23 -12
- package/dist/audit/cli/nextStepCommand.js.map +1 -1
- package/dist/audit/cli/nextStepHelpers.d.ts +3 -2
- package/dist/audit/cli/nextStepHelpers.d.ts.map +1 -1
- package/dist/audit/cli/nextStepHelpers.js +65 -37
- package/dist/audit/cli/nextStepHelpers.js.map +1 -1
- package/dist/audit/cli/reviewRun.d.ts +1 -1
- package/dist/audit/cli/reviewRun.d.ts.map +1 -1
- package/dist/audit/cli/reviewRun.js +2 -9
- package/dist/audit/cli/reviewRun.js.map +1 -1
- package/dist/audit/cli/sampleRunCommand.d.ts +215 -0
- package/dist/audit/cli/sampleRunCommand.d.ts.map +1 -1
- package/dist/audit/cli/sampleRunCommand.js +60 -3
- package/dist/audit/cli/sampleRunCommand.js.map +1 -1
- package/dist/audit/cli/semanticReviewStep.d.ts.map +1 -1
- package/dist/audit/cli/semanticReviewStep.js +9 -1
- package/dist/audit/cli/semanticReviewStep.js.map +1 -1
- package/dist/audit/cli/steps.d.ts +6 -6
- package/dist/audit/contracts/workerSchemas.d.ts +36 -26
- package/dist/audit/contracts/workerSchemas.d.ts.map +1 -1
- package/dist/audit/contracts/workerSchemas.js +11 -1
- package/dist/audit/contracts/workerSchemas.js.map +1 -1
- package/dist/audit/contracts/wrapperResponse.d.ts +52 -28
- package/dist/audit/contracts/wrapperResponse.d.ts.map +1 -1
- package/dist/audit/contracts/wrapperResponse.js +8 -5
- package/dist/audit/contracts/wrapperResponse.js.map +1 -1
- package/dist/audit/decompose/sources.d.ts.map +1 -1
- package/dist/audit/decompose/sources.js +21 -5
- package/dist/audit/decompose/sources.js.map +1 -1
- package/dist/audit/extractors/analyzers/registry.d.ts +8 -5
- package/dist/audit/extractors/analyzers/registry.d.ts.map +1 -1
- package/dist/audit/extractors/analyzers/registry.js +28 -7
- package/dist/audit/extractors/analyzers/registry.js.map +1 -1
- package/dist/audit/extractors/designAssessment.js +1 -1
- package/dist/audit/extractors/gitHistory.d.ts +1 -1
- package/dist/audit/extractors/gitHistory.js +1 -1
- package/dist/audit/extractors/graph.d.ts +1 -1
- package/dist/audit/extractors/graph.js +1 -1
- package/dist/audit/extractors/risk.d.ts +1 -1
- package/dist/audit/extractors/risk.js +1 -1
- package/dist/audit/io/artifacts.d.ts.map +1 -1
- package/dist/audit/io/artifacts.js +5 -5
- package/dist/audit/io/artifacts.js.map +1 -1
- package/dist/audit/orchestrator/acquisitionExecutor.d.ts +12 -7
- package/dist/audit/orchestrator/acquisitionExecutor.d.ts.map +1 -1
- package/dist/audit/orchestrator/acquisitionExecutor.js +1 -1
- package/dist/audit/orchestrator/acquisitionExecutor.js.map +1 -1
- package/dist/audit/orchestrator/advance.d.ts +14 -10
- package/dist/audit/orchestrator/advance.d.ts.map +1 -1
- package/dist/audit/orchestrator/advance.js +49 -24
- package/dist/audit/orchestrator/advance.js.map +1 -1
- package/dist/audit/orchestrator/advanceTypes.d.ts +14 -0
- package/dist/audit/orchestrator/advanceTypes.d.ts.map +1 -1
- package/dist/audit/orchestrator/autoFixExecutor.d.ts +20 -1
- package/dist/audit/orchestrator/autoFixExecutor.d.ts.map +1 -1
- package/dist/audit/orchestrator/autoFixExecutor.js +77 -15
- package/dist/audit/orchestrator/autoFixExecutor.js.map +1 -1
- package/dist/audit/orchestrator/dependencyMap.js +1 -1
- package/dist/audit/orchestrator/dependencyMap.js.map +1 -1
- package/dist/audit/orchestrator/designReviewPrompt.d.ts.map +1 -1
- package/dist/audit/orchestrator/designReviewPrompt.js +7 -0
- package/dist/audit/orchestrator/designReviewPrompt.js.map +1 -1
- package/dist/audit/orchestrator/executorRunners.d.ts.map +1 -1
- package/dist/audit/orchestrator/executorRunners.js +18 -4
- package/dist/audit/orchestrator/executorRunners.js.map +1 -1
- package/dist/audit/orchestrator/executors.d.ts +54 -0
- package/dist/audit/orchestrator/executors.d.ts.map +1 -1
- package/dist/audit/orchestrator/executors.js +352 -0
- package/dist/audit/orchestrator/executors.js.map +1 -1
- package/dist/audit/orchestrator/hostInputPause.d.ts +28 -13
- package/dist/audit/orchestrator/hostInputPause.d.ts.map +1 -1
- package/dist/audit/orchestrator/hostInputPause.js +17 -11
- package/dist/audit/orchestrator/hostInputPause.js.map +1 -1
- package/dist/audit/orchestrator/intentInterpreter.d.ts +15 -18
- package/dist/audit/orchestrator/intentInterpreter.d.ts.map +1 -1
- package/dist/audit/orchestrator/intentInterpreter.js +12 -26
- package/dist/audit/orchestrator/intentInterpreter.js.map +1 -1
- package/dist/audit/orchestrator/localCommands.d.ts +24 -1
- package/dist/audit/orchestrator/localCommands.d.ts.map +1 -1
- package/dist/audit/orchestrator/localCommands.js +44 -9
- package/dist/audit/orchestrator/localCommands.js.map +1 -1
- package/dist/audit/orchestrator/nextStep.d.ts +13 -0
- package/dist/audit/orchestrator/nextStep.d.ts.map +1 -1
- package/dist/audit/orchestrator/nextStep.js +13 -0
- package/dist/audit/orchestrator/nextStep.js.map +1 -1
- package/dist/audit/orchestrator/requeueCommand.d.ts +1 -1
- package/dist/audit/orchestrator/runtimeCommand.d.ts +32 -1
- package/dist/audit/orchestrator/runtimeCommand.d.ts.map +1 -1
- package/dist/audit/orchestrator/runtimeCommand.js +90 -3
- package/dist/audit/orchestrator/runtimeCommand.js.map +1 -1
- package/dist/audit/orchestrator/structureExecutors.js +1 -1
- package/dist/audit/orchestrator/structureExecutors.js.map +1 -1
- package/dist/audit/orchestrator/syntaxResolutionExecutor.d.ts +11 -1
- package/dist/audit/orchestrator/syntaxResolutionExecutor.d.ts.map +1 -1
- package/dist/audit/orchestrator/syntaxResolutionExecutor.js +25 -9
- package/dist/audit/orchestrator/syntaxResolutionExecutor.js.map +1 -1
- package/dist/audit/supervisor/operatorHandoff.d.ts +0 -1
- package/dist/audit/supervisor/operatorHandoff.d.ts.map +1 -1
- package/dist/audit/supervisor/operatorHandoff.js +2 -1
- package/dist/audit/supervisor/operatorHandoff.js.map +1 -1
- package/dist/audit/types.d.ts +18 -18
- package/dist/remediate/contractPipeline/derive.d.ts.map +1 -1
- package/dist/remediate/contractPipeline/derive.js +1 -0
- package/dist/remediate/contractPipeline/derive.js.map +1 -1
- package/dist/remediate/contractPipeline/semanticProjection.d.ts +5 -4
- package/dist/remediate/contractPipeline/semanticProjection.d.ts.map +1 -1
- package/dist/remediate/contractPipeline/semanticProjection.js +34 -19
- package/dist/remediate/contractPipeline/semanticProjection.js.map +1 -1
- package/dist/remediate/index.d.ts.map +1 -1
- package/dist/remediate/index.js +3 -2
- package/dist/remediate/index.js.map +1 -1
- package/dist/remediate/intake.js +1 -1
- package/dist/remediate/intake.js.map +1 -1
- package/dist/remediate/phases/close.d.ts +45 -2
- package/dist/remediate/phases/close.d.ts.map +1 -1
- package/dist/remediate/phases/close.js +111 -43
- package/dist/remediate/phases/close.js.map +1 -1
- package/dist/remediate/phases/closeVerifyAnalyzerLeads.js +2 -2
- package/dist/remediate/phases/closeVerifyAnalyzerLeads.js.map +1 -1
- package/dist/remediate/phases/triage.d.ts.map +1 -1
- package/dist/remediate/phases/triage.js +61 -19
- package/dist/remediate/phases/triage.js.map +1 -1
- package/dist/remediate/review/autonomousGate.d.ts +3 -7
- package/dist/remediate/review/autonomousGate.d.ts.map +1 -1
- package/dist/remediate/review/autonomousGate.js +10 -24
- package/dist/remediate/review/autonomousGate.js.map +1 -1
- package/dist/remediate/riskSignal.d.ts +8 -20
- package/dist/remediate/riskSignal.d.ts.map +1 -1
- package/dist/remediate/riskSignal.js +21 -76
- package/dist/remediate/riskSignal.js.map +1 -1
- package/dist/remediate/state/accessMemory.d.ts +2 -3
- package/dist/remediate/state/accessMemory.d.ts.map +1 -1
- package/dist/remediate/state/accessMemory.js +3 -6
- package/dist/remediate/state/accessMemory.js.map +1 -1
- package/dist/remediate/state/store.d.ts +6 -7
- package/dist/remediate/state/store.d.ts.map +1 -1
- package/dist/remediate/state/store.js.map +1 -1
- package/dist/remediate/state/types.d.ts +2 -84
- package/dist/remediate/state/types.d.ts.map +1 -1
- package/dist/remediate/state/types.js +1 -39
- package/dist/remediate/state/types.js.map +1 -1
- package/dist/remediate/steps/contractPipeline.d.ts +13 -19
- package/dist/remediate/steps/contractPipeline.d.ts.map +1 -1
- package/dist/remediate/steps/contractPipeline.js +157 -66
- package/dist/remediate/steps/contractPipeline.js.map +1 -1
- package/dist/remediate/steps/dispatch/hostHandoff.d.ts +1 -1
- package/dist/remediate/steps/dispatch/hostHandoff.d.ts.map +1 -1
- package/dist/remediate/steps/dispatch/hostHandoff.js +297 -141
- package/dist/remediate/steps/dispatch/hostHandoff.js.map +1 -1
- package/dist/remediate/steps/nextStep.d.ts +16 -7
- package/dist/remediate/steps/nextStep.d.ts.map +1 -1
- package/dist/remediate/steps/nextStep.js +207 -216
- package/dist/remediate/steps/nextStep.js.map +1 -1
- package/dist/remediate/steps/stepUtils.d.ts +10 -2
- package/dist/remediate/steps/stepUtils.d.ts.map +1 -1
- package/dist/remediate/steps/stepUtils.js +13 -3
- package/dist/remediate/steps/stepUtils.js.map +1 -1
- package/dist/remediate/steps/types.d.ts +1 -1
- package/dist/remediate/steps/types.d.ts.map +1 -1
- package/dist/remediate/steps/types.js.map +1 -1
- package/dist/remediate/types/options.d.ts +1 -1
- package/dist/remediate/validation/artifacts.d.ts.map +1 -1
- package/dist/remediate/validation/artifacts.js +1 -16
- package/dist/remediate/validation/artifacts.js.map +1 -1
- package/dist/remediate/validation/remediationState.d.ts +0 -1
- package/dist/remediate/validation/remediationState.d.ts.map +1 -1
- package/dist/remediate/validation/remediationState.js +0 -51
- package/dist/remediate/validation/remediationState.js.map +1 -1
- package/dist/shared/analyzerPolicy.d.ts +18 -6
- package/dist/shared/analyzerPolicy.d.ts.map +1 -1
- package/dist/shared/analyzerPolicy.js +15 -3
- package/dist/shared/analyzerPolicy.js.map +1 -1
- package/dist/shared/analyzers/acquisitionEngine.d.ts +71 -32
- package/dist/shared/analyzers/acquisitionEngine.d.ts.map +1 -1
- package/dist/shared/analyzers/acquisitionEngine.js +86 -28
- package/dist/shared/analyzers/acquisitionEngine.js.map +1 -1
- package/dist/shared/analyzers/binaryAcquisition.d.ts +8 -2
- package/dist/shared/analyzers/binaryAcquisition.d.ts.map +1 -1
- package/dist/shared/analyzers/binaryAcquisition.js +4 -4
- package/dist/shared/analyzers/binaryAcquisition.js.map +1 -1
- package/dist/shared/constitutionalDocPaths.d.ts.map +1 -1
- package/dist/shared/constitutionalDocPaths.js +7 -0
- package/dist/shared/constitutionalDocPaths.js.map +1 -1
- package/dist/shared/engine/obligationEngine.d.ts +44 -3
- package/dist/shared/engine/obligationEngine.d.ts.map +1 -1
- package/dist/shared/engine/obligationEngine.js +53 -7
- package/dist/shared/engine/obligationEngine.js.map +1 -1
- package/dist/shared/findings/dedupe.d.ts.map +1 -1
- package/dist/shared/findings/dedupe.js +8 -0
- package/dist/shared/findings/dedupe.js.map +1 -1
- package/dist/shared/friction/frictionRecord.d.ts +42 -2
- package/dist/shared/friction/frictionRecord.d.ts.map +1 -1
- package/dist/shared/friction/frictionRecord.js +111 -3
- package/dist/shared/friction/frictionRecord.js.map +1 -1
- package/dist/shared/friction/triage.d.ts +15 -0
- package/dist/shared/friction/triage.d.ts.map +1 -1
- package/dist/shared/friction/triage.js +33 -2
- package/dist/shared/friction/triage.js.map +1 -1
- package/dist/shared/index.d.ts +15 -9
- package/dist/shared/index.d.ts.map +1 -1
- package/dist/shared/index.js +15 -7
- package/dist/shared/index.js.map +1 -1
- package/dist/shared/intent/constraintClauses.d.ts +31 -0
- package/dist/shared/intent/constraintClauses.d.ts.map +1 -0
- package/dist/shared/intent/constraintClauses.js +37 -0
- package/dist/shared/intent/constraintClauses.js.map +1 -0
- package/dist/shared/io/fileLock.d.ts.map +1 -1
- package/dist/shared/io/fileLock.js +22 -3
- package/dist/shared/io/fileLock.js.map +1 -1
- package/dist/shared/io/frictionCapture.d.ts +73 -19
- package/dist/shared/io/frictionCapture.d.ts.map +1 -1
- package/dist/shared/io/frictionCapture.js +21 -30
- package/dist/shared/io/frictionCapture.js.map +1 -1
- package/dist/shared/io/lockedJsonStore.d.ts +24 -3
- package/dist/shared/io/lockedJsonStore.d.ts.map +1 -1
- package/dist/shared/io/lockedJsonStore.js +35 -7
- package/dist/shared/io/lockedJsonStore.js.map +1 -1
- package/dist/shared/io/stepContractWriter.d.ts +3 -3
- package/dist/shared/io/stepContractWriter.d.ts.map +1 -1
- package/dist/shared/io/stepContractWriter.js +97 -13
- package/dist/shared/io/stepContractWriter.js.map +1 -1
- package/dist/shared/submission/expectedSubmissions.d.ts +18 -0
- package/dist/shared/submission/expectedSubmissions.d.ts.map +1 -1
- package/dist/shared/submission/expectedSubmissions.js +17 -0
- package/dist/shared/submission/expectedSubmissions.js.map +1 -1
- package/dist/shared/submission/handRecovery.d.ts.map +1 -1
- package/dist/shared/submission/handRecovery.js +9 -11
- package/dist/shared/submission/handRecovery.js.map +1 -1
- package/dist/shared/submission/hostHandoffCore.d.ts +155 -0
- package/dist/shared/submission/hostHandoffCore.d.ts.map +1 -0
- package/dist/shared/submission/hostHandoffCore.js +210 -0
- package/dist/shared/submission/hostHandoffCore.js.map +1 -0
- package/dist/shared/submission/submissionLedger.d.ts +5 -1
- package/dist/shared/submission/submissionLedger.d.ts.map +1 -1
- package/dist/shared/submission/submissionLedger.js +33 -3
- package/dist/shared/submission/submissionLedger.js.map +1 -1
- package/dist/shared/tooling/commandShape.d.ts +29 -0
- package/dist/shared/tooling/commandShape.d.ts.map +1 -1
- package/dist/shared/tooling/commandShape.js +82 -10
- package/dist/shared/tooling/commandShape.js.map +1 -1
- package/dist/shared/tooling/exec.d.ts +32 -0
- package/dist/shared/tooling/exec.d.ts.map +1 -1
- package/dist/shared/tooling/exec.js +189 -0
- package/dist/shared/tooling/exec.js.map +1 -1
- package/docs/audit-pkg/contracts.md +2 -1
- package/docs/audit-pkg/operator-guide.md +12 -6
- package/docs/audit-pkg/product.md +22 -6
- package/package.json +13 -6
- package/schemas/audit-code-v1alpha1.schema.json +297 -0
- package/schemas/audit_result.schema.json +4 -0
- package/schemas/audit_results.schema.json +4 -0
- package/scripts/audit/postinstall.mjs +4 -2
- package/scripts/postinstall.mjs +1 -1
- package/scripts/remediate/postinstall.mjs +3 -1
- package/scripts/shared/install-host-assets.mjs +15 -8
- package/skills/audit-code/SKILL.md +4 -2
- package/skills/audit-code/audit-code.prompt.md +5 -2
- package/skills/audit-code/opencode-command-template.txt +1 -1
- package/wrapper/audit-code-wrapper-build.mjs +3 -3
- package/wrapper/audit-code-wrapper-install-hosts.mjs +3 -3
- package/wrapper/audit-code-wrapper-lib.mjs +82 -16
- package/wrapper/installer-verb-help.mjs +14 -4
- package/wrapper/remediate-code-wrapper-install-hosts.mjs +3 -3
- package/docs/audit-pkg/development.md +0 -113
- package/docs/audit-pkg/release.md +0 -140
|
@@ -1,12 +1,12 @@
|
|
|
1
1
|
import { loadRemediateSessionConfig } from "./sessionConfigLoad.js";
|
|
2
|
-
import { existsSync, statSync } from "node:fs";
|
|
2
|
+
import { existsSync, readFileSync, statSync } from "node:fs";
|
|
3
3
|
import { mkdir, readFile, rename, writeFile } from "node:fs/promises";
|
|
4
4
|
import { dirname, join, resolve } from "node:path";
|
|
5
5
|
import { StateStore } from "../state/store.js";
|
|
6
6
|
// IO / validation / rendering helpers
|
|
7
7
|
import { discardOnSchemaVersionMismatch, readOptionalJsonFile, stagedAndUntracked, writeJsonFile, writeTextFile, buildAuditDeliverablePair, formatValidationIssues, isRecord, withFsRetry, RunLogger, coerceJsonObjectArg, headCommit, projectAuditFindingsReportSubset,
|
|
8
8
|
// obligation engine + intent
|
|
9
|
-
interpretFreeFormIntent, advance, decideFrictionTriage, buildFrictionTriageBlock,
|
|
9
|
+
interpretFreeFormIntent, interpretIntent, unresolvedFromClauses, advance, decideFrictionTriage, buildFrictionTriageBlock, linkFrictionRunIds,
|
|
10
10
|
// domain constants
|
|
11
11
|
LENSES, SEVERITIES, } from "audit-tools/shared";
|
|
12
12
|
import { applyPlanPipeline, buildCoverageLedger } from "../phases/plan.js";
|
|
@@ -27,13 +27,13 @@ import { checkAffectedFileIntegrity } from "../utils/fileIntegrity.js";
|
|
|
27
27
|
import { applyIntentOrdering } from "../intent/intentOrdering.js";
|
|
28
28
|
import { resolveIntakeStep } from "./intakeResolver.js";
|
|
29
29
|
import { runToolOwnedFinalGate, writeFinalGateRedRecord, writeFinalGateOutcomeRecord, } from "./finalGate.js";
|
|
30
|
-
import { buildNextContractPipelineStep, shouldEnterContractPipeline, writePathASeedFromFindings,
|
|
30
|
+
import { buildNextContractPipelineStep, shouldEnterContractPipeline, writePathASeedFromFindings, } from "./contractPipeline.js";
|
|
31
31
|
import { contractArtifactExists, contractPipelineDir, } from "../contractPipeline/artifactStore.js";
|
|
32
32
|
import { buildReviewRequest, applyReviewResolution, isResolutionForRequest, screenResolutionIds, REVIEW_REQUEST_SCHEMA_VERSION, } from "../review/reviewGate.js";
|
|
33
33
|
import { buildAutonomousReviewDecision } from "../review/autonomousGate.js";
|
|
34
34
|
import { runFindingFilterPass } from "../findingFilter.js";
|
|
35
35
|
import { intakePaths, isIntakeReady, manifestIsInputBound, readIntakeArtifacts, resolveManifestSources, } from "../intake.js";
|
|
36
|
-
import { ensureIntakeRiskSignal, readIntakeRiskSignal, writeIntakeRiskSignal, escalateRiskSignal, findingRiskEvidence, distinctAffectedFiles,
|
|
36
|
+
import { ensureIntakeRiskSignal, readIntakeRiskSignal, writeIntakeRiskSignal, escalateRiskSignal, findingRiskEvidence, distinctAffectedFiles, } from "../riskSignal.js";
|
|
37
37
|
import { ambiguityReviewPrompt, clarificationPrompt, collectIntakeClarificationsPrompt, collectStartingPointPrompt, loaderCommand, reviewApprovalPrompt, synthesizeIntakePrompt, triagePrompt, } from "./prompts.js";
|
|
38
38
|
// Single-sourced prose renders of the canonical lens / severity vocabularies
|
|
39
39
|
// (`audit-tools/shared` `LENSES` / `SEVERITIES`) for the intent-checkpoint
|
|
@@ -449,7 +449,7 @@ function stripPlanTimeBookkeeping(value) {
|
|
|
449
449
|
* The re-plan carry-forward identity of a finding: canonical JSON with the
|
|
450
450
|
* plan-time bookkeeping keys stripped, so a re-plan whose only delta is a
|
|
451
451
|
* recomputed file hash or a re-evaluated grounding flag carries the prior item
|
|
452
|
-
*
|
|
452
|
+
* forward, while a real change to the finding does not.
|
|
453
453
|
*
|
|
454
454
|
* EXPORTED so the invariant suite can call THIS function. It was module-internal,
|
|
455
455
|
* and the suite claiming to cover the invariant declared its own copy of the key
|
|
@@ -488,14 +488,15 @@ function carryForwardMatchingItems(previous, replanned) {
|
|
|
488
488
|
for (const finding of replanned.plan.findings) {
|
|
489
489
|
const previousFinding = previousFindings.get(finding.id);
|
|
490
490
|
const previousItem = previous.items[finding.id];
|
|
491
|
-
//
|
|
492
|
-
//
|
|
493
|
-
//
|
|
494
|
-
//
|
|
491
|
+
// A still-pending item carries no work to preserve, so it is re-minted from
|
|
492
|
+
// the fresh plan rather than carried forward. (This test used to also admit
|
|
493
|
+
// a pending item that held an `item_spec`; the document phase that produced
|
|
494
|
+
// one was dissolved by N-R13 and the field is gone, so the second condition
|
|
495
|
+
// could never be true and is not restated here.)
|
|
495
496
|
if (!previousFinding || !previousItem) {
|
|
496
497
|
continue;
|
|
497
498
|
}
|
|
498
|
-
if (previousItem.status === "pending"
|
|
499
|
+
if (previousItem.status === "pending") {
|
|
499
500
|
continue;
|
|
500
501
|
}
|
|
501
502
|
if (findingCarryForwardKey(previousFinding) !== findingCarryForwardKey(finding)) {
|
|
@@ -740,6 +741,11 @@ async function buildImplementDispatchStep(ctx) {
|
|
|
740
741
|
host_handoff: handoff.handoff_record,
|
|
741
742
|
});
|
|
742
743
|
}
|
|
744
|
+
// Name the runs this dispatch round relates to on the friction record (semantics:
|
|
745
|
+
// `FrictionRunLinks`). Each reference is sourced from the envelope that owns it — the
|
|
746
|
+
// persisted handoff record for the dispatch run, the step contract's own run id for
|
|
747
|
+
// the step — never synthesized from the other.
|
|
748
|
+
await linkFrictionRunIds(artifactsDir, stateRunId(state), { step_run_id: runId, dispatch_run_id: handoff.handoff_record.run_id }, "remediate-code");
|
|
743
749
|
// Ingest issues reached the PROMPT and nothing else — a channel that survives
|
|
744
750
|
// exactly as long as the host reads that one step. They are also the durable
|
|
745
751
|
// record of which submitted results were rejected and why, so they are logged
|
|
@@ -1254,112 +1260,6 @@ async function runReviewApprovalGate(root, artifactsDir, survivors, autonomous =
|
|
|
1254
1260
|
declined,
|
|
1255
1261
|
};
|
|
1256
1262
|
}
|
|
1257
|
-
// ── T1 slice 3b — lean-path light adversarial review gate ───────────────────────
|
|
1258
|
-
//
|
|
1259
|
-
// Mirrors the review-approval gate's emit→resume→consume idiom, but the step is
|
|
1260
|
-
// WORK (status "ready"), not a human decision — so it runs identically in
|
|
1261
|
-
// attended and autonomous modes (the host agent executes the light pass like any
|
|
1262
|
-
// phase). Fires (and at most once) only when the fast path is eligible.
|
|
1263
|
-
function leanLightReviewVerdictPath(artifactsDir) {
|
|
1264
|
-
return join(artifactsDir, "lean_light_review_verdict.json");
|
|
1265
|
-
}
|
|
1266
|
-
function leanLightReviewDecisionPath(artifactsDir) {
|
|
1267
|
-
return join(artifactsDir, "lean_light_review_decision.json");
|
|
1268
|
-
}
|
|
1269
|
-
/** Render the bounded light-adversarial-review prompt over the approved findings. */
|
|
1270
|
-
function leanLightReviewPrompt(approved, verdictPath) {
|
|
1271
|
-
const findingLines = approved
|
|
1272
|
-
.map((f) => {
|
|
1273
|
-
const files = (f.affected_files ?? [])
|
|
1274
|
-
.map((loc) => loc?.path)
|
|
1275
|
-
.filter((p) => Boolean(p))
|
|
1276
|
-
.join(", ");
|
|
1277
|
-
return `- \`${f.id}\` — ${f.title ?? "(untitled)"}${files ? ` (${files})` : ""}`;
|
|
1278
|
-
})
|
|
1279
|
-
.join("\n");
|
|
1280
|
-
const nextCommand = loaderCommand("next-step");
|
|
1281
|
-
return `# Lean fast path — light adversarial review
|
|
1282
|
-
|
|
1283
|
-
These findings cleared the simplicity gate and are headed for the lean fast path (straight to plan→implement, skipping the full contract pipeline). Before they are trusted, do ONE **lightweight adversarial pass** — the floor that replaces the full design loop here. This is proportionate, not an exhaustive counterexample search: remediation legitimately catches upstream audit errors, so nothing skips review entirely.
|
|
1284
|
-
|
|
1285
|
-
## Findings to review
|
|
1286
|
-
${findingLines || "_(none)_"}
|
|
1287
|
-
|
|
1288
|
-
## Your task
|
|
1289
|
-
Adopt a brief adversarial stance and ask, across the set:
|
|
1290
|
-
- Is any finding actually wrong, already-fixed, or not grounded in the cited code?
|
|
1291
|
-
- Would the proposed fix break a caller, an invariant, or an adjacent behavior?
|
|
1292
|
-
- Are any two of these coupled / sharing a file in a way that needs seam reconciliation (i.e. NOT really independent)?
|
|
1293
|
-
- Is anything subtler or more architectural than the simplicity gate assumed?
|
|
1294
|
-
|
|
1295
|
-
Write your verdict to exactly:
|
|
1296
|
-
|
|
1297
|
-
\`${verdictPath}\`
|
|
1298
|
-
|
|
1299
|
-
\`\`\`json
|
|
1300
|
-
{
|
|
1301
|
-
"schema_version": "${LEAN_LIGHT_REVIEW_SCHEMA_VERSION}",
|
|
1302
|
-
"disposition": "clear | escalate",
|
|
1303
|
-
"concerns": ["<required & non-empty when escalate; the concrete concern(s)>"],
|
|
1304
|
-
"created_at": "<ISO-8601>"
|
|
1305
|
-
}
|
|
1306
|
-
\`\`\`
|
|
1307
|
-
|
|
1308
|
-
- **clear** — the light pass surfaced no real concern; the lean path may proceed.
|
|
1309
|
-
- **escalate** — a genuine concern surfaced. This is evidence the change is harder than assessed; the run escalates to the full contract pipeline (full independent review). When in doubt, escalate — a wrong call costs extra pipeline work, never a skipped review.
|
|
1310
|
-
|
|
1311
|
-
After writing the verdict, run:
|
|
1312
|
-
|
|
1313
|
-
\`${nextCommand}\`
|
|
1314
|
-
`;
|
|
1315
|
-
}
|
|
1316
|
-
/**
|
|
1317
|
-
* The lean light-review gate. Idempotent across next-step calls: once a decision
|
|
1318
|
-
* is recorded it is consumed directly. Returns a halt step while awaiting the
|
|
1319
|
-
* host's verdict, then `clear` (proceed to the lean plan) or `escalate` (route to
|
|
1320
|
-
* the full pipeline with the concerns).
|
|
1321
|
-
*/
|
|
1322
|
-
async function runLeanLightReviewGate(root, artifactsDir, approved) {
|
|
1323
|
-
const decisionPath = leanLightReviewDecisionPath(artifactsDir);
|
|
1324
|
-
const existing = await readOptionalJsonFile(decisionPath);
|
|
1325
|
-
if (existing) {
|
|
1326
|
-
return existing.disposition === "clear"
|
|
1327
|
-
? { kind: "clear" }
|
|
1328
|
-
: { kind: "escalate", concerns: existing.concerns ?? [] };
|
|
1329
|
-
}
|
|
1330
|
-
const verdictPath = leanLightReviewVerdictPath(artifactsDir);
|
|
1331
|
-
if (!existsSync(verdictPath)) {
|
|
1332
|
-
const step = await writeCurrentStep({
|
|
1333
|
-
stepKind: "lean_light_review",
|
|
1334
|
-
status: "ready",
|
|
1335
|
-
runId: randomRunId("LEANREVIEW"),
|
|
1336
|
-
repoRoot: root,
|
|
1337
|
-
artifactsDir,
|
|
1338
|
-
prompt: leanLightReviewPrompt(approved, verdictPath),
|
|
1339
|
-
allowedCommands: [loaderCommand("next-step")],
|
|
1340
|
-
stopCondition: "Stop after writing the lean light-review verdict and running next-step.",
|
|
1341
|
-
artifactPaths: { lean_light_review_verdict: verdictPath },
|
|
1342
|
-
});
|
|
1343
|
-
return { kind: "halt", step };
|
|
1344
|
-
}
|
|
1345
|
-
// Verdict present → interpret + record a durable decision, then archive the
|
|
1346
|
-
// consumed verdict so the gate can never re-emit.
|
|
1347
|
-
const raw = await readOptionalJsonFile(verdictPath);
|
|
1348
|
-
const interp = interpretLeanLightReviewVerdict(raw);
|
|
1349
|
-
const record = {
|
|
1350
|
-
schema_version: LEAN_LIGHT_REVIEW_SCHEMA_VERSION,
|
|
1351
|
-
disposition: interp.disposition,
|
|
1352
|
-
concerns: interp.concerns,
|
|
1353
|
-
created_at: new Date().toISOString(),
|
|
1354
|
-
};
|
|
1355
|
-
await writeJsonFile(decisionPath, record);
|
|
1356
|
-
if (existsSync(verdictPath)) {
|
|
1357
|
-
await withFsRetry(() => rename(verdictPath, `${verdictPath}.consumed-${Date.now()}`));
|
|
1358
|
-
}
|
|
1359
|
-
return interp.disposition === "clear"
|
|
1360
|
-
? { kind: "clear" }
|
|
1361
|
-
: { kind: "escalate", concerns: interp.concerns };
|
|
1362
|
-
}
|
|
1363
1263
|
/**
|
|
1364
1264
|
* Re-emit the autonomous-run leftovers (findings left LIVE, neither auto-fixed
|
|
1365
1265
|
* nor durably rejected) as a standard, re-consumable audit deliverable pair:
|
|
@@ -1517,22 +1417,13 @@ async function handleReadyIntakeContractPipeline(root, artifactsDir, options, ru
|
|
|
1517
1417
|
}
|
|
1518
1418
|
// Persist the filter dispositions so coverage is built over the originals.
|
|
1519
1419
|
await persistReviewFilterDispositions(artifactsDir, originals, filter);
|
|
1520
|
-
//
|
|
1521
|
-
// fast-path folded into the self-scaling dial; its two mechanisms now live in
|
|
1522
|
-
// `riskSignal.ts` (light-review) + `contractPipeline.ts` (lean plan builder)).
|
|
1523
|
-
// A run skips the heavy contract DESIGN loop and
|
|
1524
|
-
// synthesizes the extracted plan directly IFF its effective risk tier is `low`;
|
|
1525
|
-
// the plan→implement→close machinery (per-node verify-before-merge + the final
|
|
1526
|
-
// whole-repo gate) is the retained safety net. Runs only here — on Path A
|
|
1527
|
-
// (structured_audit), the only intake with a pre-existing finding set to judge.
|
|
1528
|
-
//
|
|
1529
|
-
// First fold the APPROVED set's finding-level risk (grounding / confidence /
|
|
1420
|
+
// Fold the APPROVED set's finding-level risk (grounding / confidence /
|
|
1530
1421
|
// coupling / systemic / architecture / count — the finding-QUALITY dimension the
|
|
1531
1422
|
// intake path/breadth/intent signal doesn't see) INTO the shared risk signal as
|
|
1532
|
-
// escalate-on-evidence.
|
|
1533
|
-
//
|
|
1534
|
-
//
|
|
1535
|
-
//
|
|
1423
|
+
// escalate-on-evidence. The tier is the SINGLE classifier and the ONLY thing it
|
|
1424
|
+
// selects is DEPTH: every run enters the contract pipeline, and a `low` tier
|
|
1425
|
+
// traverses it shallowly (collapsed round-trips, light adversarial depth). There
|
|
1426
|
+
// is no second plan producer and no bypass — see COLLAPSE_GROUPS.
|
|
1536
1427
|
const findingEvidence = findingRiskEvidence(gate.approved);
|
|
1537
1428
|
let riskSignal = await readIntakeRiskSignal(artifactsDir);
|
|
1538
1429
|
if (findingEvidence && riskSignal) {
|
|
@@ -1544,62 +1435,6 @@ async function handleReadyIntakeContractPipeline(root, artifactsDir, options, ru
|
|
|
1544
1435
|
await writeIntakeRiskSignal(artifactsDir, riskSignal);
|
|
1545
1436
|
}
|
|
1546
1437
|
}
|
|
1547
|
-
// T1 slice 3b — the lean tier is NOT zero-scrutiny: a `low`-tier run first runs
|
|
1548
|
-
// a bounded LIGHT adversarial review over the approved findings (the floor,
|
|
1549
|
-
// never off — `adversarialDepthForTier("low") === "light"`). A clear verdict
|
|
1550
|
-
// proceeds to the lean plan; a verdict that surfaces a real concern escalates
|
|
1551
|
-
// the risk signal and routes to the full pipeline below.
|
|
1552
|
-
if (gate.approved.length > 0 && riskSignal?.tier === "low") {
|
|
1553
|
-
const review = await runLeanLightReviewGate(root, artifactsDir, gate.approved);
|
|
1554
|
-
if (review.kind === "halt") {
|
|
1555
|
-
return review.step;
|
|
1556
|
-
}
|
|
1557
|
-
if (review.kind === "escalate") {
|
|
1558
|
-
// Escalate-on-evidence: raise the signal to at least `medium` so the
|
|
1559
|
-
// full pipeline's adversarial depth is `full` (see slice 3a), then
|
|
1560
|
-
// fall through to the full pipeline.
|
|
1561
|
-
await writeIntakeRiskSignal(artifactsDir, escalateRiskSignal(riskSignal, {
|
|
1562
|
-
tier: "medium",
|
|
1563
|
-
reason: `lean light review surfaced a concern: ${review.concerns.join("; ")}`,
|
|
1564
|
-
}));
|
|
1565
|
-
runLogger.event({
|
|
1566
|
-
phase: "next-step",
|
|
1567
|
-
kind: "outcome",
|
|
1568
|
-
obligation: "lean_fast_path",
|
|
1569
|
-
note: `lean_fast_path_escalated concerns=${String(review.concerns.length)} ` +
|
|
1570
|
-
`detail=${review.concerns.join("; ")}`,
|
|
1571
|
-
});
|
|
1572
|
-
process.stderr.write(`[remediate-code] Lean light review escalated (${review.concerns.join("; ")}); routing to the full contract pipeline.\n`);
|
|
1573
|
-
}
|
|
1574
|
-
else {
|
|
1575
|
-
// Clear verdict → proceed with the lean plan.
|
|
1576
|
-
const leanPlan = buildLeanExtractedPlan(gate.approved, randomRunId("LEAN"));
|
|
1577
|
-
await writeJsonFile(intakePaths(artifactsDir).extractedPlan, leanPlan);
|
|
1578
|
-
runLogger.event({
|
|
1579
|
-
phase: "next-step",
|
|
1580
|
-
kind: "outcome",
|
|
1581
|
-
obligation: "lean_fast_path",
|
|
1582
|
-
note: `lean_fast_path_routed findings=${String(gate.approved.length)} ` +
|
|
1583
|
-
`rationale=${riskSignal.rationale.join("; ")}`,
|
|
1584
|
-
});
|
|
1585
|
-
process.stderr.write(`[remediate-code] Lean fast path (risk tier low): ${riskSignal.rationale.join("; ")}; light review clear. Routing to plan→implement.\n`);
|
|
1586
|
-
const planned = await handlePendingExtractedPlan(root, artifactsDir, { status: "pending" }, leanPlan, runLogger);
|
|
1587
|
-
if (planned) {
|
|
1588
|
-
return planned;
|
|
1589
|
-
}
|
|
1590
|
-
// Defensive: a deterministically-built lean plan should always
|
|
1591
|
-
// normalize. If it somehow didn't, handlePendingExtractedPlan removed
|
|
1592
|
-
// the file; fall through to the full pipeline (the safety net) rather
|
|
1593
|
-
// than stalling the run.
|
|
1594
|
-
runLogger.event({
|
|
1595
|
-
phase: "next-step",
|
|
1596
|
-
kind: "outcome",
|
|
1597
|
-
obligation: "lean_fast_path",
|
|
1598
|
-
note: "lean_fast_path_fallback plan failed to materialize; routing to the contract pipeline",
|
|
1599
|
-
});
|
|
1600
|
-
process.stderr.write("[remediate-code] Lean fast-path plan failed to materialize; falling back to the contract pipeline.\n");
|
|
1601
|
-
}
|
|
1602
|
-
}
|
|
1603
1438
|
// Seed the pipeline with the approved survivors only. When that set is
|
|
1604
1439
|
// narrower than the originals (anything filtered or declined), route the
|
|
1605
1440
|
// seed AND the pipeline's source inputs at a filtered file so a removed
|
|
@@ -1855,13 +1690,20 @@ async function applyPlanClarificationResolution(root, artifactsDir, state, store
|
|
|
1855
1690
|
// their questions unanswered. Load-bearing now that the round runs at the
|
|
1856
1691
|
// DRAINED end of the implement phase, where `remainingPending` is normally
|
|
1857
1692
|
// false — the fall-through it guards is the common case, not a corner.
|
|
1693
|
+
// INV-RS-10 / OBL-seam-prep-remediate-core-inv-1: a resolution that disposes
|
|
1694
|
+
// of every remaining item does NOT write `closing` here. Writing it directly
|
|
1695
|
+
// satisfied the all_terminal derive (`status !== "closing"`) on the very next
|
|
1696
|
+
// scan, so the tool-owned final gate never ran for this arrival at close.
|
|
1697
|
+
// Landing in `implementing` lets the single all-terminal → closing funnel
|
|
1698
|
+
// (handleAllTerminalTransition) run the gate, exactly as every other closing
|
|
1699
|
+
// transition does; `closing_plan` is stamped by that funnel's successor, not
|
|
1700
|
+
// pre-stamped here (the gate-red pause must not leave a half-prepared close).
|
|
1858
1701
|
state.status = remainingPending
|
|
1859
1702
|
? "implementing"
|
|
1860
1703
|
: hasUnansweredClarification(state)
|
|
1861
1704
|
? "waiting_for_clarification"
|
|
1862
|
-
: "
|
|
1705
|
+
: "implementing";
|
|
1863
1706
|
state.clarifications = [];
|
|
1864
|
-
state.closing_plan ??= { action: "none" };
|
|
1865
1707
|
await store.saveState(state);
|
|
1866
1708
|
return { kind: "applied", state };
|
|
1867
1709
|
}
|
|
@@ -2132,9 +1974,8 @@ async function handlePlanning(root, artifactsDir, state, store) {
|
|
|
2132
1974
|
if (halt)
|
|
2133
1975
|
return { kind: "emit", step: halt };
|
|
2134
1976
|
}
|
|
2135
|
-
// Document phase dissolved: planning transitions directly to
|
|
2136
|
-
//
|
|
2137
|
-
// uses finding context directly when absent.
|
|
1977
|
+
// Document phase dissolved (N-R13): planning transitions directly to
|
|
1978
|
+
// implementing, and the host workload reads finding context directly.
|
|
2138
1979
|
const implementBlocks = implementableBlocks(state);
|
|
2139
1980
|
if (implementBlocks.length > 0) {
|
|
2140
1981
|
if (state.plan) {
|
|
@@ -2197,11 +2038,29 @@ async function handlePlanning(root, artifactsDir, state, store) {
|
|
|
2197
2038
|
await store.saveState(state);
|
|
2198
2039
|
return { kind: "transition", state };
|
|
2199
2040
|
}
|
|
2200
|
-
async function handleImplementing(root, artifactsDir, state, runLogger, store) {
|
|
2041
|
+
async function handleImplementing(root, artifactsDir, state, runLogger, store, options) {
|
|
2201
2042
|
const triageStart = Date.now();
|
|
2202
2043
|
runLogger.event({ phase: "next-step", kind: "executor_start", obligation: state.status, note: "triage" });
|
|
2203
2044
|
const triaged = await runTriagePhase(state, { root, artifactsDir });
|
|
2204
2045
|
runLogger.event({ phase: "next-step", kind: "executor_end", obligation: state.status, note: "triage", duration_ms: Date.now() - triageStart });
|
|
2046
|
+
// INV-RS-10 / OBL-seam-prep-remediate-core-inv-1..4 (the CP-NODE-3 review
|
|
2047
|
+
// finding): this is the ONE caller of runTriagePhase, shared by the
|
|
2048
|
+
// `implementing` and `triage` obligations, so it is also THE single seam
|
|
2049
|
+
// where a triage closing-intent crosses into close. Persisting
|
|
2050
|
+
// `status: "closing"` directly would satisfy the closing obligation on the
|
|
2051
|
+
// next scan and preempt the all-terminal → closing funnel's tool-owned final
|
|
2052
|
+
// gate — exactly the defect class the finding named. So the intent is
|
|
2053
|
+
// persisted under the NON-closing `triage` status first (keeping every
|
|
2054
|
+
// preparation runTriagePhase made — halted items already converted to
|
|
2055
|
+
// `abandoned`, `closing_context` stamped — so a gate-RED pause, which persists
|
|
2056
|
+
// nothing itself, cannot lose them: re-entering triage with zero blocked items
|
|
2057
|
+
// re-derives this exact intent instead of auto-retrying halted work), and only
|
|
2058
|
+
// then handed to the funnel, which runs the gate and owns the closing stamp.
|
|
2059
|
+
if (triaged.status === "closing") {
|
|
2060
|
+
const prepared = { ...triaged, status: "triage" };
|
|
2061
|
+
await store.saveState(prepared);
|
|
2062
|
+
return handleAllTerminalTransition(root, artifactsDir, prepared, store, options, runLogger);
|
|
2063
|
+
}
|
|
2205
2064
|
await store.saveState(triaged);
|
|
2206
2065
|
return { kind: "transition", state: triaged };
|
|
2207
2066
|
}
|
|
@@ -2763,7 +2622,7 @@ To confirm, write the final checkpoint to:
|
|
|
2763
2622
|
"intent_summary": "${draft.intent_summary ?? "<the goal>"}",
|
|
2764
2623
|
"free_form_intent": "<optional: additional guidance>",
|
|
2765
2624
|
"filters": ${JSON.stringify(draft.filters ?? {}, null, 2)},
|
|
2766
|
-
"excluded_scope": [],
|
|
2625
|
+
"excluded_scope": [{ "path": "<path or prefix>", "reason": "<why>" }],
|
|
2767
2626
|
"must_not_touch": []
|
|
2768
2627
|
}
|
|
2769
2628
|
\`\`\`
|
|
@@ -2846,7 +2705,11 @@ Once the file is written, run:
|
|
|
2846
2705
|
// on an LLM-authored free-text `intent_interpretation`.
|
|
2847
2706
|
/** Sidecar artifact recording the deterministic interpretation of free_form_intent. */
|
|
2848
2707
|
export const INTENT_INTERPRETATION_FILENAME = "intent-interpretation.json";
|
|
2849
|
-
|
|
2708
|
+
// v1alpha2: unencodable_clauses carries identity-keyed records (clause_id +
|
|
2709
|
+
// checkpoint_question), not bare strings — the shape the blocking consumer
|
|
2710
|
+
// reads. A v1alpha1 sidecar (string[]) is stale and is repaired by
|
|
2711
|
+
// re-derivation; it had no readers, so no migration path is owed.
|
|
2712
|
+
export const INTENT_INTERPRETATION_SCHEMA_VERSION = "remediate-code-intent-interpretation/v1alpha2";
|
|
2850
2713
|
/**
|
|
2851
2714
|
* Interpret a confirmed checkpoint's `free_form_intent` via the shared
|
|
2852
2715
|
* deterministic interpreter and persist the structured signals to a sidecar
|
|
@@ -2866,33 +2729,99 @@ runLogger) {
|
|
|
2866
2729
|
if (typeof raw !== "string" || raw.trim().length === 0)
|
|
2867
2730
|
return null;
|
|
2868
2731
|
const interpreted = interpretFreeFormIntent(raw);
|
|
2732
|
+
// The clause pipeline (interpretIntent) owns identity + blocking questions;
|
|
2733
|
+
// the hint interpreter above owns lens/priority/scope signals. Both are
|
|
2734
|
+
// deterministic draws over the same input.
|
|
2735
|
+
const clauseResult = interpretIntent(raw);
|
|
2736
|
+
const unencodable_clauses = [];
|
|
2737
|
+
for (const clause of clauseResult.clauses) {
|
|
2738
|
+
if (clause.encodable || !clause.checkpoint_question)
|
|
2739
|
+
continue;
|
|
2740
|
+
unencodable_clauses.push({
|
|
2741
|
+
clause_id: clause.clause_id,
|
|
2742
|
+
text: clause.text,
|
|
2743
|
+
checkpoint_question: clause.checkpoint_question,
|
|
2744
|
+
});
|
|
2745
|
+
}
|
|
2869
2746
|
const persisted = {
|
|
2870
2747
|
schema_version: INTENT_INTERPRETATION_SCHEMA_VERSION,
|
|
2871
2748
|
interpreted,
|
|
2872
|
-
unencodable_clauses
|
|
2749
|
+
unencodable_clauses,
|
|
2873
2750
|
created_at: new Date().toISOString(),
|
|
2874
2751
|
};
|
|
2875
2752
|
try {
|
|
2876
2753
|
await writeJsonFile(join(artifactsDir, INTENT_INTERPRETATION_FILENAME), persisted);
|
|
2877
2754
|
}
|
|
2878
2755
|
catch {
|
|
2879
|
-
// Best-effort
|
|
2756
|
+
// Best-effort WRITE: a write failure must never crash the decide loop.
|
|
2757
|
+
// Enforcement does not depend on it — the consumer re-derives when the
|
|
2758
|
+
// sidecar is missing (readOrRepairIntentInterpretation).
|
|
2880
2759
|
}
|
|
2881
|
-
if (
|
|
2760
|
+
if (unencodable_clauses.length > 0) {
|
|
2761
|
+
const clauseTexts = unencodable_clauses.map((c) => c.text);
|
|
2882
2762
|
runLogger?.event({
|
|
2883
2763
|
phase: "next-step",
|
|
2884
2764
|
kind: "outcome",
|
|
2885
2765
|
obligation: "interpret_intent",
|
|
2886
|
-
note: `intent_unencodable_clauses count=${String(
|
|
2887
|
-
`clauses=${
|
|
2766
|
+
note: `intent_unencodable_clauses count=${String(unencodable_clauses.length)} ` +
|
|
2767
|
+
`clauses=${clauseTexts.join("; ")}`,
|
|
2888
2768
|
});
|
|
2889
|
-
process.stderr.write(`[remediate-code] free_form_intent: ${
|
|
2890
|
-
`clause(s) could not be encoded as lens/priority/scope signals and
|
|
2891
|
-
`
|
|
2892
|
-
`${
|
|
2769
|
+
process.stderr.write(`[remediate-code] free_form_intent: ${unencodable_clauses.length} ` +
|
|
2770
|
+
`clause(s) could not be encoded as lens/priority/scope signals and ` +
|
|
2771
|
+
`block planning until answered via constraint_clauses: ` +
|
|
2772
|
+
`${clauseTexts.join("; ")}\n`);
|
|
2893
2773
|
}
|
|
2894
2774
|
return persisted;
|
|
2895
2775
|
}
|
|
2776
|
+
/**
|
|
2777
|
+
* Read the persisted intent interpretation — the LOAD-BEARING input to the
|
|
2778
|
+
* constraint-clause gate — repairing it by re-derivation when it is missing,
|
|
2779
|
+
* unparseable, or carries a stale schema_version. Returns null only when
|
|
2780
|
+
* there is nothing to interpret (no confirmed checkpoint / empty intent).
|
|
2781
|
+
*/
|
|
2782
|
+
export async function readOrRepairIntentInterpretation(artifactsDir, checkpoint, runLogger) {
|
|
2783
|
+
if (!checkpoint || checkpoint.confirmed_by !== "host")
|
|
2784
|
+
return null;
|
|
2785
|
+
const raw = checkpoint.free_form_intent;
|
|
2786
|
+
if (typeof raw !== "string" || raw.trim().length === 0)
|
|
2787
|
+
return null;
|
|
2788
|
+
const sidecarPath = join(artifactsDir, INTENT_INTERPRETATION_FILENAME);
|
|
2789
|
+
try {
|
|
2790
|
+
const parsed = parsePersistedIntentInterpretation(JSON.parse(await readFile(sidecarPath, "utf8")));
|
|
2791
|
+
if (parsed)
|
|
2792
|
+
return parsed;
|
|
2793
|
+
}
|
|
2794
|
+
catch {
|
|
2795
|
+
// Missing or unreadable — fall through to repair.
|
|
2796
|
+
}
|
|
2797
|
+
return interpretConfirmedCheckpointIntent(artifactsDir, checkpoint, runLogger);
|
|
2798
|
+
}
|
|
2799
|
+
/** Pure shape gate for the sidecar: current version + record-shaped clauses, else null. */
|
|
2800
|
+
function parsePersistedIntentInterpretation(parsed) {
|
|
2801
|
+
if (isRecord(parsed) &&
|
|
2802
|
+
parsed.schema_version === INTENT_INTERPRETATION_SCHEMA_VERSION &&
|
|
2803
|
+
Array.isArray(parsed.unencodable_clauses) &&
|
|
2804
|
+
parsed.unencodable_clauses.every((c) => isRecord(c) &&
|
|
2805
|
+
typeof c.clause_id === "string" &&
|
|
2806
|
+
typeof c.text === "string" &&
|
|
2807
|
+
typeof c.checkpoint_question === "string")) {
|
|
2808
|
+
return parsed;
|
|
2809
|
+
}
|
|
2810
|
+
return null;
|
|
2811
|
+
}
|
|
2812
|
+
/**
|
|
2813
|
+
* Sync sidecar read for the obligation's derive scan. Returns the persisted
|
|
2814
|
+
* interpretation, or null when the sidecar is missing, unreadable, or stale —
|
|
2815
|
+
* the execute path repairs via {@link readOrRepairIntentInterpretation}.
|
|
2816
|
+
*/
|
|
2817
|
+
function readPersistedIntentInterpretationSync(sidecarPath) {
|
|
2818
|
+
try {
|
|
2819
|
+
return parsePersistedIntentInterpretation(JSON.parse(readFileSync(sidecarPath, "utf8")));
|
|
2820
|
+
}
|
|
2821
|
+
catch {
|
|
2822
|
+
return null;
|
|
2823
|
+
}
|
|
2824
|
+
}
|
|
2896
2825
|
/**
|
|
2897
2826
|
* Narrow a nullable engine state to non-null inside an executor whose `derive`
|
|
2898
2827
|
* only marks it actionable when the state is present — a violation is an engine
|
|
@@ -3020,19 +2949,81 @@ function buildPreIntakeObligations(ctx, snapshot) {
|
|
|
3020
2949
|
},
|
|
3021
2950
|
{
|
|
3022
2951
|
// Past the intent gate: interpret the confirmed checkpoint's
|
|
3023
|
-
// free_form_intent once (INV-S04)
|
|
3024
|
-
//
|
|
3025
|
-
//
|
|
2952
|
+
// free_form_intent once (INV-S04), persist the structured signals, and
|
|
2953
|
+
// ENFORCE the unencodable-clause contract: an unanswered clause blocks
|
|
2954
|
+
// the decide loop until the host resolves it via a `constraint_clauses`
|
|
2955
|
+
// entry on the checkpoint (CE-004, identity-keyed — the shared matcher
|
|
2956
|
+
// in audit-tools/shared intent/constraintClauses.ts, the same core the
|
|
2957
|
+
// audit gate uses). The PERSISTED sidecar is the consumed input; a
|
|
2958
|
+
// missing or stale sidecar is repaired by re-derivation, never skipped.
|
|
3026
2959
|
id: "interpret_intent",
|
|
3027
|
-
derive: () =>
|
|
3028
|
-
|
|
3029
|
-
|
|
3030
|
-
|
|
3031
|
-
|
|
3032
|
-
|
|
2960
|
+
derive: () => {
|
|
2961
|
+
if (existingCheckpoint?.confirmed_by !== "host" ||
|
|
2962
|
+
typeof existingCheckpoint.free_form_intent !== "string" ||
|
|
2963
|
+
existingCheckpoint.free_form_intent.trim().length === 0) {
|
|
2964
|
+
return "satisfied";
|
|
2965
|
+
}
|
|
2966
|
+
const sidecar = readPersistedIntentInterpretationSync(interpretationPath);
|
|
2967
|
+
if (sidecar === null)
|
|
2968
|
+
return "missing";
|
|
2969
|
+
return unresolvedFromClauses(sidecar.unencodable_clauses, existingCheckpoint)
|
|
2970
|
+
.length > 0
|
|
2971
|
+
? "missing"
|
|
2972
|
+
: "satisfied";
|
|
2973
|
+
},
|
|
3033
2974
|
execute: async (state, c) => {
|
|
3034
|
-
await
|
|
3035
|
-
|
|
2975
|
+
const persisted = await readOrRepairIntentInterpretation(artifactsDir, existingCheckpoint, c.runLogger);
|
|
2976
|
+
const unresolved = persisted
|
|
2977
|
+
? unresolvedFromClauses(persisted.unencodable_clauses, existingCheckpoint)
|
|
2978
|
+
: [];
|
|
2979
|
+
if (unresolved.length === 0)
|
|
2980
|
+
return { kind: "transition", state };
|
|
2981
|
+
const nextCommand = loaderCommand("next-step");
|
|
2982
|
+
const checkpointPath = join(artifactsDir, "intent_checkpoint.json");
|
|
2983
|
+
const clauseLines = unresolved
|
|
2984
|
+
.map((clause) => `- **${clause.clause_id}** — "${clause.text}"\n Question: ${clause.checkpoint_question}`)
|
|
2985
|
+
.join("\n");
|
|
2986
|
+
const prompt = `
|
|
2987
|
+
# Resolve Free-Form Intent Constraints
|
|
2988
|
+
|
|
2989
|
+
${unresolved.length} clause(s) of the confirmed checkpoint's \`free_form_intent\` could
|
|
2990
|
+
not be encoded as lens/priority/scope signals. Each needs an explicit answer
|
|
2991
|
+
before planning proceeds — an unanswered clause would otherwise be silently
|
|
2992
|
+
dropped.
|
|
2993
|
+
|
|
2994
|
+
${clauseLines}
|
|
2995
|
+
|
|
2996
|
+
Answer each clause by adding a \`constraint_clauses\` entry to
|
|
2997
|
+
\`intent_checkpoint.json\` (keep the exact \`clause_id\` — answers are keyed on
|
|
2998
|
+
clause identity, not on the question text):
|
|
2999
|
+
|
|
3000
|
+
\`\`\`json
|
|
3001
|
+
"constraint_clauses": [
|
|
3002
|
+
{ "clause_id": "<the clause_id above>", "text": "<the clause text>", "checkpoint_question": "<the question above>", "host_answer": "<how to apply this constraint>" }
|
|
3003
|
+
]
|
|
3004
|
+
\`\`\`
|
|
3005
|
+
|
|
3006
|
+
Then run:
|
|
3007
|
+
|
|
3008
|
+
\`${nextCommand}\`
|
|
3009
|
+
`;
|
|
3010
|
+
return {
|
|
3011
|
+
kind: "emit",
|
|
3012
|
+
step: await writeCurrentStep({
|
|
3013
|
+
stepKind: "confirm_intent",
|
|
3014
|
+
status: "blocked",
|
|
3015
|
+
runId: stateRunId(state),
|
|
3016
|
+
repoRoot: c.root,
|
|
3017
|
+
artifactsDir,
|
|
3018
|
+
prompt,
|
|
3019
|
+
allowedCommands: [nextCommand],
|
|
3020
|
+
stopCondition: "Stop after adding constraint_clauses answers to intent_checkpoint.json and running next-step.",
|
|
3021
|
+
artifactPaths: {
|
|
3022
|
+
intent_checkpoint: checkpointPath,
|
|
3023
|
+
intent_interpretation: interpretationPath,
|
|
3024
|
+
},
|
|
3025
|
+
}),
|
|
3026
|
+
};
|
|
3036
3027
|
},
|
|
3037
3028
|
},
|
|
3038
3029
|
{
|
|
@@ -3261,13 +3252,13 @@ function buildMainObligations(ctx) {
|
|
|
3261
3252
|
return { kind: "transition", state: s };
|
|
3262
3253
|
}
|
|
3263
3254
|
}
|
|
3264
|
-
return handleImplementing(root, artifactsDir, s, runLogger, store);
|
|
3255
|
+
return handleImplementing(root, artifactsDir, s, runLogger, store, options);
|
|
3265
3256
|
},
|
|
3266
3257
|
},
|
|
3267
3258
|
{
|
|
3268
3259
|
id: "triage",
|
|
3269
3260
|
derive: (state) => (state?.status === "triage" ? "missing" : "satisfied"),
|
|
3270
|
-
execute: async (state) => handleImplementing(root, artifactsDir, requireState(state), runLogger, store),
|
|
3261
|
+
execute: async (state) => handleImplementing(root, artifactsDir, requireState(state), runLogger, store, options),
|
|
3271
3262
|
},
|
|
3272
3263
|
{
|
|
3273
3264
|
// planning with zero documentable findings is a user question, not a
|