audit-tools 0.28.7 → 0.28.9
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/audit-code-wrapper-install-hosts.mjs +133 -27
- package/dist/audit/cli/confirmIntentStep.d.ts +1 -0
- package/dist/audit/cli/confirmIntentStep.d.ts.map +1 -1
- package/dist/audit/cli/confirmIntentStep.js +14 -10
- package/dist/audit/cli/confirmIntentStep.js.map +1 -1
- package/dist/audit/cli/dispatch/quotaPool.d.ts.map +1 -1
- package/dist/audit/cli/dispatch/quotaPool.js +4 -3
- package/dist/audit/cli/dispatch/quotaPool.js.map +1 -1
- package/dist/audit/cli/dispatch.d.ts +15 -1
- package/dist/audit/cli/dispatch.d.ts.map +1 -1
- package/dist/audit/cli/dispatch.js +54 -19
- package/dist/audit/cli/dispatch.js.map +1 -1
- package/dist/audit/cli/hybridDispatch.d.ts +46 -0
- package/dist/audit/cli/hybridDispatch.d.ts.map +1 -0
- package/dist/audit/cli/hybridDispatch.js +77 -0
- package/dist/audit/cli/hybridDispatch.js.map +1 -0
- package/dist/audit/cli/nextStepHelpers.d.ts.map +1 -1
- package/dist/audit/cli/nextStepHelpers.js +111 -2
- package/dist/audit/cli/nextStepHelpers.js.map +1 -1
- package/dist/audit/cli/quotaCommand.d.ts.map +1 -1
- package/dist/audit/cli/quotaCommand.js +20 -10
- package/dist/audit/cli/quotaCommand.js.map +1 -1
- package/dist/audit/cli/reviewRun.d.ts +8 -0
- package/dist/audit/cli/reviewRun.d.ts.map +1 -1
- package/dist/audit/cli/reviewRun.js +1 -1
- package/dist/audit/cli/reviewRun.js.map +1 -1
- package/dist/audit/cli/rollingAuditDispatch.d.ts +57 -4
- package/dist/audit/cli/rollingAuditDispatch.d.ts.map +1 -1
- package/dist/audit/cli/rollingAuditDispatch.js +137 -10
- package/dist/audit/cli/rollingAuditDispatch.js.map +1 -1
- package/dist/audit/cli/scoreAuditCommand.d.ts +16 -0
- package/dist/audit/cli/scoreAuditCommand.d.ts.map +1 -0
- package/dist/audit/cli/scoreAuditCommand.js +82 -0
- package/dist/audit/cli/scoreAuditCommand.js.map +1 -0
- package/dist/audit/cli/submitPacketCommand.d.ts.map +1 -1
- package/dist/audit/cli/submitPacketCommand.js +8 -1
- package/dist/audit/cli/submitPacketCommand.js.map +1 -1
- package/dist/audit/cli.d.ts.map +1 -1
- package/dist/audit/cli.js +5 -1
- package/dist/audit/cli.js.map +1 -1
- package/dist/audit/orchestrator/designReviewProjection.d.ts +9 -1
- package/dist/audit/orchestrator/designReviewProjection.d.ts.map +1 -1
- package/dist/audit/orchestrator/designReviewProjection.js +19 -1
- package/dist/audit/orchestrator/designReviewProjection.js.map +1 -1
- package/dist/audit/orchestrator/designReviewPrompt.d.ts +2 -0
- package/dist/audit/orchestrator/designReviewPrompt.d.ts.map +1 -1
- package/dist/audit/orchestrator/designReviewPrompt.js +11 -1
- package/dist/audit/orchestrator/designReviewPrompt.js.map +1 -1
- package/dist/audit/orchestrator/executorRunners.js +1 -1
- package/dist/audit/orchestrator/executorRunners.js.map +1 -1
- package/dist/audit/orchestrator/intakeExecutors.d.ts +9 -1
- package/dist/audit/orchestrator/intakeExecutors.d.ts.map +1 -1
- package/dist/audit/orchestrator/intakeExecutors.js +16 -3
- package/dist/audit/orchestrator/intakeExecutors.js.map +1 -1
- package/dist/audit/orchestrator/intentCheckpointExecutor.d.ts.map +1 -1
- package/dist/audit/orchestrator/intentCheckpointExecutor.js +3 -0
- package/dist/audit/orchestrator/intentCheckpointExecutor.js.map +1 -1
- package/dist/audit/orchestrator/intentInterpreter.d.ts +21 -7
- package/dist/audit/orchestrator/intentInterpreter.d.ts.map +1 -1
- package/dist/audit/orchestrator/intentInterpreter.js +30 -11
- package/dist/audit/orchestrator/intentInterpreter.js.map +1 -1
- package/dist/audit/orchestrator/intentScopeDisposition.d.ts +49 -0
- package/dist/audit/orchestrator/intentScopeDisposition.d.ts.map +1 -0
- package/dist/audit/orchestrator/intentScopeDisposition.js +57 -0
- package/dist/audit/orchestrator/intentScopeDisposition.js.map +1 -0
- package/dist/audit/orchestrator/rollingDispatch.d.ts +9 -0
- package/dist/audit/orchestrator/rollingDispatch.d.ts.map +1 -1
- package/dist/audit/orchestrator/rollingDispatch.js +10 -0
- package/dist/audit/orchestrator/rollingDispatch.js.map +1 -1
- package/dist/audit/orchestrator/scope.d.ts +1 -0
- package/dist/audit/orchestrator/scope.d.ts.map +1 -1
- package/dist/audit/orchestrator/scope.js +2 -7
- package/dist/audit/orchestrator/scope.js.map +1 -1
- package/dist/audit/quota/index.d.ts +5 -0
- package/dist/audit/quota/index.d.ts.map +1 -1
- package/dist/audit/reporting/scoreAudit.d.ts +161 -0
- package/dist/audit/reporting/scoreAudit.d.ts.map +1 -0
- package/dist/audit/reporting/scoreAudit.js +224 -0
- package/dist/audit/reporting/scoreAudit.js.map +1 -0
- package/dist/audit/types/activeDispatch.d.ts +29 -1
- package/dist/audit/types/activeDispatch.d.ts.map +1 -1
- package/dist/audit/types/activeDispatch.js.map +1 -1
- package/dist/audit/types/analyzerCapability.d.ts +6 -6
- package/dist/audit/validation/auditResults.d.ts +13 -0
- package/dist/audit/validation/auditResults.d.ts.map +1 -1
- package/dist/audit/validation/auditResults.js +35 -12
- package/dist/audit/validation/auditResults.js.map +1 -1
- package/dist/remediate/contractPipeline/changeClassification.d.ts +146 -0
- package/dist/remediate/contractPipeline/changeClassification.d.ts.map +1 -0
- package/dist/remediate/contractPipeline/changeClassification.js +398 -0
- package/dist/remediate/contractPipeline/changeClassification.js.map +1 -0
- package/dist/remediate/contractPipeline/derive.d.ts.map +1 -1
- package/dist/remediate/contractPipeline/derive.js +9 -0
- package/dist/remediate/contractPipeline/derive.js.map +1 -1
- package/dist/remediate/intent/intentOrdering.d.ts +51 -0
- package/dist/remediate/intent/intentOrdering.d.ts.map +1 -0
- package/dist/remediate/intent/intentOrdering.js +150 -0
- package/dist/remediate/intent/intentOrdering.js.map +1 -0
- package/dist/remediate/phases/plan.d.ts.map +1 -1
- package/dist/remediate/phases/plan.js +14 -1
- package/dist/remediate/phases/plan.js.map +1 -1
- package/dist/remediate/providers/index.d.ts +32 -0
- package/dist/remediate/providers/index.d.ts.map +1 -1
- package/dist/remediate/providers/index.js +28 -1
- package/dist/remediate/providers/index.js.map +1 -1
- package/dist/remediate/steps/contractPipeline.d.ts +15 -1
- package/dist/remediate/steps/contractPipeline.d.ts.map +1 -1
- package/dist/remediate/steps/contractPipeline.js +282 -14
- package/dist/remediate/steps/contractPipeline.js.map +1 -1
- package/dist/remediate/steps/dispatch.d.ts +56 -1
- package/dist/remediate/steps/dispatch.d.ts.map +1 -1
- package/dist/remediate/steps/dispatch.js +120 -29
- package/dist/remediate/steps/dispatch.js.map +1 -1
- package/dist/remediate/steps/nextStep.d.ts +53 -2
- package/dist/remediate/steps/nextStep.d.ts.map +1 -1
- package/dist/remediate/steps/nextStep.js +253 -81
- package/dist/remediate/steps/nextStep.js.map +1 -1
- package/dist/remediate/steps/providerNodeDispatch.d.ts +8 -1
- package/dist/remediate/steps/providerNodeDispatch.d.ts.map +1 -1
- package/dist/remediate/steps/providerNodeDispatch.js +7 -2
- package/dist/remediate/steps/providerNodeDispatch.js.map +1 -1
- package/dist/remediate/steps/rollingSession.d.ts +55 -1
- package/dist/remediate/steps/rollingSession.d.ts.map +1 -1
- package/dist/remediate/steps/rollingSession.js +128 -19
- package/dist/remediate/steps/rollingSession.js.map +1 -1
- package/dist/remediate/validation/contractPipelineGates.d.ts +23 -8
- package/dist/remediate/validation/contractPipelineGates.d.ts.map +1 -1
- package/dist/remediate/validation/contractPipelineGates.js +43 -38
- package/dist/remediate/validation/contractPipelineGates.js.map +1 -1
- package/dist/shared/dispatch/coordinator.d.ts +170 -0
- package/dist/shared/dispatch/coordinator.d.ts.map +1 -0
- package/dist/shared/dispatch/coordinator.js +177 -0
- package/dist/shared/dispatch/coordinator.js.map +1 -0
- package/dist/shared/dispatch/hybridDispatch.d.ts +74 -0
- package/dist/shared/dispatch/hybridDispatch.d.ts.map +1 -0
- package/dist/shared/dispatch/hybridDispatch.js +54 -0
- package/dist/shared/dispatch/hybridDispatch.js.map +1 -0
- package/dist/shared/dispatch/settledPools.d.ts +21 -0
- package/dist/shared/dispatch/settledPools.d.ts.map +1 -0
- package/dist/shared/dispatch/settledPools.js +34 -0
- package/dist/shared/dispatch/settledPools.js.map +1 -0
- package/dist/shared/index.d.ts +16 -6
- package/dist/shared/index.d.ts.map +1 -1
- package/dist/shared/index.js +15 -3
- package/dist/shared/index.js.map +1 -1
- package/dist/shared/intent/clauseInterpreter.d.ts +19 -0
- package/dist/shared/intent/clauseInterpreter.d.ts.map +1 -1
- package/dist/shared/intent/clauseInterpreter.js +19 -0
- package/dist/shared/intent/clauseInterpreter.js.map +1 -1
- package/dist/shared/io/json.d.ts +25 -0
- package/dist/shared/io/json.d.ts.map +1 -1
- package/dist/shared/io/json.js +67 -0
- package/dist/shared/io/json.js.map +1 -1
- package/dist/shared/providers/opencodeProvider.d.ts.map +1 -1
- package/dist/shared/providers/opencodeProvider.js +12 -0
- package/dist/shared/providers/opencodeProvider.js.map +1 -1
- package/dist/shared/providers/providerConfirmation.d.ts.map +1 -1
- package/dist/shared/providers/providerConfirmation.js +9 -1
- package/dist/shared/providers/providerConfirmation.js.map +1 -1
- package/dist/shared/providers/providerFactory.d.ts +1 -0
- package/dist/shared/providers/providerFactory.d.ts.map +1 -1
- package/dist/shared/providers/providerFactory.js +10 -8
- package/dist/shared/providers/providerFactory.js.map +1 -1
- package/dist/shared/providers/sharedProviderConfirmation.d.ts +134 -0
- package/dist/shared/providers/sharedProviderConfirmation.d.ts.map +1 -0
- package/dist/shared/providers/sharedProviderConfirmation.js +244 -0
- package/dist/shared/providers/sharedProviderConfirmation.js.map +1 -0
- package/dist/shared/quota/apiPool.d.ts +67 -0
- package/dist/shared/quota/apiPool.d.ts.map +1 -0
- package/dist/shared/quota/apiPool.js +139 -0
- package/dist/shared/quota/apiPool.js.map +1 -0
- package/dist/shared/quota/capacity.d.ts +29 -1
- package/dist/shared/quota/capacity.d.ts.map +1 -1
- package/dist/shared/quota/capacity.js +5 -0
- package/dist/shared/quota/capacity.js.map +1 -1
- package/dist/shared/quota/claimRegistry.d.ts +64 -0
- package/dist/shared/quota/claimRegistry.d.ts.map +1 -0
- package/dist/shared/quota/claimRegistry.js +157 -0
- package/dist/shared/quota/claimRegistry.js.map +1 -0
- package/dist/shared/quota/compositeQuotaSource.d.ts +10 -1
- package/dist/shared/quota/compositeQuotaSource.d.ts.map +1 -1
- package/dist/shared/quota/compositeQuotaSource.js +34 -6
- package/dist/shared/quota/compositeQuotaSource.js.map +1 -1
- package/dist/shared/quota/httpQuotaSource.d.ts +9 -1
- package/dist/shared/quota/httpQuotaSource.d.ts.map +1 -1
- package/dist/shared/quota/httpQuotaSource.js +33 -8
- package/dist/shared/quota/httpQuotaSource.js.map +1 -1
- package/dist/shared/quota/learnedQuotaSource.d.ts +7 -1
- package/dist/shared/quota/learnedQuotaSource.d.ts.map +1 -1
- package/dist/shared/quota/learnedQuotaSource.js +18 -7
- package/dist/shared/quota/learnedQuotaSource.js.map +1 -1
- package/dist/shared/quota/quotaSource.d.ts +41 -0
- package/dist/shared/quota/quotaSource.d.ts.map +1 -1
- package/dist/shared/quota/quotaSource.js +19 -0
- package/dist/shared/quota/quotaSource.js.map +1 -1
- package/dist/shared/types/contractPipeline/obligations.d.ts +42 -0
- package/dist/shared/types/contractPipeline/obligations.d.ts.map +1 -1
- package/dist/shared/types/contractPipeline.d.ts +1 -1
- package/dist/shared/types/contractPipeline.d.ts.map +1 -1
- package/dist/shared/types/contractPipeline.js.map +1 -1
- package/dist/shared/types/intentCheckpoint.d.ts +14 -0
- package/dist/shared/types/intentCheckpoint.d.ts.map +1 -1
- package/dist/shared/types/intentCheckpoint.js +10 -0
- package/dist/shared/types/intentCheckpoint.js.map +1 -1
- package/dist/shared/types/sessionConfig.d.ts +60 -0
- package/dist/shared/types/sessionConfig.d.ts.map +1 -1
- package/dist/shared/types/sessionConfig.js +13 -0
- package/dist/shared/types/sessionConfig.js.map +1 -1
- package/package.json +3 -2
|
@@ -2,15 +2,15 @@ import { existsSync } from "node:fs";
|
|
|
2
2
|
import { mkdir, readFile, rename } from "node:fs/promises";
|
|
3
3
|
import { dirname, join, resolve } from "node:path";
|
|
4
4
|
import { StateStore } from "../state/store.js";
|
|
5
|
-
import { readOptionalJsonFile, writeJsonFile, formatValidationIssues, isRecord, withFsRetry, RunLogger, DO_NOT_TOKEN_WRAP_NOTE, DISPATCH_PROMPT_HANDOFF_NOTE, coerceJsonObjectArg, createRollingDispatcher, setQuotaStateDir, interpretFreeFormIntent, advance } from "audit-tools/shared";
|
|
5
|
+
import { readOptionalJsonFile, writeJsonFile, formatValidationIssues, isRecord, withFsRetry, RunLogger, DO_NOT_TOKEN_WRAP_NOTE, DISPATCH_PROMPT_HANDOFF_NOTE, coerceJsonObjectArg, createRollingDispatcher, setQuotaStateDir, interpretFreeFormIntent, advance, planHybridDispatch, readSettledPools, addSettledPool, sourceByPoolId } from "audit-tools/shared";
|
|
6
6
|
import { applyPlanPipeline, buildCoverageLedger } from "../phases/plan.js";
|
|
7
7
|
import { groundExtractedFindings } from "../phases/grounding.js";
|
|
8
8
|
import { runTriagePhase } from "../phases/triage.js";
|
|
9
9
|
import { runClosePhase } from "../phases/close.js";
|
|
10
10
|
import { validateRemediationPlan } from "../validation/remediationState.js";
|
|
11
|
-
import { mergeImplementResults, prepareImplementDispatch, readExtractedPlanIfPresent, buildConfirmedPools,
|
|
11
|
+
import { mergeImplementResults, prepareImplementDispatch, readExtractedPlanIfPresent, buildConfirmedPools, executeNodeInWorktree, blockScopesFromPlan, declaredPathsFromPlan, } from "./dispatch.js";
|
|
12
12
|
import { makeProviderNodeDispatcher } from "./providerNodeDispatch.js";
|
|
13
|
-
import { prepareHostRollingDispatch } from "./rollingSession.js";
|
|
13
|
+
import { prepareHostRollingDispatch, nodeClaimRegistry, nodeSettledPoolsPath } from "./rollingSession.js";
|
|
14
14
|
import { writeCurrentStep } from "./stepWriter.js";
|
|
15
15
|
import { dependencyVerifiedComplete } from "./stepUtils.js";
|
|
16
16
|
import { isTerminalStatus, isVerifiedCompleteStatus, isSkipStatus, } from "../state/itemStatus.js";
|
|
@@ -365,6 +365,28 @@ function resolvesToInProcessDispatchProvider(sessionConfig) {
|
|
|
365
365
|
const provider = sessionConfig?.provider;
|
|
366
366
|
return provider !== undefined && IN_PROCESS_DISPATCH_PROVIDERS.has(provider);
|
|
367
367
|
}
|
|
368
|
+
/**
|
|
369
|
+
* Whether a confirmed pool is one the orchestrator launches IN-PROCESS this cycle
|
|
370
|
+
* (vs. the conversation host's subagent pool). The remediate classification for the
|
|
371
|
+
* shared `planHybridDispatch` split — single-sourced off the same provider set
|
|
372
|
+
* `resolvesToInProcessDispatchProvider` uses.
|
|
373
|
+
*/
|
|
374
|
+
function isInProcessPool(pool) {
|
|
375
|
+
return IN_PROCESS_DISPATCH_PROVIDERS.has(pool.providerName);
|
|
376
|
+
}
|
|
377
|
+
/**
|
|
378
|
+
* Release a node's shared claim on a terminal accept (token-checked through the
|
|
379
|
+
* registry), then drop the held token. Idempotent: a node with no held token (a
|
|
380
|
+
* peer-owned skip, or a double-release) is a no-op. Single-sourced so both the
|
|
381
|
+
* success and the error paths of the in-process driver free the claim identically.
|
|
382
|
+
*/
|
|
383
|
+
async function releaseNodeClaim(registry, claimTokens, blockId) {
|
|
384
|
+
const token = claimTokens.get(blockId);
|
|
385
|
+
if (!token)
|
|
386
|
+
return;
|
|
387
|
+
await registry.release(blockId, token);
|
|
388
|
+
claimTokens.delete(blockId);
|
|
389
|
+
}
|
|
368
390
|
/**
|
|
369
391
|
* Drive the implement phase through the in-process rolling engine. Engages the
|
|
370
392
|
* shared `createRollingDispatcher` (via `driveRollingDispatch`) over
|
|
@@ -410,9 +432,19 @@ export async function driveRollingImplementDispatch(options) {
|
|
|
410
432
|
const promptPathByBlock = new Map(plan.items
|
|
411
433
|
.filter((i) => typeof i.block_id === "string")
|
|
412
434
|
.map((i) => [i.block_id, i.prompt_path]));
|
|
435
|
+
// Confirmed pools: quota-derived concurrency, never the raw host flag (INV-QD-11).
|
|
436
|
+
const confirmedPools = await buildConfirmedPools({
|
|
437
|
+
sessionConfig: options.sessionConfig,
|
|
438
|
+
hostMaxConcurrent: options.waveOptions?.hostMaxConcurrent,
|
|
439
|
+
hostContextTokens: options.waveOptions?.hostContextTokens,
|
|
440
|
+
hostOutputTokens: options.waveOptions?.hostOutputTokens,
|
|
441
|
+
hostModels: options.waveOptions?.hostModels,
|
|
442
|
+
hostModelId: options.waveOptions?.hostModelId,
|
|
443
|
+
});
|
|
413
444
|
// The live per-node worker: the configured provider, launched with the node's
|
|
414
445
|
// worktree-rooted prompt and cwd = its worktree. Tests inject `options.dispatchNode`
|
|
415
|
-
// to exercise the engine without spawning a real worker.
|
|
446
|
+
// to exercise the engine without spawning a real worker. A node on a source-backed
|
|
447
|
+
// pool launches FROM its source's config (A-8 generic dispatchable sources).
|
|
416
448
|
const dispatchNode = options.dispatchNode ??
|
|
417
449
|
makeProviderNodeDispatcher({
|
|
418
450
|
root,
|
|
@@ -420,16 +452,8 @@ export async function driveRollingImplementDispatch(options) {
|
|
|
420
452
|
runId,
|
|
421
453
|
sessionConfig: options.sessionConfig,
|
|
422
454
|
promptPathByBlock,
|
|
455
|
+
sourceByPoolId: sourceByPoolId(confirmedPools),
|
|
423
456
|
});
|
|
424
|
-
// Confirmed pools: quota-derived concurrency, never the raw host flag (INV-QD-11).
|
|
425
|
-
const confirmedPools = await buildConfirmedPools({
|
|
426
|
-
sessionConfig: options.sessionConfig,
|
|
427
|
-
hostMaxConcurrent: options.waveOptions?.hostMaxConcurrent,
|
|
428
|
-
hostContextTokens: options.waveOptions?.hostContextTokens,
|
|
429
|
-
hostOutputTokens: options.waveOptions?.hostOutputTokens,
|
|
430
|
-
hostModels: options.waveOptions?.hostModels,
|
|
431
|
-
hostModelId: options.waveOptions?.hostModelId,
|
|
432
|
-
});
|
|
433
457
|
// Load state to partition the eligible frontier into rolling dependency levels.
|
|
434
458
|
const state = await new StateStore(artifactsDir).loadState();
|
|
435
459
|
if (!state)
|
|
@@ -441,78 +465,64 @@ export async function driveRollingImplementDispatch(options) {
|
|
|
441
465
|
.map((level) => level.filter((b) => plannedBlockIds.has(b.block_id)))
|
|
442
466
|
.filter((level) => level.length > 0);
|
|
443
467
|
const nodeOutcomes = [];
|
|
468
|
+
// The SAME file-backed claim registry the host-subagent driver claims through
|
|
469
|
+
// (`nodeClaimRegistry`, keyed only to run + artifacts dir). Claiming a node here
|
|
470
|
+
// BEFORE creating its worktree makes the in-process and host-subagent drivers
|
|
471
|
+
// mutually exclusive on a node — exactly-one-claimant across both (A-10), the
|
|
472
|
+
// cross-driver double-dispatch guard. Owner tokens are held in-memory for the
|
|
473
|
+
// life of this run: a `rate_limited` re-queue re-enters for the same block, so a
|
|
474
|
+
// node already claimed by THIS driver reuses its token rather than self-colliding.
|
|
475
|
+
const registry = nodeClaimRegistry(artifactsDir, runId);
|
|
476
|
+
const claimTokens = new Map();
|
|
444
477
|
// Per-node worktree dispatch + verify-before-accept, wrapped so the rolling
|
|
445
478
|
// engine's dispatchNode callback always RESOLVES (never rejects).
|
|
446
479
|
const dispatchNodeWithWorktree = async (block, slot) => {
|
|
447
|
-
const branch = worktreeBranchForBlock(block.block_id, runId);
|
|
448
|
-
const wt = worktreePath(root, block.block_id, runId);
|
|
449
480
|
const resultPath = resultPathByBlock.get(block.block_id);
|
|
450
|
-
|
|
451
|
-
|
|
452
|
-
|
|
453
|
-
|
|
454
|
-
|
|
455
|
-
|
|
456
|
-
|
|
457
|
-
|
|
458
|
-
|
|
459
|
-
|
|
460
|
-
|
|
461
|
-
|
|
462
|
-
|
|
463
|
-
|
|
464
|
-
|
|
465
|
-
// source the dispatch plan's write scope is derived from).
|
|
466
|
-
seedUntrackedDeclaredPaths(root, wt, block.touched_files ?? []);
|
|
467
|
-
const result = await dispatchNode({
|
|
468
|
-
block,
|
|
469
|
-
slot,
|
|
470
|
-
worktreeRoot: wt,
|
|
471
|
-
resultPath,
|
|
472
|
-
});
|
|
473
|
-
// Shared post-worker lifecycle (commit → verify → merge), identical to the
|
|
474
|
-
// host-subagent driver's `accept-node` callback. Records the LIFECYCLE
|
|
475
|
-
// outcome but returns the worker's TRANSPORT result to the engine (so a
|
|
476
|
-
// rate_limited worker re-queues; a verify-failure routes to triage via merge).
|
|
477
|
-
// Verify commands are DERIVED from the node's actually-touched tests inside
|
|
478
|
-
// acceptNodeWorktree (post-commit) — omit them here so a host-authored path
|
|
479
|
-
// can't mis-verify. The accept-time write-scope gate likewise adjudicates the
|
|
480
|
-
// node's ACTUAL git edits (ground truth) against all blocks' declared scopes.
|
|
481
|
-
const accept = acceptNodeWorktree({
|
|
482
|
-
root,
|
|
483
|
-
runId,
|
|
484
|
-
blockId: block.block_id,
|
|
485
|
-
worktreeRoot: wt,
|
|
486
|
-
branch,
|
|
487
|
-
workerOutcome: result.outcome,
|
|
488
|
-
scope: { allBlockScopes },
|
|
489
|
-
});
|
|
490
|
-
// Persist the tool-owned verify/merge outcome so finalization blocks a node
|
|
491
|
-
// that self-reported resolved but never actually landed (OBL-DS-06). Parity
|
|
492
|
-
// with the host-subagent driver's `accept-node` callback.
|
|
493
|
-
await recordNodeAcceptOutcome(artifactsDir, runId, block.block_id, accept);
|
|
494
|
-
nodeOutcomes.push({
|
|
495
|
-
block_id: block.block_id,
|
|
496
|
-
outcome: accept.outcome,
|
|
497
|
-
verify_passed: accept.verifyPassed,
|
|
498
|
-
merged: accept.merged,
|
|
499
|
-
});
|
|
500
|
-
return result;
|
|
481
|
+
// Claim BEFORE any worktree work (CE-001). A node THIS driver already holds (a
|
|
482
|
+
// rate_limited re-queue) reuses its token. A node a PEER driver holds is its to
|
|
483
|
+
// run — skip the worker + accept lifecycle entirely so we never double-dispatch
|
|
484
|
+
// or double-merge it; the run-level `mergeImplementResults` reconciles from the
|
|
485
|
+
// peer's accept outcome.
|
|
486
|
+
if (!claimTokens.has(block.block_id)) {
|
|
487
|
+
const claim = await registry.claim(block.block_id, "in-process");
|
|
488
|
+
if (!claim.acquired) {
|
|
489
|
+
nodeOutcomes.push({ block_id: block.block_id, outcome: "success", verify_passed: false, merged: false });
|
|
490
|
+
return {
|
|
491
|
+
packet: { id: block.block_id, payload: { block_id: block.block_id }, estimatedTokens: 0, complexity: 0.5 },
|
|
492
|
+
outcome: "success",
|
|
493
|
+
};
|
|
494
|
+
}
|
|
495
|
+
claimTokens.set(block.block_id, claim.ownerToken);
|
|
501
496
|
}
|
|
502
|
-
|
|
503
|
-
|
|
504
|
-
|
|
505
|
-
|
|
506
|
-
|
|
507
|
-
|
|
508
|
-
|
|
509
|
-
|
|
510
|
-
|
|
511
|
-
|
|
512
|
-
|
|
513
|
-
|
|
514
|
-
|
|
497
|
+
// The shared per-node worktree lifecycle (reset → create → link node_modules →
|
|
498
|
+
// seed → dispatch → commit/verify/write-scope/merge → record), identical to the
|
|
499
|
+
// A-8 hybrid executor and behaviourally to the host-subagent driver's
|
|
500
|
+
// `accept-node` callback. `touched_files` is the block's authoritative declared
|
|
501
|
+
// write set (the source the dispatch plan's write scope is derived from).
|
|
502
|
+
const { result, accept } = await executeNodeInWorktree({
|
|
503
|
+
block,
|
|
504
|
+
slot,
|
|
505
|
+
root,
|
|
506
|
+
artifactsDir,
|
|
507
|
+
runId,
|
|
508
|
+
resultPath,
|
|
509
|
+
seedPaths: block.touched_files ?? [],
|
|
510
|
+
allBlockScopes,
|
|
511
|
+
dispatchNode,
|
|
512
|
+
});
|
|
513
|
+
nodeOutcomes.push({
|
|
514
|
+
block_id: block.block_id,
|
|
515
|
+
outcome: accept.outcome,
|
|
516
|
+
verify_passed: accept.verifyPassed,
|
|
517
|
+
merged: accept.merged,
|
|
518
|
+
});
|
|
519
|
+
// Release the claim ONLY on a terminal accept. A `rate_limited` worker re-queues
|
|
520
|
+
// (still owned work — keep the claim so a peer can't grab it mid-retry); success /
|
|
521
|
+
// error / timeout is terminal → free it through the shared registry (token-checked).
|
|
522
|
+
if (result.outcome !== "rate_limited") {
|
|
523
|
+
await releaseNodeClaim(registry, claimTokens, block.block_id);
|
|
515
524
|
}
|
|
525
|
+
return result;
|
|
516
526
|
};
|
|
517
527
|
await driveRollingDispatch(levels, {
|
|
518
528
|
confirmedPools,
|
|
@@ -531,6 +541,86 @@ export async function driveRollingImplementDispatch(options) {
|
|
|
531
541
|
state_status: merged.status,
|
|
532
542
|
};
|
|
533
543
|
}
|
|
544
|
+
/**
|
|
545
|
+
* Flat per-node input-token estimate for the A-8 hybrid frontier split. The
|
|
546
|
+
* coordinator splits by per-pool SLOTS (concurrency) — host vs. backend — so a
|
|
547
|
+
* uniform estimate is sufficient here; it matches the default the in-process engine
|
|
548
|
+
* uses (`driveRollingDispatch`'s `() => 2000`), keeping the two paths consistent.
|
|
549
|
+
*/
|
|
550
|
+
const HYBRID_NODE_TOKEN_ESTIMATE = 2000;
|
|
551
|
+
/**
|
|
552
|
+
* Run the A-8 coordinator's IN-PROCESS partition this cycle. Each node was already
|
|
553
|
+
* claimed by the coordinator and assigned to a backend pool (NIM / codex / …), so it
|
|
554
|
+
* is launched on THAT pool's provider — binding the slot's providerName to the
|
|
555
|
+
* per-node assignment is what routes a node to its assigned backend — cwd-confined to
|
|
556
|
+
* its worktree, through the shared `executeNodeInWorktree` lifecycle (commit → verify
|
|
557
|
+
* → write-scope → merge); then the coordinator's claim is released.
|
|
558
|
+
*
|
|
559
|
+
* Nodes run concurrently: the coordinator's proactive split already bounded the
|
|
560
|
+
* partition to the backend pools' capacity, so the partition size IS the safe
|
|
561
|
+
* concurrency. The host partition runs in parallel via the host-subagent driver;
|
|
562
|
+
* both write accept-outcome sidecars the run-level `mergeImplementResults` reconciles.
|
|
563
|
+
*
|
|
564
|
+
* Run-once (no in-pass re-queue): a node whose worker rate-limits or errors is not
|
|
565
|
+
* merged (its worktree drops) and is routed to triage by the deterministic merge —
|
|
566
|
+
* bounded, never a livelock. (Cross-cycle settled-pool re-balancing is a follow-up.)
|
|
567
|
+
*/
|
|
568
|
+
export async function executeInProcessPartition(params) {
|
|
569
|
+
const { root, artifactsDir, runId, sessionConfig, partition, plan, coordinator } = params;
|
|
570
|
+
if (partition.length === 0)
|
|
571
|
+
return { nodes: [] };
|
|
572
|
+
const allBlockScopes = blockScopesFromPlan(plan);
|
|
573
|
+
const withBlockId = plan.items.filter((i) => typeof i.block_id === "string");
|
|
574
|
+
const promptPathByBlock = new Map(withBlockId.map((i) => [i.block_id, i.prompt_path]));
|
|
575
|
+
const resultPathByBlock = new Map(withBlockId.map((i) => [i.block_id, i.result_path]));
|
|
576
|
+
const state = await new StateStore(artifactsDir).loadState();
|
|
577
|
+
const blockById = new Map((state?.plan?.blocks ?? []).map((b) => [b.block_id, b]));
|
|
578
|
+
const dispatchNode = params.dispatchNode ??
|
|
579
|
+
makeProviderNodeDispatcher({
|
|
580
|
+
root,
|
|
581
|
+
artifactsDir,
|
|
582
|
+
runId,
|
|
583
|
+
sessionConfig,
|
|
584
|
+
promptPathByBlock,
|
|
585
|
+
sourceByPoolId: params.sourceByPoolId,
|
|
586
|
+
});
|
|
587
|
+
const nodes = await Promise.all(partition.map(async (a) => {
|
|
588
|
+
// `a` IS the coordinator's NodeAssignment — release it directly on terminal.
|
|
589
|
+
const resultPath = resultPathByBlock.get(a.nodeId);
|
|
590
|
+
if (!resultPath) {
|
|
591
|
+
// No prepared prompt/result for this node — release + mark error (the merge
|
|
592
|
+
// routes it to triage); never silently drop a claimed node.
|
|
593
|
+
await coordinator.release(a);
|
|
594
|
+
return { block_id: a.nodeId, outcome: "error", verify_passed: false, merged: false };
|
|
595
|
+
}
|
|
596
|
+
const block = blockById.get(a.nodeId) ?? { block_id: a.nodeId };
|
|
597
|
+
const slot = {
|
|
598
|
+
providerName: a.providerName,
|
|
599
|
+
hostModel: a.hostModel,
|
|
600
|
+
poolId: a.poolId,
|
|
601
|
+
};
|
|
602
|
+
const { accept } = await executeNodeInWorktree({
|
|
603
|
+
block,
|
|
604
|
+
slot,
|
|
605
|
+
root,
|
|
606
|
+
artifactsDir,
|
|
607
|
+
runId,
|
|
608
|
+
resultPath,
|
|
609
|
+
seedPaths: declaredPathsFromPlan(plan, a.nodeId),
|
|
610
|
+
allBlockScopes,
|
|
611
|
+
dispatchNode,
|
|
612
|
+
});
|
|
613
|
+
// Run-once → terminal; free the coordinator claim (token-checked).
|
|
614
|
+
await coordinator.release(a);
|
|
615
|
+
return {
|
|
616
|
+
block_id: a.nodeId,
|
|
617
|
+
outcome: accept.outcome,
|
|
618
|
+
verify_passed: accept.verifyPassed,
|
|
619
|
+
merged: accept.merged,
|
|
620
|
+
};
|
|
621
|
+
}));
|
|
622
|
+
return { nodes };
|
|
623
|
+
}
|
|
534
624
|
/**
|
|
535
625
|
* Whether `root` is the audit-tools monorepo — the repo the tool-owned final
|
|
536
626
|
* gate's suite (INV-RS-10, literally the audit-tools build/check/per-package
|
|
@@ -969,7 +1059,89 @@ async function buildImplementDispatchStep(ctx) {
|
|
|
969
1059
|
return { kind: "transition", state: await store.loadState() };
|
|
970
1060
|
}
|
|
971
1061
|
if (rollingEngineEnabled && canDispatchImpl) {
|
|
972
|
-
|
|
1062
|
+
// A-8 hybrid spill: when an in-process backend pool is ALSO confirmed (a
|
|
1063
|
+
// configured NIM/openai-compatible endpoint alongside the conversation host),
|
|
1064
|
+
// split the eligible frontier across BOTH pool classes via the shared
|
|
1065
|
+
// HybridSpillCoordinator (single claimant, proactive capacity split) — the
|
|
1066
|
+
// orchestrator runs the in-process partition THIS cycle while the host spawns
|
|
1067
|
+
// subagents for its partition. Pure host-subagent dispatch falls out when no
|
|
1068
|
+
// backend pool is confirmed (the coordinator has nothing to split against).
|
|
1069
|
+
const confirmedPools = await buildConfirmedPools({
|
|
1070
|
+
sessionConfig: sessionConfigImpl ?? null,
|
|
1071
|
+
hostMaxConcurrent: options.hostMaxConcurrent,
|
|
1072
|
+
hostContextTokens: options.hostContextTokens,
|
|
1073
|
+
hostOutputTokens: options.hostOutputTokens,
|
|
1074
|
+
hostModels: options.hostModels,
|
|
1075
|
+
hostModelId: options.hostModelId,
|
|
1076
|
+
});
|
|
1077
|
+
const backendPools = confirmedPools.filter(isInProcessPool);
|
|
1078
|
+
let rolling;
|
|
1079
|
+
if (backendPools.length > 0) {
|
|
1080
|
+
// Prepare the frontier ONCE (worktree-rooted prompts) so the coordinator
|
|
1081
|
+
// split, the in-process executor, and the host driver all read the same plan.
|
|
1082
|
+
const plan = await prepareImplementDispatch({ root, artifactsDir }, runId, undefined, {
|
|
1083
|
+
...waveOptsImpl,
|
|
1084
|
+
worktreeRootedPrompts: true,
|
|
1085
|
+
});
|
|
1086
|
+
const frontier = plan.items
|
|
1087
|
+
.filter((i) => typeof i.block_id === "string")
|
|
1088
|
+
.map((i) => ({ id: i.block_id, estimatedTokens: HYBRID_NODE_TOKEN_ESTIMATE }));
|
|
1089
|
+
if (frontier.length === 0) {
|
|
1090
|
+
await mergeImplementResults({ root, artifactsDir }, runId);
|
|
1091
|
+
return { kind: "transition", state: await store.loadState() };
|
|
1092
|
+
}
|
|
1093
|
+
// One coordinator over the shared claim registry splits + claims each node to
|
|
1094
|
+
// exactly one pool. DC-4: the settled set is cross-cycle (persisted) — a backend
|
|
1095
|
+
// pool that exhausted on a prior cycle is excluded here, so its work falls to the
|
|
1096
|
+
// host-subagent pool instead of re-looping on a dead backend.
|
|
1097
|
+
const settledPath = nodeSettledPoolsPath(artifactsDir, runId);
|
|
1098
|
+
const settled = await readSettledPools(settledPath);
|
|
1099
|
+
const partition = await planHybridDispatch({
|
|
1100
|
+
frontier,
|
|
1101
|
+
pools: confirmedPools,
|
|
1102
|
+
sessionConfig: sessionConfigImpl ?? {},
|
|
1103
|
+
claimRegistry: nodeClaimRegistry(artifactsDir, runId),
|
|
1104
|
+
readSettled: () => settled,
|
|
1105
|
+
onSettle: async (id) => {
|
|
1106
|
+
settled.add(id);
|
|
1107
|
+
await addSettledPool(settledPath, id);
|
|
1108
|
+
},
|
|
1109
|
+
isInProcess: isInProcessPool,
|
|
1110
|
+
});
|
|
1111
|
+
// Run the in-process partition now (each node on its assigned backend pool,
|
|
1112
|
+
// launched FROM that pool's source config — A-8 generic dispatchable sources).
|
|
1113
|
+
const inProcessOutcome = await executeInProcessPartition({
|
|
1114
|
+
root,
|
|
1115
|
+
artifactsDir,
|
|
1116
|
+
runId,
|
|
1117
|
+
sessionConfig: sessionConfigImpl ?? null,
|
|
1118
|
+
partition: partition.inProcess,
|
|
1119
|
+
plan,
|
|
1120
|
+
coordinator: partition.coordinator,
|
|
1121
|
+
sourceByPoolId: sourceByPoolId(confirmedPools),
|
|
1122
|
+
});
|
|
1123
|
+
// DC-4: a backend pool whose node rate-limited is exhausted → settle it
|
|
1124
|
+
// (cross-cycle) so the next cycle routes its share to the host pool.
|
|
1125
|
+
const rateLimited = new Set(inProcessOutcome.nodes.filter((n) => n.outcome === "rate_limited").map((n) => n.block_id));
|
|
1126
|
+
const exhaustedPools = new Set(partition.inProcess.filter((a) => rateLimited.has(a.nodeId)).map((a) => a.poolId));
|
|
1127
|
+
for (const poolId of exhaustedPools) {
|
|
1128
|
+
await partition.coordinator.settlePool(poolId);
|
|
1129
|
+
}
|
|
1130
|
+
// The backend carried the whole batch (or every host node was contested by a
|
|
1131
|
+
// peer driver) → nothing for the host this cycle; merge what landed + transition.
|
|
1132
|
+
if (partition.host.length === 0) {
|
|
1133
|
+
await mergeImplementResults({ root, artifactsDir }, runId);
|
|
1134
|
+
return { kind: "transition", state: await store.loadState() };
|
|
1135
|
+
}
|
|
1136
|
+
// Hand the host partition (pre-claimed) to the host-subagent driver.
|
|
1137
|
+
rolling = await prepareHostRollingDispatch({ root, artifactsDir }, runId, waveOptsImpl, {
|
|
1138
|
+
plan,
|
|
1139
|
+
partition: partition.host.map((a) => ({ block_id: a.nodeId, ownerToken: a.ownerToken })),
|
|
1140
|
+
});
|
|
1141
|
+
}
|
|
1142
|
+
else {
|
|
1143
|
+
rolling = await prepareHostRollingDispatch({ root, artifactsDir }, runId, waveOptsImpl);
|
|
1144
|
+
}
|
|
973
1145
|
// Everything eligible may already be done/skipped — fold straight to merge
|
|
974
1146
|
// rather than emitting a dispatch step with zero nodes.
|
|
975
1147
|
if (rolling.session.frontier.length === 0) {
|
|
@@ -2321,7 +2493,7 @@ Only \`scope_summary\` and \`intent_summary\` are required; add the optional fie
|
|
|
2321
2493
|
"confirmed_by": "host",
|
|
2322
2494
|
"scope_summary": "<the files/areas in scope>",
|
|
2323
2495
|
"intent_summary": "<the goal, e.g. full-remediation / security-only>",
|
|
2324
|
-
"free_form_intent": "<optional:
|
|
2496
|
+
"free_form_intent": "<optional: interpreted into lens/priority ordering at planning; never threaded verbatim into worker prompts>",
|
|
2325
2497
|
"filters": {
|
|
2326
2498
|
"severity": ["critical", "high"],
|
|
2327
2499
|
"lenses": ["security", "reliability"],
|