akm-cli 0.9.31 → 0.10.26101002-alpha

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 (236) hide show
  1. package/CHANGELOG.md +265 -0
  2. package/STABILITY.md +79 -53
  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 +13 -27
  7. package/dist/assets/hints/cli-hints-short.md +1 -1
  8. package/dist/assets/improve-strategies/default.json +3 -3
  9. package/dist/assets/prompts/consolidate-pair.md +9 -7
  10. package/dist/cli/shared.js +31 -48
  11. package/dist/cli/unknown-flags.js +6 -43
  12. package/dist/cli.js +8 -34
  13. package/dist/commands/agent/contribute-cli.js +1 -1
  14. package/dist/commands/completions.js +1 -2
  15. package/dist/commands/config-cli.js +5 -28
  16. package/dist/commands/env/env-cli.js +5 -5
  17. package/dist/commands/env/secret-cli.js +2 -2
  18. package/dist/commands/health/checks.js +41 -16
  19. package/dist/commands/health/plugin-staleness.js +52 -22
  20. package/dist/commands/improve/autonomy-gate.js +5 -3
  21. package/dist/commands/improve/consolidate/pair-pass.js +7 -1
  22. package/dist/commands/improve/consolidate.js +6 -35
  23. package/dist/commands/improve/distill/lesson-update.js +91 -0
  24. package/dist/commands/improve/distill-guards.js +1 -43
  25. package/dist/commands/improve/distill.js +140 -20
  26. package/dist/commands/improve/eligibility.js +14 -1
  27. package/dist/commands/improve/extract-cli.js +1 -1
  28. package/dist/commands/improve/improve-cli.js +87 -102
  29. package/dist/commands/improve/improve-strategies.js +8 -12
  30. package/dist/commands/improve/improve.js +2 -2
  31. package/dist/commands/improve/loop-stages.js +0 -1
  32. package/dist/commands/improve/planner.js +0 -1
  33. package/dist/commands/improve/preparation.js +40 -99
  34. package/dist/commands/improve/proactive-maintenance.js +2 -2
  35. package/dist/commands/improve/reflect-noise.js +3 -55
  36. package/dist/commands/improve/reflect.js +3 -8
  37. package/dist/commands/improve/retrieval-scope.js +2 -2
  38. package/dist/commands/improve/salience.js +2 -9
  39. package/dist/commands/improve/stage.js +14 -9
  40. package/dist/commands/lint/index.js +5 -20
  41. package/dist/commands/observability-cli.js +3 -9
  42. package/dist/commands/proposal/drain.js +39 -6
  43. package/dist/commands/proposal/proposal-cli.js +11 -31
  44. package/dist/commands/proposal/repository.js +3 -3
  45. package/dist/commands/read/knowledge.js +3 -3
  46. package/dist/commands/read/remember-cli.js +0 -13
  47. package/dist/commands/read/search-cli.js +29 -97
  48. package/dist/commands/read/search.js +0 -11
  49. package/dist/commands/read/show.js +0 -19
  50. package/dist/commands/sources/plugin-upgrade.js +164 -101
  51. package/dist/commands/sources/sources-cli.js +1 -15
  52. package/dist/commands/sources/stash-cli.js +4 -12
  53. package/dist/commands/tasks/tasks-cli.js +4 -115
  54. package/dist/commands/workflow/plan.js +11 -33
  55. package/dist/commands/workflow-cli.js +2 -7
  56. package/dist/core/adapter/adapter-ids.js +1 -2
  57. package/dist/core/adapter/adapters/agent-skills-adapter.js +0 -4
  58. package/dist/core/adapter/adapters/akm-adapter.js +9 -48
  59. package/dist/core/adapter/adapters/akm-lint.js +10 -26
  60. package/dist/core/adapter/adapters/akm-metadata.js +2 -72
  61. package/dist/core/adapter/adapters/akm-task-adapter.js +0 -5
  62. package/dist/core/adapter/adapters/akm-workflow-adapter.js +9 -28
  63. package/dist/core/adapter/adapters/dotenv-adapter.js +1 -20
  64. package/dist/core/adapter/adapters/generic-files-adapter.js +0 -10
  65. package/dist/core/adapter/adapters/llm-wiki-adapter.js +0 -4
  66. package/dist/core/adapter/adapters/tool-dir-shared.js +0 -24
  67. package/dist/core/adapter/adapters/website-snapshot-adapter.js +4 -4
  68. package/dist/core/adapter/recognize-match.js +3 -4
  69. package/dist/core/common.js +18 -2
  70. package/dist/core/config/config-schema.js +12 -15
  71. package/dist/core/config/config-types.js +6 -15
  72. package/dist/core/config/config-walker.js +6 -23
  73. package/dist/core/config/config.js +17 -80
  74. package/dist/core/config/engine-semantics.js +7 -3
  75. package/dist/core/config/schema/engines.js +7 -7
  76. package/dist/core/config/schema/improve-processes.js +8 -61
  77. package/dist/core/config/schema/improve.js +2 -21
  78. package/dist/core/config/schema/index-config.js +6 -39
  79. package/dist/core/env-secret-ref.js +2 -45
  80. package/dist/core/errors.js +1 -2
  81. package/dist/core/extra-params.js +2 -86
  82. package/dist/core/improve-result.js +11 -1
  83. package/dist/core/mutation-target.js +2 -2
  84. package/dist/core/recognition-util.js +4 -5
  85. package/dist/core/warn.js +21 -4
  86. package/dist/core/write-source.js +3 -3
  87. package/dist/execution/target-ref.js +1 -2
  88. package/dist/indexer/search/db-search.js +4 -19
  89. package/dist/indexer/search/fts-query.js +0 -19
  90. package/dist/integrations/agent/engine-resolution.js +5 -2
  91. package/dist/integrations/agent/index.js +1 -1
  92. package/dist/integrations/agent/profiles.js +41 -98
  93. package/dist/integrations/agent/request-lowering.js +3 -3
  94. package/dist/integrations/harnesses/aider/index.js +5 -0
  95. package/dist/integrations/harnesses/amazonq/index.js +1 -0
  96. package/dist/integrations/harnesses/claude/config-import.js +2 -3
  97. package/dist/integrations/harnesses/claude/index.js +5 -0
  98. package/dist/integrations/harnesses/codex/index.js +1 -0
  99. package/dist/integrations/harnesses/copilot/index.js +1 -0
  100. package/dist/integrations/harnesses/gemini/index.js +1 -0
  101. package/dist/integrations/harnesses/index.js +18 -39
  102. package/dist/integrations/harnesses/opencode/agent-builder.js +60 -35
  103. package/dist/integrations/harnesses/opencode/config-import.js +121 -52
  104. package/dist/integrations/harnesses/opencode/index.js +22 -0
  105. package/dist/integrations/harnesses/opencode/plugin-layout.js +68 -0
  106. package/dist/integrations/harnesses/opencode/session-log-types.js +12 -0
  107. package/dist/integrations/harnesses/opencode/session-log-v1.js +72 -0
  108. package/dist/integrations/harnesses/opencode/session-log-v2.js +140 -0
  109. package/dist/integrations/harnesses/opencode/session-log.js +104 -115
  110. package/dist/integrations/harnesses/opencode/version.js +98 -0
  111. package/dist/integrations/harnesses/opencode-sdk/{harness.js → index.js} +5 -17
  112. package/dist/integrations/harnesses/opencode-sdk/sdk-runner.js +105 -206
  113. package/dist/integrations/harnesses/opencode-sdk/v1-adapter.js +199 -0
  114. package/dist/integrations/harnesses/opencode-sdk/v2-adapter.js +228 -0
  115. package/dist/integrations/harnesses/opencode-sdk/wire.js +66 -0
  116. package/dist/integrations/harnesses/openhands/index.js +1 -0
  117. package/dist/integrations/harnesses/pi/index.js +1 -0
  118. package/dist/integrations/harnesses/shared.js +2 -0
  119. package/dist/integrations/harnesses/types.js +2 -0
  120. package/dist/llm/client.js +41 -11
  121. package/dist/output/cli-hints.js +1 -1
  122. package/dist/output/context.js +10 -19
  123. package/dist/output/format-exempt.js +5 -8
  124. package/dist/output/shapes/helpers.js +1 -22
  125. package/dist/output/shapes/passthrough.js +0 -1
  126. package/dist/output/shapes.js +3 -31
  127. package/dist/output/stdout.js +5 -1
  128. package/dist/output/text/command-format.js +6 -1
  129. package/dist/output/text/workflow-format.js +4 -22
  130. package/dist/scripts/akm-migrate-node.js +13049 -14013
  131. package/dist/scripts/akm-migrate.js +16316 -17276
  132. package/dist/setup/detect.js +10 -4
  133. package/dist/setup/harness-config-import.js +5 -14
  134. package/dist/setup/setup.js +1 -1
  135. package/dist/setup/steps/tasks.js +12 -13
  136. package/dist/storage/repositories/task-history-repository.js +0 -5
  137. package/dist/storage/repositories/workflow-runs-repository.js +5 -15
  138. package/dist/tasks/embedded.js +2 -1
  139. package/dist/tasks/run/task-history.js +0 -2
  140. package/dist/tasks/schedule-gates.js +39 -0
  141. package/dist/tasks/scheduler-binding.js +0 -16
  142. package/dist/tasks/scheduler-sync.js +3 -97
  143. package/dist/tasks/source/bounded-document.js +1 -1
  144. package/dist/tasks/source/task-source-v4.js +2 -12
  145. package/dist/tasks/task-run-reserved-flags.js +2 -31
  146. package/dist/workflows/authoring/authoring.js +6 -6
  147. package/dist/workflows/compile.js +8 -65
  148. package/dist/workflows/exec/child-workflow.js +22 -3
  149. package/dist/workflows/exec/native-executor.js +12 -147
  150. package/dist/workflows/exec/run-workflow.js +31 -103
  151. package/dist/workflows/exec/step-work.js +9 -178
  152. package/dist/workflows/exec/unit-dispatch.js +0 -10
  153. package/dist/workflows/freeze/environment.js +3 -44
  154. package/dist/workflows/freeze/freeze.js +8 -41
  155. package/dist/workflows/freeze/targets/child-workflow.js +27 -63
  156. package/dist/workflows/freeze/targets/command.js +2 -16
  157. package/dist/workflows/freeze/targets/shell.js +0 -2
  158. package/dist/workflows/freeze/task-bindings.js +2 -28
  159. package/dist/workflows/ir/params.js +12 -2
  160. package/dist/workflows/parser.js +82 -232
  161. package/dist/workflows/plan.js +1 -1
  162. package/dist/workflows/renderer.js +10 -19
  163. package/dist/workflows/runtime/run-plan.js +11 -53
  164. package/dist/workflows/runtime/runs.js +4 -27
  165. package/dist/workflows/source-files.js +12 -16
  166. package/dist/workflows/source-semantics.js +1 -93
  167. package/docs/README.md +1 -1
  168. package/docs/agents/README.md +3 -3
  169. package/docs/agents/agent-install.md +1 -1
  170. package/docs/architecture/adapters.md +17 -31
  171. package/docs/architecture/architecture.md +4 -5
  172. package/docs/architecture/improvement.md +52 -21
  173. package/docs/architecture/internals/improve-workflow.md +15 -8
  174. package/docs/architecture/internals/indexing.md +1 -1
  175. package/docs/architecture/internals/search.md +1 -2
  176. package/docs/architecture/internals/storage-locations.md +5 -5
  177. package/docs/architecture/persisted-data-compat.md +13 -4
  178. package/docs/architecture/specs/0.9.0-decisions.md +6 -2
  179. package/docs/architecture/specs/improve-collapse-churn-detector-design.md +2 -2
  180. package/docs/architecture/specs/workflow-format-unification.md +1 -6
  181. package/docs/architecture/testing/manual-testing-checklist.md +20 -29
  182. package/docs/architecture/testing/task-workflow-manual-tests.md +106 -203
  183. package/docs/architecture/testing/testing-workflow.md +1 -1
  184. package/docs/architecture/workflow-engine.md +5 -56
  185. package/docs/guides/author-workflows.md +39 -69
  186. package/docs/guides/capture-knowledge.md +1 -1
  187. package/docs/guides/claude-code-vs-akm-workflows.md +1 -2
  188. package/docs/guides/discover-and-load.md +4 -4
  189. package/docs/guides/getting-started.md +1 -1
  190. package/docs/guides/improve-the-library.md +7 -7
  191. package/docs/guides/recipes/headless-install.md +2 -2
  192. package/docs/guides/run-workflows.md +1 -1
  193. package/docs/guides/use-with-any-agent.md +49 -4
  194. package/docs/integration/bundling-akm.md +7 -6
  195. package/docs/maintainers/curate-workmap.md +1 -2
  196. package/docs/maintainers/release-checklist.md +30 -1
  197. package/docs/posts/akm-command-tour.md +3 -3
  198. package/docs/product-surface.md +1 -1
  199. package/docs/reference/README.md +1 -0
  200. package/docs/reference/api.md +65 -0
  201. package/docs/reference/asset-types.md +3 -5
  202. package/docs/reference/bundle-types.md +2 -4
  203. package/docs/reference/cli.md +129 -171
  204. package/docs/reference/configuration.md +84 -19
  205. package/docs/reference/data-and-telemetry.md +1 -1
  206. package/docs/reference/env-and-secrets.md +2 -2
  207. package/docs/reference/supported-formats.md +3 -4
  208. package/docs/reference/tasks.md +12 -19
  209. package/docs/reference/website-sources.md +1 -1
  210. package/docs/reference/workflow-schema.md +89 -317
  211. package/docs/reference/workflows.md +20 -48
  212. package/package.json +12 -2
  213. package/schemas/akm-config.json +2 -243
  214. package/schemas/akm-task.json +2 -3
  215. package/schemas/akm-workflow.json +47 -84
  216. package/dist/assets/improve-strategies/catchup.json +0 -41
  217. package/dist/assets/improve-strategies/proactive-maintenance.json +0 -24
  218. package/dist/assets/improve-strategies/quick.json +0 -17
  219. package/dist/assets/improve-strategies/reflect-distill.json +0 -29
  220. package/dist/assets/improve-strategies/thorough.json +0 -49
  221. package/dist/assets/tasks/improve/akm-improve-catchup.yml +0 -6
  222. package/dist/assets/tasks/improve/akm-improve-frequent.yml +0 -4
  223. package/dist/assets/tasks/improve/akm-improve-nightly.yml +0 -4
  224. package/dist/cli/retired-commands.js +0 -118
  225. package/dist/commands/tasks/validate.js +0 -176
  226. package/dist/core/config/legacy-source-shape-shim.js +0 -117
  227. package/dist/integrations/harnesses/ids.js +0 -28
  228. package/dist/tasks/source/task-source-v3-frozen.js +0 -424
  229. package/dist/tasks/source/task-to-v4.js +0 -863
  230. package/dist/workflows/exec/worktree.js +0 -372
  231. package/dist/workflows/freeze/child-output-references.js +0 -83
  232. package/dist/workflows/freeze/identity.js +0 -22
  233. package/dist/workflows/freeze/targets/script.js +0 -50
  234. package/dist/workflows/freeze/targets/task.js +0 -124
  235. package/dist/workflows/github-yaml.js +0 -506
  236. package/dist/workflows/runtime/run-outputs.js +0 -80
