audit-tools 0.32.8 → 0.32.10
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 +4 -2
- package/dist/audit/cli/dispatch/quotaPool.d.ts +20 -2
- package/dist/audit/cli/dispatch/quotaPool.d.ts.map +1 -1
- package/dist/audit/cli/dispatch/quotaPool.js +38 -7
- package/dist/audit/cli/dispatch/quotaPool.js.map +1 -1
- package/dist/audit/cli/dispatch/tierRouting.d.ts +4 -1
- package/dist/audit/cli/dispatch/tierRouting.d.ts.map +1 -1
- package/dist/audit/cli/dispatch/tierRouting.js +7 -4
- package/dist/audit/cli/dispatch/tierRouting.js.map +1 -1
- package/dist/audit/cli/dispatch/types.d.ts +9 -4
- package/dist/audit/cli/dispatch/types.d.ts.map +1 -1
- package/dist/audit/cli/dispatch.d.ts +7 -0
- package/dist/audit/cli/dispatch.d.ts.map +1 -1
- package/dist/audit/cli/dispatch.js +24 -12
- package/dist/audit/cli/dispatch.js.map +1 -1
- package/dist/audit/cli/mergeAndIngestCommand.d.ts +0 -9
- package/dist/audit/cli/mergeAndIngestCommand.d.ts.map +1 -1
- package/dist/audit/cli/mergeAndIngestCommand.js +29 -1
- package/dist/audit/cli/mergeAndIngestCommand.js.map +1 -1
- package/dist/audit/cli/nextStepCommand.d.ts.map +1 -1
- package/dist/audit/cli/nextStepCommand.js +23 -8
- package/dist/audit/cli/nextStepCommand.js.map +1 -1
- package/dist/audit/cli/prompts.d.ts +36 -2
- package/dist/audit/cli/prompts.d.ts.map +1 -1
- package/dist/audit/cli/prompts.js +44 -5
- package/dist/audit/cli/prompts.js.map +1 -1
- package/dist/audit/cli/rollingAuditDispatch.d.ts +18 -1
- package/dist/audit/cli/rollingAuditDispatch.d.ts.map +1 -1
- package/dist/audit/cli/rollingAuditDispatch.js +25 -1
- package/dist/audit/cli/rollingAuditDispatch.js.map +1 -1
- package/dist/audit/cli/semanticReviewStep.d.ts.map +1 -1
- package/dist/audit/cli/semanticReviewStep.js +34 -9
- package/dist/audit/cli/semanticReviewStep.js.map +1 -1
- package/dist/audit/cli/steps.d.ts +22 -12
- package/dist/audit/cli/steps.d.ts.map +1 -1
- package/dist/audit/cli/steps.js +4 -2
- package/dist/audit/cli/steps.js.map +1 -1
- package/dist/audit/orchestrator/rollingDispatch.d.ts +7 -6
- package/dist/audit/orchestrator/rollingDispatch.d.ts.map +1 -1
- package/dist/audit/orchestrator/rollingDispatch.js +37 -19
- package/dist/audit/orchestrator/rollingDispatch.js.map +1 -1
- package/dist/audit/quota/index.d.ts +137 -8
- package/dist/audit/quota/index.d.ts.map +1 -1
- package/dist/audit/quota/index.js +9 -8
- package/dist/audit/quota/index.js.map +1 -1
- package/dist/remediate/dispatch/ownershipRegistry.d.ts +2 -22
- package/dist/remediate/dispatch/ownershipRegistry.d.ts.map +1 -1
- package/dist/remediate/dispatch/ownershipRegistry.js +7 -45
- package/dist/remediate/dispatch/ownershipRegistry.js.map +1 -1
- package/dist/remediate/steps/dispatch.d.ts +22 -1
- package/dist/remediate/steps/dispatch.d.ts.map +1 -1
- package/dist/remediate/steps/dispatch.js +89 -5
- package/dist/remediate/steps/dispatch.js.map +1 -1
- package/dist/remediate/steps/nextStep.d.ts.map +1 -1
- package/dist/remediate/steps/nextStep.js +89 -129
- package/dist/remediate/steps/nextStep.js.map +1 -1
- package/dist/remediate/steps/rollingSession.d.ts +15 -17
- package/dist/remediate/steps/rollingSession.d.ts.map +1 -1
- package/dist/remediate/steps/rollingSession.js +33 -57
- package/dist/remediate/steps/rollingSession.js.map +1 -1
- package/dist/remediate/steps/types.d.ts +12 -4
- package/dist/remediate/steps/types.d.ts.map +1 -1
- package/dist/remediate/steps/types.js +1 -1
- package/dist/shared/dispatch/admissionLoop.d.ts +245 -0
- package/dist/shared/dispatch/admissionLoop.d.ts.map +1 -0
- package/dist/shared/dispatch/admissionLoop.js +204 -0
- package/dist/shared/dispatch/admissionLoop.js.map +1 -0
- package/dist/{remediate → shared}/dispatch/ownershipScheduler.d.ts +23 -2
- package/dist/shared/dispatch/ownershipScheduler.d.ts.map +1 -0
- package/dist/{remediate → shared}/dispatch/ownershipScheduler.js +34 -6
- package/dist/shared/dispatch/ownershipScheduler.js.map +1 -0
- package/dist/shared/dispatch/pathIdentity.d.ts +26 -0
- package/dist/shared/dispatch/pathIdentity.d.ts.map +1 -0
- package/dist/shared/dispatch/pathIdentity.js +49 -0
- package/dist/shared/dispatch/pathIdentity.js.map +1 -0
- package/dist/shared/dispatch/rollingDispatch.d.ts +37 -0
- package/dist/shared/dispatch/rollingDispatch.d.ts.map +1 -1
- package/dist/shared/dispatch/rollingDispatch.js +117 -18
- package/dist/shared/dispatch/rollingDispatch.js.map +1 -1
- package/dist/shared/dispatch/unifiedRolling.d.ts +131 -0
- package/dist/shared/dispatch/unifiedRolling.d.ts.map +1 -0
- package/dist/shared/dispatch/unifiedRolling.js +161 -0
- package/dist/shared/dispatch/unifiedRolling.js.map +1 -0
- package/dist/shared/index.d.ts +13 -2
- package/dist/shared/index.d.ts.map +1 -1
- package/dist/shared/index.js +19 -2
- package/dist/shared/index.js.map +1 -1
- package/dist/shared/quota/claimLease.d.ts.map +1 -1
- package/dist/shared/quota/claimLease.js +8 -1
- package/dist/shared/quota/claimLease.js.map +1 -1
- package/dist/shared/quota/packetCost.d.ts +29 -0
- package/dist/shared/quota/packetCost.d.ts.map +1 -0
- package/dist/shared/quota/packetCost.js +51 -0
- package/dist/shared/quota/packetCost.js.map +1 -0
- package/dist/shared/quota/reservationLedger.d.ts +100 -0
- package/dist/shared/quota/reservationLedger.d.ts.map +1 -0
- package/dist/shared/quota/reservationLedger.js +224 -0
- package/dist/shared/quota/reservationLedger.js.map +1 -0
- package/dist/shared/quota/state.d.ts +20 -0
- package/dist/shared/quota/state.d.ts.map +1 -1
- package/dist/shared/quota/state.js +50 -0
- package/dist/shared/quota/state.js.map +1 -1
- package/dist/shared/quota/types.d.ts +11 -0
- package/dist/shared/quota/types.d.ts.map +1 -1
- package/dist/shared/quota/types.js.map +1 -1
- package/dist/shared/types/sessionConfig.d.ts +10 -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/docs/audit-pkg/contracts.md +5 -4
- package/docs/audit-pkg/operator-guide.md +9 -10
- package/package.json +1 -1
- package/dist/remediate/dispatch/ownershipScheduler.d.ts.map +0 -1
- package/dist/remediate/dispatch/ownershipScheduler.js.map +0 -1
|
@@ -2,7 +2,7 @@ import { existsSync, statSync } 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, writeTextFile, buildAuditDeliverablePair, formatValidationIssues, isRecord, withFsRetry, RunLogger, DISPATCH_PROMPT_HANDOFF_NOTE, renderQuotaCoverageNudge, renderTokenBudgetView, coerceJsonObjectArg,
|
|
5
|
+
import { readOptionalJsonFile, writeJsonFile, writeTextFile, buildAuditDeliverablePair, formatValidationIssues, isRecord, withFsRetry, RunLogger, DISPATCH_PROMPT_HANDOFF_NOTE, renderQuotaCoverageNudge, renderTokenBudgetView, coerceJsonObjectArg, driveRolling, resolveLedgerBudgets, setQuotaStateDir, interpretFreeFormIntent, advance, decideFrictionTriage, buildFrictionTriageBlock, planHybridDispatch, readSettledPools, addSettledPool, sourceByPoolId, classifyProvider, selectDispatchDriver, renderDispatchDriverInstruction, HostSessionQuotaSource, buildProviderModelKey, captureStepBoundaryFriction, LENSES, SEVERITIES, resolveHostProviderName } 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";
|
|
@@ -10,7 +10,6 @@ import { runClosePhase } from "../phases/close.js";
|
|
|
10
10
|
import { validateRemediationPlan } from "../validation/remediationState.js";
|
|
11
11
|
import { mergeImplementResults, prepareImplementDispatch, readExtractedPlanIfPresent, buildConfirmedPools, executeNodeInWorktree, blockScopesFromPlan, declaredPathsFromPlan, targetedCommandsForBlock, } from "./dispatch.js";
|
|
12
12
|
import { makeProviderNodeDispatcher } from "./providerNodeDispatch.js";
|
|
13
|
-
import { ownershipSubWaves } from "../dispatch/ownershipScheduler.js";
|
|
14
13
|
import { prepareHostRollingDispatch, nodeClaimRegistry, nodeSettledPoolsPath } from "./rollingSession.js";
|
|
15
14
|
import { ClaimRegistry } from "../../shared/quota/claimRegistry.js";
|
|
16
15
|
import { claimWithBackoff, withClaimHeartbeat } from "../../shared/quota/claimLease.js";
|
|
@@ -39,19 +38,6 @@ import { ambiguityReviewPrompt, clarificationPrompt, collectIntakeClarifications
|
|
|
39
38
|
// from the canonical enum (the very drift `types/lens.ts` exists to prevent).
|
|
40
39
|
const VALID_LENSES_PROSE = LENSES.map((lens) => `\`${lens}\``).join(", ");
|
|
41
40
|
const VALID_SEVERITIES_PROSE = SEVERITIES.map((sev) => `\`${sev}\``).join(", ");
|
|
42
|
-
/**
|
|
43
|
-
* The host's resolved provider identity for quota-key / driver-classification
|
|
44
|
-
* purposes: the configured `sessionConfig.provider`, defaulting to the
|
|
45
|
-
* conversation host (`claude-code`) when unset or `auto`. Single-sourced so the
|
|
46
|
-
* `?? "claude-code"` default and the `auto`-exclusion live in ONE place rather
|
|
47
|
-
* than being re-spelled (with an ad-hoc cast) at each dispatch call site.
|
|
48
|
-
*/
|
|
49
|
-
function resolveHostProviderName(sessionConfig) {
|
|
50
|
-
const provider = sessionConfig?.provider;
|
|
51
|
-
if (provider === undefined || provider === "auto")
|
|
52
|
-
return "claude-code";
|
|
53
|
-
return provider;
|
|
54
|
-
}
|
|
55
41
|
export function resolveHostDispatchCapability(options) {
|
|
56
42
|
if (options.hostCanDispatchSubagents !== undefined) {
|
|
57
43
|
return options.hostCanDispatchSubagents;
|
|
@@ -339,9 +325,9 @@ function blockedByUnsatisfiedDependency(state) {
|
|
|
339
325
|
// The rolling scheduler replaced the wave-batch shim: instead of dispatching one
|
|
340
326
|
// fixed-size wave per next-step and folding back through `implementing`, a node
|
|
341
327
|
// becomes eligible the instant every dependency reaches a verified-complete
|
|
342
|
-
// disposition (INV-RS-01). Concurrency is
|
|
343
|
-
//
|
|
344
|
-
//
|
|
328
|
+
// disposition (INV-RS-01). Concurrency is emergent from admission control (the
|
|
329
|
+
// `dispatch-quota.json` `admission.granted_packet_ids` the tool admits against the
|
|
330
|
+
// live budget — INV-S05 / INV-QD-11), never a computed wave cap.
|
|
345
331
|
// A shared rebuild is interposed between dependency levels so a downstream node
|
|
346
332
|
// typechecks/runs against the freshly-built upstream `audit-tools/shared`
|
|
347
333
|
// surface; the rebuild is single-flight (CE-001) so the same package is never
|
|
@@ -495,103 +481,69 @@ export async function driveRollingDispatch(levels, options) {
|
|
|
495
481
|
setQuotaStateDir(options.quotaStateDir);
|
|
496
482
|
}
|
|
497
483
|
const estimateTokens = options.estimateTokens ?? (() => 2000);
|
|
498
|
-
const
|
|
499
|
-
|
|
500
|
-
|
|
501
|
-
|
|
502
|
-
|
|
503
|
-
|
|
504
|
-
|
|
505
|
-
|
|
506
|
-
|
|
507
|
-
|
|
508
|
-
|
|
509
|
-
|
|
510
|
-
|
|
511
|
-
|
|
512
|
-
|
|
513
|
-
|
|
514
|
-
|
|
484
|
+
const scopeForBlock = options.scopeForBlock ?? ((b) => b.touched_files ?? []);
|
|
485
|
+
const allBlocks = levels.flat();
|
|
486
|
+
const blockById = new Map(allBlocks.map((b) => [b.block_id, b]));
|
|
487
|
+
const hostSession = options.hostSession;
|
|
488
|
+
// Admission control: when a metered pool reports a finite absolute budget, the shared
|
|
489
|
+
// reservation ledger leases each node's cost before dispatch, so co-located dispatch
|
|
490
|
+
// loops on one account (a second IDE, the host + its subagents) never collectively
|
|
491
|
+
// over-admit. On the claude-code host (percent-only quota, no finite ceiling) the ledger
|
|
492
|
+
// is omitted and the reactive 429 floor is the safety — no per-dispatch lock overhead.
|
|
493
|
+
const ledgerCfg = resolveLedgerBudgets({
|
|
494
|
+
pools: options.confirmedPools,
|
|
495
|
+
sessionConfig: options.sessionConfig,
|
|
496
|
+
pendingItemTokens: allBlocks.map((b) => estimateTokens(b)),
|
|
497
|
+
});
|
|
498
|
+
// The remediate terminal adapter over the unified shared driver. What stays here is
|
|
499
|
+
// remediate's projection: a block → its ownership node (declared write-scope, so the
|
|
500
|
+
// shared `ownershipSubWaves` serializes same-file nodes / parallelizes disjoint ones)
|
|
501
|
+
// and → its dispatch packet; the shared rebuild between dependency levels; and the
|
|
502
|
+
// host-session escalation wiring. The loop, sub-wave split, and quota_paused/empty_pool
|
|
503
|
+
// terminal merge are the unified driver's.
|
|
504
|
+
const run = await driveRolling({
|
|
505
|
+
levels,
|
|
506
|
+
confirmedPools: options.confirmedPools,
|
|
507
|
+
sessionConfig: options.sessionConfig,
|
|
508
|
+
...(ledgerCfg.reservationLedger
|
|
509
|
+
? {
|
|
510
|
+
reservationLedger: ledgerCfg.reservationLedger,
|
|
511
|
+
resolvePoolBudget: ledgerCfg.resolvePoolBudget,
|
|
512
|
+
resolveOutputReservation: (_packet, poolId) => ledgerCfg.resolveOutputReservation(poolId),
|
|
515
513
|
}
|
|
516
|
-
|
|
517
|
-
|
|
518
|
-
const scopeForBlock = options.scopeForBlock ?? ((b) => b.touched_files ?? []);
|
|
519
|
-
// File-ownership-disjoint admission (INV-SOO): split the level into ordered
|
|
520
|
-
// sub-waves, each a maximal file-disjoint subset (deterministic block_id
|
|
521
|
-
// tie-break AFTER the disjointness filter, INV-SOO-08). Same-file nodes land
|
|
522
|
-
// in successive sub-waves (serialize, INV-SOO-01/02) and a freed file becomes
|
|
523
|
-
// schedulable for the next sub-wave; different-file nodes share a sub-wave and
|
|
524
|
-
// parallelize up to the quota cap the dispatcher enforces (INV-SOO-03/05).
|
|
525
|
-
const subWaves = ownershipSubWaves(level.map((b) => ({
|
|
514
|
+
: {}),
|
|
515
|
+
toNode: (b) => ({
|
|
526
516
|
block_id: b.block_id,
|
|
527
517
|
write_paths: scopeForBlock(b),
|
|
528
518
|
...(b.cofile_parallel_safe !== undefined
|
|
529
519
|
? { cofile_parallel_safe: b.cofile_parallel_safe }
|
|
530
520
|
: {}),
|
|
531
|
-
})
|
|
532
|
-
|
|
533
|
-
|
|
534
|
-
|
|
535
|
-
|
|
536
|
-
|
|
537
|
-
|
|
538
|
-
|
|
539
|
-
|
|
540
|
-
|
|
541
|
-
|
|
542
|
-
|
|
543
|
-
|
|
544
|
-
|
|
545
|
-
|
|
546
|
-
|
|
547
|
-
|
|
548
|
-
|
|
549
|
-
|
|
550
|
-
|
|
551
|
-
|
|
552
|
-
|
|
553
|
-
|
|
554
|
-
|
|
555
|
-
|
|
556
|
-
|
|
557
|
-
});
|
|
558
|
-
dispatcher.enqueue(packets);
|
|
559
|
-
levelResults.push(...(await dispatcher.run()));
|
|
560
|
-
// Piece D: surface the wave's partial-completion terminal (quota_paused /
|
|
561
|
-
// empty_pool). Aggregate across waves keeping the EARLIEST-reset quota_paused
|
|
562
|
-
// terminal (a paused wave is retryable and should resume soonest); union the
|
|
563
|
-
// stranded ids so a later step re-dispatches all of them. An empty_pool
|
|
564
|
-
// terminal only stands when no quota_paused terminal was seen.
|
|
565
|
-
out.terminal = mergePartialTerminals(out.terminal, dispatcher.getTerminal());
|
|
566
|
-
}
|
|
567
|
-
out.levels.push({ blockIds: level.map((b) => b.block_id), results: levelResults });
|
|
568
|
-
}
|
|
569
|
-
return out;
|
|
570
|
-
}
|
|
571
|
-
/**
|
|
572
|
-
* Fold a newly-observed partial-completion terminal into the run's aggregate
|
|
573
|
-
* (piece D). Prefers `quota_paused` (retryable) over `empty_pool`; among
|
|
574
|
-
* quota_paused terminals keeps the EARLIEST `earliest_reset_at`; always unions the
|
|
575
|
-
* stranded ids so no stranded node is lost across waves.
|
|
576
|
-
*/
|
|
577
|
-
function mergePartialTerminals(prior, next) {
|
|
578
|
-
if (!next)
|
|
579
|
-
return prior;
|
|
580
|
-
if (!prior)
|
|
581
|
-
return next;
|
|
582
|
-
const strandedIds = [...new Set([...prior.stranded_ids, ...next.stranded_ids])];
|
|
583
|
-
const priorPaused = prior.reason === "quota_paused";
|
|
584
|
-
const nextPaused = next.reason === "quota_paused";
|
|
585
|
-
if (priorPaused || nextPaused) {
|
|
586
|
-
const resets = [prior.earliest_reset_at, next.earliest_reset_at].filter((r) => typeof r === "string");
|
|
587
|
-
const earliest = resets.length > 0 ? resets.sort()[0] : undefined;
|
|
588
|
-
return {
|
|
589
|
-
reason: "quota_paused",
|
|
590
|
-
stranded_ids: strandedIds,
|
|
591
|
-
...(earliest ? { earliest_reset_at: earliest } : {}),
|
|
592
|
-
};
|
|
593
|
-
}
|
|
594
|
-
return { reason: prior.reason, stranded_ids: strandedIds };
|
|
521
|
+
}),
|
|
522
|
+
toPacket: (b) => ({
|
|
523
|
+
id: b.block_id,
|
|
524
|
+
payload: { block_id: b.block_id },
|
|
525
|
+
estimatedTokens: estimateTokens(b),
|
|
526
|
+
complexity: 0.5,
|
|
527
|
+
}),
|
|
528
|
+
dispatchPacket: async (packet, slot) => options.dispatchNode(blockById.get(packet.payload.block_id), slot),
|
|
529
|
+
...(options.root !== undefined ? { root: options.root } : {}),
|
|
530
|
+
// Single-flight (CE-001) is enforced by the unified driver.
|
|
531
|
+
rebuildBetweenLevels: options.rebuildSharedBetweenLevels,
|
|
532
|
+
// Host-session escalation: feed recordLimit (write) at the rate_limited observation
|
|
533
|
+
// point and read isEscalated (strand-not-requeue). The SAME instance sized the pools,
|
|
534
|
+
// so the bounded re-limit count is account-wide.
|
|
535
|
+
...(hostSession
|
|
536
|
+
? {
|
|
537
|
+
recordRateLimit: (packet, result) => hostSession.recordLimit(result.rateLimit?.channel ?? "error", result.rateLimit?.text ?? "", packet.id),
|
|
538
|
+
isPacketEscalated: (packetId) => hostSession.isEscalated(packetId),
|
|
539
|
+
}
|
|
540
|
+
: {}),
|
|
541
|
+
});
|
|
542
|
+
return {
|
|
543
|
+
levels: run.levels.map((l) => ({ blockIds: l.nodeIds, results: l.results })),
|
|
544
|
+
rebuilds: run.rebuilds,
|
|
545
|
+
...(run.terminal ? { terminal: run.terminal } : {}),
|
|
546
|
+
};
|
|
595
547
|
}
|
|
596
548
|
/**
|
|
597
549
|
* Backends the orchestrator can drive IN-PROCESS as the per-node implement worker
|
|
@@ -665,6 +617,10 @@ export async function driveRollingImplementDispatch(options) {
|
|
|
665
617
|
hostModelId: options.waveOptions?.hostModelId,
|
|
666
618
|
// Each node runs in its own worktree, so its prompt is rooted there.
|
|
667
619
|
worktreeRootedPrompts: true,
|
|
620
|
+
// The in-process rolling engine admits + leases per-packet itself, so the
|
|
621
|
+
// dispatch-quota grant here must NOT lease (a host grant lease would
|
|
622
|
+
// double-count the same work against the shared account budget).
|
|
623
|
+
grantLeases: false,
|
|
668
624
|
});
|
|
669
625
|
if (plan.items.length === 0) {
|
|
670
626
|
return null;
|
|
@@ -1366,7 +1322,10 @@ async function buildImplementDispatchStep(ctx) {
|
|
|
1366
1322
|
const driverSelection = selectDispatchDriver({
|
|
1367
1323
|
classification: classifyProvider(hostProvider),
|
|
1368
1324
|
eligibleItemCount: rolling.session.frontier.length,
|
|
1369
|
-
|
|
1325
|
+
// The granted set's size IS the instantaneous admission width — there is no
|
|
1326
|
+
// separate concurrency number. The whole granted set runs at once, so the
|
|
1327
|
+
// driver-selection "slots" is the granted-set size.
|
|
1328
|
+
slots: rolling.session.frontier.length,
|
|
1370
1329
|
});
|
|
1371
1330
|
const rollMerge = loaderCommand(`merge-implement-results --run-id ${runId}`);
|
|
1372
1331
|
const rollNext = loaderCommand("next-step");
|
|
@@ -1383,21 +1342,23 @@ async function buildImplementDispatchStep(ctx) {
|
|
|
1383
1342
|
prompt: `
|
|
1384
1343
|
# Dispatch Implementation Work (host-subagent rolling, worktree-isolated)
|
|
1385
1344
|
|
|
1386
|
-
Each
|
|
1345
|
+
Each granted node runs in its OWN git worktree (hard isolation between nodes). The
|
|
1387
1346
|
TOOL owns commit -> verify -> merge + write-scope; you only spawn a subagent per
|
|
1388
1347
|
node and call \`accept-node\` as each finishes.
|
|
1389
1348
|
|
|
1390
|
-
|
|
1391
|
-
|
|
1349
|
+
The tool ADMITTED this set against the live budget (and any declared in-flight cap):
|
|
1350
|
+
dispatch EXACTLY the ${rolling.session.frontier.length} node(s) below and no more.
|
|
1351
|
+
Their count is the whole grant — there is no separate concurrency cap. When they are
|
|
1352
|
+
all accepted, merge and re-invoke next-step; the tool re-grants the pending remainder.
|
|
1392
1353
|
|
|
1393
|
-
${renderDispatchDriverInstruction(driverSelection,
|
|
1354
|
+
${renderDispatchDriverInstruction(driverSelection, `the ${rolling.session.frontier.length} granted node(s)`)}
|
|
1394
1355
|
|
|
1395
|
-
Spawn ONE subagent for EACH
|
|
1356
|
+
Spawn ONE subagent for EACH granted node below. Give the subagent that node's
|
|
1396
1357
|
\`prompt\`, and set its working directory to the node's **worktree** path. The
|
|
1397
1358
|
subagent edits source files INSIDE that worktree and writes ONLY its result file.
|
|
1398
1359
|
Do NOT let any subagent edit the main repository tree.
|
|
1399
1360
|
|
|
1400
|
-
|
|
1361
|
+
Granted nodes (worktrees already created):
|
|
1401
1362
|
${nodeLines}
|
|
1402
1363
|
|
|
1403
1364
|
As EACH subagent finishes, run (substituting the finished node's block id):
|
|
@@ -1406,11 +1367,8 @@ As EACH subagent finishes, run (substituting the finished node's block id):
|
|
|
1406
1367
|
|
|
1407
1368
|
It runs the commit -> verify -> merge lifecycle for that node and prints a JSON
|
|
1408
1369
|
directive on stdout:
|
|
1409
|
-
- \`{"directive":"
|
|
1410
|
-
|
|
1411
|
-
${rolling.session.slots} running.
|
|
1412
|
-
- \`{"directive":"wait",...}\` — other nodes are still in flight; do not spawn more yet.
|
|
1413
|
-
- \`{"directive":"done",...}\` — every node has been accepted. Then run:
|
|
1370
|
+
- \`{"directive":"wait",...}\` — other granted nodes are still in flight; do not spawn more.
|
|
1371
|
+
- \`{"directive":"done",...}\` — every granted node has been accepted. Then run:
|
|
1414
1372
|
|
|
1415
1373
|
\`${rollMerge}\`
|
|
1416
1374
|
|
|
@@ -1431,8 +1389,8 @@ ${DISPATCH_PROMPT_HANDOFF_NOTE}
|
|
|
1431
1389
|
}
|
|
1432
1390
|
// Rolling per-node dispatch: prepare EVERY currently-eligible node (deps all
|
|
1433
1391
|
// verified-complete), never a single artificially-serialized block. There is
|
|
1434
|
-
// no wave-size cap — concurrency is
|
|
1435
|
-
// (`dispatch-quota.json`
|
|
1392
|
+
// no wave-size cap — concurrency is emergent from admission control
|
|
1393
|
+
// (`dispatch-quota.json` `admission.granted_packet_ids`). `prepareImplementDispatch`
|
|
1436
1394
|
// itself only admits verified-complete-eligible blocks
|
|
1437
1395
|
// (`dependencyVerifiedComplete`), so this is the rolling-eligible frontier.
|
|
1438
1396
|
const dispatchPlan = await prepareImplementDispatch({ root, artifactsDir }, runId, undefined, waveOptsImpl);
|
|
@@ -1504,15 +1462,17 @@ Read the dispatch plan and quota JSONs:
|
|
|
1504
1462
|
\`${implQuotaPath}\`
|
|
1505
1463
|
|
|
1506
1464
|
Every item in \`items\` is a node whose dependencies are all VERIFIED-COMPLETE
|
|
1507
|
-
(INV-RS-01)
|
|
1508
|
-
|
|
1509
|
-
|
|
1465
|
+
(INV-RS-01). The tool admitted a budget-bounded subset: dispatch EXACTLY the block
|
|
1466
|
+
ids in the quota file's \`admission.granted_packet_ids\` and no others — that granted
|
|
1467
|
+
set is the whole grant (there is no separate concurrency cap). Each item's
|
|
1510
1468
|
\`model_hint.tier\` suggests which model to use (small/standard/deep). If your
|
|
1511
1469
|
provider has rate limits, pace launches accordingly.
|
|
1512
1470
|
|
|
1513
|
-
For each item in \`items
|
|
1514
|
-
\`prompt_path\`. Each subagent may edit source
|
|
1515
|
-
block and must write only its assigned \`result_path\`.
|
|
1471
|
+
For each GRANTED item in \`items\` (its \`block_id\` in \`admission.granted_packet_ids\`),
|
|
1472
|
+
dispatch one subagent with that item's \`prompt_path\`. Each subagent may edit source
|
|
1473
|
+
files needed for that bounded block and must write only its assigned \`result_path\`.
|
|
1474
|
+
After the granted set is merged and you run next-step, the tool re-grants the pending
|
|
1475
|
+
remainder.
|
|
1516
1476
|
|
|
1517
1477
|
${SHARED_REBUILD_BETWEEN_LEVELS_NOTE}
|
|
1518
1478
|
|