akm-cli 0.9.30 → 0.9.32

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 (165) hide show
  1. package/CHANGELOG.md +40 -0
  2. package/README.md +10 -10
  3. package/ROADMAP.md +112 -0
  4. package/dist/commands/feedback-cli.js +21 -13
  5. package/dist/commands/improve/improve-cli.js +33 -12
  6. package/dist/core/errors.js +1 -0
  7. package/dist/core/file-lock.js +10 -1
  8. package/dist/indexer/usage/usage-events.js +6 -2
  9. package/dist/llm/client.js +33 -3
  10. package/dist/scripts/akm-migrate-node.js +6 -1
  11. package/dist/scripts/akm-migrate.js +6 -1
  12. package/docs/README.md +42 -41
  13. package/docs/agents/README.md +15 -0
  14. package/docs/agents/agent-install.md +14 -0
  15. package/docs/architecture/README.md +67 -0
  16. package/docs/architecture/adapters.md +272 -0
  17. package/docs/architecture/akm-architecture-decision-history.md +2055 -0
  18. package/docs/architecture/akm-core-principles.md +126 -0
  19. package/docs/architecture/architecture.md +579 -0
  20. package/docs/architecture/comparisons/claude-code-vs-akm-workflows-full.md +550 -0
  21. package/docs/architecture/decisions/0001-no-interpolation-attached-structured-context.md +72 -0
  22. package/docs/architecture/decisions/0002-unit-reuse-and-input-hash-scope.md +142 -0
  23. package/docs/architecture/decisions/0003-child-env-allowlist-and-provenance.md +189 -0
  24. package/docs/architecture/decisions/0004-task-input-contract-and-flag-coercion.md +97 -0
  25. package/docs/architecture/decisions/0005-task-result-vocabulary-and-legacy-read-mapping.md +108 -0
  26. package/docs/architecture/decisions/0006-task-source-version-routing.md +124 -0
  27. package/docs/architecture/decisions/0007-workflow-composition-bounds.md +83 -0
  28. package/docs/architecture/decisions/0008-task-binding-normalization.md +88 -0
  29. package/docs/architecture/decisions/0009-child-run-publication-column-parity.md +80 -0
  30. package/docs/architecture/decisions/0010-driver-protocol-cut.md +36 -0
  31. package/docs/architecture/decisions/0011-engine-run-loop-invariants.md +125 -0
  32. package/docs/architecture/decisions/README.md +62 -0
  33. package/docs/architecture/improvement.md +440 -0
  34. package/docs/architecture/internals/classification.md +154 -0
  35. package/docs/architecture/internals/fresh-host-rebuild-runbook.md +40 -0
  36. package/docs/architecture/internals/functional-contract-patterns.md +340 -0
  37. package/docs/architecture/internals/health-advisories.md +110 -0
  38. package/docs/architecture/internals/improve-workflow.md +713 -0
  39. package/docs/architecture/internals/indexing.md +498 -0
  40. package/docs/architecture/internals/registry-network-boundary.md +57 -0
  41. package/docs/architecture/internals/search.md +249 -0
  42. package/docs/architecture/internals/storage-locations.md +943 -0
  43. package/docs/architecture/persisted-data-compat.md +59 -0
  44. package/docs/architecture/reviews/env-secret-access.md +319 -0
  45. package/docs/architecture/runtime-boundary-design.md +153 -0
  46. package/docs/architecture/specs/0.9.0-close-out-plan.md +361 -0
  47. package/docs/architecture/specs/0.9.0-decisions.md +528 -0
  48. package/docs/architecture/specs/0.9.0-docs-code-drift-register.md +745 -0
  49. package/docs/architecture/specs/0.9.0-open-items-register.md +135 -0
  50. package/docs/architecture/specs/0.9.0-public-api-issue-backlog.md +740 -0
  51. package/docs/architecture/specs/0.9.0-release-surface-review.md +636 -0
  52. package/docs/architecture/specs/agent-command-engine-model-design.md +513 -0
  53. package/docs/architecture/specs/akm-0.9.0-bundle-adapter-spec.md +501 -0
  54. package/docs/architecture/specs/akm-0.9.0-ref-grammar-decision.md +291 -0
  55. package/docs/architecture/specs/akm-format-neutral-bundle-workspace-spec.md +2059 -0
  56. package/docs/architecture/specs/di-seams-plan.md +539 -0
  57. package/docs/architecture/specs/driver-protocol-keep-or-cut.md +275 -0
  58. package/docs/architecture/specs/fact-asset-type.md +142 -0
  59. package/docs/architecture/specs/improve-collapse-churn-detector-design.md +634 -0
  60. package/docs/architecture/specs/okf-support.md +148 -0
  61. package/docs/architecture/specs/ref.md +329 -0
  62. package/docs/architecture/specs/reviews/task-workflow-unification/README.md +72 -0
  63. package/docs/architecture/specs/reviews/task-workflow-unification/findings-consolidated.md +152 -0
  64. package/docs/architecture/specs/reviews/task-workflow-unification/library-eval-alternatives.md +352 -0
  65. package/docs/architecture/specs/reviews/task-workflow-unification/library-eval-bunqueue.md +300 -0
  66. package/docs/architecture/specs/reviews/task-workflow-unification/library-eval-openworkflow.md +402 -0
  67. package/docs/architecture/specs/reviews/task-workflow-unification/opus-review-1.md +758 -0
  68. package/docs/architecture/specs/reviews/task-workflow-unification/opus-review-2.md +645 -0
  69. package/docs/architecture/specs/reviews/task-workflow-unification/opus-review-3.md +602 -0
  70. package/docs/architecture/specs/reviews/task-workflow-unification/panel-r1-adversarial.md +27 -0
  71. package/docs/architecture/specs/reviews/task-workflow-unification/panel-r1-architecture.md +27 -0
  72. package/docs/architecture/specs/reviews/task-workflow-unification/panel-r1-coverage.md +74 -0
  73. package/docs/architecture/specs/reviews/task-workflow-unification/panel-r1-security.md +15 -0
  74. package/docs/architecture/specs/reviews/task-workflow-unification/panel-r2-adversarial.md +132 -0
  75. package/docs/architecture/specs/reviews/task-workflow-unification/panel-r2-verification.md +147 -0
  76. package/docs/architecture/specs/stash-conventions-code-spec.md +275 -0
  77. package/docs/architecture/specs/stash-organization-conventions.md +377 -0
  78. package/docs/architecture/specs/task-workflow-format-unification.md +721 -0
  79. package/docs/architecture/specs/workflow-engine-buy-vs-build.md +231 -0
  80. package/docs/architecture/specs/workflow-format-unification.md +437 -0
  81. package/docs/architecture/testing/manual-testing-checklist.md +3420 -0
  82. package/docs/architecture/testing/okf-v0.2-conformance-runbook.md +924 -0
  83. package/docs/architecture/testing/task-workflow-manual-tests.md +1817 -0
  84. package/docs/architecture/testing/testing-workflow.md +446 -0
  85. package/docs/architecture/workflow-engine.md +274 -0
  86. package/docs/guides/README.md +33 -0
  87. package/docs/guides/author-bundles.md +310 -0
  88. package/docs/guides/author-workflows.md +520 -0
  89. package/docs/guides/bundles.md +321 -0
  90. package/docs/guides/capture-knowledge.md +136 -0
  91. package/docs/guides/claude-code-vs-akm-workflows.md +30 -0
  92. package/docs/guides/concepts.md +93 -0
  93. package/docs/guides/discover-and-load.md +158 -0
  94. package/docs/guides/getting-started.md +240 -0
  95. package/docs/guides/improve-the-library.md +246 -0
  96. package/docs/guides/knowledge-management.md +18 -0
  97. package/docs/guides/recipes/headless-install.md +97 -0
  98. package/docs/guides/recipes/website-source.md +28 -0
  99. package/docs/guides/run-workflows.md +279 -0
  100. package/docs/guides/scheduling.md +123 -0
  101. package/docs/guides/use-with-any-agent.md +129 -0
  102. package/docs/guides/wikis.md +81 -0
  103. package/docs/maintainers/README.md +19 -0
  104. package/docs/maintainers/curate-workmap.md +336 -0
  105. package/docs/maintainers/eval.md +3 -0
  106. package/docs/maintainers/local-development.md +108 -0
  107. package/docs/maintainers/release-checklist.md +103 -0
  108. package/docs/migration/README.md +2 -2
  109. package/docs/migration/release-notes/0.0.13.md +1 -1
  110. package/docs/migration/release-notes/0.1.0.md +1 -1
  111. package/docs/migration/release-notes/0.2.0.md +1 -1
  112. package/docs/migration/release-notes/0.3.0.md +1 -1
  113. package/docs/migration/release-notes/0.5.0.md +1 -1
  114. package/docs/migration/release-notes/0.6.0.md +1 -1
  115. package/docs/migration/release-notes/0.7.0.md +1 -1
  116. package/docs/migration/release-notes/0.7.3.md +2 -2
  117. package/docs/migration/release-notes/0.7.4.md +1 -1
  118. package/docs/migration/release-notes/0.7.5.md +1 -1
  119. package/docs/migration/release-notes/0.8.0.md +1 -1
  120. package/docs/migration/release-notes/0.9.0.md +1 -1
  121. package/docs/migration/release-notes/0.9.14.md +1 -1
  122. package/docs/migration/release-notes/0.9.15.md +1 -1
  123. package/docs/migration/release-notes/0.9.16.md +1 -1
  124. package/docs/migration/release-notes/0.9.17.md +1 -1
  125. package/docs/migration/release-notes/0.9.19.md +1 -1
  126. package/docs/migration/release-notes/0.9.2.md +1 -1
  127. package/docs/migration/release-notes/README.md +27 -31
  128. package/docs/migration/v0.5-to-v0.6.md +759 -0
  129. package/docs/migration/v0.8-to-v0.9.md +1 -1
  130. package/docs/plans/0.9.2-agent-command-workflow-plan.md +423 -0
  131. package/docs/posts/README.md +30 -0
  132. package/docs/posts/akm-command-tour.md +495 -0
  133. package/docs/posts/akm-logo-sized.webp +0 -0
  134. package/docs/posts/belief-aware-memory-12.md +139 -0
  135. package/docs/posts/config-v2-lessons-16.md +191 -0
  136. package/docs/posts/improve-loop-autonomous-10.md +225 -0
  137. package/docs/posts/improve-pipeline-debugging-0803.md +179 -0
  138. package/docs/posts/intro-part-01.md +76 -0
  139. package/docs/posts/intro-part-02.md +221 -0
  140. package/docs/posts/intro-part-03.md +190 -0
  141. package/docs/posts/intro-part-04.md +221 -0
  142. package/docs/posts/llm-mode-reflect-13.md +166 -0
  143. package/docs/posts/openpalm-built-on-akm.md +86 -0
  144. package/docs/posts/progressive-disclosure-06.md +129 -0
  145. package/docs/posts/proposal-safety-net-15.md +112 -0
  146. package/docs/posts/release-0.5.0.md +247 -0
  147. package/docs/posts/release-0.6.0.md +117 -0
  148. package/docs/posts/release-0.7.0.md +159 -0
  149. package/docs/posts/release-0.8.0.md +332 -0
  150. package/docs/posts/scattered-skills-05.md +100 -0
  151. package/docs/posts/task-assets-persistent-workflows-11.md +222 -0
  152. package/docs/posts/team-skills-07.md +198 -0
  153. package/docs/posts/wiki-agent-knowledge-08.md +223 -0
  154. package/docs/posts/workflows-vaults-09.md +321 -0
  155. package/docs/product-surface.md +54 -0
  156. package/docs/reference/README.md +1 -2
  157. package/docs/reference/asset-types.md +168 -0
  158. package/docs/reference/cli.md +5 -6
  159. package/docs/reference/data-and-telemetry.md +6 -0
  160. package/docs/reference/env-and-secrets.md +126 -0
  161. package/docs/reference/memory.md +124 -0
  162. package/docs/reference/refs.md +156 -0
  163. package/docs/reference/registry.md +489 -0
  164. package/docs/reference/website-sources.md +141 -0
  165. package/package.json +12 -15