@@ -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;
@@ -109,11 +109,10 @@ function resolveTaskInputBindings(bindings, stepId, scope) {
109
109
  */
110
110
  export function computeStepWorkList(plan, input) {
111
111
  const root = plan.root;
112
- // Route-only steps (YAML `route:`) carry no execution subgraph.
113
112
  if (!root) {
114
113
  return {
115
114
  ok: false,
116
- error: `Step "${plan.stepId}" has no execution subgraph (a route-only step); the native executor cannot dispatch it.`,
115
+ error: `Step "${plan.stepId}" has no execution subgraph; the native executor cannot dispatch it.`,
117
116
  };
118
117
  }
119
118
  const template = root.kind === "map" ? root.template : root;
@@ -121,7 +120,7 @@ export function computeStepWorkList(plan, input) {
121
120
  const scope = { params: input.params, stepOutputs: input.stepOutputs };
122
121
  // Instructions are ALWAYS the step's body prose, byte-exact — never
123
122
  // templated, never scanned for reference syntax (workflow-format-
124
- // unification, spec §2.3). Only `map.over` / `route.input` / `inputs[]`
123
+ // unification, spec §2.3). Only `map.over` / `inputs[]`
125
124
  // carry the closed reference grammar.
126
125
  // Resolve the step's declared `inputs:` ONCE (shared by every unit in this
127
126
  // step — map items differ, declared inputs do not): prior-step artifacts
@@ -177,7 +176,7 @@ export function computeStepWorkList(plan, input) {
177
176
  : [unitIdFor(template.id, undefined, false, true)];
178
177
  const gateLoop = input.gateLoop ?? 1;
179
178
  const target = template.frozenTarget;
180
- const frozenExec = target.kind === "shell" || target.kind === "script" ? target.exec : undefined;
179
+ const frozenExec = target.kind === "shell" ? target.exec : undefined;
181
180
  const runner = target.kind === "command" ? target.runner.kind : "exec";
182
181
  // Taken verbatim from the frozen plan, resolved once at freeze (an exec
183
182
  // unit's on its exec spec). A frozen `null` means genuinely unbounded
@@ -271,7 +270,6 @@ function buildStepWorkUnit(ctx, unitId, item, index) {
271
270
  ...(template.schema ? { schema: template.schema } : {}),
272
271
  ...(template.retry ? { retry: template.retry } : {}),
273
272
  onError: template.onError,
274
- ...(template.isolation ? { isolation: template.isolation } : {}),
275
273
  // the SAME resolved `with:` bindings `taskInputs` already
276
274
  // carries, exposed under the name `child-workflow.ts`'s drive contract
277
275
  // reads. Absent (never `{}`) when the step binds nothing.
@@ -330,7 +328,8 @@ function computeUnitInputHash(ctx, item) {
330
328
  frozenTarget: ctx.target,
331
329
  environment: ctx.template.environment,
332
330
  schema: ctx.template.schema ?? null,
333
- isolation: ctx.template.isolation ?? "none",
331
+ // Retired `isolation: worktree` field; kept constant so in-flight runs keep their journaled input hashes.
332
+ isolation: "none",
334
333
  ...(ctx.taskInputs !== undefined ? { taskInputs: ctx.taskInputs } : {}),
335
334
  ...(ctx.input.gateFeedback ? { gateFeedback: ctx.input.gateFeedback } : {}),
336
335
  }))
@@ -422,7 +421,7 @@ function stepTemplate(stepPlan) {
422
421
  }
423
422
  /**
424
423
  * The step ids another step can still read (named by `inputs[]`, `map.over`,
425
- * or `route.input` — the whole reference surface). A step outside this set
424
+ * — the whole reference surface). A step outside this set
426
425
  * need not keep its artifact in memory once journaled. Derived from the plan alone.
427
426
  */
428
427
  export function referencedStepIds(plan) {
@@ -437,8 +436,6 @@ export function referencedStepIds(plan) {
437
436
  note(step.root.over);
438
437
  for (const reference of stepTemplate(step)?.inputs ?? [])
439
438
  note(reference);
440
- if (step.route)
441
- note(step.route.input);
442
439
  }
443
440
  return referenced;
444
441
  }
@@ -830,148 +827,6 @@ export async function journalGateEvaluationFinish(gate, errored, rejection) {
830
827
  throw new UsageError(`Gate ${unitId} was already finished; refusing a duplicate terminal write.`);
831
828
  }
832
829
  }
833
- /**
834
- * Resolve a route's input (a single whole-value reference string — `params.x` or
835
- * `steps.<id>.output…`, with no `${{ }}` delimiters) and pick the branch. No
836
- * ambient key search. Only primitive values route; the comparison is exact
837
- * string equality against the declared `when:` matches.
838
- */
839
- export function evaluateRoute(route, scope) {
840
- const resolved = resolveStepReference(route.input, scope);
841
- if (!resolved.ok) {
842
- return { ok: false, error: `route input ${route.input} failed to resolve: ${resolved.error.message}` };
843
- }
844
- const value = resolved.value;
845
- if (typeof value === "object" && value !== null) {
846
- return {
847
- ok: false,
848
- error: `route input ${route.input} resolved to a non-primitive value; branches match on strings/numbers/booleans.`,
849
- };
850
- }
851
- const valueString = typeof value === "string" ? value : String(value);
852
- // Own-property check: `when` is author-controlled, and a value such as
853
- // "constructor" must not resolve through Object.prototype.
854
- const selected = Object.hasOwn(route.when, valueString) ? route.when[valueString] : route.defaultStepId;
855
- if (!selected) {
856
- return {
857
- ok: false,
858
- error: `value "${valueString}" matched no "when:" branch and the route declares no default.`,
859
- };
860
- }
861
- return { ok: true, value: valueString, selected };
862
- }
863
- /**
864
- * Cascade a SKIPPED router: it never evaluated its route, so every declared
865
- * target (branches + default) is marked skip-on-reach unless an earlier router
866
- * already claimed it. Shared by the live skip path and the journal replay.
867
- */
868
- export function cascadeSkippedRouter(route, routerId, routeUnselected) {
869
- const targets = [...Object.values(route.when), ...(route.defaultStepId ? [route.defaultStepId] : [])];
870
- for (const target of targets) {
871
- if (!routeUnselected.has(target)) {
872
- routeUnselected.set(target, { router: routerId, selected: null });
873
- }
874
- }
875
- }
876
- /**
877
- * Record one router's decision in the skip bookkeeping: the selected target is
878
- * protected, every other declared target (branches + default) is marked
879
- * skip-on-reach unless an earlier router already claimed it. Shared by the live
880
- * evaluation path and the journal replay.
881
- */
882
- export function applyRouteDecision(route, routerId, selected, routeSelected, routeUnselected) {
883
- routeSelected.add(selected);
884
- const targets = [...Object.values(route.when), ...(route.defaultStepId ? [route.defaultStepId] : [])];
885
- for (const target of targets) {
886
- if (target !== selected && !routeUnselected.has(target)) {
887
- routeUnselected.set(target, { router: routerId, selected });
888
- }
889
- }
890
- }
891
- /**
892
- * The `stepOutputs` scope a route resolves against: every prior step's recorded
893
- * evidence plus the just-finished step's fresh evidence — each projected
894
- * through {@link projectStepOutput}. Same projection as unit templates, so the
895
- * two scopes cannot drift.
896
- */
897
- export function routeStepOutputs(evidence, currentStepId, currentEvidence) {
898
- const outputs = {};
899
- for (const [stepId, stepEvidence] of Object.entries(evidence)) {
900
- if (stepEvidence !== undefined)
901
- outputs[stepId] = projectStepOutput(stepEvidence);
902
- }
903
- outputs[currentStepId] = projectStepOutput(currentEvidence);
904
- return outputs;
905
- }
906
- /** The `selected` target journaled on a route step's evidence, if well-formed. */
907
- function journaledRouteSelection(evidence) {
908
- const route = evidence?.route;
909
- if (typeof route !== "object" || route === null || Array.isArray(route))
910
- return undefined;
911
- const selected = route.selected;
912
- return typeof selected === "string" && selected !== "" ? selected : undefined;
913
- }
914
- /** The set of steps a route may legally select: its `when` branches + default. */
915
- function routeTargets(route) {
916
- return new Set([...Object.values(route.when), ...(route.defaultStepId ? [route.defaultStepId] : [])]);
917
- }
918
- /** A journaled route decision must name a target the route declares; anything else fails loudly. */
919
- function assertRouteTargetDeclared(route, stepId, selected, runId) {
920
- const targets = routeTargets(route);
921
- if (!targets.has(selected)) {
922
- throw new UsageError(`Workflow run ${runId} has a completed route step "${stepId}" whose journaled route decision selected ` +
923
- `"${selected}", which is not a declared branch or default target of the route (valid targets: ` +
924
- `${[...targets].join(", ") || "(none)"}). The route evidence was corrupted or manually edited — refusing to ` +
925
- `apply a bogus route decision that would skip the real branch targets. Start a new run.`);
926
- }
927
- }
928
- /**
929
- * Replay journaled route decisions into the skip bookkeeping (resume path).
930
- * For every COMPLETED route step of the frozen plan, in spine order: the
931
- * journaled decision wins; else a re-derivation from the frozen plan +
932
- * journaled evidence; else fail loudly. A SKIPPED route step cascades its
933
- * targets into the skip set exactly as on the live path.
934
- */
935
- export function seedJournaledRouteDecisions(plan, state, routeSelected, routeUnselected) {
936
- const evidence = {};
937
- for (const s of state.workflow.steps)
938
- evidence[s.id] = s.evidence;
939
- for (const stepPlan of plan.steps) {
940
- if (!stepPlan.route)
941
- continue;
942
- const stepState = state.workflow.steps.find((s) => s.id === stepPlan.stepId);
943
- if (!stepState)
944
- continue;
945
- if (stepState.status === "skipped") {
946
- cascadeSkippedRouter(stepPlan.route, stepPlan.stepId, routeUnselected);
947
- continue;
948
- }
949
- if (stepState.status !== "completed")
950
- continue;
951
- let selected = journaledRouteSelection(stepState.evidence);
952
- if (selected !== undefined) {
953
- // a stored decision must name a declared target — a bogus one
954
- // (tampered/hand-edited evidence) fails loudly rather than seeding a skip
955
- // set that buries the real branches.
956
- assertRouteTargetDeclared(stepPlan.route, stepPlan.stepId, selected, state.run.id);
957
- }
958
- if (selected === undefined) {
959
- const scope = {
960
- params: state.run.params ?? {},
961
- stepOutputs: routeStepOutputs(evidence, stepPlan.stepId, stepState.evidence ?? {}),
962
- };
963
- const decision = evaluateRoute(stepPlan.route, scope);
964
- if (decision.ok)
965
- selected = decision.selected;
966
- }
967
- if (selected === undefined) {
968
- throw new UsageError(`Workflow run ${state.run.id} has a completed route step "${stepPlan.stepId}" with no journaled route ` +
969
- `decision, and the decision cannot be re-derived from the journaled evidence. Refusing to guess which ` +
970
- `branch was selected. The run journal is inconsistent; abandon this run and start a new one.`);
971
- }
972
- applyRouteDecision(stepPlan.route, stepPlan.stepId, selected, routeSelected, routeUnselected);
973
- }
974
- }
975
830
  /**
976
831
  * The blocked-step notes for a verifier-infrastructure failure (bug: judge
977
832
  * outage must not burn the gate budget). Shared by every judge-failure path —
@@ -1050,8 +905,6 @@ async function blockFinalizedStepForChildWorkflow(input, childBlocked) {
1050
905
  * Perform one completion attempt for an executed step:
1051
906
  * - a hard unit failure fails the step (a retryable artifact-schema mismatch
1052
907
  * with loops left returns `retry` without a gate row);
1053
- * - a route decision is evaluated, journaled on the evidence, and applied to
1054
- * the skip bookkeeping; an unroutable value fails the step;
1055
908
  * - the gate judges a summary built from the promoted artifact: a rejection
1056
909
  * returns `retry` (loops left) or `gate-exhausted`, a pass `advanced`;
1057
910
  * - a judge infrastructure failure is not a verdict: it blocks the step for
@@ -1093,34 +946,12 @@ export async function finalizeExecutedStep(input) {
1093
946
  return blockFinalizedStep(input, "this step declares completion criteria but no verification judge is available " +
1094
947
  "(the frozen plan resolves no judge — set workflow.judgeEngine, or restore the judge configuration)");
1095
948
  }
1096
- // Route evaluation BEFORE completion: an unroutable value is an
1097
- // authoring/config failure that must fail the step deterministically.
1098
- let summaryOverride;
1099
- if (stepPlan.route) {
1100
- const scope = {
1101
- params: input.params,
1102
- stepOutputs: routeStepOutputs(input.priorEvidence, stepId, result.evidence),
1103
- };
1104
- const decision = evaluateRoute(stepPlan.route, scope);
1105
- if (!decision.ok) {
1106
- const notes = `Step "${stepId}" route failed: ${decision.error}`;
1107
- await completeWorkflowStep({ runId, stepId, status: "failed", notes, evidence: result.evidence });
1108
- return { kind: "failed", summary: notes, routeFailure: true };
1109
- }
1110
- applyRouteDecision(stepPlan.route, stepId, decision.selected, input.routeSelected, input.routeUnselected);
1111
- // Journal the decision on the evidence: resume replays it via
1112
- // seedJournaledRouteDecisions, so the skip set survives re-invocation.
1113
- result.evidence.route = { input: stepPlan.route.input, value: decision.value, selected: decision.selected };
1114
- if (!stepPlan.root) {
1115
- summaryOverride = `Step "${stepId}" routed on ${stepPlan.route.input}: value "${decision.value}" selected step "${decision.selected}".`;
1116
- }
1117
- }
1118
949
  // Artifact-judging gate: a criteria-bearing executing step is judged on a
1119
950
  // summary BUILT FROM the promoted artifact; everything else keeps the machine
1120
- // summary (a route-only step's summary IS its decision).
951
+ // summary.
1121
952
  const summary = stepPlan.root && completionCriteria.length > 0
1122
953
  ? buildArtifactSummary(stepId, result.units, result.evidence)
1123
- : (summaryOverride ?? result.summary);
954
+ : result.summary;
1124
955
  // Journal engine-driven judge calls as unit rows. With no criteria there is
1125
956
  // no judge invocation or row; a criteria-bearing plan without a judge is a
1126
957
  // configuration error rather than a silent bypass.
@@ -1233,7 +1064,7 @@ export async function finalizeExecutedStep(input) {
1233
1064
  return blockFinalizedStep(input, judgeFailure ?? "the verification judge failed");
1234
1065
  }
1235
1066
  if (!rejection) {
1236
- return { kind: "advanced", ...(summaryOverride !== undefined ? { summaryOverride } : {}) };
1067
+ return { kind: "advanced" };
1237
1068
  }
1238
1069
  if (loopsRemaining) {
1239
1070
  return { kind: "retry", gateFeedback: { feedback: rejection.feedback, missing: rejection.missing } };
@@ -73,16 +73,6 @@ export async function dispatchWorkflowExecution(request, feedback) {
73
73
  ...(notices ? { notices } : {}),
74
74
  };
75
75
  }
76
- if (request.cwd && lowered.runner.kind === "llm") {
77
- return {
78
- ok: false,
79
- text: "",
80
- failureReason: "isolation_unsupported",
81
- error: `unit ${JSON.stringify(request.unitId)} declares isolation: worktree but resolved to the "llm" runner, ` +
82
- `which has no working directory to isolate. Use the agent or sdk runner for isolated units.`,
83
- ...(notices ? { notices } : {}),
84
- };
85
- }
86
76
  let result;
87
77
  try {
88
78
  const eventSource = forwardedDispatchEventSource(request);
@@ -12,23 +12,13 @@ import { UsageError } from "../../core/errors.js";
12
12
  import { deriveInstallations } from "../../indexer/installations.js";
13
13
  import { resolveSourceEntries } from "../../indexer/search/search-source.js";
14
14
  import { resolveAssetPath } from "../../sources/resolve.js";
15
- import { isInferredSecretName } from "../../tasks/log-redaction.js";
16
15
  import { SECRET_TOKEN_RE } from "../exec/environment.js";
17
- import { detectSecretShapedParams } from "../exec/param-secrets.js";
18
16
  /**
19
- * A step's frozen environment: literal `env:` values, `pass_env` names, then
17
+ * A step's frozen environment: `pass_env` names, then
20
18
  * one value-free descriptor per `unit.env` ref (owner, key set, secret-token
21
19
  * names). Values are read at dispatch (`exec/environment.ts`), never frozen.
22
20
  */
23
21
  export function freezeEnvironment(source, exec, context) {
24
- const literals = Object.entries(source.env ?? {}).map(([name, raw]) => {
25
- const value = String(raw);
26
- // A literal is frozen into plan_json; a credential belongs in an env ref, as for task env.
27
- if (isInferredSecretName(name) || detectSecretShapedParams({ [name]: value }).length > 0) {
28
- throw new UsageError(`Workflow step ${source.id} env.${name} is a secret-shaped literal env value. Store credentials in an env asset (unit.env) rather than workflow source.`, "WORKFLOW_SOURCE_INVALID");
29
- }
30
- return Object.freeze({ kind: "literal", name, value });
31
- });
32
22
  const passThrough = (exec?.passEnv ?? []).map((name) => Object.freeze({ kind: "pass-through", name }));
33
23
  const seen = new Set();
34
24
  const envRefs = [];
@@ -62,7 +52,7 @@ export function freezeEnvironment(source, exec, context) {
62
52
  precedence,
63
53
  }));
64
54
  }
