akm-cli 0.9.2-alpha.4 → 0.9.2

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 (143) hide show
  1. package/CHANGELOG.md +493 -0
  2. package/STABILITY.md +23 -5
  3. package/dist/assets/hints/cli-hints-full.md +12 -7
  4. package/dist/assets/tasks/core/extract.yml +3 -5
  5. package/dist/assets/tasks/core/improve.yml +3 -5
  6. package/dist/assets/tasks/core/index-refresh.yml +3 -5
  7. package/dist/assets/tasks/core/sync.yml +3 -5
  8. package/dist/assets/tasks/core/version-check.yml +3 -5
  9. package/dist/assets/tasks/improve/akm-graph-refresh-weekly.yml +3 -5
  10. package/dist/assets/tasks/improve/akm-improve-catchup.yml +6 -6
  11. package/dist/assets/tasks/improve/akm-improve-consolidate.yml +3 -5
  12. package/dist/assets/tasks/improve/akm-improve-frequent.yml +3 -5
  13. package/dist/assets/tasks/improve/akm-improve-nightly.yml +3 -5
  14. package/dist/cli/unknown-flags.js +12 -1
  15. package/dist/cli.js +8 -1
  16. package/dist/commands/command/command-execution.js +23 -2
  17. package/dist/commands/health/improve-metrics.js +38 -0
  18. package/dist/commands/health/windows.js +8 -4
  19. package/dist/commands/health.js +8 -4
  20. package/dist/commands/lint/index.js +1 -1
  21. package/dist/commands/migrate-cli.js +130 -24
  22. package/dist/commands/proposal/validators/proposal-validators.js +7 -2
  23. package/dist/commands/tasks/explain.js +304 -0
  24. package/dist/commands/tasks/tasks-cli.js +185 -3
  25. package/dist/commands/tasks/tasks.js +265 -52
  26. package/dist/commands/workflow/plan.js +159 -0
  27. package/dist/commands/workflow-cli.js +94 -2
  28. package/dist/core/activation-policy.js +2 -12
  29. package/dist/core/adapter/adapters/akm-lint.js +7 -4
  30. package/dist/core/adapter/adapters/akm-metadata.js +26 -14
  31. package/dist/core/adapter/adapters/akm-task-adapter.js +13 -10
  32. package/dist/core/errors.js +45 -0
  33. package/dist/core/json-schema.js +15 -5
  34. package/dist/core/state/migrations.js +57 -0
  35. package/dist/core/state-db.js +16 -14
  36. package/dist/core/subprocess.js +47 -13
  37. package/dist/execution/guarded-source.js +44 -0
  38. package/dist/execution/input-contract.js +250 -0
  39. package/dist/execution/target-ref.js +63 -0
  40. package/dist/indexer/usage/usage-events.js +14 -3
  41. package/dist/integrations/agent/execution-lowering.js +12 -1
  42. package/dist/output/shapes/passthrough.js +2 -0
  43. package/dist/output/text/helpers.js +1 -1
  44. package/dist/output/text/migrate.js +12 -3
  45. package/dist/output/text/workflow-format.js +192 -10
  46. package/dist/output/text/workflow.js +2 -1
  47. package/dist/runtime.js +1 -0
  48. package/dist/scripts/akm-migrate-node.js +11838 -10118
  49. package/dist/scripts/akm-migrate.js +11828 -10117
  50. package/dist/setup/steps/tasks.js +34 -17
  51. package/dist/storage/repositories/task-history-repository.js +5 -1
  52. package/dist/storage/repositories/workflow-runs-repository.js +144 -6
  53. package/dist/tasks/backends/launchd.js +31 -84
  54. package/dist/tasks/embedded.js +13 -7
  55. package/dist/tasks/model/invocation.js +4 -0
  56. package/dist/tasks/prepare/prepare-script-target.js +9 -0
  57. package/dist/tasks/prepare/prepare-support.js +154 -0
  58. package/dist/tasks/prepare/prepare.js +117 -0
  59. package/dist/tasks/prepare/prepared-execution.js +4 -0
  60. package/dist/tasks/prepare/script-capture.js +80 -0
  61. package/dist/tasks/run/attempt-lifecycle.js +165 -0
  62. package/dist/tasks/run/load-task.js +117 -0
  63. package/dist/tasks/run/provenance.js +20 -0
  64. package/dist/tasks/run/run-command-task.js +92 -0
  65. package/dist/tasks/run/run-native-task.js +222 -0
  66. package/dist/tasks/run/run-task.js +99 -0
  67. package/dist/tasks/run/run-workflow-task.js +222 -0
  68. package/dist/tasks/run/task-history.js +134 -0
  69. package/dist/tasks/run/task-log.js +179 -0
  70. package/dist/tasks/run/task-result.js +19 -0
  71. package/dist/tasks/scheduler-binding.js +66 -2
  72. package/dist/tasks/scheduler-invocation.js +63 -3
  73. package/dist/tasks/scheduler-sync.js +77 -14
  74. package/dist/tasks/source/bounded-document.js +455 -0
  75. package/dist/tasks/source/parse-task-source.js +59 -0
  76. package/dist/tasks/source/project-v4.js +62 -0
  77. package/dist/tasks/source/task-input-diagnostics.js +36 -0
  78. package/dist/tasks/source/task-source-v4.js +626 -0
  79. package/dist/tasks/source-v3.js +10 -733
  80. package/dist/tasks/task-run-reserved-flags.js +79 -0
  81. package/dist/workflows/authoring/authoring.js +17 -8
  82. package/dist/workflows/exec/child-invocation.js +34 -0
  83. package/dist/workflows/exec/child-workflow.js +370 -0
  84. package/dist/workflows/exec/exec-unit.js +50 -170
  85. package/dist/workflows/exec/frozen-judge.js +19 -2
  86. package/dist/workflows/exec/native-executor.js +49 -27
  87. package/dist/workflows/exec/param-secrets.js +12 -0
  88. package/dist/workflows/exec/run-workflow.js +48 -59
  89. package/dist/workflows/exec/step-work.js +222 -80
  90. package/dist/workflows/exec/unit-dispatch.js +72 -0
  91. package/dist/workflows/freeze/child-output-references.js +94 -0
  92. package/dist/workflows/freeze/environment.js +174 -0
  93. package/dist/workflows/freeze/identity.js +22 -0
  94. package/dist/workflows/freeze/resolve-steps.js +78 -0
  95. package/dist/workflows/freeze/source-freeze.js +57 -0
  96. package/dist/workflows/freeze/step-values.js +68 -0
  97. package/dist/workflows/freeze/targets/child-workflow.js +206 -0
  98. package/dist/workflows/freeze/targets/command.js +81 -0
  99. package/dist/workflows/freeze/targets/script.js +57 -0
  100. package/dist/workflows/freeze/targets/shell.js +31 -0
  101. package/dist/workflows/freeze/targets/task.js +179 -0
  102. package/dist/workflows/freeze/task-bindings.js +180 -0
  103. package/dist/workflows/ir/compile.js +59 -11
  104. package/dist/workflows/ir/environment-v4.js +3 -3
  105. package/dist/workflows/ir/freeze-v4.js +41 -7
  106. package/dist/workflows/ir/params.js +58 -131
  107. package/dist/workflows/ir/plan-hash.js +3 -3
  108. package/dist/workflows/ir/schema-v4.js +246 -17
  109. package/dist/workflows/parser.js +74 -2
  110. package/dist/workflows/program/schema.js +5 -2
  111. package/dist/workflows/resource-limits.js +20 -0
  112. package/dist/workflows/runtime/plan-classifier.js +24 -7
  113. package/dist/workflows/runtime/run-outputs.js +103 -0
  114. package/dist/workflows/runtime/runs.js +114 -9
  115. package/dist/workflows/runtime/workflow-asset-loader.js +14 -6
  116. package/dist/workflows/source-files.js +5 -5
  117. package/dist/workflows/source-ir/compare.js +17 -0
  118. package/dist/workflows/source-ir/compile.js +7 -3
  119. package/dist/workflows/source-ir/github-yaml.js +64 -17
  120. package/dist/workflows/source-ir/schema.js +69 -21
  121. package/dist/workflows/source-ir/semantics.js +7 -25
  122. package/dist/workflows/source-ir/triggers.js +79 -0
  123. package/dist/workflows/source-ir/uses.js +33 -7
  124. package/docs/migration/README.md +1 -1
  125. package/docs/migration/release-notes/0.9.2.md +87 -11
  126. package/docs/migration/release-notes/README.md +3 -2
  127. package/docs/migration/v0.8-to-v0.9.md +13 -11
  128. package/docs/migration/v0.9.0-troubleshooting.md +20 -13
  129. package/docs/migration/v0.9.1-to-v0.9.2.md +598 -49
  130. package/docs/reference/README.md +1 -1
  131. package/docs/reference/cli.md +140 -46
  132. package/docs/reference/configuration.md +6 -5
  133. package/docs/reference/supported-formats.md +9 -5
  134. package/docs/reference/tasks.md +338 -75
  135. package/docs/reference/workflow-schema.md +290 -16
  136. package/docs/reference/workflows.md +57 -7
  137. package/package.json +1 -1
  138. package/schemas/akm-task.json +173 -118
  139. package/schemas/akm-workflow.json +28 -0
  140. package/dist/tasks/runner.js +0 -941
  141. package/dist/tasks/runtime-v3.js +0 -281
  142. package/dist/workflows/ir/source-freeze-v4.js +0 -506
  143. package/dist/workflows/source-ir/ordering.js +0 -38
