audit-tools 0.34.29 → 0.34.30
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/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/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/marshal.d.ts.map +1 -1
- package/dist/remediate/steps/dispatch/marshal.js +16 -11
- 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.map +1 -1
- package/dist/remediate/steps/nextStep.js +82 -20
- 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/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 +5 -9
- 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/docs/audit-pkg/operator-guide.md +19 -9
- package/docs/audit-pkg/release.md +4 -0
- package/package.json +10 -3
- 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
|
// ---------------------------------------------------------------------------
|
|
@@ -496,7 +512,7 @@ export async function driveRollingDispatch(levels, options) {
|
|
|
496
512
|
setQuotaStateDir(options.quotaStateDir);
|
|
497
513
|
}
|
|
498
514
|
const estimateTokens = options.estimateTokens ?? (() => 2000);
|
|
499
|
-
const scopeForBlock = options.scopeForBlock ?? ((b) => b.touched_files
|
|
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,
|
|
@@ -824,7 +840,7 @@ export async function driveRollingImplementDispatch(options) {
|
|
|
824
840
|
// source surface (`touched_files` — the clean surface the harvest attributes to,
|
|
825
841
|
// excluding the synthetic result-path in the dispatch write-scope). Absent on the
|
|
826
842
|
// 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
|
|
843
|
+
const continuityScores = computeBlockContinuityScores(await readRemediationAccessMemory(artifactsDir), levels.flat(), (block) => block.touched_files);
|
|
828
844
|
const driven = await driveRollingDispatch(levels, {
|
|
829
845
|
confirmedPools,
|
|
830
846
|
sessionConfig: options.sessionConfig ?? {},
|
|
@@ -834,7 +850,7 @@ export async function driveRollingImplementDispatch(options) {
|
|
|
834
850
|
root,
|
|
835
851
|
hostSession,
|
|
836
852
|
continuityScores,
|
|
837
|
-
scopeForBlock: (block) => writePathsByBlock.get(block.block_id) ?? block.touched_files
|
|
853
|
+
scopeForBlock: (block) => writePathsByBlock.get(block.block_id) ?? block.touched_files,
|
|
838
854
|
tierForBlock: (block) => tierByBlock.get(block.block_id),
|
|
839
855
|
// Reactive cost verification: a declared-free source pool observed charging has
|
|
840
856
|
// been demoted by the engine; surface it as reviewable friction so the operator
|
|
@@ -1939,7 +1955,11 @@ async function handlePendingExtractedPlan(root, artifactsDir, existing, extracte
|
|
|
1939
1955
|
run_start_dirty: [...stagedAndUntracked(root)].sort(),
|
|
1940
1956
|
};
|
|
1941
1957
|
}
|
|
1942
|
-
|
|
1958
|
+
// Discarded on mismatch, so the gate re-asks rather than replaying operator
|
|
1959
|
+
// decisions under semantics they were not made under. Re-asking costs a repeat
|
|
1960
|
+
// answer; applying them blind could act on a keep/decline that no longer means
|
|
1961
|
+
// what it meant when it was recorded.
|
|
1962
|
+
const reviewDecision = discardOnSchemaVersionMismatch(await readOptionalJsonFile(reviewDecisionPath(artifactsDir)), REVIEW_DECISION_SCHEMA_VERSION);
|
|
1943
1963
|
// Coverage ledger. Path A (structured_audit): the single filter pass ran at
|
|
1944
1964
|
// intake over the ORIGINAL findings and persisted its dispositions — build
|
|
1945
1965
|
// coverage over those originals so every audit finding gets exactly one
|
|
@@ -2113,8 +2133,8 @@ async function runReviewApprovalGate(root, artifactsDir, survivors, autonomous =
|
|
|
2113
2133
|
};
|
|
2114
2134
|
}
|
|
2115
2135
|
// Consume the resolution into a durable, reasoned decision record.
|
|
2116
|
-
|
|
2117
|
-
|
|
2136
|
+
// Regenerable: a stale-schema request is treated as absent and rebuilt below.
|
|
2137
|
+
const request = discardOnSchemaVersionMismatch(await readOptionalJsonFile(requestPath), REVIEW_REQUEST_SCHEMA_VERSION) ?? buildReviewRequest(survivors, randomRunId("path-a-review"));
|
|
2118
2138
|
const resolution = await readOptionalJsonFile(resolutionPath);
|
|
2119
2139
|
if (!isResolutionForRequest(request, resolution)) {
|
|
2120
2140
|
// Stale cross-run resolution (plan_id mismatch): archive it and RE-HALT
|
|
@@ -2147,7 +2167,9 @@ async function runReviewApprovalGate(root, artifactsDir, survivors, autonomous =
|
|
|
2147
2167
|
// decision approves a SUBSET with declined EMPTY (leftovers live but not
|
|
2148
2168
|
// approved), so keying the replay on the declined set alone would silently
|
|
2149
2169
|
// re-approve every leftover on the next call.
|
|
2150
|
-
|
|
2170
|
+
// Discarded on mismatch — see the note at the sibling read: a stale-schema
|
|
2171
|
+
// decision record re-asks rather than replaying stale operator intent.
|
|
2172
|
+
const decision = discardOnSchemaVersionMismatch(await readOptionalJsonFile(decisionPath), REVIEW_DECISION_SCHEMA_VERSION);
|
|
2151
2173
|
const declined = decision?.declined ?? [];
|
|
2152
2174
|
const declinedIds = new Set(declined.map((d) => d.finding_id));
|
|
2153
2175
|
const approvedIds = decision ? new Set(decision.approved_ids ?? []) : undefined;
|
|
@@ -2712,7 +2734,17 @@ async function applyPlanClarificationResolution(artifactsDir, state, store) {
|
|
|
2712
2734
|
await withFsRetry(() => rename(resolutionPath, `${resolutionPath}.consumed-${Date.now()}`));
|
|
2713
2735
|
}
|
|
2714
2736
|
const remainingPending = state.plan.findings.some((f) => state.items?.[f.id]?.status === "pending");
|
|
2715
|
-
|
|
2737
|
+
// Undecided-remainder guard, mirroring triage's still-blocked guard: a
|
|
2738
|
+
// resolution covering only SOME of the paused items must not fall through to
|
|
2739
|
+
// closing, which would force-close the undecided ones as `abandoned` and drop
|
|
2740
|
+
// their questions unanswered. Load-bearing now that the round runs at the
|
|
2741
|
+
// DRAINED end of the implement phase, where `remainingPending` is normally
|
|
2742
|
+
// false — the fall-through it guards is the common case, not a corner.
|
|
2743
|
+
state.status = remainingPending
|
|
2744
|
+
? "implementing"
|
|
2745
|
+
: hasUnansweredClarification(state)
|
|
2746
|
+
? "waiting_for_clarification"
|
|
2747
|
+
: "closing";
|
|
2716
2748
|
state.clarifications = [];
|
|
2717
2749
|
state.closing_plan ??= { action: "none" };
|
|
2718
2750
|
await store.saveState(state);
|
|
@@ -2789,8 +2821,8 @@ async function runPlanningReviewGate(root, artifactsDir, state, store) {
|
|
|
2789
2821
|
return handleWaitingForReviewApproval(root, artifactsDir, request);
|
|
2790
2822
|
}
|
|
2791
2823
|
// Resolution present: consume it into a durable, reasoned decision record.
|
|
2792
|
-
|
|
2793
|
-
|
|
2824
|
+
// Regenerable: a stale-schema request is treated as absent and rebuilt below.
|
|
2825
|
+
const request = discardOnSchemaVersionMismatch(await readOptionalJsonFile(requestPath), REVIEW_REQUEST_SCHEMA_VERSION) ?? buildReviewRequest(findings, reviewPlanId);
|
|
2794
2826
|
const resolution = await readOptionalJsonFile(resolutionPath);
|
|
2795
2827
|
if (!isResolutionForRequest(request, resolution)) {
|
|
2796
2828
|
// Stale cross-run resolution: archive it and re-halt with the live request.
|
|
@@ -3751,6 +3783,7 @@ const MAIN_PRIORITY = [
|
|
|
3751
3783
|
"waiting_for_triage",
|
|
3752
3784
|
"planning_documentable",
|
|
3753
3785
|
"partial_terminal",
|
|
3786
|
+
"deferred_clarification",
|
|
3754
3787
|
"implementing",
|
|
3755
3788
|
"triage",
|
|
3756
3789
|
"planning_zero",
|
|
@@ -3875,6 +3908,35 @@ function buildMainObligations(ctx) {
|
|
|
3875
3908
|
return handleAllTerminalTransition(root, artifactsDir, s, store, options, runLogger);
|
|
3876
3909
|
},
|
|
3877
3910
|
},
|
|
3911
|
+
{
|
|
3912
|
+
// Deferred clarification round. A worker question no longer freezes the run
|
|
3913
|
+
// at merge time; it waits HERE — at the END of the implement phase, once the
|
|
3914
|
+
// eligible dispatch frontier has drained — so every sibling's remaining work
|
|
3915
|
+
// lands first and the questions are asked in one batched window (the goals
|
|
3916
|
+
// doc's bounded-window promise).
|
|
3917
|
+
//
|
|
3918
|
+
// Ordered ABOVE `implementing` only because the derive already requires an
|
|
3919
|
+
// empty frontier: while any node is dispatchable this obligation is
|
|
3920
|
+
// satisfied and `implementing` dispatches it. Ordered above `triage` /
|
|
3921
|
+
// `all_terminal` / `closing` so an unanswered question can never be swept
|
|
3922
|
+
// past into close (triage with no blocked items routes straight to closing,
|
|
3923
|
+
// which would force-close the paused item as `abandoned` and lose the
|
|
3924
|
+
// question). It is NOT the only mid-phase halt — `partial_terminal` still
|
|
3925
|
+
// owns the quota pause, and keeps its higher slot.
|
|
3926
|
+
id: "deferred_clarification",
|
|
3927
|
+
derive: (state) => state != null &&
|
|
3928
|
+
(state.status === "implementing" || state.status === "triage") &&
|
|
3929
|
+
hasUnansweredClarification(state) &&
|
|
3930
|
+
implementableBlocks(state).length === 0
|
|
3931
|
+
? "missing"
|
|
3932
|
+
: "satisfied",
|
|
3933
|
+
execute: async (state) => {
|
|
3934
|
+
const s = requireState(state);
|
|
3935
|
+
s.status = "waiting_for_clarification";
|
|
3936
|
+
await store.saveState(s);
|
|
3937
|
+
return { kind: "transition", state: s };
|
|
3938
|
+
},
|
|
3939
|
+
},
|
|
3878
3940
|
{
|
|
3879
3941
|
id: "implementing",
|
|
3880
3942
|
derive: (state) => state?.status === "implementing" ? "missing" : "satisfied",
|