65
- return [...literals, ...passThrough, ...envRefs];
55
+ return [...passThrough, ...envRefs];
66
56
  }
67
57
  /** Load a command or persona the workflow names, resolved through the workflow's own bundles. */
68
58
  export async function workflowExecutionSource(ref, kind, context) {
@@ -143,39 +133,8 @@ function configuredOwner(bundle, config) {
143
133
  adapter: component?.adapter ?? "akm",
144
134
  };
145
135
  }
146
- const SCRIPT_EXTENSIONS = [
147
- "",
148
- ".sh",
149
- ".ts",
150
- ".js",
151
- ".py",
152
- ".rb",
153
- ".go",
154
- ".pl",
155
- ".php",
156
- ".lua",
157
- ".r",
158
- ".swift",
159
- ".kt",
160
- ".kts",
161
- ".ps1",
162
- ".cmd",
163
- ".bat",
164
- ];
165
136
  function assetExtensions(type) {
166
- if (type === "script")
167
- return SCRIPT_EXTENSIONS;
168
137
  if (type === "env")
169
138
  return ["", ".env"];
170
- return ["", ".md", ".yml"];
171
- }
172
- export function qualifyRef(ref, plural, asset, config) {
173
- const parsed = parseBundleRef(ref);
174
- if (parsed.bundle)
175
- return ref;
176
- const bundle = parseBundleRef(asset.ref).bundle ?? config.defaultBundle;
177
- if (!bundle)
178
- throw new UsageError(`Workflow ref ${ref} has no owning bundle.`, "WORKFLOW_SOURCE_INVALID");
179
- const concept = parsed.conceptId.startsWith(`${plural}/`) ? parsed.conceptId : `${plural}/${parsed.conceptId}`;
180
- return makeBundleRef(bundle, concept);
139
+ return ["", ".md"];
181
140
  }