audit-tools 0.34.29 → 0.34.31
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 +32 -11
- package/dist/audit/cli/args.d.ts +2 -2
- package/dist/audit/cli/args.d.ts.map +1 -1
- package/dist/audit/cli/args.js.map +1 -1
- package/dist/audit/cli/dispatch/paths.d.ts.map +1 -1
- package/dist/audit/cli/dispatch/paths.js +3 -9
- package/dist/audit/cli/dispatch/paths.js.map +1 -1
- package/dist/audit/cli/prepareDispatchCommand.d.ts +32 -0
- package/dist/audit/cli/prepareDispatchCommand.d.ts.map +1 -1
- package/dist/audit/cli/prepareDispatchCommand.js +39 -16
- package/dist/audit/cli/prepareDispatchCommand.js.map +1 -1
- package/dist/audit/cli/providerConfirmationStep.d.ts.map +1 -1
- package/dist/audit/cli/providerConfirmationStep.js +19 -7
- package/dist/audit/cli/providerConfirmationStep.js.map +1 -1
- package/dist/audit/cli/quotaCommand.d.ts.map +1 -1
- package/dist/audit/cli/quotaCommand.js +8 -20
- package/dist/audit/cli/quotaCommand.js.map +1 -1
- package/dist/audit/extractors/analyzers/typescript.d.ts.map +1 -1
- package/dist/audit/extractors/analyzers/typescript.js +3 -2
- package/dist/audit/extractors/analyzers/typescript.js.map +1 -1
- package/dist/audit/extractors/graph.d.ts.map +1 -1
- package/dist/audit/extractors/graph.js +4 -5
- package/dist/audit/extractors/graph.js.map +1 -1
- package/dist/audit/io/artifacts.d.ts +1 -12
- package/dist/audit/io/artifacts.d.ts.map +1 -1
- package/dist/audit/io/artifacts.js +9 -39
- package/dist/audit/io/artifacts.js.map +1 -1
- package/dist/audit/orchestrator/designReviewSnapshot.d.ts.map +1 -1
- package/dist/audit/orchestrator/designReviewSnapshot.js +4 -2
- package/dist/audit/orchestrator/designReviewSnapshot.js.map +1 -1
- package/dist/audit/orchestrator/intakeExecutors.d.ts.map +1 -1
- package/dist/audit/orchestrator/intakeExecutors.js +59 -3
- package/dist/audit/orchestrator/intakeExecutors.js.map +1 -1
- package/dist/remediate/contractPipeline/reviewSnapshot.d.ts +8 -0
- package/dist/remediate/contractPipeline/reviewSnapshot.d.ts.map +1 -1
- package/dist/remediate/contractPipeline/reviewSnapshot.js +10 -2
- package/dist/remediate/contractPipeline/reviewSnapshot.js.map +1 -1
- package/dist/remediate/contractPipeline/testPlanCarry.d.ts +7 -1
- package/dist/remediate/contractPipeline/testPlanCarry.d.ts.map +1 -1
- package/dist/remediate/contractPipeline/testPlanCarry.js +9 -3
- package/dist/remediate/contractPipeline/testPlanCarry.js.map +1 -1
- package/dist/remediate/phases/close.d.ts.map +1 -1
- package/dist/remediate/phases/close.js +10 -8
- package/dist/remediate/phases/close.js.map +1 -1
- package/dist/remediate/phases/plan.js +2 -2
- package/dist/remediate/phases/plan.js.map +1 -1
- package/dist/remediate/riskSignal.d.ts.map +1 -1
- package/dist/remediate/riskSignal.js +5 -2
- package/dist/remediate/riskSignal.js.map +1 -1
- package/dist/remediate/state/itemStatus.d.ts +26 -3
- package/dist/remediate/state/itemStatus.d.ts.map +1 -1
- package/dist/remediate/state/itemStatus.js +56 -18
- package/dist/remediate/state/itemStatus.js.map +1 -1
- package/dist/remediate/state/store.d.ts.map +1 -1
- package/dist/remediate/state/store.js +18 -0
- package/dist/remediate/state/store.js.map +1 -1
- package/dist/remediate/state/types.d.ts +18 -42
- package/dist/remediate/state/types.d.ts.map +1 -1
- package/dist/remediate/state/types.js +5 -3
- package/dist/remediate/state/types.js.map +1 -1
- package/dist/remediate/steps/contractPipeline.d.ts +1 -1
- package/dist/remediate/steps/contractPipeline.d.ts.map +1 -1
- package/dist/remediate/steps/contractPipeline.js +7 -1
- package/dist/remediate/steps/contractPipeline.js.map +1 -1
- package/dist/remediate/steps/dispatch/implementPrompt.d.ts +2 -2
- package/dist/remediate/steps/dispatch/implementPrompt.d.ts.map +1 -1
- package/dist/remediate/steps/dispatch/marshal.d.ts.map +1 -1
- package/dist/remediate/steps/dispatch/marshal.js +34 -16
- package/dist/remediate/steps/dispatch/marshal.js.map +1 -1
- package/dist/remediate/steps/dispatch/worktreeLifecycle.d.ts.map +1 -1
- package/dist/remediate/steps/dispatch/worktreeLifecycle.js +10 -7
- package/dist/remediate/steps/dispatch/worktreeLifecycle.js.map +1 -1
- package/dist/remediate/steps/finalGate.d.ts.map +1 -1
- package/dist/remediate/steps/finalGate.js +11 -4
- package/dist/remediate/steps/finalGate.js.map +1 -1
- package/dist/remediate/steps/nextStep.d.ts +7 -2
- package/dist/remediate/steps/nextStep.d.ts.map +1 -1
- package/dist/remediate/steps/nextStep.js +99 -35
- package/dist/remediate/steps/nextStep.js.map +1 -1
- package/dist/remediate/steps/stepUtils.d.ts +23 -0
- package/dist/remediate/steps/stepUtils.d.ts.map +1 -1
- package/dist/remediate/steps/stepUtils.js +55 -1
- package/dist/remediate/steps/stepUtils.js.map +1 -1
- package/dist/remediate/steps/types.d.ts +21 -0
- 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 +9 -0
- package/dist/remediate/validation/artifacts.js.map +1 -1
- package/dist/remediate/validation/contractPipelineGates.d.ts +7 -3
- package/dist/remediate/validation/contractPipelineGates.d.ts.map +1 -1
- package/dist/remediate/validation/contractPipelineGates.js +9 -21
- package/dist/remediate/validation/contractPipelineGates.js.map +1 -1
- package/dist/shared/constitutionalDocPaths.d.ts +13 -0
- package/dist/shared/constitutionalDocPaths.d.ts.map +1 -0
- package/dist/shared/constitutionalDocPaths.js +73 -0
- package/dist/shared/constitutionalDocPaths.js.map +1 -0
- package/dist/shared/index.d.ts +5 -2
- package/dist/shared/index.d.ts.map +1 -1
- package/dist/shared/index.js +8 -1
- package/dist/shared/index.js.map +1 -1
- package/dist/shared/io/frictionCapture.d.ts.map +1 -1
- package/dist/shared/io/frictionCapture.js +6 -1
- package/dist/shared/io/frictionCapture.js.map +1 -1
- package/dist/shared/io/pathContainment.d.ts +46 -0
- package/dist/shared/io/pathContainment.d.ts.map +1 -0
- package/dist/shared/io/pathContainment.js +40 -0
- package/dist/shared/io/pathContainment.js.map +1 -0
- package/dist/shared/io/schemaVersion.d.ts +67 -0
- package/dist/shared/io/schemaVersion.d.ts.map +1 -0
- package/dist/shared/io/schemaVersion.js +87 -0
- package/dist/shared/io/schemaVersion.js.map +1 -0
- package/dist/shared/providers/auditorSources.d.ts +9 -1
- package/dist/shared/providers/auditorSources.d.ts.map +1 -1
- package/dist/shared/providers/auditorSources.js +46 -2
- package/dist/shared/providers/auditorSources.js.map +1 -1
- package/dist/shared/providers/claudeWorkerProvider.d.ts +0 -1
- package/dist/shared/providers/claudeWorkerProvider.d.ts.map +1 -1
- package/dist/shared/providers/claudeWorkerProvider.js +4 -2
- package/dist/shared/providers/claudeWorkerProvider.js.map +1 -1
- package/dist/shared/providers/openAiCompatibleProvider.d.ts.map +1 -1
- package/dist/shared/providers/openAiCompatibleProvider.js +47 -10
- package/dist/shared/providers/openAiCompatibleProvider.js.map +1 -1
- package/dist/shared/providers/sharedProviderConfirmation.d.ts +42 -1
- package/dist/shared/providers/sharedProviderConfirmation.d.ts.map +1 -1
- package/dist/shared/providers/sharedProviderConfirmation.js +197 -3
- package/dist/shared/providers/sharedProviderConfirmation.js.map +1 -1
- package/dist/shared/quota/coverage.js +1 -1
- package/dist/shared/repair/brokeredDispatch.d.ts.map +1 -1
- package/dist/shared/repair/brokeredDispatch.js +10 -48
- package/dist/shared/repair/brokeredDispatch.js.map +1 -1
- package/dist/shared/types/auditorDescriptor.d.ts +8 -2
- package/dist/shared/types/auditorDescriptor.d.ts.map +1 -1
- package/dist/shared/types/auditorDescriptor.js.map +1 -1
- package/dist/shared/types/sessionConfig.d.ts +25 -1
- package/dist/shared/types/sessionConfig.d.ts.map +1 -1
- package/dist/shared/types/sessionConfig.js +4 -0
- package/dist/shared/types/sessionConfig.js.map +1 -1
- package/dist/shared/validation/findingGrounding.d.ts +19 -7
- package/dist/shared/validation/findingGrounding.d.ts.map +1 -1
- package/dist/shared/validation/findingGrounding.js +22 -10
- package/dist/shared/validation/findingGrounding.js.map +1 -1
- package/docs/audit-pkg/operator-guide.md +19 -9
- package/docs/audit-pkg/release.md +4 -0
- package/package.json +10 -3
- package/scripts/audit/postinstall.mjs +16 -1
- package/scripts/remediate/postinstall.mjs +17 -1
- package/dist/remediate/coverage/findingLedger.d.ts +0 -99
- package/dist/remediate/coverage/findingLedger.d.ts.map +0 -1
- package/dist/remediate/coverage/findingLedger.js +0 -110
- package/dist/remediate/coverage/findingLedger.js.map +0 -1
|
@@ -3,7 +3,7 @@ import { existsSync, statSync } from "node:fs";
|
|
|
3
3
|
import { mkdir, readFile, rename } from "node:fs/promises";
|
|
4
4
|
import { dirname, join, resolve } from "node:path";
|
|
5
5
|
import { StateStore } from "../state/store.js";
|
|
6
|
-
import { readConfirmedDispatchPolicy, readConfirmedCapabilityRanks, resolveDispatchExclusion, readOptionalJsonFile, readValidatedRepoSessionIntent, stagedAndUntracked, writeJsonFile, writeTextFile, buildAuditDeliverablePair, formatValidationIssues, isRecord, withFsRetry, RunLogger, DISPATCH_PROMPT_HANDOFF_NOTE, renderHostScratchNote, hostScratchDir, renderQuotaCoverageNudge, renderTokenBudgetView, coerceJsonObjectArg, driveRolling, resolveLedgerBudgets, setQuotaStateDir, detectHostDispatchWall, admissionBlockedOnBudget, classifyEmptyGrantCause, reconcileAdmissionLeasesFromQuotaFile, buildQuotaPausedTerminal, interpretFreeFormIntent, advance, decideFrictionTriage, buildFrictionTriageBlock, planHybridDispatch, readSettledPools, addSettledPool, isPoolSettlingOutcome, isInProcessWorkerProvider, sourceByPoolId, classifyProvider, selectDispatchDriver, renderDispatchDriverInstruction, HostSessionQuotaSource, quotaPoolKey, captureStepBoundaryFriction, captureZeroCapacityFriction, captureCostDriftFriction, captureCreditExhaustionFriction, captureQuotaUnclassifiedFriction, captureModelUnavailableFriction, capturePacketTooLargeFriction, createDispatchDecisionLog, LENSES, SEVERITIES, resolveHostProviderName, resolveHostDispatchProviderName, resolveHostDispatchCapability as sharedResolveHostDispatchCapability, resolveAutonomousMode, resolveRollingEngineFlag, DEFAULT_CONTEXT_TOKENS } from "audit-tools/shared";
|
|
6
|
+
import { discardOnSchemaVersionMismatch, readConfirmedDispatchPolicy, readConfirmedCapabilityRanks, resolveDispatchExclusion, readOptionalJsonFile, readValidatedRepoSessionIntent, stagedAndUntracked, writeJsonFile, writeTextFile, buildAuditDeliverablePair, formatValidationIssues, isRecord, withFsRetry, RunLogger, DISPATCH_PROMPT_HANDOFF_NOTE, renderHostScratchNote, hostScratchDir, renderQuotaCoverageNudge, renderTokenBudgetView, coerceJsonObjectArg, driveRolling, resolveLedgerBudgets, setQuotaStateDir, detectHostDispatchWall, admissionBlockedOnBudget, classifyEmptyGrantCause, reconcileAdmissionLeasesFromQuotaFile, buildQuotaPausedTerminal, interpretFreeFormIntent, advance, decideFrictionTriage, buildFrictionTriageBlock, planHybridDispatch, readSettledPools, addSettledPool, isPoolSettlingOutcome, isInProcessWorkerProvider, sourceByPoolId, classifyProvider, selectDispatchDriver, renderDispatchDriverInstruction, HostSessionQuotaSource, quotaPoolKey, captureStepBoundaryFriction, captureZeroCapacityFriction, captureCostDriftFriction, captureCreditExhaustionFriction, captureQuotaUnclassifiedFriction, captureModelUnavailableFriction, capturePacketTooLargeFriction, createDispatchDecisionLog, LENSES, SEVERITIES, resolveHostProviderName, resolveHostDispatchProviderName, resolveHostDispatchCapability as sharedResolveHostDispatchCapability, resolveAutonomousMode, resolveRollingEngineFlag, DEFAULT_CONTEXT_TOKENS } from "audit-tools/shared";
|
|
7
7
|
import { readRemediationAccessMemory, computeBlockContinuityScores } from "../state/accessMemory.js";
|
|
8
8
|
import { applyPlanPipeline, buildCoverageLedger } from "../phases/plan.js";
|
|
9
9
|
import { groundExtractedFindings } from "../phases/grounding.js";
|
|
@@ -18,7 +18,7 @@ import { claimWithBackoff, withClaimHeartbeat } from "../../shared/quota/claimLe
|
|
|
18
18
|
import { AUDIT_FINDINGS_FILENAME, AUDIT_REPORT_FILENAME, auditArtifactsDir, auditFindingsPath, auditReportPath, auditToolsDir, nodeClaimsPath, promotedAuditFindingsPath, promotedAuditReportPath, remediationArtifactsDir, } from "../../shared/io/auditToolsPaths.js";
|
|
19
19
|
import { resolveRepoRoot } from "../../shared/io/repoRoot.js";
|
|
20
20
|
import { writeCurrentStep } from "./stepWriter.js";
|
|
21
|
-
import { dependencyVerifiedComplete } from "./stepUtils.js";
|
|
21
|
+
import { dependencyAwaitingClarification, dependencyVerifiedComplete, } from "./stepUtils.js";
|
|
22
22
|
import { isTerminalStatus, isVerifiedCompleteStatus, } from "../state/itemStatus.js";
|
|
23
23
|
import { deduplicateCrossLensFindings, fixupBlocksAfterDedup, } from "../dedup/crossLensDedup.js";
|
|
24
24
|
import { checkAffectedFileIntegrity } from "../utils/fileIntegrity.js";
|
|
@@ -26,7 +26,7 @@ import { resolveIntakeStep } from "./intakeResolver.js";
|
|
|
26
26
|
import { runToolOwnedFinalGate, applyCoarseReblock, readFinalGateSidecar, writeFinalGateSidecar, } from "./finalGate.js";
|
|
27
27
|
import { buildNextContractPipelineStep, shouldEnterContractPipeline, writePathASeedFromFindings, buildLeanExtractedPlan, } from "./contractPipeline.js";
|
|
28
28
|
import { contractArtifactExists, contractPipelineDir, } from "../contractPipeline/artifactStore.js";
|
|
29
|
-
import { buildReviewRequest, applyReviewResolution, isResolutionForRequest, } from "../review/reviewGate.js";
|
|
29
|
+
import { buildReviewRequest, applyReviewResolution, isResolutionForRequest, REVIEW_REQUEST_SCHEMA_VERSION, } from "../review/reviewGate.js";
|
|
30
30
|
import { buildAutonomousReviewDecision } from "../review/autonomousGate.js";
|
|
31
31
|
import { runFindingFilterPass } from "../findingFilter.js";
|
|
32
32
|
import { intakePaths, isIntakeReady, readIntakeArtifacts, resolveManifestSources, } from "../intake.js";
|
|
@@ -320,19 +320,35 @@ function implementableBlocks(state) {
|
|
|
320
320
|
}));
|
|
321
321
|
}
|
|
322
322
|
/**
|
|
323
|
-
* Pending nodes that are
|
|
324
|
-
* reach a verified-complete disposition (a prerequisite was
|
|
325
|
-
*
|
|
326
|
-
*
|
|
327
|
-
*
|
|
328
|
-
*
|
|
323
|
+
* Pending nodes that are genuinely DEAD-ENDED: at least one dependency did not
|
|
324
|
+
* reach a verified-complete disposition and never will (a prerequisite was
|
|
325
|
+
* skipped or blocked, or the edges are cyclic). Once no eligible block remains,
|
|
326
|
+
* these are marked `blocked` — their upstream surface never landed — rather than
|
|
327
|
+
* looping forever.
|
|
328
|
+
*
|
|
329
|
+
* A node held only by an UNANSWERED WORKER QUESTION is deliberately excluded
|
|
330
|
+
* (`dependencyAwaitingClarification`): since the clarification round is deferred
|
|
331
|
+
* to the end of the implement phase, such a node reaches this sweep while its
|
|
332
|
+
* answer is still outstanding, and blocking it would report "upstream failed"
|
|
333
|
+
* for what is really "awaiting an answer". It stays `pending` and is re-decided
|
|
334
|
+
* once the answer lands.
|
|
329
335
|
*/
|
|
330
336
|
function blockedByUnsatisfiedDependency(state) {
|
|
331
337
|
if (!state.plan || !state.items)
|
|
332
338
|
return [];
|
|
333
339
|
return state.plan.blocks.filter((block) => !dependencyVerifiedComplete(block, state) &&
|
|
340
|
+
!dependencyAwaitingClarification(block, state) &&
|
|
334
341
|
block.items.some((findingId) => state.items?.[findingId]?.status === "pending"));
|
|
335
342
|
}
|
|
343
|
+
/**
|
|
344
|
+
* Whether any item is paused on a worker question that has not been answered yet.
|
|
345
|
+
* Drives the deferred clarification round (the `deferred_clarification`
|
|
346
|
+
* obligation): the question waits until the implement frontier drains, then is
|
|
347
|
+
* asked in one batched window.
|
|
348
|
+
*/
|
|
349
|
+
function hasUnansweredClarification(state) {
|
|
350
|
+
return Object.values(state.items ?? {}).some((it) => it.status === "needs_clarification");
|
|
351
|
+
}
|
|
336
352
|
// ---------------------------------------------------------------------------
|
|
337
353
|
// Rolling per-node scheduler (CP-BLOCK-N-rolling-scheduler)
|
|
338
354
|
// ---------------------------------------------------------------------------
|
|
@@ -495,8 +511,8 @@ export async function driveRollingDispatch(levels, options) {
|
|
|
495
511
|
if (options.quotaStateDir) {
|
|
496
512
|
setQuotaStateDir(options.quotaStateDir);
|
|
497
513
|
}
|
|
498
|
-
const estimateTokens = options.estimateTokens
|
|
499
|
-
const scopeForBlock = options.scopeForBlock ?? ((b) => b.touched_files
|
|
514
|
+
const estimateTokens = options.estimateTokens;
|
|
515
|
+
const scopeForBlock = options.scopeForBlock ?? ((b) => b.touched_files);
|
|
500
516
|
const allBlocks = levels.flat();
|
|
501
517
|
const blockById = new Map(allBlocks.map((b) => [b.block_id, b]));
|
|
502
518
|
const hostSession = options.hostSession;
|
|
@@ -787,7 +803,7 @@ export async function driveRollingImplementDispatch(options) {
|
|
|
787
803
|
artifactsDir,
|
|
788
804
|
runId,
|
|
789
805
|
resultPath,
|
|
790
|
-
seedPaths: block.touched_files
|
|
806
|
+
seedPaths: block.touched_files,
|
|
791
807
|
allBlockScopes,
|
|
792
808
|
additionalVerifyCommands: targetedCommandsForBlock(state, block.block_id),
|
|
793
809
|
dispatchNode,
|
|
@@ -819,12 +835,15 @@ export async function driveRollingImplementDispatch(options) {
|
|
|
819
835
|
// the merge-time write-scope gate reads — for file-ownership-disjoint admission
|
|
820
836
|
// (INV-SOO). A block with no plan scope falls back to its touched_files.
|
|
821
837
|
const writePathsByBlock = new Map(allBlockScopes.map((s) => [s.block_id, s.write_paths]));
|
|
838
|
+
const tokensByBlock = new Map(plan.items
|
|
839
|
+
.filter((i) => typeof i.block_id === "string")
|
|
840
|
+
.map((i) => [i.block_id, i.estimated_input_tokens]));
|
|
822
841
|
// Continuity bias (context-efficiency track, increment 2d): load the harvested
|
|
823
842
|
// access-memory and reduce it to a per-block mass keyed on each block's declared
|
|
824
843
|
// source surface (`touched_files` — the clean surface the harvest attributes to,
|
|
825
844
|
// excluding the synthetic result-path in the dispatch write-scope). Absent on the
|
|
826
845
|
// first pass (no merge yet) ⇒ empty map ⇒ pure block_id sub-wave ordering.
|
|
827
|
-
const continuityScores = computeBlockContinuityScores(await readRemediationAccessMemory(artifactsDir), levels.flat(), (block) => block.touched_files
|
|
846
|
+
const continuityScores = computeBlockContinuityScores(await readRemediationAccessMemory(artifactsDir), levels.flat(), (block) => block.touched_files);
|
|
828
847
|
const driven = await driveRollingDispatch(levels, {
|
|
829
848
|
confirmedPools,
|
|
830
849
|
sessionConfig: options.sessionConfig ?? {},
|
|
@@ -834,7 +853,19 @@ export async function driveRollingImplementDispatch(options) {
|
|
|
834
853
|
root,
|
|
835
854
|
hostSession,
|
|
836
855
|
continuityScores,
|
|
837
|
-
scopeForBlock: (block) => writePathsByBlock.get(block.block_id) ?? block.touched_files
|
|
856
|
+
scopeForBlock: (block) => writePathsByBlock.get(block.block_id) ?? block.touched_files,
|
|
857
|
+
// The node's real size, read off the plan item that already carries it. No
|
|
858
|
+
// fallback: a block in the drive that is absent from the plan is a producer
|
|
859
|
+
// desync, and defaulting it would silently restore the flat estimate that
|
|
860
|
+
// made every node look identical to the fit gates.
|
|
861
|
+
estimateTokens: (block) => {
|
|
862
|
+
const estimate = tokensByBlock.get(block.block_id);
|
|
863
|
+
if (estimate === undefined) {
|
|
864
|
+
throw new Error(`Block ${block.block_id} is being dispatched but carries no dispatch-plan item, ` +
|
|
865
|
+
`so its token estimate is unknown. The drive and the plan have desynced.`);
|
|
866
|
+
}
|
|
867
|
+
return estimate;
|
|
868
|
+
},
|
|
838
869
|
tierForBlock: (block) => tierByBlock.get(block.block_id),
|
|
839
870
|
// Reactive cost verification: a declared-free source pool observed charging has
|
|
840
871
|
// been demoted by the engine; surface it as reviewable friction so the operator
|
|
@@ -914,19 +945,6 @@ export async function driveRollingImplementDispatch(options) {
|
|
|
914
945
|
...(driven.terminal ? { terminal: driven.terminal } : {}),
|
|
915
946
|
};
|
|
916
947
|
}
|
|
917
|
-
/**
|
|
918
|
-
* Flat per-node input-token estimate for the A-8 hybrid frontier split. The
|
|
919
|
-
* coordinator splits by per-pool SLOTS (concurrency) — host vs. backend — so a
|
|
920
|
-
* uniform estimate is sufficient here; it matches the default the in-process engine
|
|
921
|
-
* uses (`driveRollingDispatch`'s `() => 2000`), keeping the two paths consistent.
|
|
922
|
-
*
|
|
923
|
-
* ⚠ **Deliberately still flat.** Replacing it with the real per-node estimate was
|
|
924
|
-
* built and REVERTED (2026-07-24): it is accurate, and that is precisely the
|
|
925
|
-
* problem — an honest estimate makes "this node fits no pool" REACHABLE, and both
|
|
926
|
-
* consumers mishandle that case terminally rather than resumably. See the backlog
|
|
927
|
-
* entry; the fit gates cannot consume a real estimate until that routing is fixed.
|
|
928
|
-
*/
|
|
929
|
-
const HYBRID_NODE_TOKEN_ESTIMATE = 2000;
|
|
930
948
|
// ---------------------------------------------------------------------------
|
|
931
949
|
// Tool-owned final completion gate (INV-RS-10) + coarse re-block (INV-RS-09)
|
|
932
950
|
// ---------------------------------------------------------------------------
|
|
@@ -1338,7 +1356,7 @@ async function buildImplementDispatchStep(ctx) {
|
|
|
1338
1356
|
});
|
|
1339
1357
|
const frontier = plan.items
|
|
1340
1358
|
.filter((i) => typeof i.block_id === "string")
|
|
1341
|
-
.map((i) => ({ id: i.block_id, estimatedTokens:
|
|
1359
|
+
.map((i) => ({ id: i.block_id, estimatedTokens: i.estimated_input_tokens }));
|
|
1342
1360
|
if (frontier.length === 0) {
|
|
1343
1361
|
const merged = await mergeImplementResults({ root, artifactsDir }, runId);
|
|
1344
1362
|
return { kind: "transition", state: merged };
|
|
@@ -1939,7 +1957,11 @@ async function handlePendingExtractedPlan(root, artifactsDir, existing, extracte
|
|
|
1939
1957
|
run_start_dirty: [...stagedAndUntracked(root)].sort(),
|
|
1940
1958
|
};
|
|
1941
1959
|
}
|
|
1942
|
-
|
|
1960
|
+
// Discarded on mismatch, so the gate re-asks rather than replaying operator
|
|
1961
|
+
// decisions under semantics they were not made under. Re-asking costs a repeat
|
|
1962
|
+
// answer; applying them blind could act on a keep/decline that no longer means
|
|
1963
|
+
// what it meant when it was recorded.
|
|
1964
|
+
const reviewDecision = discardOnSchemaVersionMismatch(await readOptionalJsonFile(reviewDecisionPath(artifactsDir)), REVIEW_DECISION_SCHEMA_VERSION);
|
|
1943
1965
|
// Coverage ledger. Path A (structured_audit): the single filter pass ran at
|
|
1944
1966
|
// intake over the ORIGINAL findings and persisted its dispositions — build
|
|
1945
1967
|
// coverage over those originals so every audit finding gets exactly one
|
|
@@ -2113,8 +2135,8 @@ async function runReviewApprovalGate(root, artifactsDir, survivors, autonomous =
|
|
|
2113
2135
|
};
|
|
2114
2136
|
}
|
|
2115
2137
|
// Consume the resolution into a durable, reasoned decision record.
|
|
2116
|
-
|
|
2117
|
-
|
|
2138
|
+
// Regenerable: a stale-schema request is treated as absent and rebuilt below.
|
|
2139
|
+
const request = discardOnSchemaVersionMismatch(await readOptionalJsonFile(requestPath), REVIEW_REQUEST_SCHEMA_VERSION) ?? buildReviewRequest(survivors, randomRunId("path-a-review"));
|
|
2118
2140
|
const resolution = await readOptionalJsonFile(resolutionPath);
|
|
2119
2141
|
if (!isResolutionForRequest(request, resolution)) {
|
|
2120
2142
|
// Stale cross-run resolution (plan_id mismatch): archive it and RE-HALT
|
|
@@ -2147,7 +2169,9 @@ async function runReviewApprovalGate(root, artifactsDir, survivors, autonomous =
|
|
|
2147
2169
|
// decision approves a SUBSET with declined EMPTY (leftovers live but not
|
|
2148
2170
|
// approved), so keying the replay on the declined set alone would silently
|
|
2149
2171
|
// re-approve every leftover on the next call.
|
|
2150
|
-
|
|
2172
|
+
// Discarded on mismatch — see the note at the sibling read: a stale-schema
|
|
2173
|
+
// decision record re-asks rather than replaying stale operator intent.
|
|
2174
|
+
const decision = discardOnSchemaVersionMismatch(await readOptionalJsonFile(decisionPath), REVIEW_DECISION_SCHEMA_VERSION);
|
|
2151
2175
|
const declined = decision?.declined ?? [];
|
|
2152
2176
|
const declinedIds = new Set(declined.map((d) => d.finding_id));
|
|
2153
2177
|
const approvedIds = decision ? new Set(decision.approved_ids ?? []) : undefined;
|
|
@@ -2712,7 +2736,17 @@ async function applyPlanClarificationResolution(artifactsDir, state, store) {
|
|
|
2712
2736
|
await withFsRetry(() => rename(resolutionPath, `${resolutionPath}.consumed-${Date.now()}`));
|
|
2713
2737
|
}
|
|
2714
2738
|
const remainingPending = state.plan.findings.some((f) => state.items?.[f.id]?.status === "pending");
|
|
2715
|
-
|
|
2739
|
+
// Undecided-remainder guard, mirroring triage's still-blocked guard: a
|
|
2740
|
+
// resolution covering only SOME of the paused items must not fall through to
|
|
2741
|
+
// closing, which would force-close the undecided ones as `abandoned` and drop
|
|
2742
|
+
// their questions unanswered. Load-bearing now that the round runs at the
|
|
2743
|
+
// DRAINED end of the implement phase, where `remainingPending` is normally
|
|
2744
|
+
// false — the fall-through it guards is the common case, not a corner.
|
|
2745
|
+
state.status = remainingPending
|
|
2746
|
+
? "implementing"
|
|
2747
|
+
: hasUnansweredClarification(state)
|
|
2748
|
+
? "waiting_for_clarification"
|
|
2749
|
+
: "closing";
|
|
2716
2750
|
state.clarifications = [];
|
|
2717
2751
|
state.closing_plan ??= { action: "none" };
|
|
2718
2752
|
await store.saveState(state);
|
|
@@ -2789,8 +2823,8 @@ async function runPlanningReviewGate(root, artifactsDir, state, store) {
|
|
|
2789
2823
|
return handleWaitingForReviewApproval(root, artifactsDir, request);
|
|
2790
2824
|
}
|
|
2791
2825
|
// Resolution present: consume it into a durable, reasoned decision record.
|
|
2792
|
-
|
|
2793
|
-
|
|
2826
|
+
// Regenerable: a stale-schema request is treated as absent and rebuilt below.
|
|
2827
|
+
const request = discardOnSchemaVersionMismatch(await readOptionalJsonFile(requestPath), REVIEW_REQUEST_SCHEMA_VERSION) ?? buildReviewRequest(findings, reviewPlanId);
|
|
2794
2828
|
const resolution = await readOptionalJsonFile(resolutionPath);
|
|
2795
2829
|
if (!isResolutionForRequest(request, resolution)) {
|
|
2796
2830
|
// Stale cross-run resolution: archive it and re-halt with the live request.
|
|
@@ -3751,6 +3785,7 @@ const MAIN_PRIORITY = [
|
|
|
3751
3785
|
"waiting_for_triage",
|
|
3752
3786
|
"planning_documentable",
|
|
3753
3787
|
"partial_terminal",
|
|
3788
|
+
"deferred_clarification",
|
|
3754
3789
|
"implementing",
|
|
3755
3790
|
"triage",
|
|
3756
3791
|
"planning_zero",
|
|
@@ -3875,6 +3910,35 @@ function buildMainObligations(ctx) {
|
|
|
3875
3910
|
return handleAllTerminalTransition(root, artifactsDir, s, store, options, runLogger);
|
|
3876
3911
|
},
|
|
3877
3912
|
},
|
|
3913
|
+
{
|
|
3914
|
+
// Deferred clarification round. A worker question no longer freezes the run
|
|
3915
|
+
// at merge time; it waits HERE — at the END of the implement phase, once the
|
|
3916
|
+
// eligible dispatch frontier has drained — so every sibling's remaining work
|
|
3917
|
+
// lands first and the questions are asked in one batched window (the goals
|
|
3918
|
+
// doc's bounded-window promise).
|
|
3919
|
+
//
|
|
3920
|
+
// Ordered ABOVE `implementing` only because the derive already requires an
|
|
3921
|
+
// empty frontier: while any node is dispatchable this obligation is
|
|
3922
|
+
// satisfied and `implementing` dispatches it. Ordered above `triage` /
|
|
3923
|
+
// `all_terminal` / `closing` so an unanswered question can never be swept
|
|
3924
|
+
// past into close (triage with no blocked items routes straight to closing,
|
|
3925
|
+
// which would force-close the paused item as `abandoned` and lose the
|
|
3926
|
+
// question). It is NOT the only mid-phase halt — `partial_terminal` still
|
|
3927
|
+
// owns the quota pause, and keeps its higher slot.
|
|
3928
|
+
id: "deferred_clarification",
|
|
3929
|
+
derive: (state) => state != null &&
|
|
3930
|
+
(state.status === "implementing" || state.status === "triage") &&
|
|
3931
|
+
hasUnansweredClarification(state) &&
|
|
3932
|
+
implementableBlocks(state).length === 0
|
|
3933
|
+
? "missing"
|
|
3934
|
+
: "satisfied",
|
|
3935
|
+
execute: async (state) => {
|
|
3936
|
+
const s = requireState(state);
|
|
3937
|
+
s.status = "waiting_for_clarification";
|
|
3938
|
+
await store.saveState(s);
|
|
3939
|
+
return { kind: "transition", state: s };
|
|
3940
|
+
},
|
|
3941
|
+
},
|
|
3878
3942
|
{
|
|
3879
3943
|
id: "implementing",
|
|
3880
3944
|
derive: (state) => state?.status === "implementing" ? "missing" : "satisfied",
|