@@ -0,0 +1,231 @@
1
+ # Workflow Engine: Buy vs Build
2
+
3
+ Status: DECISION RECORD — A/B investigation complete, recommendation stated
4
+ Date: 2026-08-01
5
+ Question: can a third-party library offload the maintenance burden of akm's
6
+ hand-rolled workflow/task engine?
7
+ Constraints (owner): no server; markdown authoring preserved (simple
8
+ JSON/YAML acceptable only if markdown becomes cumbersome).
9
+ Evidence: [the review corpus](./reviews/task-workflow-unification/README.md) — see its
10
+ library-evaluation section
11
+ Companion: [`task-workflow-format-unification.md`](./task-workflow-format-unification.md)
12
+ (Design A's authoring/config layer, independently reviewed across five rounds)
13
+
14
+ ---
15
+
16
+ ## 0. Answer
17
+
18
+ **Build — keep the engine.** Not for lack of a good library: OpenWorkflow is
19
+ genuinely well-built and empirically durable. The blocker is that the thing
20
+ being "offloaded" is mostly not there. Measured against the actual tree,
21
+ **~7% of `src/workflows/exec` + `ir` is the generic durable-execution
22
+ concern these libraries sell**; the other ~93% is akm's product and would
23
+ survive adoption unchanged — while adoption *subtracts* capabilities the
24
+ engine has today.
25
+
26
+ Take the dependency **below** the engine instead, and cut scope **above**
27
+ it (§7). That is where the maintenance burden actually is.
28
+
29
+ ## 1. Method
30
+
31
+ Three independent Opus investigations, all forbidden from relying on
32
+ training knowledge — every claim verified against live artifacts:
33
+
34
+ | Investigation | Scope | Report |
35
+ |---|---|---|
36
+ | bunqueue | Full rubric + source read | [`library-eval-bunqueue.md`](./reviews/task-workflow-unification/library-eval-bunqueue.md) |
37
+ | OpenWorkflow | Full rubric + **executed** (SIGKILL durability probe, schema-collision repro) | [`library-eval-openworkflow.md`](./reviews/task-workflow-unification/library-eval-openworkflow.md) |
38
+ | Landscape | 25+ candidates against the hard constraints | [`library-eval-alternatives.md`](./reviews/task-workflow-unification/library-eval-alternatives.md) |
39
+
40
+ Rubric derived from what the engine demonstrably does (established across
41
+ the five review rounds behind the companion spec): embedded in a
42
+ short-lived CLI process; crash-resume from `state.db`; completed-unit reuse
43
+ keyed on **content hashes**; map fan-out with concurrency caps and
44
+ reducers; minutes-long units with leases and stall recovery; judged gate
45
+ loops; and the `brief`/`report` driver protocol.
46
+
47
+ ## 2. The decisive measurement
48
+
49
+ `src/workflows/exec` + `src/workflows/ir` = **8,705 LOC**. Composition:
50
+
51
+ | Component | LOC | Would a library replace it? |
52
+ |---|---|---|
53
+ | `brief.ts` + `report.ts` — external-driver protocol | **2,690 (31%)** | No — no library has an analogue |
54
+ | `ir/` — schema, compile, freeze, plan hash, params | 1,635 | No — determinism/replay identity is akm's contract |
55
+ | Dispatch, gates, worktrees, step-work | ~3,750 | No — agent/LLM dispatch, judged gates, redaction |
56
+ | Journal/resume/lease/concurrency plumbing | **~630 (7%)** | **Yes — this is the addressable surface** |
57
+
58
+ Rows sum to 8,705. The plumbing row is a *region* count, not a file-level
59
+ partition — no whole-file split yields it (the only fully generic files are
60
+ `scheduler.ts` 118 + `unit-writer.ts` 29). The defensible band is ~490–720,
61
+ depending on whether the journal-row rehydration helpers and the gate
62
+ resume seed count as plumbing or as product; the conclusion is unchanged
63
+ anywhere in that band.
64
+
65
+ For calibration, the generic concurrency concern is `src/core/concurrent.ts`
66
+ (40) + `src/workflows/concurrency-policy.ts` (20) = **60 lines** — both of
67
+ which sit *outside* the 8,705.
68
+
69
+ A dependency that replaces 7% while adding an integration seam, a schema
70
+ bridge, and a pre-1.0 upstream is not a burden transfer. It is a burden
71
+ trade at unfavourable odds.
72
+
73
+ ## 3. Candidate results
74
+
75
+ | | bunqueue | OpenWorkflow | Weft | Mastra | XState v5 |
76
+ |---|---|---|---|---|---|
77
+ | License | MIT | Apache-2.0 | MIT | Apache-2.0 | MIT |
78
+ | Server-free | ⚠ single-process only | ✅ | ⚠ single-engine only | ✅ | ✅ |
79
+ | SQLite durability + resume | ⚠ name-keyed | ✅ *(SIGKILL-verified)* | ✅ | ⚠ snapshot, no lease | ❌ |
80
+ | Bounded fan-out + reducer | ❌ | ❌ *(issue #20, 9mo)* | ⚠ | ✅ | ⚠ |
81
+ | Error-classified retry | ❌ | ❌ | ⚠ | ⚠ | ⚠ |
82
+ | Per-step timeout / cancel | ⚠ 30s default | ❌ *(no AbortSignal)* | ⚠ | ✅ | ✅ |
83
+ | Windows CI | ❌ | ❌ | ✅ target | ⚠ | ✅ |
84
+ | Bus factor | 1 | 1 | 1 | team | team |
85
+ | **% of exec+ir deletable** | **0–3%** | **8–12%** | ~6% | 10–15% | 4–6% |
86
+
87
+ **Eliminated on the no-server constraint** (one line each in the landscape
88
+ report): Temporal, Inngest, Trigger.dev, Hatchet, Windmill, n8n, Restate,
89
+ Conductor, Camunda/Zeebe, Dapr, Azure Durable Functions, AWS durable
90
+ execution, Convex, Cloudflare Workflows, Vercel Workflow DevKit, Upstash,
91
+ Obelisk, HotMesh, Resonate. **On storage**: BullMQ (Redis), pg-boss/Absurd
92
+ (Postgres), Agenda (Mongo), SideQuest (LGPL — license). **Not
93
+ orchestrators**: bree, croner, better-queue, p-queue, `@deepkit/workflow`,
94
+ liteflow. **Dead**: workflow-es (2022, `inversify@^4`).
95
+
96
+ Three verifications that overturned plausible assumptions:
97
+
98
+ - **DBOS Transact TS is still Postgres-only.** The tarball for
99
+ `@dbos-inc/dbos-sdk@4.25.14` (2026-07-30) contains zero SQLite files;
100
+ only `pg` and `postgresql://`. The June 2026 SQLite announcement was the
101
+ **Go** SDK.
102
+ - **Effect's workflow/cluster packages fail on akm's exact topology** —
103
+ open upstream issues #6176/#6179: `SqliteError: database is locked` with
104
+ one runner plus multiple short-lived client processes on one store.
105
+ - **LangGraph JS's SQLite checkpointer hard-depends on `better-sqlite3`**
106
+ (native addon), which would break `bun build --compile` single-binary
107
+ distribution across all five targets.
108
+
109
+ ## 4. The architectural finding (independent of maturity)
110
+
111
+ Every candidate expresses a workflow as **code** — a function whose steps
112
+ are memoized by *call position* as execution proceeds. akm expresses a
113
+ workflow as **data**: markdown compiles to a frozen plan, and units are
114
+ memoized by *content hash* (today `hashVersion 4`: frozen template bytes,
115
+ item, declared inputs, params snapshot, dispatch snapshot, invocation,
116
+ schema, env names, isolation, gate feedback — the companion spec proposes
117
+ `hashVersion 5`, adding post-append/post-fill template bytes, the `uses:`
118
+ target kind plus resolved-asset content hash, the persona snapshot, shell text/`shell`/`cwd`, and env *literal*
119
+ values).
120
+
121
+ Two consequences that no amount of library maturity fixes:
122
+
123
+ 1. **Adoption inverts the memoization key.** A code-DSL engine would have
124
+ to be handed a closure generated from akm's plan at runtime; its cache
125
+ then keys on call order, not content. Edit a step's prose and a
126
+ call-order cache happily reuses the completed result — precisely the
127
+ silent-replay-reuse bug the review rounds forced into `hashVersion 5`.
128
+ akm would keep its own hash layer *on top*, so the library's durability
129
+ is redundant rather than substitutive.
130
+ 2. **Gate loops are content-addressed retries.** A failed gate re-runs its
131
+ step *with judge feedback* — same call position, materially different
132
+ ask. akm handles this by folding `gateFeedback` into the hash preimage.
133
+ Under call-order memoization, loop 2 either wrongly reuses loop 1 or
134
+ wrongly re-runs everything.
135
+
136
+ This is why bunqueue's reviewer landed on the same phrase independently:
137
+ *code-DSL saga engine for one long-lived process* vs *frozen-data-plan
138
+ executor for concurrent short-lived CLI runs*.
139
+
140
+ ## 5. Design A — build (the reviewed plan)
141
+
142
+ The companion spec, as it stands after five review rounds: markdown tasks
143
+ and workflows on one parser, `uses:`/`run:` targets, steps-are-tasks
144
+ composition, the five-layer cascade, IR v4 with a shell invocation kind,
145
+ `hashVersion 5`, env provenance split, ceiling-inherited shell execution,
146
+ migration inside the journaled 0.9.0 cutover.
147
+
148
+ Maintenance burden, honestly: akm owns ~630 LOC of journal/lease/resume
149
+ plumbing plus the IR pipeline. The plumbing is the *stable* part — the
150
+ tables, leases, and claim rows have not been the source of churn; format
151
+ and dispatch semantics have.
152
+
153
+ **Risk:** correctness of concurrency and replay is akm's problem. Mitigated
154
+ by what already exists — the journal, `plan_hash`, deterministic replay
155
+ tests, and the chaos/crash-window suites.
156
+
157
+ ## 6. Design B — buy (OpenWorkflow, at its strongest)
158
+
159
+ Designed to win if it can. OpenWorkflow scores best of the candidates
160
+ (embedded, zero runtime deps, empirically SIGKILL-durable, Apache-2.0).
161
+
162
+ **Shape.** Markdown authoring is preserved exactly — the constraint is not
163
+ the discriminator. `akm workflow run` compiles markdown → frozen plan
164
+ → *generates an OpenWorkflow definition* whose steps call akm dispatch.
165
+ OpenWorkflow owns run/step journaling, resume, memoization, heartbeats.
166
+ akm keeps the parser, IR/freeze, cascade, gates, dispatch, redaction.
167
+
168
+ **What is deleted:** ~670 LOC (~8%, the low end of §3's 8–12% estimate) —
169
+ the run/step journal writer, resume bookkeeping, lease renewal. That is
170
+ ~40 LOC above §2's ~630 central estimate and sits inside its ~490–720
171
+ band: OpenWorkflow absorbs a little more than the narrowest reading of
172
+ "plumbing," which is the most favourable assumption available to it.
173
+
174
+ **What must be built to stand still** (each verified absent upstream):
175
+
176
+ | Gap | Consequence |
177
+ |---|---|
178
+ | No concurrency cap (issue #20, open 9 months) | Re-implement bounded fan-out — the capability was the point |
179
+ | No per-step timeout, **no `AbortSignal`** | Lose in-flight cancellation of agent spawns; a runaway unit cannot be preempted |
180
+ | No error classification | Re-implement the 12-value failure taxonomy driving retry |
181
+ | Schema collision — **reproduced**: `no such column: parent_step_attempt_id`, no table-prefix option | A **second DB file**, which breaks single-transaction atomicity across the gate spine |
182
+ | 1,000 step-attempt cap | 10× below akm's 10,000-unit expansion limit |
183
+ | `brief`/`report` (2,690 LOC) are built on the `workflow_run_units` row shape (`WorkflowRunUnitRow`, consumed field-by-field — `input_hash`, `phase`, `claim_holder`, `attempts`, `result_json`, `failure_reason` — through the `withWorkflowRunsRepo` seam, not raw SQL) | The protocol must be re-pointed at a foreign schema it does not control |
184
+
185
+ **Net:** ~670 LOC deleted, ~400+ LOC of adapters and re-implementations
186
+ added, atomicity and cancellation lost, a 10× capacity regression, and a
187
+ bus-factor-1 pre-1.0 dependency placed in the execution path of an
188
+ autonomous scheduled system. The landscape reviewer's phrasing for the
189
+ comparable case applies: *net LOC plausibly negative*.
190
+
191
+ ## 7. Recommendation
192
+
193
+ **Adopt Design A.** Then pursue the two moves that actually reduce burden —
194
+ neither of which is a workflow engine:
195
+
196
+ 1. **Dependency below the engine, not instead of it.** Replace
197
+ `core/concurrent.ts` with `p-limit`/`p-queue` if desired. Small, real,
198
+ zero architectural risk. (Optional; 40 lines — the other 20 of §2's
199
+ calibration figure are `workflows/concurrency-policy.ts`, whose
200
+ cap-precedence policy survives the swap unchanged.)
201
+ 2. **Cut scope above the engine — the real lever.** `brief`/`report` is
202
+ **2,690 LOC, 31% of the engine**, and is Experimental/opt-in behind
203
+ `experimental.workflowEngine` while `run` is Stable and ungated. That
204
+ is the largest maintenance liability in the subsystem *and* the most
205
+ removable. Recommend an explicit keep/cut decision on the external
206
+ driver protocol as its own proposal — cutting it removes ~4× more code
207
+ than the best library adoption would, with no new dependency.
208
+
209
+ **Worth stealing** (patterns, not packages): bunqueue's `pivot()` for plan
210
+ changes mid-run, its non-terminal `compensation-stuck` state, and its
211
+ seeded `simulatedClock()` for deterministic time tests; OpenWorkflow's
212
+ SIGKILL-probe test methodology, which is a stronger durability check than
213
+ akm's current crash-window suites.
214
+
215
+ ## 8. Reopen triggers
216
+
217
+ Revisit if any becomes true:
218
+
219
+ - DBOS Transact **TS** ships a SQLite system DB (Go/Python already have it).
220
+ - Weft reaches 1.0 with documented **multi-process lease coordination** —
221
+ it is the only library designed for akm's runtime shape (Bun-native,
222
+ `bun:sqlite`, `windows-x64` compile target); today it explicitly refuses
223
+ concurrent engines against one store and is 8 weeks old.
224
+ - OpenWorkflow lands concurrency caps (#20), `AbortSignal`, and a table
225
+ prefix — the three gaps that force the second DB file and the capability
226
+ regressions.
227
+ - akm's own plumbing becomes a churn source. It has not been; format and
228
+ dispatch semantics have.
229
+
230
+ The trigger is not "a library got popular." It is "a library removes more
231
+ than it adds," measured the way §2 measures it.
@@ -0,0 +1,437 @@
1
+ # Workflow Format Unification
2
+
3
+ Status: PROPOSAL v2 — owner decisions from review round 1 applied
4
+ Date: 2026-07-30
5
+ Supersedes: the dual markdown/YAML-program authoring surface (both unreleased)
6
+
7
+ Owner decisions incorporated in v2: no interpolation syntax in prose (§2.3),
8
+ gate rubrics live in the body (§2.4), steps have no titles — bare ids only
9
+ (§2.2).
10
+
11
+ ## 0. Why now
12
+
13
+ The workflow feature has never shipped (`experimental.workflowEngine` opt-in,
14
+ 0.9.0-rc). There are currently **two authoring formats** for one asset type,
15
+ and the seams between them have started leaking into unrelated systems — the
16
+ OKF provenance work (#730) tripped over workflow's closed frontmatter
17
+ allowlist, which exists only because the markdown format grew its own bespoke
18
+ validation instead of sharing the asset envelope every other type uses. This
19
+ is the moment to unify: nothing on disk to migrate, no users to break.
20
+
21
+ ## 1. Review of the two current formats
22
+
23
+ ### 1.1 Format A — markdown workflow (`src/workflows/parser.ts`, ~500 lines)
24
+
25
+ A bespoke prose grammar: one magic `# Workflow:` H1, magic `## Step:` H2s, a
26
+ magic `Step ID:` body line, magic `### Instructions` / `### Completion
27
+ Criteria` subsection names. Capabilities: **linear steps only** — no routing,
28
+ fan-out, retry, timeout, engine/model selection, output schemas, or budgets.
29
+ The compile path (`ir/compile.ts` `compileWorkflowPlan`) lowers every step to
30
+ one fail-fast unit node with verbatim instructions.
31
+
32
+ ### 1.2 Format B — YAML program (`src/workflows/program/*`, ~1,300 lines)
33
+
34
+ The full orchestration surface: `unit | map | route` steps, `${{ … }}`
35
+ template expressions, retries keyed on the failure taxonomy, timeouts,
36
+ per-unit engine/model/llm overrides, JSON-Schema-typed params and step
37
+ outputs, gates with `max_loops`, budgets, worktree isolation.
38
+ Instructions are embedded YAML block scalars.
39
+
40
+ ### 1.3 The divergences (each one is a standing cost)
41
+
42
+ | Concern | Markdown | YAML program |
43
+ |---|---|---|
44
+ | Step id grammar | `[A-Za-z0-9][A-Za-z0-9._-]*` — dots allowed | `[A-Za-z_][A-Za-z0-9_-]*` — dots forbidden |
45
+ | Params | name → description string, untyped | name → JSON Schema |
46
+ | Templating | **none** — instructions verbatim | `${{ … }}` expressions, real substitution |
47
+ | Gate | `### Completion Criteria` bullets; no `max_loops` | `gate:` with criteria/max_loops |
48
+ | Capabilities | linear only | full orchestration |
49
+ | Frontmatter validation | closed hand-maintained allowlist | closed key lists, JSON Schema published |
50
+ | Title | required `# Workflow:` H1 prefix | `name:` field duplicating the filename |
51
+ | Version | internal `schemaVersion 1` | `version: 2` (IR is v3) |
52
+
53
+ Beyond the table, four structural costs:
54
+
55
+ 1. **The fake templating trap.** Every shipped example workflow
56
+ (`scripts/akm-eval/example-stash/workflows/*.md`) uses `{{ repo }}`-style
57
+ moustaches that **the engine never substitutes** — markdown instructions
58
+ compile `templating: "verbatim"`. The YAML format substitutes `${{ … }}`
59
+ for real. Two syntaxes; one is decorative and exists only as a convention
60
+ the executing agent is hoped to honor.
61
+ 2. **Content sniffing everywhere.** Two structural probes
62
+ (`looksLikeWorkflow`, `looksLikeWorkflowProgram`) consulted by the indexer
63
+ matchers, the workflow adapter (`recognize()` returns `"markdown" |
64
+ "yaml-program"`), the asset loader (extension dispatch + probe), and the
65
+ proposal validator (which sniffs *content* when no path is available).
66
+ `canonicalizeWorkflowName` collapses `foo.md` / `foo.yaml` / `foo.yml` to
67
+ one identity, so the two formats can silently collide on one ref.
68
+ 3. **The closed-allowlist special case.** Workflow markdown is the only AKM
69
+ type validating frontmatter against a closed hand-maintained key set. It
70
+ is why #730's provenance stamping needed a re-validation fallback in the
71
+ promotion path, why the allowlist had to learn `generated`/`verified`/
72
+ `provenance` by name, and why its error message is now stale
73
+ (`validator.ts:94` still enumerates the pre-#730 keys). Every future
74
+ machine-stamped key repeats this.
75
+ 4. **Double everything.** Two parsers, two templates, two doc sections, two
76
+ test suites, two JSON representations feeding one `WorkflowPlanDraft`.
77
+ ~1,800 lines of format code for one asset type.
78
+
79
+ ### 1.4 What is genuinely good and must survive
80
+
81
+ - **The IR and engine.** Both frontends compile to one `WorkflowPlanDraft` →
82
+ frozen IR v3. Deterministic replay, journaled units, leases, brief/report —
83
+ none of that is format-coupled. This proposal changes **frontends only**.
84
+ - **The closed reference grammar** (`program/expressions.ts`) — though in v2
85
+ of this proposal it *shrinks* from a template language to a bare reference
86
+ string format (§2.3).
87
+ - **The program vocabulary** (unit/map/route, gate control, retry taxonomy,
88
+ budgets, defaults): well-designed, schema-validated, kept nearly verbatim.
89
+ - **Markdown prose as the instruction medium.** The example workflows prove
90
+ the point: real instructions are long structured prose with code fences,
91
+ sub-headings, and lists. Markdown is their native medium.
92
+
93
+ ## 2. Design
94
+
95
+ ### 2.1 Principle
96
+
97
+ > **One file. Machine surface in frontmatter, prose surface in the body,
98
+ > joined by step id. Prose is never templated — data reaches units as
99
+ > attached context, not string splices.**
100
+
101
+ A workflow is an ordinary AKM markdown asset — same envelope as every other
102
+ type, OKF-conformant frontmatter + body — whose frontmatter carries the
103
+ entire orchestration graph and whose body carries per-step instructions and
104
+ gate rubrics under plain headings.
105
+
106
+ ### 2.2 The format
107
+
108
+ ````markdown
109
+ ---
110
+ type: workflow
111
+ description: Drive a batch of GitHub issues to merged PRs.
112
+ params:
113
+ repo: { type: string, description: Target repository owner/name }
114
+ issues: { type: array, description: Issue numbers to implement }
115
+ defaults: { timeout: 10m, on_error: fail }
116
+ budget: { max_units: 60 }
117
+ steps:
118
+ - id: intake
119
+ output: { type: object }
120
+ - id: implement
121
+ map:
122
+ over: steps.intake.output.issues
123
+ concurrency: 3
124
+ unit: { isolation: worktree, retry: { max: 2, on: [timeout] } }
125
+ # `output` describes the REDUCER RESULT: the default `collect` reducer
126
+ # folds per-item unit results into an array.
127
+ output: { type: array }
128
+ # Retry lives here, not in a backward route: a failed gate re-runs this
129
+ # step with the judge's feedback, bounded by max_loops.
130
+ gate: { max_loops: 2 }
131
+ - id: verdict
132
+ inputs: [steps.implement.output]
133
+ output: { type: object }
134
+ - id: pick-outcome
135
+ route:
136
+ input: steps.verdict.output.status
137
+ when: [{ match: clean, step: announce }]
138
+ default: escalate # both targets are LATER steps — routes are forward-only
139
+ - id: escalate
140
+ inputs: [steps.verdict.output]
141
+ - id: announce
142
+ inputs: [steps.implement.output]
143
+ ---
144
+
145
+ # GitHub Issues Parallel Implementer
146
+
147
+ Free preamble prose. Indexed for search, shown in `akm show`, never
148
+ dispatched. Any headings except level-2 are fine here.
149
+
150
+ ## intake
151
+
152
+ Everything under this heading, byte-exact, is the step's instructions —
153
+ sub-headings, fences, lists, all of it. The run's params arrive as attached
154
+ context; refer to them by name in prose ("clone the `repo` parameter's
155
+ repository").
156
+
157
+ ## implement
158
+
159
+ This section is the **map unit template**. The engine attaches each unit's
160
+ item (and its index) as context; the prose says "the issue you were given."
161
+
162
+ ### gate
163
+
164
+ The gate rubric — as long as it needs to be. Full prose, bullets, examples
165
+ of passing and failing artifacts. The judge receives this whole section.
166
+
167
+ - Every issue in the working set has a mergeable PR or a recorded blocker.
168
+ - No PR was opened against a branch other than the declared base.
169
+
170
+ ## verdict
171
+
172
+ Inspect the implementation results and report whether the batch is clean or
173
+ needs escalation.
174
+
175
+ ## escalate
176
+
177
+ Escalate blockers from the verdict, with the verdict artifact attached as
178
+ context (declared via `inputs:` above).
179
+
180
+ ## announce
181
+
182
+ Post the summary. The `implement` step's artifact is attached as context
183
+ (declared via `inputs:` above).
184
+ ````
185
+
186
+ **Frontmatter** (validated by one published JSON Schema):
187
+
188
+ - The standard AKM asset envelope — `type`, `description`, `tags`,
189
+ `when_to_use`, `xrefs`, `updated`/`timestamp`, and the OKF v0.2 families
190
+ (`generated`, `verified`, `provenance`, `status`, `stale_after`) — via a
191
+ shared `$ref`'d envelope definition (§2.5).
192
+ - The orchestration keys, adopted from program v2 with these changes:
193
+ - `version:` and `name:` **dropped**. Identity is the ref; the frozen plan
194
+ already versions execution semantics (IR v3); the authoring schema
195
+ evolves additively like every other asset type.
196
+ - `instructions:` keys **removed** — prose lives only in the body.
197
+ - `gate.criteria` **removed** — rubrics live in the body (§2.4). `gate:`
198
+ retains only the optional `max_loops` configuration.
199
+ - **No titles anywhere.** No step `title:` key, no display-title heading
200
+ suffix. A step is its id; the asset's human name is its `description`
201
+ and H1 like any other asset.
202
+ - New `inputs:` key on unit/map steps: the prior-step artifacts this step
203
+ consumes, as bare reference strings (§2.3). Replaces prose splicing as
204
+ the way a step sees upstream data, and gives replay hashing its exact
205
+ input set. Sub-paths are legal (`steps.x.output.issues`, not just
206
+ `steps.x.output`) so a step re-dispatches only when the slice it
207
+ consumes changes.
208
+ - A step with no `map:` and no `route:` **is** a unit step; bare
209
+ `- id: validate` is the complete minimal declaration. `unit:` remains as
210
+ the optional dispatch-override bag (engine/model/llm/timeout/retry/
211
+ on_error/env/isolation).
212
+ - One id grammar everywhere: `[A-Za-z_][A-Za-z0-9_-]*`.
213
+ - Keys stay snake_case (`on_error`, `max_loops`) — the existing AKM/OKF
214
+ frontmatter convention.
215
+
216
+ **Body** (three rules):
217
+
218
+ 1. Every level-2 heading must be `## <step-id>` for a declared step, exactly.
219
+ (Fenced code blocks are skipped when scanning for headings.)
220
+ 2. A unit or map step **must** have a section (its instructions / per-item
221
+ template, byte-exact to the next H2 or EOF). A route step **may** have one
222
+ (documentation, plus a gate rubric if gated). Everything before the first
223
+ H2 is free preamble.
224
+ 3. Inside a step section, an optional `### gate` sub-heading starts the
225
+ step's gate rubric (running to the section end). It is the format's
226
+ **single reserved marker**. Omitted or empty rubric text skips validation;
227
+ a non-empty rubric enables optional validation. Frontmatter `gate:` without
228
+ a rubric is valid but inert.
229
+
230
+ No `Step ID:` lines, no `# Workflow:` prefix, no reserved H3s beyond `gate`.
231
+
232
+ ### 2.3 No interpolation syntax — references and attached context
233
+
234
+ v1 of this proposal kept `${{ … }}` templating in prose and added a `$${{`
235
+ escape. The owner asked the right question — *why have the syntax at all?* —
236
+ and the answer is that only the **engine** ever needed deterministic value
237
+ resolution, and the engine only reads **whole-value frontmatter positions**:
238
+
239
+ - `map.over` — the list to fan out over.
240
+ - `route.input` — the value routing matches on.
241
+ - `inputs:` — the artifacts a step consumes (new, replacing prose splicing).
242
+
243
+ Whole-value positions need no delimiters. These become **bare reference
244
+ strings** with the same closed grammar, shrunk from four roots to two:
245
+
246
+ params.<name>
247
+ steps.<id>.output( .<ident> | [<int>] )*
248
+
249
+ `item` and `item_index` are **deleted from the language**: with no splicing
250
+ there is nothing to substitute — the engine attaches each map unit's item and
251
+ index as context alongside the prompt.
252
+
253
+ **Prose is never templated.** Each dispatched unit receives, as structured
254
+ attached context: the run params (params are run-scoped and documented
255
+ non-secret), its item + index (map units), and the artifacts named by its
256
+ step's `inputs:`. Instructions refer to these in plain language — which is
257
+ exactly how the classic driver and all nine example workflows already
258
+ functioned, since their moustaches were never substituted.
259
+
260
+ What this buys:
261
+
262
+ - The escape-syntax question evaporates — there are no delimiters in prose.
263
+ - The P1 injection class dies at the root: data never enters the instruction
264
+ string at all, spliced or otherwise.
265
+ - `program/expressions.ts` shrinks from a template parser (segments,
266
+ literals, offsets) to a small reference-string parser.
267
+ - Bodies are pure markdown — cohesive with every other asset, no syntax an
268
+ OKF reader or a human editor has to know about.
269
+
270
+ Determinism and replay identity are preserved: unit identity hashes the
271
+ frozen template bytes + canonical item JSON + declared-input artifact hashes
272
+ + the params snapshot — the same inputs the journal's replay machinery keys
273
+ on today, minus the string splice. `inputs:` gives the hash its exact
274
+ upstream set, so a step re-dispatches only when data it actually consumes
275
+ changes (the precision splicing used to provide implicitly).
276
+
277
+ The honest trade-off: prose can no longer compose a value mid-string with
278
+ engine-guaranteed fidelity ("run `git checkout <item>`") — the executing
279
+ agent performs that mapping from attached context. Every current executor is
280
+ an agent/LLM engine, so this is the same trust the instructions already
281
+ extend everywhere else. If a non-agent unit kind (raw shell/exec) is ever
282
+ added, *that unit kind* reintroduces substitution as its own need — scoped
283
+ there, not in prose.
284
+
285
+ ### 2.3a Routes are forward-only; gates are the retry mechanism
286
+
287
+ Two invariants inherited unchanged from the program format, doubly enforced
288
+ today (`program/parser.ts:459` and `ir/schema.ts:227`), and preserved here:
289
+
290
+ - **A route target must be a LATER step**, and a step never routes to itself.
291
+ The plan stays a DAG, so termination is structural rather than a runtime
292
+ budget's job.
293
+ - **Retry is a gate, not a backward edge.** A failed gate re-runs its own
294
+ step with the judge's feedback, bounded by `gate.max_loops`; a declared
295
+ `output:` schema that the artifact fails is the one error the gate loop may
296
+ retry through (`artifactSchemaFailure`). Anything wanting "go back and fix
297
+ it" expresses it as a gate on the step being fixed.
298
+
299
+ A backward `default:` therefore does not mean "loop" — it is a lint error.
300
+
301
+ ### 2.3b `output:` describes the reducer result
302
+
303
+ For a map step, `output:` validates the **promoted artifact**, i.e. what the
304
+ reducer produced — not one unit's result. The default `collect` reducer folds
305
+ per-item results into an **array**, so a collected map step's schema is
306
+ `{ type: array }`. (`vote` folds to the winning value.) A unit step's
307
+ `output:` describes that single unit's structured result.
308
+
309
+ ### 2.4 Gates: control in frontmatter, rubric in the body
310
+
311
+ Owner decision: rubrics are often long — real rubric documents, not
312
+ one-liners — and belong in the body. The split:
313
+
314
+ - **Frontmatter `gate:`** optionally carries `max_loops`. It does not enable
315
+ validation by itself; without a non-empty body rubric it is inert.
316
+ - **Body `### gate`** (inside the step's section) carries the rubric: full
317
+ prose, bullets, worked examples. The judge receives the whole section
318
+ byte-exact. Omitted or empty text skips validation. This replaces both the
319
+ markdown format's `### Completion Criteria` bullets and the program's
320
+ `gate.criteria` string list.
321
+
322
+ ### 2.5 Validation model — and the end of the allowlist special case
323
+
324
+ One published JSON Schema (`schemas/akm-workflow.json`, repurposed) validates
325
+ the entire frontmatter. Its top level `$ref`s a new **shared asset-envelope
326
+ schema** — the common keys every AKM markdown type carries, including the
327
+ machine-stamped OKF families. Consequences:
328
+
329
+ - Workflow stops being the only type with a bespoke closed key set; "closed"
330
+ becomes `additionalProperties: false` over `envelope ∪ workflow-keys`,
331
+ defined in one place.
332
+ - A future machine-stamped key is added to the envelope definition **once**,
333
+ and every schema-validated type inherits it — the #730 fallback dance and
334
+ the stale hand-written key-list message become structurally impossible.
335
+ The promotion-path re-validation backstop is deleted.
336
+ - Editors get frontmatter completion/validation via the published schema
337
+ (yaml-language-server association) — tooling no bespoke markdown grammar
338
+ can offer.
339
+
340
+ Semantic passes keep their existing implementations from the program parser,
341
+ retargeted at frontmatter: duplicate ids, route targets exist / are
342
+ forward-only / are unique, reference strings resolve to earlier steps,
343
+ timeout format, retry-reason taxonomy, resource limits. Body checks are the
344
+ three rules of §2.2.
345
+
346
+ Recognition simplifies to: frontmatter `type: workflow`, or residence under
347
+ `workflows/` — no content sniffing. `WORKFLOW_EXTENSIONS` shrinks to `.md`.
348
+
349
+ ### 2.6 OKF cohesion
350
+
351
+ The unified asset **is** an OKF-shaped document: frontmatter + markdown
352
+ body, no inline syntax. Provenance stamping applies with zero
353
+ special-casing. A third-party OKF reader sees a normal document — prose
354
+ overview, sections per step — degrading gracefully to readable procedure
355
+ documentation. `status: draft` / `stale_after` become meaningful on
356
+ workflows for free (e.g. a future rule that draft workflows refuse `run` —
357
+ noted, not proposed here).
358
+
359
+ ## 3. What gets deleted
360
+
361
+ - `src/workflows/parser.ts`'s grammar: `# Workflow:` / `## Step:` /
362
+ `Step ID:` / reserved-H3 subsections (frontmatter/toc plumbing is reused
363
+ by the new body binder).
364
+ - `looksLikeWorkflow` / `looksLikeWorkflowProgram` sniffing and the
365
+ `"markdown" | "yaml-program"` mode split in the adapter, matchers, loader,
366
+ and proposal validator.
367
+ - The YAML program as a distinct on-disk format; its field validation
368
+ survives as the frontmatter semantic passes.
369
+ - **The template expression language**: `${{ … }}` delimiters, segment
370
+ parsing, `item`/`item_index` roots, and the escape question — replaced by
371
+ bare reference strings in three frontmatter positions plus context
372
+ attachment.
373
+ - The workflow-only closed frontmatter allowlist and its hand-maintained
374
+ error string, replaced by the schema.
375
+ - The #730 promotion-path re-validation fallback.
376
+ - Step titles in all forms; the `name:` and `version:` keys.
377
+ - One of the two authoring templates, one of the two doc sections, the
378
+ extension-collapse logic beyond `.md`, `isWorkflowProgramPath`.
379
+
380
+ Net: two formats' worth of parsing/dispatch (~1,800 lines) replaced by one
381
+ frontmatter schema + semantic passes + a three-rule body binder, with the
382
+ IR, engine, and journal reused unchanged — and the template language itself
383
+ reduced to a reference-string parser.
384
+
385
+ ## 4. Blast radius (all unreleased surfaces)
386
+
387
+ - **Engine seam (the one non-frontend change):** unit dispatch gains context
388
+ attachment (params / item / declared inputs) and unit-identity hashing
389
+ keys on (template, item, input hashes, params) instead of instantiated
390
+ strings. Journal shape is already compatible; freeze pins the plan either
391
+ way.
392
+ - **Templates**: both authoring templates → one.
393
+ - **Examples**: 9 example-bundle workflows rewritten; their fake moustaches
394
+ become plain prose references to attached context — which is how they
395
+ already behaved in practice.
396
+ - **Fixtures/goldens**: `format-family-goldens/`, `all-types` fixtures,
397
+ parser/program suites (program-parser tests largely port).
398
+ - **Docs**: `docs/reference/workflows.md` collapses its two format sections;
399
+ the expression-language section shrinks to the reference grammar.
400
+ - **Index cache**: `workflow_documents` re-derives on reindex (unreleased).
401
+ - **CLI**: `akm workflow create` emits the unified template; `create
402
+ <name>.yaml` dies.
403
+
404
+ ## 5. Decision log and remaining questions
405
+
406
+ Resolved by owner (review round 1):
407
+
408
+ 1. ~~Escape syntax~~ — **moot**: no interpolation in prose at all (§2.3).
409
+ 2. ~~Gate criteria location~~ — **body**, under the single reserved
410
+ `### gate` marker; control fields stay in frontmatter (§2.4).
411
+ 3. ~~Step titles~~ — **none**; a step is its id.
412
+ 4. ~~Heading form~~ — **bare `## <id>`**.
413
+
414
+ Resolved by owner (review round 2):
415
+
416
+ 5. **`inputs:` accepts sub-paths.** `steps.x.output` and
417
+ `steps.x.output.issues` are both legal — the reference grammar already
418
+ parses the path form, and a narrower declared input means a narrower
419
+ replay hash, so a step re-dispatches only when the slice it actually
420
+ consumes changes.
421
+ 6. **All run params attach to every unit.** No per-step params declaration.
422
+ Params are run-scoped and documented non-secret (`docs/reference/
423
+ workflows.md`, "Params are not secret"); secrets travel as `env:` refs,
424
+ which remain per-unit. This keeps the attachment model uniform: every
425
+ unit receives params, its item + index if it is a map unit, and the
426
+ artifacts its step's `inputs:` names.
427
+
428
+ No open questions remain. This proposal is ready to implement.
429
+
430
+ ## 6. Non-goals
431
+
432
+ - No engine, IR, freeze, journal, lease, or brief/report changes beyond the
433
+ dispatch-context seam noted in §4.
434
+ - No new orchestration capabilities (no new step kinds, reducers, or
435
+ reference roots).
436
+ - No change to task/env/script/secret formats; task remains a YAML type —
437
+ tasks are machine schedules, not prose procedures.