akm-cli 0.9.29 → 0.9.31
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +41 -0
- package/README.md +10 -10
- package/ROADMAP.md +112 -0
- package/dist/commands/improve/improve-cli.js +33 -12
- package/dist/commands/sources/plugin-upgrade.js +46 -23
- package/dist/core/file-lock.js +10 -1
- package/dist/llm/client.js +33 -3
- package/dist/scripts/akm-migrate-node.js +5 -1
- package/dist/scripts/akm-migrate.js +5 -1
- package/docs/README.md +42 -41
- package/docs/agents/README.md +15 -0
- package/docs/agents/agent-install.md +14 -0
- package/docs/architecture/README.md +67 -0
- package/docs/architecture/adapters.md +272 -0
- package/docs/architecture/akm-architecture-decision-history.md +2055 -0
- package/docs/architecture/akm-core-principles.md +126 -0
- package/docs/architecture/architecture.md +579 -0
- package/docs/architecture/comparisons/claude-code-vs-akm-workflows-full.md +550 -0
- package/docs/architecture/decisions/0001-no-interpolation-attached-structured-context.md +72 -0
- package/docs/architecture/decisions/0002-unit-reuse-and-input-hash-scope.md +142 -0
- package/docs/architecture/decisions/0003-child-env-allowlist-and-provenance.md +189 -0
- package/docs/architecture/decisions/0004-task-input-contract-and-flag-coercion.md +97 -0
- package/docs/architecture/decisions/0005-task-result-vocabulary-and-legacy-read-mapping.md +108 -0
- package/docs/architecture/decisions/0006-task-source-version-routing.md +124 -0
- package/docs/architecture/decisions/0007-workflow-composition-bounds.md +83 -0
- package/docs/architecture/decisions/0008-task-binding-normalization.md +88 -0
- package/docs/architecture/decisions/0009-child-run-publication-column-parity.md +80 -0
- package/docs/architecture/decisions/0010-driver-protocol-cut.md +36 -0
- package/docs/architecture/decisions/0011-engine-run-loop-invariants.md +125 -0
- package/docs/architecture/decisions/README.md +62 -0
- package/docs/architecture/improvement.md +440 -0
- package/docs/architecture/internals/classification.md +154 -0
- package/docs/architecture/internals/fresh-host-rebuild-runbook.md +40 -0
- package/docs/architecture/internals/functional-contract-patterns.md +340 -0
- package/docs/architecture/internals/health-advisories.md +110 -0
- package/docs/architecture/internals/improve-workflow.md +713 -0
- package/docs/architecture/internals/indexing.md +498 -0
- package/docs/architecture/internals/registry-network-boundary.md +57 -0
- package/docs/architecture/internals/search.md +249 -0
- package/docs/architecture/internals/storage-locations.md +943 -0
- package/docs/architecture/persisted-data-compat.md +59 -0
- package/docs/architecture/reviews/env-secret-access.md +319 -0
- package/docs/architecture/runtime-boundary-design.md +153 -0
- package/docs/architecture/specs/0.9.0-close-out-plan.md +361 -0
- package/docs/architecture/specs/0.9.0-decisions.md +528 -0
- package/docs/architecture/specs/0.9.0-docs-code-drift-register.md +745 -0
- package/docs/architecture/specs/0.9.0-open-items-register.md +135 -0
- package/docs/architecture/specs/0.9.0-public-api-issue-backlog.md +740 -0
- package/docs/architecture/specs/0.9.0-release-surface-review.md +636 -0
- package/docs/architecture/specs/agent-command-engine-model-design.md +513 -0
- package/docs/architecture/specs/akm-0.9.0-bundle-adapter-spec.md +501 -0
- package/docs/architecture/specs/akm-0.9.0-ref-grammar-decision.md +291 -0
- package/docs/architecture/specs/akm-format-neutral-bundle-workspace-spec.md +2059 -0
- package/docs/architecture/specs/di-seams-plan.md +539 -0
- package/docs/architecture/specs/driver-protocol-keep-or-cut.md +275 -0
- package/docs/architecture/specs/fact-asset-type.md +142 -0
- package/docs/architecture/specs/improve-collapse-churn-detector-design.md +634 -0
- package/docs/architecture/specs/okf-support.md +148 -0
- package/docs/architecture/specs/ref.md +329 -0
- package/docs/architecture/specs/reviews/task-workflow-unification/README.md +72 -0
- package/docs/architecture/specs/reviews/task-workflow-unification/findings-consolidated.md +152 -0
- package/docs/architecture/specs/reviews/task-workflow-unification/library-eval-alternatives.md +352 -0
- package/docs/architecture/specs/reviews/task-workflow-unification/library-eval-bunqueue.md +300 -0
- package/docs/architecture/specs/reviews/task-workflow-unification/library-eval-openworkflow.md +402 -0
- package/docs/architecture/specs/reviews/task-workflow-unification/opus-review-1.md +758 -0
- package/docs/architecture/specs/reviews/task-workflow-unification/opus-review-2.md +645 -0
- package/docs/architecture/specs/reviews/task-workflow-unification/opus-review-3.md +602 -0
- package/docs/architecture/specs/reviews/task-workflow-unification/panel-r1-adversarial.md +27 -0
- package/docs/architecture/specs/reviews/task-workflow-unification/panel-r1-architecture.md +27 -0
- package/docs/architecture/specs/reviews/task-workflow-unification/panel-r1-coverage.md +74 -0
- package/docs/architecture/specs/reviews/task-workflow-unification/panel-r1-security.md +15 -0
- package/docs/architecture/specs/reviews/task-workflow-unification/panel-r2-adversarial.md +132 -0
- package/docs/architecture/specs/reviews/task-workflow-unification/panel-r2-verification.md +147 -0
- package/docs/architecture/specs/stash-conventions-code-spec.md +275 -0
- package/docs/architecture/specs/stash-organization-conventions.md +377 -0
- package/docs/architecture/specs/task-workflow-format-unification.md +721 -0
- package/docs/architecture/specs/workflow-engine-buy-vs-build.md +231 -0
- package/docs/architecture/specs/workflow-format-unification.md +437 -0
- package/docs/architecture/testing/manual-testing-checklist.md +3420 -0
- package/docs/architecture/testing/okf-v0.2-conformance-runbook.md +924 -0
- package/docs/architecture/testing/task-workflow-manual-tests.md +1817 -0
- package/docs/architecture/testing/testing-workflow.md +446 -0
- package/docs/architecture/workflow-engine.md +274 -0
- package/docs/guides/README.md +33 -0
- package/docs/guides/author-bundles.md +310 -0
- package/docs/guides/author-workflows.md +520 -0
- package/docs/guides/bundles.md +321 -0
- package/docs/guides/capture-knowledge.md +136 -0
- package/docs/guides/claude-code-vs-akm-workflows.md +30 -0
- package/docs/guides/concepts.md +93 -0
- package/docs/guides/discover-and-load.md +158 -0
- package/docs/guides/getting-started.md +240 -0
- package/docs/guides/improve-the-library.md +246 -0
- package/docs/guides/knowledge-management.md +18 -0
- package/docs/guides/recipes/headless-install.md +97 -0
- package/docs/guides/recipes/website-source.md +28 -0
- package/docs/guides/run-workflows.md +279 -0
- package/docs/guides/scheduling.md +123 -0
- package/docs/guides/use-with-any-agent.md +129 -0
- package/docs/guides/wikis.md +81 -0
- package/docs/maintainers/README.md +19 -0
- package/docs/maintainers/curate-workmap.md +336 -0
- package/docs/maintainers/eval.md +3 -0
- package/docs/maintainers/local-development.md +108 -0
- package/docs/maintainers/release-checklist.md +103 -0
- package/docs/migration/README.md +2 -2
- package/docs/migration/release-notes/0.0.13.md +1 -1
- package/docs/migration/release-notes/0.1.0.md +1 -1
- package/docs/migration/release-notes/0.2.0.md +1 -1
- package/docs/migration/release-notes/0.3.0.md +1 -1
- package/docs/migration/release-notes/0.5.0.md +1 -1
- package/docs/migration/release-notes/0.6.0.md +1 -1
- package/docs/migration/release-notes/0.7.0.md +1 -1
- package/docs/migration/release-notes/0.7.3.md +2 -2
- package/docs/migration/release-notes/0.7.4.md +1 -1
- package/docs/migration/release-notes/0.7.5.md +1 -1
- package/docs/migration/release-notes/0.8.0.md +1 -1
- package/docs/migration/release-notes/0.9.0.md +1 -1
- package/docs/migration/release-notes/0.9.14.md +1 -1
- package/docs/migration/release-notes/0.9.15.md +1 -1
- package/docs/migration/release-notes/0.9.16.md +1 -1
- package/docs/migration/release-notes/0.9.17.md +1 -1
- package/docs/migration/release-notes/0.9.19.md +1 -1
- package/docs/migration/release-notes/0.9.2.md +1 -1
- package/docs/migration/release-notes/README.md +27 -31
- package/docs/migration/v0.5-to-v0.6.md +759 -0
- package/docs/migration/v0.8-to-v0.9.md +1 -1
- package/docs/plans/0.9.2-agent-command-workflow-plan.md +423 -0
- package/docs/posts/README.md +30 -0
- package/docs/posts/akm-command-tour.md +495 -0
- package/docs/posts/akm-logo-sized.webp +0 -0
- package/docs/posts/belief-aware-memory-12.md +139 -0
- package/docs/posts/config-v2-lessons-16.md +191 -0
- package/docs/posts/improve-loop-autonomous-10.md +225 -0
- package/docs/posts/improve-pipeline-debugging-0803.md +179 -0
- package/docs/posts/intro-part-01.md +76 -0
- package/docs/posts/intro-part-02.md +221 -0
- package/docs/posts/intro-part-03.md +190 -0
- package/docs/posts/intro-part-04.md +221 -0
- package/docs/posts/llm-mode-reflect-13.md +166 -0
- package/docs/posts/openpalm-built-on-akm.md +86 -0
- package/docs/posts/progressive-disclosure-06.md +129 -0
- package/docs/posts/proposal-safety-net-15.md +112 -0
- package/docs/posts/release-0.5.0.md +247 -0
- package/docs/posts/release-0.6.0.md +117 -0
- package/docs/posts/release-0.7.0.md +159 -0
- package/docs/posts/release-0.8.0.md +332 -0
- package/docs/posts/scattered-skills-05.md +100 -0
- package/docs/posts/task-assets-persistent-workflows-11.md +222 -0
- package/docs/posts/team-skills-07.md +198 -0
- package/docs/posts/wiki-agent-knowledge-08.md +223 -0
- package/docs/posts/workflows-vaults-09.md +321 -0
- package/docs/product-surface.md +54 -0
- package/docs/reference/README.md +1 -2
- package/docs/reference/asset-types.md +168 -0
- package/docs/reference/cli.md +5 -6
- package/docs/reference/env-and-secrets.md +126 -0
- package/docs/reference/memory.md +124 -0
- package/docs/reference/refs.md +156 -0
- package/docs/reference/registry.md +489 -0
- package/docs/reference/website-sources.md +141 -0
- package/package.json +12 -15
|
@@ -0,0 +1,520 @@
|
|
|
1
|
+
# Author's Guide: Writing Workflows
|
|
2
|
+
|
|
3
|
+
This guide walks through writing and testing a workflow definition: choosing a
|
|
4
|
+
source format, the Markdown structure, a minimal complete example, common authoring mistakes,
|
|
5
|
+
and how to verify gates and outputs before you publish. It assumes you
|
|
6
|
+
already know what a workflow is; for the exhaustive, exact-syntax reference
|
|
7
|
+
— every frontmatter key, the reference grammar, gates, and outputs — see
|
|
8
|
+
[Workflow Schema](../reference/workflow-schema.md). For operating a run once
|
|
9
|
+
it's written, see [Running Workflows](../guides/run-workflows.md).
|
|
10
|
+
|
|
11
|
+
## Start from the template
|
|
12
|
+
|
|
13
|
+
Authors can choose peer Markdown or GitHub-shaped YAML sources. Markdown is
|
|
14
|
+
the full AKM authoring format: OKF-conformant frontmatter carries the
|
|
15
|
+
orchestration graph and its body carries instructions and gate rubrics.
|
|
16
|
+
GitHub-shaped YAML is a strict local subset for interoperability. Read the
|
|
17
|
+
[authoritative YAML subset](../reference/workflow-schema.md#github-shaped-yaml-subset)
|
|
18
|
+
before translating an Actions-shaped file; AKM does not accept arbitrary
|
|
19
|
+
GitHub semantics. `.yaml` is not recognized—use `.yml`.
|
|
20
|
+
|
|
21
|
+
Use `akm workflow create --print` to print a valid Markdown starter, then edit
|
|
22
|
+
it and register it with `akm workflow create`. Author a YAML source directly
|
|
23
|
+
under `workflows/<name>.yml` and validate either source with `akm lint`:
|
|
24
|
+
|
|
25
|
+
```sh
|
|
26
|
+
akm workflow create my-release --print # Print the template, without writing
|
|
27
|
+
akm workflow create my-release --from ./my-release.md
|
|
28
|
+
akm lint --type workflows # Check for structural errors before using it
|
|
29
|
+
```
|
|
30
|
+
|
|
31
|
+
Both formats compile to source IR version 1. Choose Markdown for maps, routes,
|
|
32
|
+
gates, typed artifacts, and prose-rich agent instructions. Choose the bounded
|
|
33
|
+
YAML subset for a single local self-hosted job made of token-safe `run` and
|
|
34
|
+
supported local `uses` steps.
|
|
35
|
+
|
|
36
|
+
## A minimal complete example
|
|
37
|
+
|
|
38
|
+
```markdown
|
|
39
|
+
---
|
|
40
|
+
type: workflow
|
|
41
|
+
description: Ship a tagged release to production
|
|
42
|
+
params:
|
|
43
|
+
version: { type: string, description: The semver version string to release }
|
|
44
|
+
steps:
|
|
45
|
+
- id: validate
|
|
46
|
+
- id: build
|
|
47
|
+
inputs: [steps.validate.output]
|
|
48
|
+
---
|
|
49
|
+
|
|
50
|
+
# Ship Release
|
|
51
|
+
|
|
52
|
+
## validate
|
|
53
|
+
|
|
54
|
+
Check that the `version` parameter follows semver and the tag does not
|
|
55
|
+
already exist.
|
|
56
|
+
|
|
57
|
+
### gate
|
|
58
|
+
|
|
59
|
+
- `git tag v<version>` does not already exist.
|
|
60
|
+
- The version string matches `^\d+\.\d+\.\d+$`.
|
|
61
|
+
|
|
62
|
+
## build
|
|
63
|
+
|
|
64
|
+
Run `npm run build && npm test`, using the validation from `validate`,
|
|
65
|
+
attached to this unit as input. Fix any failures before proceeding.
|
|
66
|
+
```
|
|
67
|
+
|
|
68
|
+
Walking through it: `validate` and `build` are both bare unit steps — neither
|
|
69
|
+
declares `unit:`, `map:`, or `route:`, so each is "still a unit step," the
|
|
70
|
+
minimal declaration. `build` names `steps.validate.output` in `inputs:`, so
|
|
71
|
+
the engine attaches `validate`'s result to `build`'s dispatched context, and
|
|
72
|
+
`build`'s own re-dispatch on a resumed run is keyed to that exact slice — not
|
|
73
|
+
the whole run. `validate` has a `### gate`; `build` does not, so `build`
|
|
74
|
+
completes as soon as its unit succeeds, with no verification pass.
|
|
75
|
+
|
|
76
|
+
For a richer example — fan-out with `map`, `route`-based branching, retries,
|
|
77
|
+
and a run `budget` — see
|
|
78
|
+
[Workflow Schema: Richer example](../reference/workflow-schema.md#richer-example).
|
|
79
|
+
|
|
80
|
+
## Deterministic steps: run a command, gate on it
|
|
81
|
+
|
|
82
|
+
Not every step needs a model. Running the test suite, building, linting, or
|
|
83
|
+
invoking a script is *deterministic work*: there is exactly one right answer and
|
|
84
|
+
the command already knows it. A step whose `unit:` declares `exec:` runs that
|
|
85
|
+
command directly — no LLM, no agent, no tokens, no nondeterminism.
|
|
86
|
+
|
|
87
|
+
The canonical shape is a **test step that gates the rest of the run**:
|
|
88
|
+
|
|
89
|
+
```markdown
|
|
90
|
+
---
|
|
91
|
+
type: workflow
|
|
92
|
+
description: Fix a failing test, then verify the suite is green
|
|
93
|
+
params:
|
|
94
|
+
failure: { type: string, description: The failing test name or error }
|
|
95
|
+
steps:
|
|
96
|
+
- id: fix
|
|
97
|
+
- id: test
|
|
98
|
+
inputs: [steps.fix.output]
|
|
99
|
+
unit:
|
|
100
|
+
exec:
|
|
101
|
+
command: ["bun", "run", "test:unit"]
|
|
102
|
+
timeout: "10m"
|
|
103
|
+
retry: { max: 1, on: [timeout] }
|
|
104
|
+
- id: report
|
|
105
|
+
inputs: [steps.test.output]
|
|
106
|
+
---
|
|
107
|
+
|
|
108
|
+
# Fix and Verify
|
|
109
|
+
|
|
110
|
+
## fix
|
|
111
|
+
|
|
112
|
+
Find the cause of the failure described in the run parameters and fix it.
|
|
113
|
+
Explain what you changed and why.
|
|
114
|
+
|
|
115
|
+
## test
|
|
116
|
+
|
|
117
|
+
Run the unit test suite. This step is deterministic: the command's exit code
|
|
118
|
+
decides whether the run continues.
|
|
119
|
+
|
|
120
|
+
## report
|
|
121
|
+
|
|
122
|
+
Summarize the fix and the test results attached to this unit.
|
|
123
|
+
```
|
|
124
|
+
|
|
125
|
+
What this buys you:
|
|
126
|
+
|
|
127
|
+
- **The suite is the gate.** `test` uses the default `on_error: fail`, so a
|
|
128
|
+
non-zero exit fails the unit with `non_zero_exit`, fails the step, and stops
|
|
129
|
+
the run before `report` ever dispatches. No judge, no rubric, no prompt — the
|
|
130
|
+
exit code is the verdict.
|
|
131
|
+
- **The output is real.** `steps.test.output` is the command's stdout (trailing
|
|
132
|
+
newlines stripped), so `report` receives the actual test output rather than a
|
|
133
|
+
model's recollection of it.
|
|
134
|
+
- **It costs nothing.** No tokens, no latency beyond the command itself, and the
|
|
135
|
+
same input always produces the same dispatch.
|
|
136
|
+
- **It resumes correctly.** A completed exec unit is journaled like any other; a
|
|
137
|
+
resumed run reuses the row instead of re-running the suite.
|
|
138
|
+
|
|
139
|
+
### Getting a typed result instead of raw text
|
|
140
|
+
|
|
141
|
+
If the command can print JSON, declare an `output` schema on the unit and the
|
|
142
|
+
step artifact becomes a validated structure:
|
|
143
|
+
|
|
144
|
+
```yaml
|
|
145
|
+
- id: test
|
|
146
|
+
unit:
|
|
147
|
+
exec:
|
|
148
|
+
command: ["bun", "run", "test:unit", "--reporter=json"]
|
|
149
|
+
output:
|
|
150
|
+
type: object
|
|
151
|
+
required: [passed, failed]
|
|
152
|
+
properties:
|
|
153
|
+
passed: { type: number }
|
|
154
|
+
failed: { type: number }
|
|
155
|
+
```
|
|
156
|
+
|
|
157
|
+
stdout must then be **exactly one JSON value** — no log noise around it — and
|
|
158
|
+
downstream steps can address `steps.test.output.failed`.
|
|
159
|
+
|
|
160
|
+
### What the command can see
|
|
161
|
+
|
|
162
|
+
The child does **not** inherit your environment. It starts empty and gets a
|
|
163
|
+
small default allowlist — `PATH`, `HOME`, the locale/temp/identity variables,
|
|
164
|
+
and the Windows essentials (`SystemRoot`, `COMSPEC`, `PATHEXT`, …) that
|
|
165
|
+
process creation itself needs — then your `env:` bindings, then the `AKM_*`
|
|
166
|
+
context variables. Ordinary commands (`bun`, `git`, `make`, `cargo`) work
|
|
167
|
+
unchanged; an unrelated `SOME_OTHER_SERVICE_TOKEN` sitting in the shell that
|
|
168
|
+
ran `akm workflow run` does not reach them.
|
|
169
|
+
|
|
170
|
+
Widen it only by naming what the unit needs:
|
|
171
|
+
|
|
172
|
+
```yaml
|
|
173
|
+
- id: build
|
|
174
|
+
unit:
|
|
175
|
+
env: [env/build]
|
|
176
|
+
exec:
|
|
177
|
+
command: ["cargo", "build", "--release"]
|
|
178
|
+
pass_env: [CARGO_HOME, SCCACHE_DIR] # a few extra names
|
|
179
|
+
```
|
|
180
|
+
|
|
181
|
+
`pass_env:` is for a **per-machine** variable an `env:` binding cannot express
|
|
182
|
+
(an env asset stores a committed value; `CARGO_HOME` differs per build agent).
|
|
183
|
+
Secrets and fixed values belong in exact named `env:` bindings: those values
|
|
184
|
+
are redacted out of everything journaled, and `pass_env:` values are not.
|
|
185
|
+
|
|
186
|
+
Workflow starts freeze the durable plan v4 family's current executable format
|
|
187
|
+
(`irVersion: 5`), which rejects `inherit_env` instead of persisting an
|
|
188
|
+
unbounded ambient environment. Pre-`irVersion`-5 stored plans do not execute;
|
|
189
|
+
start a new run after updating the source.
|
|
190
|
+
|
|
191
|
+
Named bindings and `pass_env:` are part of the unit's input hash, so changing
|
|
192
|
+
either re-runs the command instead of reusing a row recorded under another
|
|
193
|
+
scope.
|
|
194
|
+
|
|
195
|
+
Full list of allowlisted names:
|
|
196
|
+
[Workflow Schema: The child's environment is an allowlist](../reference/workflow-schema.md#the-childs-environment-is-an-allowlist).
|
|
197
|
+
|
|
198
|
+
### Things to get right
|
|
199
|
+
|
|
200
|
+
- **`command:` is an argv array, not a shell string.** `["bun", "run", "test"]`,
|
|
201
|
+
never `"bun run test"`. Nothing is shell-parsed, which is what makes it safe;
|
|
202
|
+
if you truly need a pipeline, write `["bash", "-lc", "a | b"]` and own that
|
|
203
|
+
choice explicitly.
|
|
204
|
+
- **No interpolation.** The argv is frozen. A `map` step's item reaches the
|
|
205
|
+
command as `AKM_ITEM` (canonical JSON) and the run params as `AKM_PARAMS` —
|
|
206
|
+
read them from the environment, do not try to splice them into `command:`.
|
|
207
|
+
- **Secrets go in `env:`, never in `command:`.** `command:` is stored verbatim in
|
|
208
|
+
the frozen plan; `env:` bindings are carried by name and their values are
|
|
209
|
+
redacted out of everything journaled.
|
|
210
|
+
- **Long commands need a `timeout:`.** An exec unit defaults to 10 minutes. Use
|
|
211
|
+
`timeout: "30m"` for a slow suite, or `timeout: "none"` only when you really
|
|
212
|
+
mean unbounded.
|
|
213
|
+
- **Don't assume your shell's environment.** The child gets an allowlist, not
|
|
214
|
+
an inheritance. If a command fails with "not found" or reads a missing
|
|
215
|
+
toolchain variable, name it in `pass_env:`; use a named `env:` binding for a
|
|
216
|
+
fixed or secret value. The durable v4 family (`irVersion: 5`) deliberately
|
|
217
|
+
has no whole-process fallback.
|
|
218
|
+
- **A very chatty command still passes; its artifact just says so.** akm retains
|
|
219
|
+
8 MiB of stdout and 8 MiB of stderr. Past that it keeps draining and discards,
|
|
220
|
+
so the command runs to completion and its exit code decides the step. The
|
|
221
|
+
artifact is then the retained head with a `__akm_exec_output_truncated__` block
|
|
222
|
+
appended, so nothing downstream can mistake it for the whole output. The one
|
|
223
|
+
case that still *fails* is a unit with a declared `output:` schema — a
|
|
224
|
+
truncated prefix is not one JSON value, so there is nothing to validate.
|
|
225
|
+
- **Bulk data reaches a command as a path, not as an environment variable.**
|
|
226
|
+
`AKM_INPUTS` / `AKM_PARAMS` / `AKM_ITEM` are bounded by what *process creation*
|
|
227
|
+
accepts on the current platform (96 KiB per variable on Linux/macOS, 32 767
|
|
228
|
+
bytes on Windows). Over that, the unit fails `exec_context_too_large` before
|
|
229
|
+
anything is spawned. If a workflow must also run on Windows, keep context under
|
|
230
|
+
the Windows number — akm will not enforce it on your Linux box, but a Windows
|
|
231
|
+
runner will.
|
|
232
|
+
|
|
233
|
+
Full reference: [Workflow Schema: Exec (shell) units](../reference/workflow-schema.md#exec-shell-units).
|
|
234
|
+
|
|
235
|
+
## Common authoring mistakes
|
|
236
|
+
|
|
237
|
+
- **Templating prose.** There is no `${{ … }}`/`{{ … }}` interpolation
|
|
238
|
+
anywhere in a workflow body. Write instructions in plain language that
|
|
239
|
+
refer to attached context — "using the intake step's artifact attached to
|
|
240
|
+
this unit" — never by splicing a value into the string. See
|
|
241
|
+
[Workflow Schema: The reference grammar](../reference/workflow-schema.md#the-reference-grammar).
|
|
242
|
+
- **Mismatched or missing step headings.** Every `## <step-id>` must match a
|
|
243
|
+
step declared in frontmatter exactly — no titles, no `Step:`/`Step ID:`
|
|
244
|
+
lines, no `# Workflow:` prefix on the H1.
|
|
245
|
+
- **A `unit`/`map` step with no body section.** Its instructions (or, for a
|
|
246
|
+
map step, its per-item template) are required — a `route`-only step is the
|
|
247
|
+
one case a body section is optional.
|
|
248
|
+
- **Misplaced or misspelled `### gate`.** It is the format's single reserved
|
|
249
|
+
marker, and it must be a `###` sub-heading inside the step's own section.
|
|
250
|
+
An empty `### gate` section is the same as omitting it — no verification
|
|
251
|
+
runs.
|
|
252
|
+
- **Referencing an item outside a map unit.** `item`/`item_index` are not
|
|
253
|
+
part of the reference language anywhere — they only arrive as attached
|
|
254
|
+
context inside that map step's own unit template.
|
|
255
|
+
- **Backward routes.** Every `route` target (`when.step`, `default`) must be
|
|
256
|
+
a step declared *later* in the workflow; "loop back until it passes" is a
|
|
257
|
+
bounded `### gate` on the step doing the work, not a route back to an
|
|
258
|
+
earlier step.
|
|
259
|
+
- **Referencing an unknown step or param.** `akm lint --type workflows`
|
|
260
|
+
checks every bare reference statically (unknown step, unknown param, bad
|
|
261
|
+
path) — run it before you rely on a workflow working.
|
|
262
|
+
- **Writing `exec.command` as a shell string.** `command: "bun run test"` is
|
|
263
|
+
rejected; it must be an argv array (`["bun", "run", "test"]`). Nothing is
|
|
264
|
+
ever shell-parsed, so metacharacters inside an argument stay literal.
|
|
265
|
+
- **Expecting an exec command to inherit your environment.** It gets a default
|
|
266
|
+
allowlist (`PATH`, `HOME`, locale/temp/identity, the Windows essentials) plus
|
|
267
|
+
your `env:` bindings and the `AKM_*` context — nothing else. Widen it with
|
|
268
|
+
`exec.pass_env:` (a few non-secret names). `pass_env` lives inside `exec:`;
|
|
269
|
+
the unit-level `env:` key is a list of exact env asset binding refs. A new v4
|
|
270
|
+
start rejects `inherit_env`.
|
|
271
|
+
- **Asking a model to do deterministic work.** "Run the test suite and tell me
|
|
272
|
+
if it passed" is an [exec step](#deterministic-steps-run-a-command-gate-on-it),
|
|
273
|
+
not a prompt.
|
|
274
|
+
|
|
275
|
+
## Typing what a step returns
|
|
276
|
+
|
|
277
|
+
A step's `output:` (and each `params:` entry) is a JSON Schema, and the engine
|
|
278
|
+
validates the step's artifact against it before the step can complete. The
|
|
279
|
+
runtime enforces a **bounded subset** of JSON Schema — anything outside it is
|
|
280
|
+
an authoring error at `akm lint`, never a silent no-op, because a gate
|
|
281
|
+
depending on a schema that constrains nothing is worse than a loud failure.
|
|
282
|
+
|
|
283
|
+
Enforced: `type`, `enum`, `properties`, `required`, `items`,
|
|
284
|
+
`additionalProperties: false`, `minItems`, `maxItems`, `minLength`,
|
|
285
|
+
`maxLength`, `minimum`, `maximum`, and the combinators `allOf`, `anyOf`,
|
|
286
|
+
`oneOf`, `not`.
|
|
287
|
+
|
|
288
|
+
```yaml
|
|
289
|
+
- id: release
|
|
290
|
+
output:
|
|
291
|
+
type: object
|
|
292
|
+
required: [version, verdict]
|
|
293
|
+
additionalProperties: false
|
|
294
|
+
properties:
|
|
295
|
+
version: { type: string, minLength: 1 }
|
|
296
|
+
verdict: { type: string, enum: [pass, fail] }
|
|
297
|
+
detail:
|
|
298
|
+
oneOf:
|
|
299
|
+
- { type: string, minLength: 1 }
|
|
300
|
+
- { type: "null" }
|
|
301
|
+
```
|
|
302
|
+
|
|
303
|
+
Outside the subset (each fails lint with the keyword named, its line, and a
|
|
304
|
+
suggested replacement where one exists): `$ref`/`$defs` — inline the schema
|
|
305
|
+
instead, since nothing resolves references; `const` — use a single-value
|
|
306
|
+
`enum`; `pattern` and `format` — no regex or string-format constraint is
|
|
307
|
+
evaluated at run time, so list the allowed values with `enum` when you can,
|
|
308
|
+
bound the size with `minLength`/`maxLength`, and otherwise check the shape in
|
|
309
|
+
the step's `### gate` rubric, which can also say *why* a value is wrong;
|
|
310
|
+
`patternProperties`, `if`/`then`/`else`, `uniqueItems`, `multipleOf`,
|
|
311
|
+
tuple-form `items`, and schema-form `additionalProperties` (only
|
|
312
|
+
`additionalProperties: false` is enforced). Annotation keywords —
|
|
313
|
+
`description`, `title`, `default`, `examples` — always pass through untouched,
|
|
314
|
+
and documenting each property is worth the keystrokes.
|
|
315
|
+
|
|
316
|
+
If lint reports a very broken workflow, note that its errors are sorted by
|
|
317
|
+
line, so fix from the top and re-run — the later ones are usually fallout from
|
|
318
|
+
the first.
|
|
319
|
+
|
|
320
|
+
## Choosing engines and models
|
|
321
|
+
|
|
322
|
+
Set `defaults.engine`/`defaults.model` (or per-unit `unit.engine`/`unit.model`)
|
|
323
|
+
rather than hardcoding an exact model id, so the workflow stays
|
|
324
|
+
harness-agnostic. Reference semantic aliases — `fast`, `balanced`, `reasoning`,
|
|
325
|
+
or aliases defined in the installed/user `models.json` files — in `model:` fields; see
|
|
326
|
+
[Workflow Schema: Model references](../reference/workflow-schema.md#model-references)
|
|
327
|
+
for the exact resolution order and config shape.
|
|
328
|
+
|
|
329
|
+
Point `reasoning` work at a capable model and keep high-volume fan-out units
|
|
330
|
+
on `fast`/`balanced`. The richer example's `review` map step is a good template: `reasoning` on the
|
|
331
|
+
per-item reviewer, `balanced` as the run default for everything else.
|
|
332
|
+
|
|
333
|
+
## Fan-out width
|
|
334
|
+
|
|
335
|
+
**A `map` step runs its items in parallel by default — 4 at a time as of
|
|
336
|
+
0.9.1.** (Earlier versions defaulted to 1, so a fan-out over 500 items crawled
|
|
337
|
+
through them one by one unless you said otherwise.) Map units are independent
|
|
338
|
+
by construction, so parallel is the honest default; the number is a modest 4
|
|
339
|
+
rather than "whatever the machine can take" so it stays predictable across the
|
|
340
|
+
machines a workflow gets shared with.
|
|
341
|
+
|
|
342
|
+
Say so explicitly when the default is wrong for a step:
|
|
343
|
+
|
|
344
|
+
```yaml
|
|
345
|
+
- id: review
|
|
346
|
+
map:
|
|
347
|
+
over: steps.discover.output.files
|
|
348
|
+
concurrency: 1 # serial — this step's units are NOT independent
|
|
349
|
+
```
|
|
350
|
+
|
|
351
|
+
`concurrency: 1` is a genuine opt-out and always wins. Reach for it when the
|
|
352
|
+
units touch a shared resource, when their side effects must happen in list
|
|
353
|
+
order, or when they hit an external service you must not burst. To change the
|
|
354
|
+
default for every workflow on a machine instead — including restoring the old
|
|
355
|
+
serial behavior wholesale — set
|
|
356
|
+
`akm config set workflow.defaultMapConcurrency 1`.
|
|
357
|
+
|
|
358
|
+
The declared width is a **ceiling, not a promise**. Three other limits clamp it
|
|
359
|
+
and the smallest wins: `workflow.maxConcurrency`, the selected engine's
|
|
360
|
+
`engines.<name>.concurrency`, and the host's CPU cap. The engine limit is the
|
|
361
|
+
one that surprises people: an LLM engine pointed at **localhost defaults to 1**,
|
|
362
|
+
because local model servers hold a single loaded model and fall over under
|
|
363
|
+
concurrent requests — so a `map` against a local model stays serial no matter
|
|
364
|
+
what the step declares, until you raise that engine's own `concurrency`. Remote
|
|
365
|
+
LLM endpoints default to 4. See
|
|
366
|
+
[Workflow Schema: Fan-out and concurrency](../reference/workflow-schema.md#fan-out-and-concurrency)
|
|
367
|
+
for the full table.
|
|
368
|
+
|
|
369
|
+
All of it is frozen into the run when it starts, so an in-flight or resumed run
|
|
370
|
+
keeps the widths it began with even if you edit config or upgrade akm.
|
|
371
|
+
|
|
372
|
+
A workflow that fans out is authorizing **N parallel agents**, not one — the
|
|
373
|
+
same trust model described in
|
|
374
|
+
[Running Workflows: workflow sources are executed code](run-workflows.md#security-workflow-sources-are-executed-code)
|
|
375
|
+
applies with multiplied blast radius. Give the workflow explicit safety and
|
|
376
|
+
parameter metadata (document every `params` entry, keep destructive steps
|
|
377
|
+
described plainly in the body) so a reader — human or agent — can judge that
|
|
378
|
+
blast radius before running it.
|
|
379
|
+
|
|
380
|
+
## Verify before you publish
|
|
381
|
+
|
|
382
|
+
1. **Lint the structure.**
|
|
383
|
+
|
|
384
|
+
```sh
|
|
385
|
+
akm lint --type workflows
|
|
386
|
+
```
|
|
387
|
+
|
|
388
|
+
This catches the body-rule violations above, plus every static reference
|
|
389
|
+
check (unknown step, unknown param, bad path, backward route).
|
|
390
|
+
|
|
391
|
+
2. **Run it for real.** A dry inspection of the markdown doesn't tell you
|
|
392
|
+
whether a gate actually rejects bad output or an `output` schema actually
|
|
393
|
+
matches what units return. Run the workflow against representative
|
|
394
|
+
params:
|
|
395
|
+
|
|
396
|
+
```sh
|
|
397
|
+
akm workflow run workflows/my-release --version 1.2.3
|
|
398
|
+
```
|
|
399
|
+
|
|
400
|
+
3. **Inspect the evidence.** Check that each step's promoted artifact is
|
|
401
|
+
what you expect, and that a gate's rubric is judging that artifact, not
|
|
402
|
+
engine prose:
|
|
403
|
+
|
|
404
|
+
```sh
|
|
405
|
+
akm workflow status <run-id> --units
|
|
406
|
+
```
|
|
407
|
+
|
|
408
|
+
`--units` shows per-unit diagnostics (status, `failure_reason`, raw
|
|
409
|
+
result/error text) without polluting the deterministic artifact a gate
|
|
410
|
+
judges — see
|
|
411
|
+
[Running Workflows: Check status](run-workflows.md#check-status).
|
|
412
|
+
|
|
413
|
+
4. **Deliberately break a gate once.** Run the workflow with params you
|
|
414
|
+
expect to fail validation, and confirm the gate actually rejects rather
|
|
415
|
+
than silently passing — a missing `workflow.judgeEngine` or a malformed
|
|
416
|
+
verdict rejects the gate rather than bypassing it (see
|
|
417
|
+
[Workflow Schema: Gates and verification](../reference/workflow-schema.md#gates-and-verification)),
|
|
418
|
+
so this is worth confirming once per workflow rather than assuming.
|
|
419
|
+
|
|
420
|
+
## Composing a task with typed inputs
|
|
421
|
+
|
|
422
|
+
A `uses: tasks/<ref>` step in the GitHub-shaped YAML format can bind a task
|
|
423
|
+
source v4 target's declared `inputs:` through `with:`. Given this task:
|
|
424
|
+
|
|
425
|
+
```yaml
|
|
426
|
+
# tasks/ticket-review.yml
|
|
427
|
+
version: 4
|
|
428
|
+
name: Ticket review
|
|
429
|
+
inputs:
|
|
430
|
+
ticket:
|
|
431
|
+
type: string
|
|
432
|
+
required: true
|
|
433
|
+
scope:
|
|
434
|
+
type: string
|
|
435
|
+
enum: [changed, all]
|
|
436
|
+
default: changed
|
|
437
|
+
uses: commands/review
|
|
438
|
+
```
|
|
439
|
+
|
|
440
|
+
a workflow step can bind `ticket` from an EARLIER step's output, and override
|
|
441
|
+
`scope` with a literal:
|
|
442
|
+
|
|
443
|
+
```yaml
|
|
444
|
+
# workflows/nightly.yml
|
|
445
|
+
name: Nightly review
|
|
446
|
+
on:
|
|
447
|
+
workflow_dispatch: {}
|
|
448
|
+
jobs:
|
|
449
|
+
main:
|
|
450
|
+
runs-on: [self-hosted]
|
|
451
|
+
steps:
|
|
452
|
+
- id: pick
|
|
453
|
+
run: echo T-42
|
|
454
|
+
- id: dispatch
|
|
455
|
+
uses: tasks/ticket-review
|
|
456
|
+
with:
|
|
457
|
+
ticket: { from: "steps.pick.output" }
|
|
458
|
+
scope: all
|
|
459
|
+
```
|
|
460
|
+
|
|
461
|
+
`{from: "steps.<id>.output(.<segment>)*"}` and `{from: "params.<name>"}` are
|
|
462
|
+
the only two reference roots; anything else, or an object carrying `from`
|
|
463
|
+
plus any other key, is `INPUT_BINDING_INVALID` at freeze — never silently
|
|
464
|
+
reinterpreted as a literal. `pick`'s output is resolved just before the
|
|
465
|
+
`dispatch` unit dispatches, then validated against `ticket`'s declared
|
|
466
|
+
schema; a literal (like `scope: all` here) is validated at freeze instead,
|
|
467
|
+
before the plan is ever published.
|
|
468
|
+
|
|
469
|
+
The composed target — `commands/review` here — receives the resolved
|
|
470
|
+
bindings on whichever delivery surface matches its kind: an
|
|
471
|
+
`akm/command`/`commands/<ref>` target gets a `## Task inputs` block appended
|
|
472
|
+
to its prompt; a `run:` shell or `scripts/<ref>` target gets one
|
|
473
|
+
`AKM_TASK_INPUTS` environment variable (canonical JSON of the resolved
|
|
474
|
+
bindings). Inspect exactly what each step's `with:` would deliver, without
|
|
475
|
+
running anything, with `akm workflow plan workflows/nightly --format json`
|
|
476
|
+
— its per-step `inputBindings` show `dispatch`'s `scope` as the literal
|
|
477
|
+
`all` and `ticket` as the unresolved reference `steps.pick.output`, the
|
|
478
|
+
same shape freezing a real run would produce. `akm task explain` is not a
|
|
479
|
+
substitute here: it only reflects a task's OWN CLI flags, declared
|
|
480
|
+
defaults, and `schedule[].inputs` — it never reads a workflow step's
|
|
481
|
+
`with:` binding at all, so pointing it at `tasks/ticket-review` prints
|
|
482
|
+
`scope`'s task-level default (`changed`), not the `all` this step actually
|
|
483
|
+
sends.
|
|
484
|
+
|
|
485
|
+
## Troubleshooting
|
|
486
|
+
|
|
487
|
+
**Every workflow run needs a selected engine.** Freezing resolves an engine
|
|
488
|
+
for each unit. With no `defaults.engine`, akm falls back to a config-free
|
|
489
|
+
`opencode-sdk` engine — provider, model, and auth come from opencode's own
|
|
490
|
+
configuration — and announces it once in the run's `warnings`. The fallback
|
|
491
|
+
needs the **`opencode` binary on PATH**: the bundled `@opencode-ai/sdk`
|
|
492
|
+
package is an HTTP client only and spawns `opencode serve` to have something
|
|
493
|
+
to talk to, so installing the npm package alone is not enough. With no
|
|
494
|
+
binary, freezing fails with `INVALID_CONFIG_FILE` and exit 78.
|
|
495
|
+
|
|
496
|
+
A workflow with a non-empty `### gate` additionally requires
|
|
497
|
+
`workflow.judgeEngine` to name a configured LLM or agent engine — the gate
|
|
498
|
+
judge is not covered by the fallback.
|
|
499
|
+
|
|
500
|
+
`akm setup` normally selects a default execution engine. On a bare container
|
|
501
|
+
or CI image, either install opencode and let the fallback apply, or choose an
|
|
502
|
+
engine explicitly:
|
|
503
|
+
|
|
504
|
+
```sh
|
|
505
|
+
npm i -g opencode-ai # fallback route: puts `opencode` on PATH
|
|
506
|
+
# ...or pick an engine yourself:
|
|
507
|
+
akm config set engines.claude '{"kind":"agent","platform":"claude"}'
|
|
508
|
+
akm config set defaults.engine claude
|
|
509
|
+
```
|
|
510
|
+
|
|
511
|
+
## See also
|
|
512
|
+
|
|
513
|
+
- [Workflow Schema](../reference/workflow-schema.md) — exact frontmatter,
|
|
514
|
+
refs, gates, and outputs syntax
|
|
515
|
+
- [Running Workflows](run-workflows.md) — start, inspect, resume, and abandon
|
|
516
|
+
a run
|
|
517
|
+
- [Architecture: The Workflow Engine](../architecture/workflow-engine.md) —
|
|
518
|
+
persistence, dispatch, and resume internals
|
|
519
|
+
- [CLI Reference](../reference/cli.md) — full flag documentation for
|
|
520
|
+
`workflow create`, `run`, and `lint`
|