akm-cli 0.9.31 → 0.10.261001-beta
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/CHANGELOG.md +409 -0
- package/STABILITY.md +99 -57
- package/dist/api-node.mjs +20 -0
- package/dist/api.d.ts +23 -0
- package/dist/api.js +45 -0
- package/dist/assets/hints/cli-hints-full.md +15 -29
- package/dist/assets/hints/cli-hints-short.md +2 -2
- package/dist/assets/improve-strategies/default.json +3 -3
- package/dist/assets/prompts/consolidate-pair.md +9 -7
- package/dist/assets/stash-skeleton/README.md +1 -1
- package/dist/assets/tasks/improve/akm-improve-consolidate.yml +1 -1
- package/dist/cli/shared.js +31 -48
- package/dist/cli/unknown-flags.js +6 -43
- package/dist/cli.js +8 -34
- package/dist/commands/agent/contribute-cli.js +1 -1
- package/dist/commands/completions.js +1 -2
- package/dist/commands/config-cli.js +5 -28
- package/dist/commands/env/env-cli.js +5 -5
- package/dist/commands/env/secret-cli.js +2 -2
- package/dist/commands/feedback-cli.js +75 -49
- package/dist/commands/health/checks.js +41 -16
- package/dist/commands/health/plugin-staleness.js +52 -22
- package/dist/commands/improve/autonomy-gate.js +5 -3
- package/dist/commands/improve/consolidate/chunking.js +3 -10
- package/dist/commands/improve/consolidate/continuity-check.js +4 -4
- package/dist/commands/improve/consolidate/pair-pass.js +29 -10
- package/dist/commands/improve/consolidate.js +10 -65
- package/dist/commands/improve/distill/lesson-update.js +91 -0
- package/dist/commands/improve/distill-guards.js +1 -43
- package/dist/commands/improve/distill.js +148 -21
- package/dist/commands/improve/eligibility.js +14 -1
- package/dist/commands/improve/extract-cli.js +1 -1
- package/dist/commands/improve/extract-prompt.js +17 -7
- package/dist/commands/improve/extract.js +5 -7
- package/dist/commands/improve/improve-cli.js +92 -106
- package/dist/commands/improve/improve-strategies.js +8 -12
- package/dist/commands/improve/improve.js +134 -52
- package/dist/commands/improve/loop-stages.js +18 -5
- package/dist/commands/improve/planner.js +0 -1
- package/dist/commands/improve/preparation.js +40 -99
- package/dist/commands/improve/proactive-maintenance.js +2 -2
- package/dist/commands/improve/reflect-noise.js +3 -55
- package/dist/commands/improve/reflect.js +29 -34
- package/dist/commands/improve/retrieval-queries.js +37 -0
- package/dist/commands/improve/retrieval-scope.js +2 -2
- package/dist/commands/improve/salience.js +2 -9
- package/dist/commands/improve/session-asset.js +4 -1
- package/dist/commands/improve/stage.js +23 -12
- package/dist/commands/lint/index.js +5 -20
- package/dist/commands/observability-cli.js +3 -9
- package/dist/commands/proposal/drain.js +43 -6
- package/dist/commands/proposal/proposal-cli.js +11 -31
- package/dist/commands/proposal/repository.js +20 -14
- package/dist/commands/proposal/validators/proposal-quality-validators.js +0 -3
- package/dist/commands/read/knowledge.js +3 -3
- package/dist/commands/read/remember-cli.js +0 -13
- package/dist/commands/read/search-cli.js +29 -97
- package/dist/commands/read/search.js +0 -11
- package/dist/commands/read/show.js +0 -19
- package/dist/commands/sources/plugin-upgrade.js +164 -101
- package/dist/commands/sources/sources-cli.js +1 -15
- package/dist/commands/sources/stash-cli.js +6 -14
- package/dist/commands/tasks/tasks-cli.js +4 -115
- package/dist/commands/workflow/plan.js +11 -33
- package/dist/commands/workflow-cli.js +2 -7
- package/dist/core/adapter/adapter-ids.js +1 -2
- package/dist/core/adapter/adapters/agent-skills-adapter.js +0 -4
- package/dist/core/adapter/adapters/akm-adapter.js +9 -48
- package/dist/core/adapter/adapters/akm-lint.js +10 -26
- package/dist/core/adapter/adapters/akm-metadata.js +2 -72
- package/dist/core/adapter/adapters/akm-task-adapter.js +0 -5
- package/dist/core/adapter/adapters/akm-workflow-adapter.js +9 -28
- package/dist/core/adapter/adapters/dotenv-adapter.js +1 -20
- package/dist/core/adapter/adapters/generic-files-adapter.js +0 -10
- package/dist/core/adapter/adapters/llm-wiki-adapter.js +0 -4
- package/dist/core/adapter/adapters/tool-dir-shared.js +0 -24
- package/dist/core/adapter/adapters/website-snapshot-adapter.js +4 -4
- package/dist/core/adapter/recognize-match.js +3 -4
- package/dist/core/common.js +18 -2
- package/dist/core/config/config-schema.js +12 -15
- package/dist/core/config/config-types.js +6 -15
- package/dist/core/config/config-walker.js +6 -23
- package/dist/core/config/config.js +17 -80
- package/dist/core/config/engine-semantics.js +7 -3
- package/dist/core/config/schema/engines.js +7 -7
- package/dist/core/config/schema/improve-processes.js +10 -63
- package/dist/core/config/schema/improve.js +2 -21
- package/dist/core/config/schema/index-config.js +6 -39
- package/dist/core/env-secret-ref.js +2 -45
- package/dist/core/errors.js +1 -2
- package/dist/core/extra-params.js +2 -86
- package/dist/core/file-lock.js +2 -6
- package/dist/core/improve-result.js +13 -1
- package/dist/core/mutation-target.js +2 -2
- package/dist/core/paths.js +4 -0
- package/dist/core/recognition-util.js +4 -5
- package/dist/core/run-lock.js +1 -1
- package/dist/core/subprocess.js +49 -0
- package/dist/core/warn.js +21 -4
- package/dist/core/write-source.js +3 -3
- package/dist/execution/target-ref.js +1 -2
- package/dist/indexer/indexer.js +15 -6
- package/dist/indexer/passes/memory-inference.js +15 -6
- package/dist/indexer/search/db-search.js +49 -36
- package/dist/indexer/search/fts-query.js +0 -19
- package/dist/indexer/search/ranking.js +29 -1
- package/dist/indexer/usage/usage-events.js +21 -16
- package/dist/integrations/agent/engine-resolution.js +5 -2
- package/dist/integrations/agent/index.js +1 -1
- package/dist/integrations/agent/profiles.js +41 -98
- package/dist/integrations/agent/request-lowering.js +3 -3
- package/dist/integrations/harnesses/aider/index.js +5 -0
- package/dist/integrations/harnesses/amazonq/index.js +1 -0
- package/dist/integrations/harnesses/claude/config-import.js +2 -3
- package/dist/integrations/harnesses/claude/index.js +5 -0
- package/dist/integrations/harnesses/codex/index.js +1 -0
- package/dist/integrations/harnesses/copilot/index.js +1 -0
- package/dist/integrations/harnesses/gemini/index.js +1 -0
- package/dist/integrations/harnesses/index.js +18 -39
- package/dist/integrations/harnesses/opencode/agent-builder.js +60 -35
- package/dist/integrations/harnesses/opencode/config-import.js +121 -52
- package/dist/integrations/harnesses/opencode/index.js +22 -0
- package/dist/integrations/harnesses/opencode/plugin-layout.js +68 -0
- package/dist/integrations/harnesses/opencode/session-log-types.js +12 -0
- package/dist/integrations/harnesses/opencode/session-log-v1.js +72 -0
- package/dist/integrations/harnesses/opencode/session-log-v2.js +140 -0
- package/dist/integrations/harnesses/opencode/session-log.js +104 -115
- package/dist/integrations/harnesses/opencode/version.js +98 -0
- package/dist/integrations/harnesses/opencode-sdk/{harness.js → index.js} +5 -17
- package/dist/integrations/harnesses/opencode-sdk/sdk-runner.js +105 -206
- package/dist/integrations/harnesses/opencode-sdk/v1-adapter.js +199 -0
- package/dist/integrations/harnesses/opencode-sdk/v2-adapter.js +254 -0
- package/dist/integrations/harnesses/opencode-sdk/wire.js +66 -0
- package/dist/integrations/harnesses/openhands/index.js +1 -0
- package/dist/integrations/harnesses/pi/index.js +1 -0
- package/dist/integrations/harnesses/shared.js +2 -0
- package/dist/integrations/harnesses/types.js +2 -0
- package/dist/llm/client.js +41 -11
- package/dist/output/cli-hints.js +1 -1
- package/dist/output/context.js +10 -19
- package/dist/output/format-exempt.js +5 -8
- package/dist/output/shapes/helpers.js +1 -22
- package/dist/output/shapes/passthrough.js +0 -1
- package/dist/output/shapes.js +3 -31
- package/dist/output/stdout.js +5 -1
- package/dist/output/text/command-format.js +6 -1
- package/dist/output/text/workflow-format.js +4 -22
- package/dist/scripts/akm-migrate-node.js +15745 -16711
- package/dist/scripts/akm-migrate.js +16318 -17280
- package/dist/setup/detect.js +10 -4
- package/dist/setup/harness-config-import.js +5 -14
- package/dist/setup/setup.js +1 -1
- package/dist/setup/steps/tasks.js +12 -13
- package/dist/sources/providers/git-stash.js +94 -13
- package/dist/storage/repositories/index-utility-repository.js +2 -32
- package/dist/storage/repositories/index-vec-repository.js +4 -0
- package/dist/storage/repositories/task-history-repository.js +0 -5
- package/dist/storage/repositories/workflow-runs-repository.js +5 -15
- package/dist/tasks/embedded.js +2 -1
- package/dist/tasks/run/run-command-task.js +9 -1
- package/dist/tasks/run/run-workflow-task.js +4 -3
- package/dist/tasks/run/task-history.js +0 -2
- package/dist/tasks/run/task-log.js +38 -1
- package/dist/tasks/schedule-gates.js +39 -0
- package/dist/tasks/scheduler-binding.js +0 -16
- package/dist/tasks/scheduler-sync.js +3 -97
- package/dist/tasks/source/bounded-document.js +1 -1
- package/dist/tasks/source/task-source-v4.js +2 -12
- package/dist/tasks/task-run-reserved-flags.js +2 -31
- package/dist/workflows/authoring/authoring.js +6 -6
- package/dist/workflows/compile.js +8 -65
- package/dist/workflows/exec/child-workflow.js +22 -3
- package/dist/workflows/exec/native-executor.js +12 -147
- package/dist/workflows/exec/run-workflow.js +31 -103
- package/dist/workflows/exec/step-work.js +9 -178
- package/dist/workflows/exec/unit-dispatch.js +0 -10
- package/dist/workflows/freeze/environment.js +3 -44
- package/dist/workflows/freeze/freeze.js +8 -41
- package/dist/workflows/freeze/targets/child-workflow.js +27 -63
- package/dist/workflows/freeze/targets/command.js +2 -16
- package/dist/workflows/freeze/targets/shell.js +0 -2
- package/dist/workflows/freeze/task-bindings.js +2 -28
- package/dist/workflows/ir/params.js +12 -2
- package/dist/workflows/parser.js +82 -232
- package/dist/workflows/plan.js +1 -1
- package/dist/workflows/renderer.js +10 -19
- package/dist/workflows/runtime/run-plan.js +11 -53
- package/dist/workflows/runtime/runs.js +4 -27
- package/dist/workflows/source-files.js +12 -16
- package/dist/workflows/source-semantics.js +1 -93
- package/docs/README.md +1 -1
- package/docs/agents/README.md +3 -3
- package/docs/agents/agent-install.md +1 -1
- package/docs/architecture/0.10-process-rules.md +827 -0
- package/docs/architecture/adapters.md +17 -31
- package/docs/architecture/architecture.md +4 -5
- package/docs/architecture/improvement.md +115 -76
- package/docs/architecture/internals/improve-workflow.md +22 -16
- package/docs/architecture/internals/indexing.md +1 -1
- package/docs/architecture/internals/search.md +1 -2
- package/docs/architecture/internals/storage-locations.md +6 -6
- package/docs/architecture/persisted-data-compat.md +13 -4
- package/docs/architecture/specs/0.9.0-decisions.md +6 -2
- package/docs/architecture/specs/improve-collapse-churn-detector-design.md +2 -2
- package/docs/architecture/specs/workflow-format-unification.md +1 -6
- package/docs/architecture/testing/manual-testing-checklist.md +21 -30
- package/docs/architecture/testing/task-workflow-manual-tests.md +106 -203
- package/docs/architecture/testing/testing-workflow.md +1 -1
- package/docs/architecture/workflow-engine.md +5 -56
- package/docs/guides/author-workflows.md +39 -69
- package/docs/guides/bundles.md +3 -1
- package/docs/guides/capture-knowledge.md +1 -1
- package/docs/guides/claude-code-vs-akm-workflows.md +1 -2
- package/docs/guides/discover-and-load.md +4 -4
- package/docs/guides/getting-started.md +1 -1
- package/docs/guides/improve-the-library.md +10 -9
- package/docs/guides/recipes/headless-install.md +2 -2
- package/docs/guides/run-workflows.md +1 -1
- package/docs/guides/scheduling.md +9 -1
- package/docs/guides/use-with-any-agent.md +49 -4
- package/docs/integration/bundling-akm.md +7 -6
- package/docs/maintainers/curate-workmap.md +1 -2
- package/docs/maintainers/release-checklist.md +33 -1
- package/docs/migration/README.md +2 -0
- package/docs/migration/release-notes/0.10.0.md +201 -0
- package/docs/migration/release-notes/README.md +1 -0
- package/docs/migration/v0.9-to-v0.10.md +507 -0
- package/docs/posts/akm-command-tour.md +3 -3
- package/docs/product-surface.md +1 -1
- package/docs/reference/README.md +1 -0
- package/docs/reference/api.md +65 -0
- package/docs/reference/asset-types.md +3 -5
- package/docs/reference/bundle-types.md +2 -4
- package/docs/reference/cli.md +160 -188
- package/docs/reference/configuration.md +101 -20
- package/docs/reference/data-and-telemetry.md +2 -2
- package/docs/reference/env-and-secrets.md +2 -2
- package/docs/reference/supported-formats.md +3 -4
- package/docs/reference/tasks.md +12 -19
- package/docs/reference/website-sources.md +1 -1
- package/docs/reference/workflow-schema.md +89 -317
- package/docs/reference/workflows.md +20 -48
- package/package.json +12 -2
- package/schemas/akm-config.json +2 -243
- package/schemas/akm-task.json +2 -3
- package/schemas/akm-workflow.json +47 -84
- package/dist/assets/improve-strategies/catchup.json +0 -41
- package/dist/assets/improve-strategies/proactive-maintenance.json +0 -24
- package/dist/assets/improve-strategies/quick.json +0 -17
- package/dist/assets/improve-strategies/reflect-distill.json +0 -29
- package/dist/assets/improve-strategies/thorough.json +0 -49
- package/dist/assets/prompts/retrieval-relevance-judge.md +0 -6
- package/dist/assets/tasks/improve/akm-improve-catchup.yml +0 -6
- package/dist/assets/tasks/improve/akm-improve-frequent.yml +0 -4
- package/dist/assets/tasks/improve/akm-improve-nightly.yml +0 -4
- package/dist/cli/retired-commands.js +0 -118
- package/dist/commands/improve/retrieval-gate.js +0 -132
- package/dist/commands/tasks/validate.js +0 -176
- package/dist/core/config/legacy-source-shape-shim.js +0 -117
- package/dist/indexer/feedback/utility-policy.js +0 -75
- package/dist/integrations/harnesses/ids.js +0 -28
- package/dist/tasks/source/task-source-v3-frozen.js +0 -424
- package/dist/tasks/source/task-to-v4.js +0 -863
- package/dist/workflows/exec/worktree.js +0 -372
- package/dist/workflows/freeze/child-output-references.js +0 -83
- package/dist/workflows/freeze/identity.js +0 -22
- package/dist/workflows/freeze/targets/script.js +0 -50
- package/dist/workflows/freeze/targets/task.js +0 -124
- package/dist/workflows/github-yaml.js +0 -506
- package/dist/workflows/runtime/run-outputs.js +0 -80
|
@@ -20,8 +20,6 @@
|
|
|
20
20
|
* gate loops journal under `~r<n>` / `~l<n>`.
|
|
21
21
|
* - `onError: "fail"` fails on any unit failure, `"continue"` lets the gate
|
|
22
22
|
* decide; `retry: { max, on }` re-dispatches listed failure reasons.
|
|
23
|
-
* - `isolation: worktree` runs each attempt of an agent/sdk unit in a fresh
|
|
24
|
-
* detached worktree (removed when clean, kept when dirty); llm units reject it.
|
|
25
23
|
* - Run `budget` ceilings are consumed per actual dispatch and abort the step
|
|
26
24
|
* when crossed, regardless of `on_error`.
|
|
27
25
|
*/
|
|
@@ -52,9 +50,7 @@ import { scheduleUnits } from "./scheduler.js";
|
|
|
52
50
|
// path. This module dispatches; step-work.ts owns the pure decisions.
|
|
53
51
|
import { clip, computeStepWorkList, reduceEmptyStep, reduceStepOutcomes, stepOutputsFromEvidence, unitOutcomeFromRow, } from "./step-work.js";
|
|
54
52
|
import { dispatchWorkflowExecution, } from "./unit-dispatch.js";
|
|
55
|
-
import { cleanupFrozenScript, frozenScriptCommand, materializeFrozenScript } from "../../tasks/frozen-script.js";
|
|
56
53
|
import { enqueueUnitWrite } from "./unit-writer.js";
|
|
57
|
-
import { assertGitWorkTree, cleanupUnitWorktree, createUnitWorktree } from "./worktree.js";
|
|
58
54
|
/**
|
|
59
55
|
* Per-step dispatch budget against the run's `budget` ceilings, seeded from
|
|
60
56
|
* the journal and consumed per actual dispatch (never by a reused row).
|
|
@@ -171,7 +167,7 @@ function openDispatchBudget(ctx, dispatched) {
|
|
|
171
167
|
}
|
|
172
168
|
/** Resolve the live-at-dispatch prerequisites once, after durable-row reuse is known. */
|
|
173
169
|
async function prepareStepDispatchPrerequisites(input) {
|
|
174
|
-
const { plan,
|
|
170
|
+
const { plan, workUnits, willDispatch, dispatched } = input;
|
|
175
171
|
let env;
|
|
176
172
|
let sensitiveValues;
|
|
177
173
|
const frozenEnvironment = workUnits[0]?.environment;
|
|
@@ -195,35 +191,10 @@ async function prepareStepDispatchPrerequisites(input) {
|
|
|
195
191
|
};
|
|
196
192
|
}
|
|
197
193
|
}
|
|
198
|
-
let worktreeBase;
|
|
199
|
-
if (willDispatch && template.isolation === "worktree") {
|
|
200
|
-
if (template.frozenTarget.kind === "command" && template.frozenTarget.runner.kind === "llm") {
|
|
201
|
-
return {
|
|
202
|
-
ok: false,
|
|
203
|
-
result: failedStep(dispatched, `Step "${plan.stepId}" declares isolation: worktree on an llm unit — the llm runner has no ` +
|
|
204
|
-
`working directory to isolate. Use the agent or sdk runner for worktree-isolated units.`),
|
|
205
|
-
};
|
|
206
|
-
}
|
|
207
|
-
const target = workUnits[0]?.frozenTarget;
|
|
208
|
-
const frozenCwd = target && "cwdIdentity" in target ? target.cwdIdentity : undefined;
|
|
209
|
-
if (frozenCwd)
|
|
210
|
-
assertFrozenDirectoryContained(frozenCwd);
|
|
211
|
-
const base = frozenCwd?.realRoot ?? ctx.workDir ?? process.cwd();
|
|
212
|
-
const preflightWorktree = ctx.preflightWorktree ?? assertGitWorkTree;
|
|
213
|
-
const gitError = preflightWorktree(base);
|
|
214
|
-
if (gitError !== undefined) {
|
|
215
|
-
return {
|
|
216
|
-
ok: false,
|
|
217
|
-
result: failedStep(dispatched, `Step "${plan.stepId}" cannot use isolation: worktree: ${gitError}`),
|
|
218
|
-
};
|
|
219
|
-
}
|
|
220
|
-
worktreeBase = base;
|
|
221
|
-
}
|
|
222
194
|
return {
|
|
223
195
|
ok: true,
|
|
224
196
|
...(env ? { env } : {}),
|
|
225
197
|
...(sensitiveValues ? { sensitiveValues } : {}),
|
|
226
|
-
...(worktreeBase !== undefined ? { worktreeBase } : {}),
|
|
227
198
|
};
|
|
228
199
|
}
|
|
229
200
|
async function executeStepPlanInConnection(plan, ctx) {
|
|
@@ -251,32 +222,25 @@ async function executeStepPlanInConnection(plan, ctx) {
|
|
|
251
222
|
const dispatcher = ctx.dispatcher ?? defaultUnitDispatcher;
|
|
252
223
|
// Load journaled rows first: a completed unit is reused, never re-dispatched.
|
|
253
224
|
const completedRows = indexCompletedRows(await withWorkflowRunsRepo((repo) => repo.getUnitsForStep(ctx.runId, plan.stepId)));
|
|
254
|
-
// Env resolution
|
|
255
|
-
//
|
|
256
|
-
//
|
|
225
|
+
// Env resolution runs only when some unit will dispatch, so a fully-journaled
|
|
226
|
+
// step resumes even if an env asset is gone. Every unit is classified once;
|
|
227
|
+
// the preflight and dispatch share it.
|
|
257
228
|
const reuseDecisions = workUnits.map((unit) => classifyUnitReuse(unit, completedRows));
|
|
258
229
|
const willDispatch = reuseDecisions.some((decision) => decision.kind === "dispatch");
|
|
259
230
|
const prerequisites = await prepareStepDispatchPrerequisites({
|
|
260
231
|
plan,
|
|
261
|
-
template,
|
|
262
232
|
workUnits,
|
|
263
|
-
ctx,
|
|
264
233
|
willDispatch,
|
|
265
234
|
dispatched,
|
|
266
235
|
});
|
|
267
236
|
if (!prerequisites.ok)
|
|
268
237
|
return prerequisites.result;
|
|
269
|
-
const { env, sensitiveValues
|
|
238
|
+
const { env, sensitiveValues } = prerequisites;
|
|
270
239
|
// Budget ceilings + lifetime-cap accounting, and the budget-chained abort
|
|
271
240
|
// signal they trip. Extracted verbatim (behavior-identical) — see
|
|
272
241
|
// {@link openDispatchBudget}.
|
|
273
242
|
const { signal, budget, unchainSignal } = openDispatchBudget(ctx, dispatched);
|
|
274
243
|
let outcomes;
|
|
275
|
-
// Worktree removals started by finished units and awaited below, before this
|
|
276
|
-
// step reports anything. Cleanup is best-effort, but "the step resolved" must
|
|
277
|
-
// still mean "its clean worktrees are gone" — only the WAIT moves off the
|
|
278
|
-
// unit's scheduler slot, not the guarantee.
|
|
279
|
-
const pendingWorktreeCleanups = [];
|
|
280
244
|
const frozenTargetConcurrency = template.frozenTarget.kind === "command" ? template.frozenTarget.concurrency : undefined;
|
|
281
245
|
try {
|
|
282
246
|
outcomes = await scheduleUnits(workUnits, (workUnit, index) => runUnit({
|
|
@@ -284,12 +248,10 @@ async function executeStepPlanInConnection(plan, ctx) {
|
|
|
284
248
|
workUnit,
|
|
285
249
|
env,
|
|
286
250
|
sensitiveValues,
|
|
287
|
-
...(worktreeBase !== undefined ? { worktreeBase } : {}),
|
|
288
251
|
ctx,
|
|
289
252
|
signal,
|
|
290
253
|
dispatcher,
|
|
291
254
|
reuse: reuseDecisions[index],
|
|
292
|
-
pendingWorktreeCleanups,
|
|
293
255
|
budget,
|
|
294
256
|
}), {
|
|
295
257
|
concurrency: workList.list.concurrency,
|
|
@@ -300,9 +262,6 @@ async function executeStepPlanInConnection(plan, ctx) {
|
|
|
300
262
|
}
|
|
301
263
|
finally {
|
|
302
264
|
unchainSignal?.();
|
|
303
|
-
// The barrier. `allSettled` because each task already swallowed its own
|
|
304
|
-
// failure into a warn — nothing here can fail the step.
|
|
305
|
-
await Promise.allSettled(pendingWorktreeCleanups);
|
|
306
265
|
}
|
|
307
266
|
// Capture live-only diagnostics BEFORE any hard reduction replaces the unit
|
|
308
267
|
// list with a failed-step envelope. Budget/cap and journal-write failures
|
|
@@ -358,11 +317,9 @@ async function runUnit(input) {
|
|
|
358
317
|
prompt,
|
|
359
318
|
frozenTarget: workUnit.frozenTarget,
|
|
360
319
|
...(workUnit.execContext ? { execContext: workUnit.execContext } : {}),
|
|
361
|
-
//
|
|
362
|
-
//
|
|
363
|
-
//
|
|
364
|
-
// it: handing an agent unit a cwd it never had would change harness
|
|
365
|
-
// behavior, and the agent path already takes its cwd from its profile.
|
|
320
|
+
// An exec unit spawns in the engine invocation's working directory. Only
|
|
321
|
+
// exec units get it: handing an agent unit a cwd it never had would change
|
|
322
|
+
// harness behavior, and the agent path already takes its cwd from its profile.
|
|
366
323
|
...(workUnit.frozenTarget.kind !== "command" && ctx.workDir !== undefined ? { cwd: ctx.workDir } : {}),
|
|
367
324
|
timeoutMs: workUnit.timeoutMs,
|
|
368
325
|
...(workUnit.schema ? { schema: workUnit.schema } : {}),
|
|
@@ -370,7 +327,7 @@ async function runUnit(input) {
|
|
|
370
327
|
...(sensitiveValues ? { sensitiveValues } : {}),
|
|
371
328
|
...(input.signal ? { signal: input.signal } : {}),
|
|
372
329
|
// F-1: forwarded to exec-unit.ts's childEnv for a
|
|
373
|
-
// "
|
|
330
|
+
// "shell" unit, and to dispatchWorkflowExecution's
|
|
374
331
|
// runExecution eventSource option (unit-dispatch.ts)
|
|
375
332
|
// for a "command" unit — both arms observe it.
|
|
376
333
|
...(ctx.eventSource !== undefined ? { eventSource: ctx.eventSource } : {}),
|
|
@@ -424,8 +381,6 @@ async function runUnit(input) {
|
|
|
424
381
|
request: { ...request, unitId: attemptId },
|
|
425
382
|
attemptId,
|
|
426
383
|
inputHash,
|
|
427
|
-
...(input.worktreeBase !== undefined ? { worktreeBase: input.worktreeBase } : {}),
|
|
428
|
-
pendingWorktreeCleanups: input.pendingWorktreeCleanups,
|
|
429
384
|
});
|
|
430
385
|
// Attempts use `~r<n>` journal suffixes while durable step evidence remains
|
|
431
386
|
// attached to the content-derived base identity.
|
|
@@ -463,30 +418,7 @@ function journaledUnitResultJson(outcome) {
|
|
|
463
418
|
return null;
|
|
464
419
|
return JSON.stringify(clip(parts.join("\n--- unit output ---\n"), WORKFLOW_UNIT_DIAGNOSTIC_CLIP));
|
|
465
420
|
}
|
|
466
|
-
async function
|
|
467
|
-
if (input.worktreeBase === undefined)
|
|
468
|
-
return { ok: true, request: input.request };
|
|
469
|
-
const created = await createUnitWorktree(input.worktreeBase, input.ctx.runId, input.attemptId,
|
|
470
|
-
// A child-workflow target has no commit of its own (its prepared worktree goes unused).
|
|
471
|
-
input.workUnit.frozenTarget.kind === "child-workflow" ? undefined : input.workUnit.frozenTarget.gitCommitOid);
|
|
472
|
-
if (created.preservedLeftover !== undefined) {
|
|
473
|
-
warn(`Workflow unit ${input.attemptId}: a previous attempt left uncollected work in its isolation worktree; ` +
|
|
474
|
-
`preserved at ${created.preservedLeftover}`);
|
|
475
|
-
}
|
|
476
|
-
if (!created.ok) {
|
|
477
|
-
return {
|
|
478
|
-
ok: false,
|
|
479
|
-
outcome: {
|
|
480
|
-
unitId: input.request.unitId,
|
|
481
|
-
ok: false,
|
|
482
|
-
failureReason: "worktree_failed",
|
|
483
|
-
error: created.error,
|
|
484
|
-
},
|
|
485
|
-
};
|
|
486
|
-
}
|
|
487
|
-
return { ok: true, request: { ...input.request, cwd: created.path }, worktreePath: created.path };
|
|
488
|
-
}
|
|
489
|
-
async function reserveJournaledDispatch(input, worktreePath, startedAt) {
|
|
421
|
+
async function reserveJournaledDispatch(input, startedAt) {
|
|
490
422
|
const { plan, workUnit, ctx, attemptId, inputHash } = input;
|
|
491
423
|
let durableAttempt;
|
|
492
424
|
await enqueueUnitWrite(async () => {
|
|
@@ -503,7 +435,6 @@ async function reserveJournaledDispatch(input, worktreePath, startedAt) {
|
|
|
503
435
|
engine: target.kind === "command" ? target.request.engine.name : null,
|
|
504
436
|
model: target.kind === "command" ? (target.request.model?.resolved ?? null) : null,
|
|
505
437
|
inputHash,
|
|
506
|
-
worktreePath: worktreePath ?? null,
|
|
507
438
|
now: startedAt,
|
|
508
439
|
}).attempt;
|
|
509
440
|
});
|
|
@@ -538,37 +469,14 @@ async function finishJournaledDispatch(input) {
|
|
|
538
469
|
}
|
|
539
470
|
}));
|
|
540
471
|
}
|
|
541
|
-
function queueAttemptWorktreeCleanup(input, worktreePath) {
|
|
542
|
-
if (worktreePath === undefined || input.worktreeBase === undefined)
|
|
543
|
-
return;
|
|
544
|
-
const worktreeBase = input.worktreeBase;
|
|
545
|
-
input.pendingWorktreeCleanups.push((async () => {
|
|
546
|
-
try {
|
|
547
|
-
const cleanup = await cleanupUnitWorktree(worktreeBase, worktreePath);
|
|
548
|
-
if (cleanup.dirty) {
|
|
549
|
-
warn(`Workflow unit ${input.attemptId} left uncommitted changes in its isolation worktree; retained at ${worktreePath}`);
|
|
550
|
-
}
|
|
551
|
-
else if (!cleanup.removed) {
|
|
552
|
-
warn(`Workflow unit ${input.attemptId}: could not clean up isolation worktree ${worktreePath}: ${cleanup.error}`);
|
|
553
|
-
}
|
|
554
|
-
}
|
|
555
|
-
catch (err) {
|
|
556
|
-
warn(`Workflow unit ${input.attemptId}: could not clean up isolation worktree ${worktreePath}: ${message(err)}`);
|
|
557
|
-
}
|
|
558
|
-
})());
|
|
559
|
-
}
|
|
560
472
|
/** Journal one dispatch attempt: insert row, events, dispatch, finish row. */
|
|
561
473
|
async function dispatchJournaledAttempt(input) {
|
|
562
474
|
const { workUnit, ctx, dispatcher, attemptId } = input;
|
|
563
|
-
|
|
564
|
-
if (!prepared.ok)
|
|
565
|
-
return prepared.outcome;
|
|
566
|
-
let { request } = prepared;
|
|
567
|
-
const { worktreePath } = prepared;
|
|
475
|
+
let { request } = input;
|
|
568
476
|
const startedAt = new Date().toISOString();
|
|
569
477
|
let durableAttempt;
|
|
570
478
|
try {
|
|
571
|
-
durableAttempt = await reserveJournaledDispatch(input,
|
|
479
|
+
durableAttempt = await reserveJournaledDispatch(input, startedAt);
|
|
572
480
|
}
|
|
573
481
|
catch (err) {
|
|
574
482
|
// A failed dispatch-row insert means NOTHING dispatched (the row is the
|
|
@@ -576,8 +484,6 @@ async function dispatchJournaledAttempt(input) {
|
|
|
576
484
|
// letting the throw escape into the scheduler, where a swallowed worker
|
|
577
485
|
// error is indistinguishable from "never claimed" and used to be
|
|
578
486
|
// misreported as an aborted, never-dispatched unit.
|
|
579
|
-
if (worktreePath !== undefined && input.worktreeBase !== undefined)
|
|
580
|
-
await cleanupUnitWorktree(input.worktreeBase, worktreePath);
|
|
581
487
|
return {
|
|
582
488
|
unitId: request.unitId,
|
|
583
489
|
ok: false,
|
|
@@ -632,9 +538,6 @@ async function dispatchJournaledAttempt(input) {
|
|
|
632
538
|
catch (err) {
|
|
633
539
|
journalError = err;
|
|
634
540
|
}
|
|
635
|
-
// A clean worktree is removed, a dirty one kept and logged. Cleanup starts
|
|
636
|
-
// here and is awaited at the step barrier, never holding this scheduler slot.
|
|
637
|
-
queueAttemptWorktreeCleanup(input, worktreePath);
|
|
638
541
|
// A journal-write failure AFTER a successful dispatch is its own loud
|
|
639
542
|
// failure class: the unit's work ran (and may have succeeded), but its
|
|
640
543
|
// terminal state could not be recorded, so the row may be stuck `running`.
|
|
@@ -796,44 +699,6 @@ async function dispatchUnit(request, dispatcher) {
|
|
|
796
699
|
*/
|
|
797
700
|
export const defaultUnitDispatcher = async (request, feedback) => {
|
|
798
701
|
const frozenTarget = request.frozenTarget;
|
|
799
|
-
if (frozenTarget.kind === "script") {
|
|
800
|
-
assertFrozenDirectoryContained(frozenTarget.cwdIdentity);
|
|
801
|
-
const materialized = materializeFrozenScript({
|
|
802
|
-
sourceRef: frozenTarget.ref,
|
|
803
|
-
interpreter: frozenTarget.interpreter,
|
|
804
|
-
extension: frozenTarget.extension,
|
|
805
|
-
bytesBase64: frozenTarget.bytesBase64,
|
|
806
|
-
byteLength: frozenTarget.byteLength,
|
|
807
|
-
sha256: frozenTarget.contentHash,
|
|
808
|
-
});
|
|
809
|
-
try {
|
|
810
|
-
const command = frozenScriptCommand({
|
|
811
|
-
sourceRef: frozenTarget.ref,
|
|
812
|
-
interpreter: frozenTarget.interpreter,
|
|
813
|
-
extension: frozenTarget.extension,
|
|
814
|
-
bytesBase64: frozenTarget.bytesBase64,
|
|
815
|
-
byteLength: frozenTarget.byteLength,
|
|
816
|
-
sha256: frozenTarget.contentHash,
|
|
817
|
-
}, materialized.file);
|
|
818
|
-
return await runExecUnit({
|
|
819
|
-
unitId: request.unitId,
|
|
820
|
-
exec: {
|
|
821
|
-
...frozenTarget.exec,
|
|
822
|
-
command: command,
|
|
823
|
-
},
|
|
824
|
-
baseDir: request.cwd ?? frozenTarget.cwdIdentity.realCwd,
|
|
825
|
-
...(request.env ? { env: request.env } : {}),
|
|
826
|
-
...(request.execContext ? { context: request.execContext } : {}),
|
|
827
|
-
...(request.schema ? { hasOutputSchema: true } : {}),
|
|
828
|
-
timeoutMs: request.timeoutMs,
|
|
829
|
-
...(request.signal ? { signal: request.signal } : {}),
|
|
830
|
-
...(request.eventSource !== undefined ? { eventSource: request.eventSource } : {}),
|
|
831
|
-
});
|
|
832
|
-
}
|
|
833
|
-
finally {
|
|
834
|
-
cleanupFrozenScript(materialized);
|
|
835
|
-
}
|
|
836
|
-
}
|
|
837
702
|
if (frozenTarget.kind === "shell") {
|
|
838
703
|
if (frozenTarget.cwdIdentity)
|
|
839
704
|
assertFrozenDirectoryContained(frozenTarget.cwdIdentity);
|
|
@@ -21,16 +21,15 @@ import { resolveStorageLocations } from "../../storage/locations.js";
|
|
|
21
21
|
import { withWorkflowRunsConnection, withWorkflowRunsRepo } from "../../storage/repositories/workflow-runs-repository.js";
|
|
22
22
|
import { computePlanHash } from "../ir/plan-hash.js";
|
|
23
23
|
import { decodeWorkflowPlan, readRunPlan } from "../runtime/run-plan.js";
|
|
24
|
-
import { abandonWorkflowRun,
|
|
24
|
+
import { abandonWorkflowRun, getNextWorkflowStep, resumeWorkflowRun } from "../runtime/runs.js";
|
|
25
25
|
import { loadWorkflowAsset } from "../runtime/workflow-asset-loader.js";
|
|
26
26
|
import { frozenSummaryJudge } from "./frozen-judge.js";
|
|
27
27
|
import { mergeLoweringNotices } from "./lowering-notices.js";
|
|
28
28
|
import { defaultUnitDispatcher, executeStepPlan, } from "./native-executor.js";
|
|
29
|
-
// Shared step semantics —
|
|
30
|
-
// gate-evaluation journaling, and the whole step-completion path
|
|
29
|
+
// Shared step semantics — gate-evaluation journaling, and the whole step-completion path
|
|
31
30
|
// (`finalizeExecutedStep`) live in step-work.ts as ONE implementation, so the
|
|
32
31
|
// fresh-execution and resume paths cannot drift from each other.
|
|
33
|
-
import { activeGateLoop, blockStepForJudgeFailure,
|
|
32
|
+
import { activeGateLoop, blockStepForJudgeFailure, effectiveGateMaxLoops, finalizeExecutedStep, recoverGateFeedback, referencedStepIds, } from "./step-work.js";
|
|
34
33
|
export async function runWorkflowSteps(options) {
|
|
35
34
|
let target = options.target;
|
|
36
35
|
let params = options.params;
|
|
@@ -72,8 +71,8 @@ export async function runWorkflowSteps(options) {
|
|
|
72
71
|
}
|
|
73
72
|
if (remainingSteps !== undefined) {
|
|
74
73
|
// The step budget is DISTINCT PROCESSED STEPS, not `executed` entries:
|
|
75
|
-
// gate loops of one step
|
|
76
|
-
//
|
|
74
|
+
// gate loops of one step must not shrink a retry's remaining budget
|
|
75
|
+
// (they never counted against maxSteps either).
|
|
77
76
|
remainingSteps -= result.stepsProcessed;
|
|
78
77
|
if (remainingSteps <= 0)
|
|
79
78
|
return aggregate;
|
|
@@ -262,28 +261,6 @@ async function loadAuthoritativeRunPlan(options, next, stored) {
|
|
|
262
261
|
}
|
|
263
262
|
return stored;
|
|
264
263
|
}
|
|
265
|
-
/**
|
|
266
|
-
* Complete a branch target no completed router selected as `skipped` — no
|
|
267
|
-
* dispatch, no gate loop, and (per the `maxSteps` contract) no step consumed.
|
|
268
|
-
* Returns the re-read spine state so the caller can continue its walk.
|
|
269
|
-
*/
|
|
270
|
-
async function skipUnselectedRouteTarget(input) {
|
|
271
|
-
const { runId, stepId, stepPlan, skipInfo, routeUnselected, executed } = input;
|
|
272
|
-
// Cascade: a skipped step that is ITSELF a router
|
|
273
|
-
// never evaluates its route, so none of its declared targets were
|
|
274
|
-
// selected — mark them all skip-on-reach too (a target another
|
|
275
|
-
// completed router selects stays protected via routeSelected). Without
|
|
276
|
-
// this, every branch of the skipped router would run unconditionally.
|
|
277
|
-
if (stepPlan.route) {
|
|
278
|
-
cascadeSkippedRouter(stepPlan.route, stepId, routeUnselected);
|
|
279
|
-
}
|
|
280
|
-
const notes = skipInfo.selected === null
|
|
281
|
-
? `Skipped by route: step "${skipInfo.router}" was itself skipped, so none of its branch targets run.`
|
|
282
|
-
: `Skipped by route: step "${skipInfo.router}" selected "${skipInfo.selected}".`;
|
|
283
|
-
executed.push({ stepId, ok: true, unitCount: 0, failedUnits: 0, summary: notes });
|
|
284
|
-
await completeWorkflowStep({ runId, stepId, status: "skipped", notes });
|
|
285
|
-
return getNextWorkflowStep(runId);
|
|
286
|
-
}
|
|
287
264
|
/**
|
|
288
265
|
* Seed a step's starting gate loop and feedback from its journaled gate rows,
|
|
289
266
|
* so a run interrupted after a rejection resumes at the next loop with the
|
|
@@ -307,41 +284,31 @@ async function recoverGateLoopState(runId, stepPlan) {
|
|
|
307
284
|
*/
|
|
308
285
|
const STEP_FINISHED_KINDS = new Set(["advanced", "failed", "gate-exhausted"]);
|
|
309
286
|
/**
|
|
310
|
-
* One attempt at a step's work
|
|
311
|
-
*
|
|
312
|
-
* `finalizeExecutedStep`. Everything else executes its subgraph through the
|
|
313
|
-
* native executor.
|
|
287
|
+
* One attempt at a step's work: execute its subgraph through the native
|
|
288
|
+
* executor.
|
|
314
289
|
*/
|
|
315
290
|
async function executeStepSubgraph(ctx, loop) {
|
|
316
|
-
const { options, next, plan, stepPlan,
|
|
291
|
+
const { options, next, plan, stepPlan, evidence } = ctx;
|
|
317
292
|
const { gateLoop, gateFeedback, unitsDispatched, tokensUsed } = loop;
|
|
318
|
-
return
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
...(gateFeedback ? { gateFeedback } : {}),
|
|
338
|
-
// F-1: threaded to an exec unit's child env;
|
|
339
|
-
// undefined for every non-task caller (byte-identical, RunWorkflowOptions doc).
|
|
340
|
-
...(options.eventSource !== undefined ? { eventSource: options.eventSource } : {}),
|
|
341
|
-
...(options.signal ? { signal: options.signal } : {}),
|
|
342
|
-
...(options.dispatcher ? { dispatcher: options.dispatcher } : {}),
|
|
343
|
-
maxConcurrency: Math.min(options.maxConcurrency ?? Number.POSITIVE_INFINITY, plan.execution?.maxConcurrency ?? 1),
|
|
344
|
-
});
|
|
293
|
+
return await executeStepPlan(stepPlan, {
|
|
294
|
+
runId: next.run.id,
|
|
295
|
+
workflowRef: next.run.workflowRef,
|
|
296
|
+
params: next.run.params ?? {},
|
|
297
|
+
evidence,
|
|
298
|
+
unitsDispatched,
|
|
299
|
+
tokensUsed,
|
|
300
|
+
// Budget ceilings ride the FROZEN plan: a mid-run
|
|
301
|
+
// asset edit can never loosen or tighten a run's budget.
|
|
302
|
+
...(plan.budget ? { budget: plan.budget } : {}),
|
|
303
|
+
gateLoop,
|
|
304
|
+
...(gateFeedback ? { gateFeedback } : {}),
|
|
305
|
+
// F-1: threaded to an exec unit's child env;
|
|
306
|
+
// undefined for every non-task caller (byte-identical, RunWorkflowOptions doc).
|
|
307
|
+
...(options.eventSource !== undefined ? { eventSource: options.eventSource } : {}),
|
|
308
|
+
...(options.signal ? { signal: options.signal } : {}),
|
|
309
|
+
...(options.dispatcher ? { dispatcher: options.dispatcher } : {}),
|
|
310
|
+
maxConcurrency: Math.min(options.maxConcurrency ?? Number.POSITIVE_INFINITY, plan.execution?.maxConcurrency ?? 1),
|
|
311
|
+
});
|
|
345
312
|
}
|
|
346
313
|
/**
|
|
347
314
|
* Drive one step's bounded gate loop: a rejection with loops left re-executes
|
|
@@ -349,7 +316,7 @@ async function executeStepSubgraph(ctx, loop) {
|
|
|
349
316
|
* decision and running budget totals; `ctx.executed` is appended in place.
|
|
350
317
|
*/
|
|
351
318
|
async function runStepGateLoop(ctx, gate, totals) {
|
|
352
|
-
const { options, next, stepPlan, step,
|
|
319
|
+
const { options, next, stepPlan, step, executed } = ctx;
|
|
353
320
|
const { summaryJudge } = ctx;
|
|
354
321
|
const { startLoop, maxLoops } = gate;
|
|
355
322
|
let { unitsDispatched, tokensUsed } = totals;
|
|
@@ -376,7 +343,7 @@ async function runStepGateLoop(ctx, gate, totals) {
|
|
|
376
343
|
summary: result.summary,
|
|
377
344
|
...(result.notices ? { notices: result.notices } : {}),
|
|
378
345
|
});
|
|
379
|
-
//
|
|
346
|
+
// Gate and advance: the shared completion path, live or resumed.
|
|
380
347
|
let finalize;
|
|
381
348
|
try {
|
|
382
349
|
finalize = await finalizeExecutedStep({
|
|
@@ -388,10 +355,6 @@ async function runStepGateLoop(ctx, gate, totals) {
|
|
|
388
355
|
gateLoop,
|
|
389
356
|
loopsRemaining: gateLoop < maxLoops,
|
|
390
357
|
result,
|
|
391
|
-
priorEvidence: evidence,
|
|
392
|
-
params: next.run.params ?? {},
|
|
393
|
-
routeSelected,
|
|
394
|
-
routeUnselected,
|
|
395
358
|
summaryJudge,
|
|
396
359
|
signal: options.signal,
|
|
397
360
|
});
|
|
@@ -413,10 +376,6 @@ async function runStepGateLoop(ctx, gate, totals) {
|
|
|
413
376
|
// when some later reference can read it (`referencedStepIds`).
|
|
414
377
|
if (ctx.liveEvidenceConsumers.has(step.id))
|
|
415
378
|
ctx.liveEvidence.set(step.id, result.evidence);
|
|
416
|
-
// A route-only step's summary IS its decision (finalize surfaces it).
|
|
417
|
-
if (finalize.summaryOverride !== undefined) {
|
|
418
|
-
executed[executed.length - 1] = { ...executed[executed.length - 1], summary: finalize.summaryOverride };
|
|
419
|
-
}
|
|
420
379
|
return outcome({ kind: "advanced" });
|
|
421
380
|
}
|
|
422
381
|
if (finalize.kind === "judge-failed") {
|
|
@@ -451,11 +410,6 @@ async function runStepGateLoop(ctx, gate, totals) {
|
|
|
451
410
|
});
|
|
452
411
|
}
|
|
453
412
|
if (finalize.kind === "failed") {
|
|
454
|
-
// A route-failure was pushed as ok:true (the units succeeded); reflect
|
|
455
|
-
// the deterministic route failure in the executed report.
|
|
456
|
-
if (finalize.routeFailure) {
|
|
457
|
-
executed[executed.length - 1] = { ...executed[executed.length - 1], ok: false, summary: finalize.summary };
|
|
458
|
-
}
|
|
459
413
|
return outcome({ kind: "failed" });
|
|
460
414
|
}
|
|
461
415
|
// gate-exhausted: rejected with no loop budget left — stop with feedback.
|
|
@@ -486,9 +440,8 @@ liveEvidence) {
|
|
|
486
440
|
let aborted = false;
|
|
487
441
|
const maxSteps = options.maxSteps ?? Number.POSITIVE_INFINITY;
|
|
488
442
|
// The `maxSteps` budget counts DISTINCT spine steps that finished processing
|
|
489
|
-
// — never `executed.length`, which grows once per gate-loop iteration
|
|
490
|
-
//
|
|
491
|
-
// a route-skipped step consumes NOTHING (no work was dispatched for it).
|
|
443
|
+
// — never `executed.length`, which grows once per gate-loop iteration. A
|
|
444
|
+
// step's whole bounded gate loop consumes ONE step.
|
|
492
445
|
let stepsProcessed = 0;
|
|
493
446
|
let { unitsDispatched, tokensUsed } = await seedRunAccountingFromJournal(next.run.id);
|
|
494
447
|
const plan = await loadAuthoritativeRunPlan(options, next, storedPlan);
|
|
@@ -498,16 +451,6 @@ liveEvidence) {
|
|
|
498
451
|
// and holding every step's for the whole invocation is pure ballast when
|
|
499
452
|
// nothing downstream names it.
|
|
500
453
|
const liveEvidenceConsumers = referencedStepIds(plan);
|
|
501
|
-
// Route bookkeeping: targets a completed router did NOT select are skipped
|
|
502
|
-
// when the spine reaches them; a target ANY router selected is protected
|
|
503
|
-
// (two routers may share a target).
|
|
504
|
-
const routeSelected = new Set();
|
|
505
|
-
const routeUnselected = new Map();
|
|
506
|
-
// Replay journaled route decisions before the spine advances, so a
|
|
507
|
-
// re-invoked run skips the unselected branches. A done run skips this.
|
|
508
|
-
if (!next.done) {
|
|
509
|
-
seedJournaledRouteDecisions(plan, next, routeSelected, routeUnselected);
|
|
510
|
-
}
|
|
511
454
|
while (!next.done && next.step && next.run.status === "active" && stepsProcessed < maxSteps) {
|
|
512
455
|
// A caller abort (options.signal) is a graceful break.
|
|
513
456
|
if (options.signal?.aborted) {
|
|
@@ -520,19 +463,6 @@ liveEvidence) {
|
|
|
520
463
|
throw new UsageError(`Step "${step.id}" of run ${next.run.id} is not present in its frozen workflow plan (${next.run.workflowRef}). ` +
|
|
521
464
|
"The run journal is inconsistent; abandon this run and start a new one.");
|
|
522
465
|
}
|
|
523
|
-
// A branch target no completed router selected → auto-skip, no dispatch.
|
|
524
|
-
const skipInfo = routeUnselected.get(step.id);
|
|
525
|
-
if (skipInfo && !routeSelected.has(step.id)) {
|
|
526
|
-
next = await skipUnselectedRouteTarget({
|
|
527
|
-
runId: next.run.id,
|
|
528
|
-
stepId: step.id,
|
|
529
|
-
stepPlan,
|
|
530
|
-
skipInfo,
|
|
531
|
-
routeUnselected,
|
|
532
|
-
executed,
|
|
533
|
-
});
|
|
534
|
-
continue;
|
|
535
|
-
}
|
|
536
466
|
const evidence = {};
|
|
537
467
|
for (const s of next.workflow.steps)
|
|
538
468
|
evidence[s.id] = liveEvidence.get(s.id) ?? s.evidence;
|
|
@@ -589,8 +519,6 @@ liveEvidence) {
|
|
|
589
519
|
liveEvidence,
|
|
590
520
|
liveEvidenceConsumers,
|
|
591
521
|
executed,
|
|
592
|
-
routeSelected,
|
|
593
|
-
routeUnselected,
|
|
594
522
|
summaryJudge,
|
|
595
523
|
}, { startLoop, maxLoops, seededFeedback }, { unitsDispatched, tokensUsed });
|
|
596
524
|
unitsDispatched = outcome.unitsDispatched;
|