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.
Files changed (270) hide show
  1. package/CHANGELOG.md +409 -0
  2. package/STABILITY.md +99 -57
  3. package/dist/api-node.mjs +20 -0
  4. package/dist/api.d.ts +23 -0
  5. package/dist/api.js +45 -0
  6. package/dist/assets/hints/cli-hints-full.md +15 -29
  7. package/dist/assets/hints/cli-hints-short.md +2 -2
  8. package/dist/assets/improve-strategies/default.json +3 -3
  9. package/dist/assets/prompts/consolidate-pair.md +9 -7
  10. package/dist/assets/stash-skeleton/README.md +1 -1
  11. package/dist/assets/tasks/improve/akm-improve-consolidate.yml +1 -1
  12. package/dist/cli/shared.js +31 -48
  13. package/dist/cli/unknown-flags.js +6 -43
  14. package/dist/cli.js +8 -34
  15. package/dist/commands/agent/contribute-cli.js +1 -1
  16. package/dist/commands/completions.js +1 -2
  17. package/dist/commands/config-cli.js +5 -28
  18. package/dist/commands/env/env-cli.js +5 -5
  19. package/dist/commands/env/secret-cli.js +2 -2
  20. package/dist/commands/feedback-cli.js +75 -49
  21. package/dist/commands/health/checks.js +41 -16
  22. package/dist/commands/health/plugin-staleness.js +52 -22
  23. package/dist/commands/improve/autonomy-gate.js +5 -3
  24. package/dist/commands/improve/consolidate/chunking.js +3 -10
  25. package/dist/commands/improve/consolidate/continuity-check.js +4 -4
  26. package/dist/commands/improve/consolidate/pair-pass.js +29 -10
  27. package/dist/commands/improve/consolidate.js +10 -65
  28. package/dist/commands/improve/distill/lesson-update.js +91 -0
  29. package/dist/commands/improve/distill-guards.js +1 -43
  30. package/dist/commands/improve/distill.js +148 -21
  31. package/dist/commands/improve/eligibility.js +14 -1
  32. package/dist/commands/improve/extract-cli.js +1 -1
  33. package/dist/commands/improve/extract-prompt.js +17 -7
  34. package/dist/commands/improve/extract.js +5 -7
  35. package/dist/commands/improve/improve-cli.js +92 -106
  36. package/dist/commands/improve/improve-strategies.js +8 -12
  37. package/dist/commands/improve/improve.js +134 -52
  38. package/dist/commands/improve/loop-stages.js +18 -5
  39. package/dist/commands/improve/planner.js +0 -1
  40. package/dist/commands/improve/preparation.js +40 -99
  41. package/dist/commands/improve/proactive-maintenance.js +2 -2
  42. package/dist/commands/improve/reflect-noise.js +3 -55
  43. package/dist/commands/improve/reflect.js +29 -34
  44. package/dist/commands/improve/retrieval-queries.js +37 -0
  45. package/dist/commands/improve/retrieval-scope.js +2 -2
  46. package/dist/commands/improve/salience.js +2 -9
  47. package/dist/commands/improve/session-asset.js +4 -1
  48. package/dist/commands/improve/stage.js +23 -12
  49. package/dist/commands/lint/index.js +5 -20
  50. package/dist/commands/observability-cli.js +3 -9
  51. package/dist/commands/proposal/drain.js +43 -6
  52. package/dist/commands/proposal/proposal-cli.js +11 -31
  53. package/dist/commands/proposal/repository.js +20 -14
  54. package/dist/commands/proposal/validators/proposal-quality-validators.js +0 -3
  55. package/dist/commands/read/knowledge.js +3 -3
  56. package/dist/commands/read/remember-cli.js +0 -13
  57. package/dist/commands/read/search-cli.js +29 -97
  58. package/dist/commands/read/search.js +0 -11
  59. package/dist/commands/read/show.js +0 -19
  60. package/dist/commands/sources/plugin-upgrade.js +164 -101
  61. package/dist/commands/sources/sources-cli.js +1 -15
  62. package/dist/commands/sources/stash-cli.js +6 -14
  63. package/dist/commands/tasks/tasks-cli.js +4 -115
  64. package/dist/commands/workflow/plan.js +11 -33
  65. package/dist/commands/workflow-cli.js +2 -7
  66. package/dist/core/adapter/adapter-ids.js +1 -2
  67. package/dist/core/adapter/adapters/agent-skills-adapter.js +0 -4
  68. package/dist/core/adapter/adapters/akm-adapter.js +9 -48
  69. package/dist/core/adapter/adapters/akm-lint.js +10 -26
  70. package/dist/core/adapter/adapters/akm-metadata.js +2 -72
  71. package/dist/core/adapter/adapters/akm-task-adapter.js +0 -5
  72. package/dist/core/adapter/adapters/akm-workflow-adapter.js +9 -28
  73. package/dist/core/adapter/adapters/dotenv-adapter.js +1 -20
  74. package/dist/core/adapter/adapters/generic-files-adapter.js +0 -10
  75. package/dist/core/adapter/adapters/llm-wiki-adapter.js +0 -4
  76. package/dist/core/adapter/adapters/tool-dir-shared.js +0 -24
  77. package/dist/core/adapter/adapters/website-snapshot-adapter.js +4 -4
  78. package/dist/core/adapter/recognize-match.js +3 -4
  79. package/dist/core/common.js +18 -2
  80. package/dist/core/config/config-schema.js +12 -15
  81. package/dist/core/config/config-types.js +6 -15
  82. package/dist/core/config/config-walker.js +6 -23
  83. package/dist/core/config/config.js +17 -80
  84. package/dist/core/config/engine-semantics.js +7 -3
  85. package/dist/core/config/schema/engines.js +7 -7
  86. package/dist/core/config/schema/improve-processes.js +10 -63
  87. package/dist/core/config/schema/improve.js +2 -21
  88. package/dist/core/config/schema/index-config.js +6 -39
  89. package/dist/core/env-secret-ref.js +2 -45
  90. package/dist/core/errors.js +1 -2
  91. package/dist/core/extra-params.js +2 -86
  92. package/dist/core/file-lock.js +2 -6
  93. package/dist/core/improve-result.js +13 -1
  94. package/dist/core/mutation-target.js +2 -2
  95. package/dist/core/paths.js +4 -0
  96. package/dist/core/recognition-util.js +4 -5
  97. package/dist/core/run-lock.js +1 -1
  98. package/dist/core/subprocess.js +49 -0
  99. package/dist/core/warn.js +21 -4
  100. package/dist/core/write-source.js +3 -3
  101. package/dist/execution/target-ref.js +1 -2
  102. package/dist/indexer/indexer.js +15 -6
  103. package/dist/indexer/passes/memory-inference.js +15 -6
  104. package/dist/indexer/search/db-search.js +49 -36
  105. package/dist/indexer/search/fts-query.js +0 -19
  106. package/dist/indexer/search/ranking.js +29 -1
  107. package/dist/indexer/usage/usage-events.js +21 -16
  108. package/dist/integrations/agent/engine-resolution.js +5 -2
  109. package/dist/integrations/agent/index.js +1 -1
  110. package/dist/integrations/agent/profiles.js +41 -98
  111. package/dist/integrations/agent/request-lowering.js +3 -3
  112. package/dist/integrations/harnesses/aider/index.js +5 -0
  113. package/dist/integrations/harnesses/amazonq/index.js +1 -0
  114. package/dist/integrations/harnesses/claude/config-import.js +2 -3
  115. package/dist/integrations/harnesses/claude/index.js +5 -0
  116. package/dist/integrations/harnesses/codex/index.js +1 -0
  117. package/dist/integrations/harnesses/copilot/index.js +1 -0
  118. package/dist/integrations/harnesses/gemini/index.js +1 -0
  119. package/dist/integrations/harnesses/index.js +18 -39
  120. package/dist/integrations/harnesses/opencode/agent-builder.js +60 -35
  121. package/dist/integrations/harnesses/opencode/config-import.js +121 -52
  122. package/dist/integrations/harnesses/opencode/index.js +22 -0
  123. package/dist/integrations/harnesses/opencode/plugin-layout.js +68 -0
  124. package/dist/integrations/harnesses/opencode/session-log-types.js +12 -0
  125. package/dist/integrations/harnesses/opencode/session-log-v1.js +72 -0
  126. package/dist/integrations/harnesses/opencode/session-log-v2.js +140 -0
  127. package/dist/integrations/harnesses/opencode/session-log.js +104 -115
  128. package/dist/integrations/harnesses/opencode/version.js +98 -0
  129. package/dist/integrations/harnesses/opencode-sdk/{harness.js → index.js} +5 -17
  130. package/dist/integrations/harnesses/opencode-sdk/sdk-runner.js +105 -206
  131. package/dist/integrations/harnesses/opencode-sdk/v1-adapter.js +199 -0
  132. package/dist/integrations/harnesses/opencode-sdk/v2-adapter.js +254 -0
  133. package/dist/integrations/harnesses/opencode-sdk/wire.js +66 -0
  134. package/dist/integrations/harnesses/openhands/index.js +1 -0
  135. package/dist/integrations/harnesses/pi/index.js +1 -0
  136. package/dist/integrations/harnesses/shared.js +2 -0
  137. package/dist/integrations/harnesses/types.js +2 -0
  138. package/dist/llm/client.js +41 -11
  139. package/dist/output/cli-hints.js +1 -1
  140. package/dist/output/context.js +10 -19
  141. package/dist/output/format-exempt.js +5 -8
  142. package/dist/output/shapes/helpers.js +1 -22
  143. package/dist/output/shapes/passthrough.js +0 -1
  144. package/dist/output/shapes.js +3 -31
  145. package/dist/output/stdout.js +5 -1
  146. package/dist/output/text/command-format.js +6 -1
  147. package/dist/output/text/workflow-format.js +4 -22
  148. package/dist/scripts/akm-migrate-node.js +15745 -16711
  149. package/dist/scripts/akm-migrate.js +16318 -17280
  150. package/dist/setup/detect.js +10 -4
  151. package/dist/setup/harness-config-import.js +5 -14
  152. package/dist/setup/setup.js +1 -1
  153. package/dist/setup/steps/tasks.js +12 -13
  154. package/dist/sources/providers/git-stash.js +94 -13
  155. package/dist/storage/repositories/index-utility-repository.js +2 -32
  156. package/dist/storage/repositories/index-vec-repository.js +4 -0
  157. package/dist/storage/repositories/task-history-repository.js +0 -5
  158. package/dist/storage/repositories/workflow-runs-repository.js +5 -15
  159. package/dist/tasks/embedded.js +2 -1
  160. package/dist/tasks/run/run-command-task.js +9 -1
  161. package/dist/tasks/run/run-workflow-task.js +4 -3
  162. package/dist/tasks/run/task-history.js +0 -2
  163. package/dist/tasks/run/task-log.js +38 -1
  164. package/dist/tasks/schedule-gates.js +39 -0
  165. package/dist/tasks/scheduler-binding.js +0 -16
  166. package/dist/tasks/scheduler-sync.js +3 -97
  167. package/dist/tasks/source/bounded-document.js +1 -1
  168. package/dist/tasks/source/task-source-v4.js +2 -12
  169. package/dist/tasks/task-run-reserved-flags.js +2 -31
  170. package/dist/workflows/authoring/authoring.js +6 -6
  171. package/dist/workflows/compile.js +8 -65
  172. package/dist/workflows/exec/child-workflow.js +22 -3
  173. package/dist/workflows/exec/native-executor.js +12 -147
  174. package/dist/workflows/exec/run-workflow.js +31 -103
  175. package/dist/workflows/exec/step-work.js +9 -178
  176. package/dist/workflows/exec/unit-dispatch.js +0 -10
  177. package/dist/workflows/freeze/environment.js +3 -44
  178. package/dist/workflows/freeze/freeze.js +8 -41
  179. package/dist/workflows/freeze/targets/child-workflow.js +27 -63
  180. package/dist/workflows/freeze/targets/command.js +2 -16
  181. package/dist/workflows/freeze/targets/shell.js +0 -2
  182. package/dist/workflows/freeze/task-bindings.js +2 -28
  183. package/dist/workflows/ir/params.js +12 -2
  184. package/dist/workflows/parser.js +82 -232
  185. package/dist/workflows/plan.js +1 -1
  186. package/dist/workflows/renderer.js +10 -19
  187. package/dist/workflows/runtime/run-plan.js +11 -53
  188. package/dist/workflows/runtime/runs.js +4 -27
  189. package/dist/workflows/source-files.js +12 -16
  190. package/dist/workflows/source-semantics.js +1 -93
  191. package/docs/README.md +1 -1
  192. package/docs/agents/README.md +3 -3
  193. package/docs/agents/agent-install.md +1 -1
  194. package/docs/architecture/0.10-process-rules.md +827 -0
  195. package/docs/architecture/adapters.md +17 -31
  196. package/docs/architecture/architecture.md +4 -5
  197. package/docs/architecture/improvement.md +115 -76
  198. package/docs/architecture/internals/improve-workflow.md +22 -16
  199. package/docs/architecture/internals/indexing.md +1 -1
  200. package/docs/architecture/internals/search.md +1 -2
  201. package/docs/architecture/internals/storage-locations.md +6 -6
  202. package/docs/architecture/persisted-data-compat.md +13 -4
  203. package/docs/architecture/specs/0.9.0-decisions.md +6 -2
  204. package/docs/architecture/specs/improve-collapse-churn-detector-design.md +2 -2
  205. package/docs/architecture/specs/workflow-format-unification.md +1 -6
  206. package/docs/architecture/testing/manual-testing-checklist.md +21 -30
  207. package/docs/architecture/testing/task-workflow-manual-tests.md +106 -203
  208. package/docs/architecture/testing/testing-workflow.md +1 -1
  209. package/docs/architecture/workflow-engine.md +5 -56
  210. package/docs/guides/author-workflows.md +39 -69
  211. package/docs/guides/bundles.md +3 -1
  212. package/docs/guides/capture-knowledge.md +1 -1
  213. package/docs/guides/claude-code-vs-akm-workflows.md +1 -2
  214. package/docs/guides/discover-and-load.md +4 -4
  215. package/docs/guides/getting-started.md +1 -1
  216. package/docs/guides/improve-the-library.md +10 -9
  217. package/docs/guides/recipes/headless-install.md +2 -2
  218. package/docs/guides/run-workflows.md +1 -1
  219. package/docs/guides/scheduling.md +9 -1
  220. package/docs/guides/use-with-any-agent.md +49 -4
  221. package/docs/integration/bundling-akm.md +7 -6
  222. package/docs/maintainers/curate-workmap.md +1 -2
  223. package/docs/maintainers/release-checklist.md +33 -1
  224. package/docs/migration/README.md +2 -0
  225. package/docs/migration/release-notes/0.10.0.md +201 -0
  226. package/docs/migration/release-notes/README.md +1 -0
  227. package/docs/migration/v0.9-to-v0.10.md +507 -0
  228. package/docs/posts/akm-command-tour.md +3 -3
  229. package/docs/product-surface.md +1 -1
  230. package/docs/reference/README.md +1 -0
  231. package/docs/reference/api.md +65 -0
  232. package/docs/reference/asset-types.md +3 -5
  233. package/docs/reference/bundle-types.md +2 -4
  234. package/docs/reference/cli.md +160 -188
  235. package/docs/reference/configuration.md +101 -20
  236. package/docs/reference/data-and-telemetry.md +2 -2
  237. package/docs/reference/env-and-secrets.md +2 -2
  238. package/docs/reference/supported-formats.md +3 -4
  239. package/docs/reference/tasks.md +12 -19
  240. package/docs/reference/website-sources.md +1 -1
  241. package/docs/reference/workflow-schema.md +89 -317
  242. package/docs/reference/workflows.md +20 -48
  243. package/package.json +12 -2
  244. package/schemas/akm-config.json +2 -243
  245. package/schemas/akm-task.json +2 -3
  246. package/schemas/akm-workflow.json +47 -84
  247. package/dist/assets/improve-strategies/catchup.json +0 -41
  248. package/dist/assets/improve-strategies/proactive-maintenance.json +0 -24
  249. package/dist/assets/improve-strategies/quick.json +0 -17
  250. package/dist/assets/improve-strategies/reflect-distill.json +0 -29
  251. package/dist/assets/improve-strategies/thorough.json +0 -49
  252. package/dist/assets/prompts/retrieval-relevance-judge.md +0 -6
  253. package/dist/assets/tasks/improve/akm-improve-catchup.yml +0 -6
  254. package/dist/assets/tasks/improve/akm-improve-frequent.yml +0 -4
  255. package/dist/assets/tasks/improve/akm-improve-nightly.yml +0 -4
  256. package/dist/cli/retired-commands.js +0 -118
  257. package/dist/commands/improve/retrieval-gate.js +0 -132
  258. package/dist/commands/tasks/validate.js +0 -176
  259. package/dist/core/config/legacy-source-shape-shim.js +0 -117
  260. package/dist/indexer/feedback/utility-policy.js +0 -75
  261. package/dist/integrations/harnesses/ids.js +0 -28
  262. package/dist/tasks/source/task-source-v3-frozen.js +0 -424
  263. package/dist/tasks/source/task-to-v4.js +0 -863
  264. package/dist/workflows/exec/worktree.js +0 -372
  265. package/dist/workflows/freeze/child-output-references.js +0 -83
  266. package/dist/workflows/freeze/identity.js +0 -22
  267. package/dist/workflows/freeze/targets/script.js +0 -50
  268. package/dist/workflows/freeze/targets/task.js +0 -124
  269. package/dist/workflows/github-yaml.js +0 -506
  270. 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, template, workUnits, ctx, willDispatch, dispatched } = input;
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 and worktree preflight run only when some unit will
255
- // dispatch, so a fully-journaled step resumes even if an env asset or git is
256
- // gone. Every unit is classified once; the preflight and dispatch share it.
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, worktreeBase } = prerequisites;
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
- // A NON-isolated exec unit spawns in the engine invocation's working
362
- // directory. `dispatchJournaledAttempt` overwrites this with the unit's
363
- // fresh worktree when `isolation: worktree` is in play. Only exec units get
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
- // "script"/"shell" unit, and to dispatchWorkflowExecution's
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 prepareAttemptWorktree(input) {
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
- const prepared = await prepareAttemptWorktree(input);
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, worktreePath, startedAt);
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, completeWorkflowStep, getNextWorkflowStep, resumeWorkflowRun, } from "../runtime/runs.js";
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 — route evaluation + cascaded-skip bookkeeping,
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, cascadeSkippedRouter, effectiveGateMaxLoops, finalizeExecutedStep, recoverGateFeedback, referencedStepIds, seedJournaledRouteDecisions, } from "./step-work.js";
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 and route-skips must not shrink a retry's
76
- // remaining budget (they never counted against maxSteps either).
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. Route-only steps (YAML `route:` — no execution
311
- * subgraph) dispatch no units; they only decide the spine's path in
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, step, evidence } = ctx;
291
+ const { options, next, plan, stepPlan, evidence } = ctx;
317
292
  const { gateLoop, gateFeedback, unitsDispatched, tokensUsed } = loop;
318
- return !stepPlan.root && stepPlan.route
319
- ? {
320
- ok: true,
321
- units: [],
322
- evidence: {},
323
- summary: `Step "${step.id}" is a route step — no units dispatched.`,
324
- unitsDispatched,
325
- }
326
- : await executeStepPlan(stepPlan, {
327
- runId: next.run.id,
328
- workflowRef: next.run.workflowRef,
329
- params: next.run.params ?? {},
330
- evidence,
331
- unitsDispatched,
332
- tokensUsed,
333
- // Budget ceilings ride the FROZEN plan: a mid-run
334
- // asset edit can never loosen or tighten a run's budget.
335
- ...(plan.budget ? { budget: plan.budget } : {}),
336
- gateLoop,
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, evidence, executed, routeSelected, routeUnselected } = ctx;
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
- // Route, gate, and advance: the shared completion path, live or resumed.
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 and
490
- // once per route-skip. A step's whole bounded gate loop consumes ONE step;
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;