@@ -59,29 +59,234 @@ jobs:
59
59
  ref: commands/review
60
60
  ```
61
61
 
62
+ AKM's public position on this format: **AKM YAML uses a familiar
63
+ GitHub-step-shaped syntax but is an AKM workflow format, executed by AKM's
64
+ native engine.** It is not Marketplace-action-compatible, does not evaluate
65
+ GitHub expressions or contexts, has no hosted runner images or service
66
+ containers, and does not execute multiple jobs.
67
+
62
68
  The accepted 0.9.2 subset is deliberately closed:
63
69
 
64
70
  - `on` accepts five-field `schedule` entries and an empty or null
65
71
  `workflow_dispatch`; workflow_dispatch inputs are unsupported.
66
72
  - Service events are rejected.
67
73
  A rejected service event creates no watcher and no polling daemon.
74
+ - **`jobs:` must contain exactly one job.** A document with zero, two, or
75
+ more jobs fails at the adapter with reason `multi-job-unsupported`,
76
+ surfaced as `UsageError` code `COMPOSITION_INVALID` when the workflow is
77
+ frozen (`akm workflow run` or `akm workflow plan`). Split a multi-job
78
+ document into separate single-job workflows and compose them with a
79
+ child-workflow step (`uses: workflows/<ref>`) instead. A job's `needs:`
80
+ must be empty — a non-empty `needs:` fails for the same reason, since a
81
+ single job has nothing to depend on.
68
82
  - Each job requires exactly `runs-on: [self-hosted]`. `name`, `needs`, and
69
83
  `steps` are the remaining job fields.
70
84
  - Each step requires `id` and exactly one `uses` or `run`; optional fields are
71
85
  `name`, `with`, `env`, `shell`, and contained `working-directory`.
72
86
  - A `run` accepts only token-safe local command tokens.
73
87
  Shell expansion and operators are unsupported and rejected, even when a host shell is named.
74
- - `uses` delegates to the task-v3 ref classifier. `akm/command`, command,
75
- script, and task composition are local targets.
76
- Local actions and Docker actions are unsupported and rejected (including `./` and `docker://`); remote actions are rejected
77
- because acquisition is out of scope; nested workflows are unsupported.
88
+ - `uses` is classified as a canonical asset ref (`commands/`, `scripts/`,
89
+ `tasks/`, `workflows/`), plus the `akm/command` builtin. `akm/command`,
90
+ command, script, task, and **child-workflow** composition are local
91
+ targets see [Child workflows](#child-workflows) for `uses:
92
+ workflows/<ref>`.
93
+ Local actions and Docker actions are unsupported and rejected (including
94
+ `./` and `docker://`); a GitHub Action locator (`owner/repo[/path]@ref`,
95
+ e.g. `actions/checkout@v4`) and every other unrecognized shape fail the
96
+ same way — `unsupported-uses-target` — since AKM never acquires or
97
+ executes a remote action. AKM does not recognize the locator grammar as a
98
+ distinct case; it is simply not one of the four canonical asset-ref
99
+ families or the `akm/command` builtin.
100
+ - `with:` on a **task-composed** step (`uses: tasks/<ref>`) **binds** the
101
+ target task source's declared `inputs:` — see
102
+ [Typed inputs and output](tasks.md#typed-inputs-and-output). Each value is either a literal
103
+ (validated against the input's declared schema at freeze) or a reference
104
+ `{from: "steps.<id>.output(.<segment>)*"}`, resolved just before the unit
105
+ dispatches and re-validated against the same schema then. The reference
106
+ grammar also accepts `{from: "params.<name>"}`, naming a declared param of
107
+ the *composing* workflow itself — but a composing step is only authorable
108
+ in a GitHub-shaped document (this section), whose root keys are exactly
109
+ `name`, `on`, and `jobs` with `workflow_dispatch` inputs rejected, so it
110
+ can never declare `params:` of its own. In practice that reference form
111
+ therefore always fails freeze here, with "does not name a declared
112
+ workflow param; declared params: (none)". An unknown `with:` key, a
113
+ missing required input with no default, or a reference naming a step that
114
+ doesn't exist earlier in the job all fail at **freeze** with `UsageError`
115
+ code `INPUT_BINDING_INVALID`, before the plan is ever published. If the
116
+ target task declares **no** `inputs:` at all (a `version: 4` task with no
117
+ `inputs:` key) — or the step targets
118
+ `uses: commands/<ref>` / `uses: scripts/<ref>`, which are never binding
119
+ surfaces — any authored
120
+ `with:`, including an empty mapping (`with: {}`), is rejected at freeze
121
+ with `UsageError` code `COMPOSITION_INVALID`, exit 2. Omitting `with:`
122
+ entirely always freezes normally, regardless of target. `with:` on
123
+ `uses: akm/command` is unaffected by any of this and is still required to
124
+ supply the builtin action's arguments, as in the example above. `with:` on
125
+ a **child-workflow** target (direct or task-wrapped) binds the child's
126
+ declared `params:` instead of `inputs:` — see
127
+ [Child workflows](#child-workflows).
78
128
  - GitHub expressions and contexts are unsupported and rejected anywhere in
79
129
  the parsed tree.
80
130
 
81
- Multi-job documents are dependency-validated, indexed, and displayable, but
82
- cannot execute in 0.9.2 because the runtime boundary is single-job execution.
83
- The runtime refuses instead of flattening `needs` or fabricating job
84
- semantics.
131
+ ## Child workflows
132
+
133
+ A step can compose another workflow, two ways. Composition is authored only
134
+ through the GitHub-shaped `jobs.<id>.steps[].uses` surface — the composing
135
+ (parent) document must be GitHub-shaped YAML, since the Markdown-frontmatter
136
+ step schema has no `uses:` key at all (see [Source formats and shared
137
+ IR](#source-formats-and-shared-ir)). The **child** workflow being composed
138
+ may itself be authored in either format, Markdown or GitHub-shaped:
139
+
140
+ - **Direct** — `uses: workflows/<ref>`:
141
+
142
+ ```yaml
143
+ - id: dispatch
144
+ uses: workflows/release-checklist
145
+ with:
146
+ channel: stable
147
+ ```
148
+
149
+ - **Task-wrapped** — `uses: tasks/<ref>` where `<ref>` names a task source
150
+ v4 document whose own target is a workflow. The task's own effective
151
+ `inputs:` (its declared defaults plus whatever the *task's own* callers
152
+ bound) supply the child's params; a `with:` authored on the *workflow
153
+ step itself* binds on top of that, exactly as it would for a direct step.
154
+
155
+ Both forms bind against the child workflow's own declared `params:`
156
+ frontmatter key — **not** a task's `inputs:` contract, since a workflow has
157
+ no `inputs:`. `with:` follows the same grammar as everywhere else in this
158
+ document: each value is a literal (validated against the param's declared
159
+ type at freeze) or a `{from: "steps.<id>.output(.<segment>)*"}` reference,
160
+ resolved just before the unit dispatches. (The grammar also accepts
161
+ `{from: "params.<name>"}`, naming a declared param of the *composing*
162
+ workflow — but that form is unreachable here for the same reason noted
163
+ above: the composing document is necessarily GitHub-shaped, so it never
164
+ declares `params:` of its own.) An unknown key or an invalid reference
165
+ fails at freeze with `UsageError` code `INPUT_BINDING_INVALID`, exactly like
166
+ a task-composed step's `with:`.
167
+
168
+ ### Frozen before publication
169
+
170
+ Composing a child workflow is not a runtime call — it is a **freeze-time**
171
+ resolution. When the parent workflow's plan is frozen, AKM loads the child's
172
+ source, compiles it, validates it, and freezes the child's own complete plan
173
+ *before the parent run is published*. The result is embedded whole inside
174
+ the parent step's frozen target (`kind: "child-workflow"`); nothing about the
175
+ child is re-read at dispatch time. Concretely:
176
+
177
+ - Editing the child's source **after** the parent run has started has no
178
+ effect on that run — the parent already carries its own frozen copy of the
179
+ child's plan.
180
+ - Editing the child's source in the narrow window **between** the parent's
181
+ freeze and its publication fails the whole parent publication atomically,
182
+ with no run row written — the same source-race protection that already
183
+ covers the parent's own command/script/task sources extends to every
184
+ transitive child source file.
185
+ - The child's *own* source files (its workflow document plus every
186
+ command/script/task it in turn resolves) become part of the parent run's
187
+ guarded source read set, exactly like any other source the parent
188
+ workflow depends on.
189
+
190
+ See [Architecture: The Workflow Engine](https://github.com/itlackey/akm/blob/main/docs/architecture/workflow-engine.md#child-workflows)
191
+ for the embedded-plan integrity chain (`irVersion`, `planHash`,
192
+ `contentHash`) and why a tampered embedded child plan fails to decode.
193
+
194
+ ### Composition limits
195
+
196
+ Three bounds are enforced at **freeze**, before the parent run is published,
197
+ each failing with `UsageError` code `COMPOSITION_INVALID` (exit 2):
198
+
199
+ | Limit | Value | Message names |
200
+ | --- | --- | --- |
201
+ | Composition depth | 8 levels below the root | the limit and the ref path, e.g. `Workflow step <id> cannot compose <ref>: workflow composition is limited to 8 levels. Path: <a -> b -> …>.` |
202
+ | Cycle detection | a workflow (direct or task-wrapped) reaching itself through any chain | the cycle path, e.g. `Workflow step <id> cannot compose <ref>: that would create a composition cycle. Path: <a -> tasks/w -> b -> a>.` |
203
+ | Aggregate embedded plan bytes | 1 MiB total, summed across every embedded descendant of one root freeze | the cap and the running total, e.g. `Workflow step <id> cannot compose <ref>: the embedded child plans would total <N> bytes, over the <cap>-byte limit for one workflow run.` |
204
+
205
+ The same workflow reached twice through disjoint branches (a diamond, not a
206
+ cycle) is not a violation — each occurrence embeds its own independent copy;
207
+ deduplicating identical embedded plans is not implemented. A step whose
208
+ `uses: workflows/<ref>` (or task-wrapped equivalent) does not resolve to a
209
+ real asset fails with the ordinary asset-resolution error, unchanged by any
210
+ of this.
211
+
212
+ ### Child execution
213
+
214
+ Running a step whose own target is `kind: "child-workflow"` publishes (or
215
+ finds, if one already exists — see "Identity and retries" below) a child
216
+ run and drives it to completion, or as far as it gets, before the parent
217
+ step is finalized. The drive happens **inline, in the parent's own
218
+ process**: it is the same engine `akm workflow run` uses on the child's
219
+ frozen plan, not a separately scheduled job. Consequently, whatever aborts
220
+ the parent's own dispatch — `Ctrl-C`, a `--timeout`, a budget ceiling, or
221
+ the parent losing its run lease — also aborts the child drive; both runs
222
+ are left resumable, never partially torn down.
223
+
224
+ The child's final status maps onto the composing step and the parent run:
225
+
226
+ | Child status | Composing step | Parent run |
227
+ | --- | --- | --- |
228
+ | `completed` | completes; its output is the child's exported result — its declared `outputs:` (see [What a step's output is](#what-a-steps-output-is)), or `{runId, status}` when the child declares none | continues |
229
+ | `failed` | `failed` | `failed` |
230
+ | `blocked` | `blocked` | `blocked` |
231
+ | aborted mid-drive (parent cancelled/timed out/lost its lease) | left unfinished, not finalized | active and resumable |
232
+ | the child could not be published or its plan failed an integrity re-check | `failed` | `failed` |
233
+ | another process already holds the child's run lease | `failed` | `failed` |
234
+
235
+ **Blocked-child recovery.** A blocked child blocks its composing step —
236
+ `akm` does not resume a child for you, because a gate is a gate for a
237
+ child workflow too. The step's notes name the exact three-command
238
+ sequence: resume the **child** first, then resume and re-run the
239
+ **parent** — re-driving the parent is what advances it, because that is
240
+ what re-enters the composing step and drives the now-resumed child:
241
+
242
+ ```sh
243
+ akm workflow resume <childRunId>
244
+ akm workflow resume <parentRunId>
245
+ akm workflow run <parentRunId>
246
+ ```
247
+
248
+ **Nested blocks (composition depth 2+).** The three-command sequence above
249
+ clears a block exactly one level deep. It does **not** generalize to a
250
+ grandchild block (root composes child, child composes grandchild, grandchild
251
+ blocks): re-driving the root does not cascade down into re-driving the
252
+ still-blocked grandchild, because a composing step never re-drives a child
253
+ whose own status is already `blocked` — re-running the root just re-observes
254
+ the child's own block and re-blocks the root the same way, without the
255
+ grandchild ever being reached. Resume every blocked run in the chain,
256
+ **deepest first**, then re-run only the root:
257
+
258
+ ```sh
259
+ akm workflow resume <grandchildRunId>
260
+ akm workflow resume <childRunId>
261
+ akm workflow resume <rootRunId>
262
+ akm workflow run <rootRunId>
263
+ ```
264
+
265
+ The status tree's own `resume`/`then` commands on a deeply nested node still
266
+ name only that node and the root, never an intermediate ancestor — read the
267
+ tree and resume every `blocked` row before re-running the root.
268
+
269
+ **Identity and retries.** A child run's identity is keyed by the parent run,
270
+ the parent unit, and that unit's input hash — publishing is idempotent, so
271
+ re-driving the composing step (an explicit `akm workflow resume` +
272
+ `akm workflow run`, or a `retry:` policy on the step) finds and continues
273
+ the **same** child run rather than starting a new one. Only a change to the
274
+ composing step's own inputs (params, upstream step outputs it reads, or gate
275
+ feedback from a rejected verification loop) produces a different child.
276
+
277
+ **Visibility.** `akm workflow status` on a run that composes children
278
+ renders a `children:` tree — every descendant run's ref, status, and, for a
279
+ blocked child, its resume command — recursively to the same 8-level
280
+ composition-depth bound described above. Child runs are excluded from `akm
281
+ workflow list` by default; pass `--children` to include them. A child run
282
+ id always works directly with `akm workflow status`/`resume`/`abandon`/`run`,
283
+ listed or not. See
284
+ [Running Workflows: Child runs](https://github.com/itlackey/akm/blob/main/docs/guides/run-workflows.md#child-runs) for a
285
+ worked example, and
286
+ [Architecture: The Workflow Engine](https://github.com/itlackey/akm/blob/main/docs/architecture/workflow-engine.md#child-workflows)
287
+ for the dispatch seam and why reusing the top-level run engine (rather than a
288
+ second executor) is what makes the identity and abort-propagation rules above
289
+ hold for free.
85
290
 
86
291
  ## Frontmatter keys
87
292
 
@@ -99,6 +304,9 @@ families) plus the orchestration keys:
99
304
  than dropping the settings for that step. There is no per-step opt-out (`llm:
100
305
  {}` is a no-op and `llm: null` is a parse error), so in a mixed document put
101
306
  `llm:` on the `unit:` of each LLM step instead of in `defaults:`.
307
+ - `outputs` — name → `{ from, schema? }`, a run-level export projected from
308
+ a step's own artifact (Markdown-only; see [Workflow
309
+ outputs](#workflow-outputs) below).
102
310
  - `budget` — run-lifetime ceilings (`max_units`, `max_tokens`; see
103
311
  [Budget ceilings](#budget-ceilings) below).
104
312
  - `steps` — an ordered list. Each step has an `id`
@@ -364,6 +572,70 @@ that declares an `output` schema is unaffected — an empty response is not
364
572
  valid JSON, so it fails as a parse error and can never satisfy a schema as a
365
573
  silent `null`.
366
574
 
575
+ ## Workflow outputs
576
+
577
+ A workflow can declare a run-level export: the values a **completed run**
578
+ promotes, projected from its steps' own artifacts. This is a Markdown
579
+ frontmatter key, `outputs:`:
580
+
581
+ ```yaml
582
+ outputs:
583
+ report:
584
+ from: steps.summarize.output
585
+ changed_count:
586
+ from: steps.collect.output.total
587
+ schema: { type: integer, minimum: 0 }
588
+ ```
589
+
590
+ Each entry is a mapping of exactly `from` (required) and `schema`
591
+ (optional):
592
+
593
+ - `from` is a `steps.<id>.output(.<segment>)*` reference into a step's own
594
+ artifact — the same reference grammar `with:`/`inputs:` use elsewhere in
595
+ this document — naming a declared step. `from: params.<name>` is
596
+ rejected: a run's exports come from what its steps produced, not from its
597
+ input params verbatim.
598
+ - `schema`, when present, is validated against the same [enforced JSON
599
+ Schema subset](#the-enforced-json-schema-subset) as a step's own `output:`
600
+ schema, and is bound by the same limit: at most 256 KiB.
601
+ - Output names follow the same pattern as params, `^[A-Za-z_][A-Za-z0-9_]*$`,
602
+ and a workflow may declare at most 64.
603
+
604
+ **This is a Markdown-only key.** GitHub-shaped YAML's root is a closed set —
605
+ `name`, `on`, `jobs` — with no extension surface, the same reason a
606
+ GitHub-shaped workflow cannot declare `params:` either. Composition itself
607
+ is unaffected: the *composing* (parent) document must still be GitHub-shaped
608
+ (only `jobs.<id>.steps[].uses` composes — see [Child
609
+ workflows](#child-workflows)), and the *composed* (child) may be either
610
+ format — but a child that wants to export more than `{runId, status}` must
611
+ be authored in Markdown.
612
+
613
+ **Different from a step's own `output:` schema.** A step's `output:` (see
614
+ [Typed step artifacts](#typed-step-artifacts) below) is a typed-artifact
615
+ contract on *one step*, enforced and retryable through that step's own gate
616
+ loop. A workflow's `outputs:` is a *run-level export projection* over
617
+ already-promoted step artifacts, resolved once, after every step has
618
+ finished. They compose freely: a step can declare `output:` and the
619
+ workflow's `outputs:` can project that same step's artifact under an export
620
+ name.
621
+
622
+ **Resolution happens once, at run completion**, over the run's persisted
623
+ step evidence — never re-evaluated afterward. If a declared `from` cannot
624
+ be resolved, reads a step artifact that was too large to persist in full, or
625
+ its resolved value fails its declared `schema`, run completion itself
626
+ rolls back: the run stays `active`, its final step stays `pending`, and no
627
+ event is emitted. Fix the declaration (or the step that produces the value)
628
+ and the next completion attempt resolves outputs again — a bad `outputs:`
629
+ entry cannot silently strand a run as `completed` with missing exports.
630
+
631
+ **What a completed run exports.** A run with an `outputs:` declaration
632
+ exports exactly those resolved values. A run with none exports
633
+ `{runId, status}` instead — synthesized whenever it is read, never
634
+ persisted. This is what a parent step composing this workflow as a child
635
+ sees at `steps.<id>.output`: see [Child execution](#child-execution) for how
636
+ a composing step's own reference into a child's exports is checked at
637
+ freeze time.
638
+
367
639
  ## Typed step artifacts
368
640
 
369
641
  When a step declares `output`, the promoted step artifact (the unit's
@@ -627,6 +899,7 @@ context blocks a model unit gets in its prompt:
627
899
  | `AKM_PARAMS` | the run params, canonical JSON |
628
900
  | `AKM_ITEM`, `AKM_ITEM_INDEX` | a `map` unit's item (canonical JSON) and 0-based index |
629
901
  | `AKM_INPUTS` | the step's declared `inputs:` artifacts, keyed by reference string |
902
+ | `AKM_TASK_INPUTS` | a task-composed step's resolved `with:` bindings (canonical JSON), present only when the bindings are non-empty |
630
903
 
631
904
  These are applied *after* your `env:` bindings, so a binding can never shadow
632
905
  them.
@@ -748,16 +1021,17 @@ a committed *value*, so it cannot carry "whatever this build agent's
748
1021
  Values passed through this way are **not** redacted from the command's output
749
1022
  the way `env:` binding values are, so never list a credential here.
750
1023
 
751
- #### Durable v4 forbids `inherit_env`
1024
+ #### Durable v4 (`irVersion: 5`) forbids `inherit_env`
752
1025
 
753
- Every new workflow start freezes a durable v4 plan. V4 rejects
754
- `inherit_env: true` and any other request for whole-process inheritance; use
755
- exact named environment bindings and `pass_env:` instead. Both mechanisms are
756
- dispatch-significant, keep the visible environment surface bounded, and form
757
- part of the unit's input hash.
1026
+ Every new workflow start freezes the durable plan v4 family's current
1027
+ executable format, `irVersion: 5`. It rejects `inherit_env: true` and any other
1028
+ request for whole-process inheritance; use exact named environment bindings
1029
+ and `pass_env:` instead. Both mechanisms are dispatch-significant, keep the
1030
+ visible environment surface bounded, and form part of the unit's input hash.
758
1031
 
759
- The historical `inherit_env` spelling is unsupported. Pre-v4 stored plans are
760
- rejected; they are never upgraded or replayed through a second runtime.
1032
+ The historical `inherit_env` spelling is unsupported. Pre-`irVersion`-5 stored
1033
+ plans are rejected; they are never upgraded or replayed through a second
1034
+ runtime.
761
1035
 
762
1036
  ### What `akm show` reports for an exec step
763
1037
 
@@ -46,20 +46,70 @@ artifacts, and exec vocabulary. The YAML adapter accepts the documented local
46
46
  `name`/`on`/`jobs` subset. `.yaml` is not a workflow source.
47
47
 
48
48
  Both adapters produce strict source IR version 1. New starts resolve source
49
- owners and executable targets, then freeze durable plan v4. Only v4 plans
50
- execute; pre-v4 rows are rejected and must be replaced by a new run.
49
+ owners and executable targets, then freeze durable plan `irVersion` 5. Only
50
+ the current `irVersion` executes: a run frozen at an older version keeps
51
+ `status`, `list`, and `abandon` working, but `resume`/`next`/`complete`/`run`
52
+ fail closed — abandon it and start a new run from current source. See
53
+ [Architecture: The Workflow Engine](https://github.com/itlackey/akm/blob/main/docs/architecture/workflow-engine.md#resume-is-journaled-replay)
54
+ for the exact policy and
55
+ [Migrating from akm 0.9.1 to 0.9.2](https://github.com/itlackey/akm/blob/main/docs/migration/v0.9.1-to-v0.9.2.md#workflow-cutover)
56
+ if you are upgrading with runs in flight.
57
+
58
+ A step can compose another workflow as a child — directly
59
+ (`uses: workflows/<ref>`) or through a task whose own target is a workflow
60
+ (`uses: tasks/<ref>`) — frozen completely into the parent's plan before the
61
+ parent run is published. See
62
+ [Workflow Schema: Child workflows](../reference/workflow-schema.md#child-workflows)
63
+ for both forms and their limits. Running a step that composes a child
64
+ workflow drives that child to completion (or as far as it gets) with the
65
+ same engine the parent uses, then maps the child's final status onto the
66
+ composing step: a completed child promotes its declared `outputs:` (or
67
+ `{runId, status}` when it declares none) as the step's own output and the
68
+ parent continues; a failed child fails the step and the run; a blocked
69
+ child blocks the composing step and the run, with recovery notes naming the
70
+ exact `akm workflow resume`/`akm workflow run` sequence. `akm workflow
71
+ status` on a run that composes children renders a `children:` tree showing
72
+ every descendant run's ref and status. See
73
+ [Workflow Schema: Child execution](../reference/workflow-schema.md#child-execution)
74
+ for the full status mapping and the blocked-child recovery flow, and
75
+ [Running Workflows: Child runs](https://github.com/itlackey/akm/blob/main/docs/guides/run-workflows.md#child-runs) for a
76
+ walkthrough.
77
+
78
+ ## Workflow outputs
79
+
80
+ A workflow may declare a run-level export in its Markdown frontmatter —
81
+ `outputs: {<name>: {from: steps.<id>.output(.<segment>)*, schema?}}`, up to
82
+ 64 entries — resolved once, from persisted step evidence, at run
83
+ completion. A run with no `outputs:` declaration exports `{runId, status}`
84
+ instead; a composing parent step promotes a completed child's `outputs:`
85
+ (or that same `{runId, status}` fallback) as its own step output — see
86
+ [Workflow Schema: Workflow outputs](../reference/workflow-schema.md#workflow-outputs).
87
+
88
+ ## Inspecting a workflow without running it
89
+
90
+ `akm workflow plan <ref>` compiles, resolves, and freezes a workflow exactly
91
+ as starting a run would, then stops — zero durable writes, no published run.
92
+ It prints the canonical step graph, per-step frozen target kinds,
93
+ task/child expansion, input bindings, and freeze-time lowering notices, and
94
+ is secret-free by construction. Use it to check what a workflow would
95
+ actually do — including which child workflows it would compose — before
96
+ committing to a run. See
97
+ [CLI reference: workflow plan](cli.md#workflow-plan).
51
98
 
52
99
  ## Unsupported boundary and 0.9.3
53
100
 
54
101
  The 0.9.2 GitHub-shaped adapter is a local interoperability seam, not GitHub
55
102
  Actions. Full GitHub expressions and contexts, local/Docker/remote actions,
56
- nested workflows, service events, arbitrary hosted runners, and multi-job
57
- runtime execution remain outside 0.9.2. Valid multi-job sources can be indexed
58
- and displayed, but the 0.9.2 runtime executes a single job only.
103
+ service events, and arbitrary hosted runners remain outside 0.9.2.
104
+ **Multi-job YAML is rejected outright** `jobs:` must contain exactly one
105
+ job; a document with zero, two, or more jobs fails to compile at all (it is
106
+ not "indexed but not executed" — it never becomes a valid workflow). Split a
107
+ multi-job source into single-job workflows and compose them with a
108
+ child-workflow step instead. AKM neither fetches remote actions nor creates
109
+ event watchers or polling daemons.
59
110
 
60
111
  These full GitHub semantics, actions, service events, and runner behaviors are
61
- explicit 0.9.3-or-later work. AKM neither fetches remote actions nor creates
62
- event watchers or polling daemons in the meantime.
112
+ explicit 0.9.3-or-later work.
63
113
 
64
114
  Version 0.9.3 may extend full GitHub expressions and contexts, actions,
65
115
  service events, and runners; none of those capabilities is implied by 0.9.2.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "akm-cli",
3
- "version": "0.9.2-alpha.4",
3
+ "version": "0.9.2",
4
4
  "type": "module",
5
5
  "description": "akm (Agent Knowledge Manager) — a portable, local-first capability library for AI agents. Discover, load, share, and improve reusable skills, scripts, workflows, and knowledge across any shell-capable coding agent, including Claude Code, OpenCode, and Cursor.",
6
6
  "keywords": [