akm-cli 0.9.30 → 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 +29 -0
- package/README.md +10 -10
- package/ROADMAP.md +112 -0
- package/dist/commands/improve/improve-cli.js +33 -12
- 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,550 @@
|
|
|
1
|
+
Full technical comparison. For a short vendor-neutral decision guide, see docs/guides/claude-code-vs-akm-workflows.md.
|
|
2
|
+
|
|
3
|
+
# Workflow architecture: Claude Code workflows vs. akm workflows
|
|
4
|
+
|
|
5
|
+
A technical comparison of two things that share a name but occupy different
|
|
6
|
+
layers of the stack: the **Claude Code `Workflow` tool** (the harness-native
|
|
7
|
+
orchestration DSL) and **akm workflows** (`akm workflow …`, the workflow
|
|
8
|
+
subsystem in this repo).
|
|
9
|
+
|
|
10
|
+
The short version: they are not competitors. An akm workflow is a durable
|
|
11
|
+
workflow asset authored in either of two peer source formats, Markdown `.md`
|
|
12
|
+
or GitHub-shaped YAML `.yml`, and executed by the Stable
|
|
13
|
+
`akm workflow run` command. Both adapters compile through source IR version 1.
|
|
14
|
+
Every new start freezes the durable plan v4 family's executable `irVersion: 5`
|
|
15
|
+
before execution. The native engine
|
|
16
|
+
fans work out to concurrent runner units, retries, gates on typed artifacts,
|
|
17
|
+
and enforces budget ceilings. Execution is native: akm dispatches each unit to
|
|
18
|
+
a configured agent harness (ten are supported) rather than asking the calling
|
|
19
|
+
session to run it.
|
|
20
|
+
|
|
21
|
+
> **Scope.** This report analyzes akm's own workflow subsystem
|
|
22
|
+
> (`src/workflows/**`, `src/commands/workflow-cli.ts`) against the Claude Code
|
|
23
|
+
> `Workflow` tool. It deliberately **excludes** any `.workflow.mjs` Claude Code
|
|
24
|
+
> workflow scripts that may live elsewhere in this repo (e.g. under internal
|
|
25
|
+
> review tooling) — those are *consumers* of the Claude Code system, not
|
|
26
|
+
> instances of akm's own workflow engine.
|
|
27
|
+
|
|
28
|
+
---
|
|
29
|
+
|
|
30
|
+
## Part A — Claude Code workflows: technical details
|
|
31
|
+
|
|
32
|
+
### A.1 Representation: an imperative JavaScript program
|
|
33
|
+
|
|
34
|
+
A Claude Code workflow **is a program**, not a document. The model authors a
|
|
35
|
+
self-contained JavaScript script, passed inline to the `Workflow` tool via its
|
|
36
|
+
`script` parameter. Every script begins with a **pure-literal** `meta` export:
|
|
37
|
+
|
|
38
|
+
```js
|
|
39
|
+
export const meta = {
|
|
40
|
+
name: 'review-changes',
|
|
41
|
+
description: 'Review changed files across dimensions, verify each finding',
|
|
42
|
+
phases: [{ title: 'Review' }, { title: 'Verify' }],
|
|
43
|
+
}
|
|
44
|
+
// body: uses agent()/parallel()/pipeline()/phase()/log()
|
|
45
|
+
```
|
|
46
|
+
|
|
47
|
+
`meta` must be a static literal (no variables, calls, spreads, or template
|
|
48
|
+
interpolation) so the harness can read the workflow's shape — its name,
|
|
49
|
+
description, and declared phases — **without executing the body**. The body
|
|
50
|
+
is arbitrary control flow: loops, conditionals, fan-out, accumulation.
|
|
51
|
+
|
|
52
|
+
The scripting surface is a small set of injected hooks and globals:
|
|
53
|
+
|
|
54
|
+
| Hook / global | Role |
|
|
55
|
+
|---|---|
|
|
56
|
+
| `agent(prompt, opts?)` | Spawn a subagent (a fresh LLM context). Returns its final text, or — with `opts.schema` — a validated structured object (the subagent is forced to call a `StructuredOutput` tool and the result is schema-checked with retries). Returns `null` if the agent is skipped mid-run or dies after retries are exhausted. |
|
|
57
|
+
| `parallel(thunks)` | Run tasks concurrently with a **barrier** — awaits all before returning. Failed thunks resolve to `null` (the call itself never rejects — filter with `.filter(Boolean)`). |
|
|
58
|
+
| `pipeline(items, ...stages)` | Run each item through all stages independently with **no barrier** between stages — item A can be in stage 3 while item B is still in stage 1. Each stage callback receives `(prevResult, originalItem, index)`. |
|
|
59
|
+
| `phase(title)` | Open a progress group; subsequent `agent()` calls are grouped under it. |
|
|
60
|
+
| `log(message)` | Emit a narrator line to the user. |
|
|
61
|
+
| `workflow(nameOrRef, args?)` | Run another workflow inline as a sub-step (one level of nesting only; the nested run shares the parent's concurrency cap, agent counter, and token budget). |
|
|
62
|
+
| `args` | The caller-supplied input value, verbatim. |
|
|
63
|
+
| `budget` | The turn's token target (`total`, `spent()`, `remaining()`) — a hard ceiling once reached. |
|
|
64
|
+
|
|
65
|
+
The unit of work is `agent()` — **a subagent with its own LLM context, model,
|
|
66
|
+
effort, and tool set**. Options include `label`, `phase`, `schema`, `model`,
|
|
67
|
+
`effort`, `isolation: 'worktree'` (an isolated git worktree so parallel file
|
|
68
|
+
mutations don't collide — expensive, so reserved for agents that mutate files
|
|
69
|
+
in parallel and would otherwise conflict; auto-removed if left unchanged), and
|
|
70
|
+
`agentType` (a named custom subagent). A subagent's model/effort/tools default
|
|
71
|
+
to the session's resolved model when unset, and subagents can reach
|
|
72
|
+
session-connected MCP tools via on-demand `ToolSearch`. Subagents are told
|
|
73
|
+
their final text *is* the return value, so a well-written `agent()` prompt
|
|
74
|
+
asks for raw data, not human-facing prose.
|
|
75
|
+
|
|
76
|
+
### A.2 Execution engine: the harness runs the script
|
|
77
|
+
|
|
78
|
+
The defining fact: **the Claude Code harness itself executes the script**, in a
|
|
79
|
+
controlled JavaScript interpreter. The model *writes* the orchestration; the
|
|
80
|
+
harness *runs* it deterministically, intercepting each `agent()` call to spawn a
|
|
81
|
+
real subagent, meter tokens, and update progress.
|
|
82
|
+
|
|
83
|
+
The script runs in a **restricted sandbox**, not full Node:
|
|
84
|
+
|
|
85
|
+
- Standard JS built-ins are available (`JSON`, `Math`, `Array`, …).
|
|
86
|
+
- **No filesystem or Node API access** — a workflow script cannot read or write
|
|
87
|
+
files directly; only its subagents (which have tools) touch the world.
|
|
88
|
+
- **`Date.now()`, `Math.random()`, and argless `new Date()` throw** — they would
|
|
89
|
+
make replay non-deterministic and break resume (see A.5). Timestamps come in
|
|
90
|
+
via `args`; randomness is faked by varying prompts/labels by index.
|
|
91
|
+
- It is **TypeScript-hostile**: plain JS only, no type annotations/generics.
|
|
92
|
+
|
|
93
|
+
Invocation is **asynchronous / background by default**: the tool returns
|
|
94
|
+
immediately with a `runId` (`wf_…`), and a `<task-notification>` fires when the
|
|
95
|
+
workflow completes. `/workflows` streams live progress in the meantime.
|
|
96
|
+
|
|
97
|
+
### A.3 Concurrency model: parallel by construction
|
|
98
|
+
|
|
99
|
+
Concurrency is the entire point. Fan-out is first-class:
|
|
100
|
+
|
|
101
|
+
- Concurrent `agent()` calls are capped at `min(16, cores − 2)` per workflow;
|
|
102
|
+
excess calls queue and drain as slots free.
|
|
103
|
+
- A single `parallel()`/`pipeline()` call accepts up to **4096 items**; total
|
|
104
|
+
agents across a workflow's lifetime are capped at **1000** (a runaway
|
|
105
|
+
backstop).
|
|
106
|
+
- `pipeline()` is the default multi-stage primitive precisely because it has no
|
|
107
|
+
barrier: wall-clock equals the slowest single-item chain, not sum-of-slowest
|
|
108
|
+
per stage.
|
|
109
|
+
|
|
110
|
+
On top of these primitives the ecosystem layers *quality patterns* — adversarial
|
|
111
|
+
verify (N skeptics per finding), judge panels, loop-until-dry discovery,
|
|
112
|
+
multi-modal sweeps, completeness critics — all expressed as ordinary control
|
|
113
|
+
flow over `agent()`. A handful of shapes recur often enough to be canonical:
|
|
114
|
+
review-then-verify as a `pipeline()` (each dimension's findings verify as soon
|
|
115
|
+
as that dimension finishes, no cross-dimension barrier), loop-until-count or
|
|
116
|
+
loop-until-budget (keep spawning finders until a target count or
|
|
117
|
+
`budget.remaining()` falls below a threshold), and adversarial/perspective-
|
|
118
|
+
diverse verify (N skeptics per finding, kept only if a majority survive).
|
|
119
|
+
`parallel()`'s barrier is reserved for the cases that genuinely need every
|
|
120
|
+
prior result together (dedup/merge, early-exit on zero); everything else
|
|
121
|
+
defaults to `pipeline()`. The load-bearing point underneath all of this: the
|
|
122
|
+
orchestration, the model policy, and any safety envelope are all *code* —
|
|
123
|
+
versioned, reviewable, and re-runnable, not something reconstructed from
|
|
124
|
+
scratch in an agent's head each time.
|
|
125
|
+
|
|
126
|
+
### A.4 Progress tracking
|
|
127
|
+
|
|
128
|
+
Progress is **push-based and live**:
|
|
129
|
+
|
|
130
|
+
- `meta.phases` declares named phase groups up front; `phase()` / `opts.phase`
|
|
131
|
+
assign agents to them. Titles are matched exactly.
|
|
132
|
+
- `/workflows` renders a live progress tree (phases → agents → status).
|
|
133
|
+
- `log()` emits narrator lines above the tree.
|
|
134
|
+
- Completion delivers a `<task-notification>` back into the session.
|
|
135
|
+
|
|
136
|
+
Under the hood each run has a **transcript directory** containing
|
|
137
|
+
`journal.jsonl` (each `agent()` call's actual return value) and `agent-<id>.jsonl`
|
|
138
|
+
files (per-subagent transcripts) — the durable record used for diagnosis and
|
|
139
|
+
resume.
|
|
140
|
+
|
|
141
|
+
### A.5 State, resume, and budget
|
|
142
|
+
|
|
143
|
+
- **Resume** keys on `runId`. Relaunching with `{scriptPath, resumeFromRunId}`
|
|
144
|
+
replays the longest unchanged **prefix** of `agent()` calls from cache
|
|
145
|
+
(same `(prompt, opts)` → instant cached result); the first edited/new call
|
|
146
|
+
and everything after it runs live. Same script + same args → 100% cache hit.
|
|
147
|
+
This is why determinism (A.2) is enforced.
|
|
148
|
+
- **Iteration** is file-based: every invocation persists its script under the
|
|
149
|
+
session directory and returns the path; you edit that file and re-invoke with
|
|
150
|
+
`scriptPath`.
|
|
151
|
+
- **Budget** ties depth to the user's "+Nk" directive. `budget.total` is a hard
|
|
152
|
+
ceiling; `budget.spent()`/`remaining()` are shared across the main loop and
|
|
153
|
+
all workflows, enabling `while (budget.remaining() > 50_000) { … }` scaling.
|
|
154
|
+
|
|
155
|
+
### A.6 Lifecycle & trust
|
|
156
|
+
|
|
157
|
+
A Claude Code workflow is **session-scoped and turn-shaped**: it is one
|
|
158
|
+
well-bounded fan-out inside the current agent session, gone when the session
|
|
159
|
+
ends (only the transcript persists). Trust is handled by the harness sandbox —
|
|
160
|
+
the script can't touch the filesystem; only its tool-bearing subagents can, under
|
|
161
|
+
the session's normal permission model.
|
|
162
|
+
|
|
163
|
+
---
|
|
164
|
+
|
|
165
|
+
## Part B — akm workflows: technical details
|
|
166
|
+
|
|
167
|
+
This part deliberately stays high-level: `docs/reference/workflows.md` is the
|
|
168
|
+
maintained, exhaustive reference for the peer workflow source formats,
|
|
169
|
+
reference grammar, gates, budgets, and the run lease. What follows is the
|
|
170
|
+
comparison-relevant subset — read the reference doc for anything this section
|
|
171
|
+
doesn't answer.
|
|
172
|
+
|
|
173
|
+
### B.0 Peer source formats, one stable execution surface
|
|
174
|
+
|
|
175
|
+
Peer workflow sources are Markdown `.md` and GitHub-shaped YAML `.yml`, both
|
|
176
|
+
addressed as `workflows/<name>`. The Markdown form declares the orchestration
|
|
177
|
+
graph (`unit`/`map`/`route`, inputs, outputs, retries, isolation, and budgets)
|
|
178
|
+
in frontmatter, while `## <step-id>` body sections carry unit/map
|
|
179
|
+
instructions and optional `### gate` rubrics. The YAML form accepts the
|
|
180
|
+
documented local `name`/`on`/`jobs` subset. Both adapters lower into the same
|
|
181
|
+
strict source IR version 1.
|
|
182
|
+
|
|
183
|
+
`akm workflow run` is the stable, ungated start/resume/execute surface. The
|
|
184
|
+
public `start`/`next`/`complete` lifecycle is removed. `status`, `list`,
|
|
185
|
+
`create`, `resume`, and `abandon` remain stable support commands.
|
|
186
|
+
|
|
187
|
+
There is no external-driver protocol: the former experimental
|
|
188
|
+
`akm workflow brief|report` surface was removed, so `run` is the single
|
|
189
|
+
execution path.
|
|
190
|
+
|
|
191
|
+
### B.1 Representation
|
|
192
|
+
|
|
193
|
+
The Markdown template (`src/assets/workflows/workflow-template.md`, validated
|
|
194
|
+
against `schemas/akm-workflow.json`) keeps machine orchestration in
|
|
195
|
+
frontmatter and human-authorable prose in the body. The peer `.yml` adapter
|
|
196
|
+
accepts the bounded GitHub-shaped subset documented in
|
|
197
|
+
`docs/reference/workflow-schema.md`. Bare whole-value references
|
|
198
|
+
(`params.<name>` and `steps.<id>.output.<path>`) wire `map.over`,
|
|
199
|
+
`route.input`, and `inputs`; prose is never interpolated. Runtime params, map
|
|
200
|
+
items, and declared input artifacts reach units as attached context.
|
|
201
|
+
|
|
202
|
+
### B.2 Parsers & compiled models
|
|
203
|
+
|
|
204
|
+
The Markdown and YAML adapters both produce source IR v1 before target
|
|
205
|
+
resolution. For Markdown, `parseWorkflow` (`src/workflows/parser.ts`) parses
|
|
206
|
+
the frontmatter graph and binds body prose into a `WorkflowDocument`
|
|
207
|
+
(`src/workflows/schema.ts`). It accumulates `WorkflowError`s instead of
|
|
208
|
+
throwing, and every element carries a `SourceRef` line span. The IR compiler
|
|
209
|
+
(`src/workflows/ir/compile.ts`) lowers source IR into the durable plan graph
|
|
210
|
+
(`src/workflows/ir/schema.ts`) executed by the engine. Parsed documents
|
|
211
|
+
cached in `index.db` are regenerable; only run state (B.3, below) is not.
|
|
212
|
+
|
|
213
|
+
### B.3 Persistence: durable SQLite run state
|
|
214
|
+
|
|
215
|
+
Run state lives in **`state.db`**
|
|
216
|
+
(`src/storage/repositories/workflow-runs-repository.ts`; the former
|
|
217
|
+
`workflow.db` was folded into `state.db` in the 0.9.0 three-database cutover),
|
|
218
|
+
whose rows are explicitly **non-regenerable**. Four tables carry current run
|
|
219
|
+
state:
|
|
220
|
+
|
|
221
|
+
- `workflow_runs` — `id`, `workflow_ref`, `workflow_title`, `status`
|
|
222
|
+
(`active|completed|blocked|failed`), `params_json`, `current_step_id`,
|
|
223
|
+
timestamps, `scope_key`, `agent_harness`, `agent_session_id`,
|
|
224
|
+
`checkin_armed_at`, plus the engine's own columns: `plan_json`/`plan_hash`
|
|
225
|
+
(the frozen compiled plan) and `engine_lease_holder`/
|
|
226
|
+
`engine_lease_until` (the run lease, see B.5).
|
|
227
|
+
- `workflow_run_steps` — `(run_id, step_id)` PK, `step_title`, `instructions`,
|
|
228
|
+
`completion_json`, `sequence_index`, `status`
|
|
229
|
+
(`pending|completed|blocked|failed|skipped`), `notes`, `evidence_json`,
|
|
230
|
+
`completed_at`, `summary`.
|
|
231
|
+
- `workflow_run_units` — one row per dispatched unit of
|
|
232
|
+
work under the native engine: `run_id`,
|
|
233
|
+
`unit_id`, `step_id`, `status` (`pending|running|completed|failed|skipped`),
|
|
234
|
+
`input_hash`, `result_json`, `tokens`, `failure_reason`, `worktree_path`,
|
|
235
|
+
`session_id`, `last_checkin_at`, `attempts`, `claim_holder`/
|
|
236
|
+
`claim_expires_at`. Every dispatched unit journals here;
|
|
237
|
+
only historical runs from the removed manual lifecycle can lack unit rows.
|
|
238
|
+
- `workflow_run_unit_attempts` — the append-only durable-v4 dispatch journal,
|
|
239
|
+
keyed by `(run_id, unit_id, attempt)`, with a unique `dispatch_id`. Explicit
|
|
240
|
+
retries append a new attempt; crash reclaim reuses the existing dispatch
|
|
241
|
+
identity.
|
|
242
|
+
|
|
243
|
+
Schema evolves through an additive, idempotent migration engine, recorded in
|
|
244
|
+
`schema_migrations`. The first three tables above are the current baseline
|
|
245
|
+
(`001-initial-schema` in `src/core/state/migrations.ts`), and
|
|
246
|
+
`022-workflow-unit-attempts` adds the append-only attempt journal. The pre-cutover
|
|
247
|
+
`workflow.db` history that produced it is preserved for reference in
|
|
248
|
+
`scripts/akm-migrate/migrate/legacy/workflow-migrations-bodies.ts` —
|
|
249
|
+
`004-workflow-run-units` (the table itself), `005`/`007` (unit session id /
|
|
250
|
+
check-in columns), `006-frozen-plan-and-lease` (the engine's frozen plan +
|
|
251
|
+
run lease), `008`/`009` (unit attempts / claim columns).
|
|
252
|
+
|
|
253
|
+
### B.4 Execution model: one native surface, durable memory
|
|
254
|
+
|
|
255
|
+
`akm workflow run` is the one stable native execution surface. Peer source formats use the same source IR v1 before durable freezing: Markdown `.md` and YAML `.yml`.
|
|
256
|
+
A new start by ref compiles source
|
|
257
|
+
IR v1 and freezes the resulting durable-v4-family `irVersion: 5` plan on the
|
|
258
|
+
run row (`plan_json`/`plan_hash`); edits to the source file need a new run.
|
|
259
|
+
Pre-`irVersion`-5
|
|
260
|
+
stored plans are rejected rather than keeping a parallel runtime. The
|
|
261
|
+
command dispatches each step's units to the configured runner (`llm`, `agent`,
|
|
262
|
+
or `sdk`), with:
|
|
263
|
+
|
|
264
|
+
- **Real concurrency.** A `map` step's units run through a scheduler
|
|
265
|
+
(`src/workflows/exec/scheduler.ts`) capped by `min(map's concurrency,
|
|
266
|
+
workflow.maxConcurrency, the selected engine's concurrency, a CPU-derived
|
|
267
|
+
host safety limit)`. The CPU-derived default — `min(16, max(1, cores − 2))`
|
|
268
|
+
— is, deliberately, the same formula Claude Code's own `agent()` cap uses
|
|
269
|
+
(A.3).
|
|
270
|
+
- **Retries** (`retry: { max, on: [<failure-reason>…] }`), journaled per
|
|
271
|
+
attempt so no attempt's row is clobbered.
|
|
272
|
+
- **Worktree isolation** (`isolation: worktree`) — a fresh detached git
|
|
273
|
+
worktree per unit attempt for file-mutating agent/sdk units, so parallel
|
|
274
|
+
fan-out can't trample a shared working tree; a clean one is auto-removed,
|
|
275
|
+
a dirty one is retained and its path logged.
|
|
276
|
+
- **Typed step artifacts** — a step's declared `output` JSON Schema validates
|
|
277
|
+
the promoted artifact (a solo unit's result, a `collect` fan-out's array, or
|
|
278
|
+
a `vote` fan-out's majority winner) before the step can complete.
|
|
279
|
+
- **Gates that judge the artifact**, with `gate.max_loops` turning a rejection
|
|
280
|
+
into a bounded evaluator-optimizer loop (re-run with the judge's feedback
|
|
281
|
+
threaded into every unit prompt). Omitted or empty rubrics skip validation;
|
|
282
|
+
criteria-bearing gates freeze `workflow.judgeEngine`, and unavailable or
|
|
283
|
+
malformed verdicts fail closed.
|
|
284
|
+
- **Budget ceilings** (`budget.max_units`/`max_tokens`), enforced across
|
|
285
|
+
resumes because both counters are seeded from the unit journal.
|
|
286
|
+
|
|
287
|
+
Execution is always the engine's: `akm workflow run` dispatches each unit to
|
|
288
|
+
a configured harness (`src/workflows/exec/step-work.ts` holds the shared step
|
|
289
|
+
semantics) and a run lease (B.5) keeps a single invocation on the spine. akm
|
|
290
|
+
formerly shipped an experimental `brief`/`report` protocol that let a calling
|
|
291
|
+
agent session execute units itself; it was removed in favour of the single
|
|
292
|
+
native path, since native dispatch already covers ten harnesses — Claude Code
|
|
293
|
+
among them — for less code than the protocol cost.
|
|
294
|
+
|
|
295
|
+
### B.5 Scoping & concurrency guards (two different guards)
|
|
296
|
+
|
|
297
|
+
Runs are partitioned by **`scope_key`** — a `sha256` of the nearest project
|
|
298
|
+
anchor (`.akm/config.json` root → git root → bundle dir → cwd),
|
|
299
|
+
`src/workflows/authoring/scope-key.ts`. Within a `(workflow_ref, scope_key)`
|
|
300
|
+
pair, `startWorkflowRun` enforces a **single active run** unless `--force` is
|
|
301
|
+
passed internally. The public CLI exposes no parallel-start flag: invoking
|
|
302
|
+
`workflow run` by ref continues the active run in that scope. This guard is
|
|
303
|
+
scope-local by design (two unrelated projects sharing one `state.db` must be
|
|
304
|
+
able to run the same-named workflow independently), so two *different*
|
|
305
|
+
scopes can each hold their own active run of the same ref — starting one
|
|
306
|
+
warns about an active run in another scope (naming its id and scope) rather
|
|
307
|
+
than blocking it, and `akm workflow list --all-scopes` / `status` / `resume`
|
|
308
|
+
/ `abandon <run-id>` see and act across scopes (#942).
|
|
309
|
+
|
|
310
|
+
Orchestrated runs add a second, unrelated guard: the **run lease**. `akm
|
|
311
|
+
workflow run` takes a lease (a random holder id, 90s expiry, renewed between
|
|
312
|
+
steps) before dispatching anything; a second `run` invocation against a
|
|
313
|
+
live-leased run refuses. The native engine owns the step spine while it drives.
|
|
314
|
+
An expired lease is claimable, so a crashed engine never
|
|
315
|
+
wedges a run. `status` stays read-only and always works.
|
|
316
|
+
|
|
317
|
+
### B.6 Progress tracking
|
|
318
|
+
|
|
319
|
+
Progress inspection is **pull-based**: `workflow status` reports step rows,
|
|
320
|
+
notes, evidence, and summaries; there is no daemon.
|
|
321
|
+
|
|
322
|
+
What's new: **`akm log --run <run-id>`** reads a run's `workflow_*` /
|
|
323
|
+
`workflow_unit_*` events off the general append-only events stream;
|
|
324
|
+
`--since '@offset:<id>'` resumes from the last seen row-id cursor, so a
|
|
325
|
+
cooperating process can poll it on an interval — there's no daemon, and
|
|
326
|
+
no dedicated workflow-watch command (0.9.0 dropped one; this is the general
|
|
327
|
+
`log` surface instead). It's not a push channel in Claude Code's
|
|
328
|
+
`/workflows`-live-tree sense (there's no server pushing to a client — the
|
|
329
|
+
poller polls under the hood), but it closes most of the practical gap: a
|
|
330
|
+
second terminal polling `akm log --run <run-id> --since '@offset:<id>'` next
|
|
331
|
+
to `akm workflow run <run-id>` gets a live-feeling tail with no daemon of its
|
|
332
|
+
own. Event metadata is ids/status/enums only, never workflow-authored
|
|
333
|
+
content, so it's safe to pipe into logs or dashboards.
|
|
334
|
+
|
|
335
|
+
### B.7 Quality gates
|
|
336
|
+
|
|
337
|
+
Native completion evaluates the step's **promoted artifact**
|
|
338
|
+
(real JSON data, clipped at 4000 chars) against the rubric, not a
|
|
339
|
+
machine-generated prose summary. A non-empty rubric requires the frozen
|
|
340
|
+
`workflow.judgeEngine`, which may be an LLM or agent engine. Only a well-formed
|
|
341
|
+
affirmative verdict advances the step; missing, failed, and malformed verifier
|
|
342
|
+
results reject. Invoked gates are journaled. `akm workflow resume` can reopen a
|
|
343
|
+
failed or blocked run after the underlying issue is corrected.
|
|
344
|
+
|
|
345
|
+
### B.8 Check-in: stall nudging without a daemon
|
|
346
|
+
|
|
347
|
+
Unchanged from before the engine: akm records the invoking harness/session
|
|
348
|
+
context (`agent_harness`, `agent_session_id`) from environment hints
|
|
349
|
+
(`src/workflows/runtime/agent-identity.ts`), arms a **check-in** timestamp
|
|
350
|
+
(`checkin_armed_at`) — not a background thread — and on the next `workflow
|
|
351
|
+
status` poll by a supervising observer, `evaluateCheckin()`
|
|
352
|
+
(`src/workflows/runtime/checkin.ts`) compares `now` against
|
|
353
|
+
`max(updated_at, checkin_armed_at)`; past a 90s stall window it surfaces a
|
|
354
|
+
`continue` notification through the normal command output. The design ADR
|
|
355
|
+
explicitly rejects a background-thread alternative: "No daemon in a CLI… the
|
|
356
|
+
command loop is already the heartbeat." A parallel, independent check-in
|
|
357
|
+
exists at the *unit* level (`last_checkin_at` marks a long-running unit live
|
|
358
|
+
so a stalled one can be reclaimed) — same no-daemon, pure-timestamp design,
|
|
359
|
+
different granularity.
|
|
360
|
+
|
|
361
|
+
The supervising observer receives that `continue` notification only while
|
|
362
|
+
polling; akm does not push it in the background.
|
|
363
|
+
|
|
364
|
+
### B.9 CLI surface
|
|
365
|
+
|
|
366
|
+
`akm workflow` (`src/commands/workflow-cli.ts`) exposes exactly six public subcommands, derived here from `workflowCommand.subCommands`, not hand-listed:
|
|
367
|
+
|
|
368
|
+
```
|
|
369
|
+
akm workflow status|list|create|resume|abandon|run
|
|
370
|
+
```
|
|
371
|
+
|
|
372
|
+
All six are stable and ungated. `create --print` prints a starter without writing, and `akm lint
|
|
373
|
+
--type workflows` structurally validates it. The removed `start`/`next`/
|
|
374
|
+
`complete` lifecycle and older `template`/`validate`/`watch` subcommands have
|
|
375
|
+
no aliases. Bare `akm workflow` is a usage error (exit 2).
|
|
376
|
+
|
|
377
|
+
---
|
|
378
|
+
|
|
379
|
+
## Part C — Side-by-side
|
|
380
|
+
|
|
381
|
+
akm has peer `.md` and `.yml` asset source formats with one stable native
|
|
382
|
+
orchestration surface.
|
|
383
|
+
|
|
384
|
+
| Dimension | Claude Code workflow | akm workflow |
|
|
385
|
+
|---|---|---|
|
|
386
|
+
| **Artifact** | Imperative JS program (`script`) | Declarative workflow asset (`.md` or `.yml`), compiled through source IR v1 to durable-v4-family `irVersion: 5` |
|
|
387
|
+
| **Authored by** | The agent, inline, per-task, ephemeral | Human or agent, saved as a reusable bundle asset |
|
|
388
|
+
| **Who executes work** | The harness runs the script; subagents do the work | The native engine, dispatching to a configured harness |
|
|
389
|
+
| **Unit of work** | `agent()` — a fresh LLM subagent context | One dispatch or one `map` item under the engine |
|
|
390
|
+
| **Concurrency** | Massively parallel (≤16 concurrent, ≤1000 total, `pipeline`/`parallel`) | Bounded map concurrency, frozen into the plan |
|
|
391
|
+
| **Control flow** | Full JS: loops, conditionals, fan-out, budget-scaled | `unit`/`map`/`route`, bare reference strings, and bounded gate retries; no backward routes |
|
|
392
|
+
| **State store** | Transcript dir (`journal.jsonl`, `agent-*.jsonl`) | SQLite `state.db`; native-engine runs add unit rows and a frozen `plan_json`/`plan_hash` |
|
|
393
|
+
| **Scope / lifetime** | One session, one turn-shaped fan-out | Cross-session, per-project `scope_key`, resumable indefinitely |
|
|
394
|
+
| **Progress model** | Push: live `/workflows` tree + `task-notification` | Pull via `status`, with near-live NDJSON event polling via `akm log` |
|
|
395
|
+
| **Resume** | Prefix-cache replay keyed on `runId` (needs determinism) | Durable row resume; native-engine units add journaled replay keyed on content-derived identity |
|
|
396
|
+
| **Quality gates** | Agent-authored (adversarial verify, judge panels, schemas) | Fail-closed LLM or agent verification of typed artifacts; `gate.max_loops` bounds retries |
|
|
397
|
+
| **Sandbox / trust** | Restricted JS interpreter, no FS; subagents use tools | No sandbox; engine units can opt into `isolation: worktree`, which is not a security boundary |
|
|
398
|
+
| **Nesting** | `workflow()`, one level deep, sharing the parent's concurrency cap/budget | None built in |
|
|
399
|
+
| **Stability** | Stable harness feature | Format and `run` both Stable and ungated |
|
|
400
|
+
|
|
401
|
+
---
|
|
402
|
+
|
|
403
|
+
## Part D — Where they overlap
|
|
404
|
+
|
|
405
|
+
Despite living on different layers, they converge on several ideas — and the
|
|
406
|
+
native engine (Part B.4) made two of these convergences much closer than they
|
|
407
|
+
used to be:
|
|
408
|
+
|
|
409
|
+
1. **Task decomposition into named units** — phases/agents vs. steps/units.
|
|
410
|
+
2. **Durable run identity and resume** — `runId` prefix-cache vs. `state.db`
|
|
411
|
+
workflow-run rows. Both are built to survive interruption and pick up where
|
|
412
|
+
they left off; the native engine's resume is also **journaled replay**
|
|
413
|
+
keyed on content-derived unit identity, close in spirit to Claude Code's
|
|
414
|
+
cache-and-replay.
|
|
415
|
+
3. **Per-unit status + evidence** — journal return values vs. step
|
|
416
|
+
`status`/`notes`/`evidence`/`summary`, plus per-unit
|
|
417
|
+
`workflow_run_units` rows with the same shape.
|
|
418
|
+
4. **A "keep going" nudge** — Claude Code's `task-notification`/resume vs.
|
|
419
|
+
akm's run-level `continue` check-in directive and its unit-level
|
|
420
|
+
`last_checkin_at` heartbeat.
|
|
421
|
+
5. **Structured validation of results** — Claude Code's `schema` option
|
|
422
|
+
(forced `StructuredOutput`, retried) vs. akm's per-unit `output` JSON
|
|
423
|
+
Schema and fail-closed artifact verifier under the native engine.
|
|
424
|
+
6. **Scaffolding + validation of the artifact** — `meta` shape checks vs.
|
|
425
|
+
`akm workflow create --print` / `akm lint --type workflows`, the peer
|
|
426
|
+
source adapters, and their shared source IR.
|
|
427
|
+
7. **Bounded, capped concurrency with the same default formula.** Claude
|
|
428
|
+
Code's `agent()` cap and akm's unit scheduler both default to
|
|
429
|
+
`min(16, cores − 2)` — not a coincidence; the engine's CPU-derived default
|
|
430
|
+
was written to match it (B.4).
|
|
431
|
+
8. **Awareness of the invoking session** — Claude Code owns the session; akm
|
|
432
|
+
*records* it (`CLAUDE_SESSION_ID` → `claude`). Under the engine this
|
|
433
|
+
awareness becomes load-bearing, not just descriptive: the run lease (B.5)
|
|
434
|
+
uses it to arbitrate which invocation may hold a given run right now.
|
|
435
|
+
9. **Native dispatch spans harnesses.** akm executes each unit by invoking a
|
|
436
|
+
configured agent CLI — ten harnesses are supported, Claude Code among them —
|
|
437
|
+
so an akm run composes with the same tools without either system embedding
|
|
438
|
+
the other.
|
|
439
|
+
|
|
440
|
+
---
|
|
441
|
+
|
|
442
|
+
## Part E — Where they still diverge, and where they no longer do
|
|
443
|
+
|
|
444
|
+
The old axis — "who holds the execution loop" — is still the right frame.
|
|
445
|
+
Claude Code owns its session-native loop; akm's one public execution surface,
|
|
446
|
+
`akm workflow run`, owns the durable workflow loop.
|
|
447
|
+
|
|
448
|
+
- **Claude Code workflows always own execution.** The harness is the runtime;
|
|
449
|
+
the script is the plan; subagents are the workers. Because the harness
|
|
450
|
+
replays the script to resume, it must constrain the script (no wall-clock,
|
|
451
|
+
no randomness, no FS) and keep it ephemeral. Parallelism is free because the
|
|
452
|
+
runtime schedules it.
|
|
453
|
+
|
|
454
|
+
- **An akm workflow under `akm workflow run` owns execution.** The native engine
|
|
455
|
+
dispatches real concurrent units through the multi-harness
|
|
456
|
+
substrate: `RunnerSpec` `llm|agent|sdk` + `executeRunner`, the same spawner
|
|
457
|
+
`akm improve`/`reflect` already uses. It retries them, isolates them in
|
|
458
|
+
worktrees, judges their typed artifacts, and enforces budget ceilings — a
|
|
459
|
+
genuine, Stable executor. It keeps a durable spine through a frozen plan and
|
|
460
|
+
journaled resume rather than trading those properties away for parallelism.
|
|
461
|
+
|
|
462
|
+
- **akm owns the plan and the durable completion semantics** — routing,
|
|
463
|
+
artifact promotion, verification, and replay — while the harness it
|
|
464
|
+
dispatches to owns only the work inside a single unit.
|
|
465
|
+
|
|
466
|
+
Concretely, per surface:
|
|
467
|
+
|
|
468
|
+
| | Claude Code workflow | akm native engine (`run`) |
|
|
469
|
+
|---|---|---|
|
|
470
|
+
| Lifetime | Ephemeral (session-scoped) | Durable (SQLite, cross-session) |
|
|
471
|
+
| Parallel? | Yes, by construction | Yes, bounded (B.4) |
|
|
472
|
+
| Self-contained? | Yes — carries its own workers | Yes, when a configured engine is available (or `opencode` is on PATH) |
|
|
473
|
+
| Sandbox | Restricted JS interpreter, no FS for the script itself | None for the shell/agent substrate; optional `isolation: worktree` is not a security boundary |
|
|
474
|
+
| Artifact | Executable script | Managed `.md` or `.yml` workflow asset, compiled and frozen |
|
|
475
|
+
|
|
476
|
+
The genuinely durable conclusion, restated for 0.9.2: Claude Code is strong
|
|
477
|
+
at in-session parallel LLM execution, while akm is strong at durable,
|
|
478
|
+
cross-session procedures. The native engine keeps the second half of that
|
|
479
|
+
trade while buying back some of the first — dispatching units to whichever
|
|
480
|
+
harness the operator configured, Claude Code included.
|
|
481
|
+
|
|
482
|
+
---
|
|
483
|
+
|
|
484
|
+
## Part F — What's left to integrate
|
|
485
|
+
|
|
486
|
+
Most of what this section used to propose as *future* integration work has
|
|
487
|
+
**shipped** as the native engine described in Part B — not as
|
|
488
|
+
Claude-Code-specific features, but as harness-agnostic ones:
|
|
489
|
+
|
|
490
|
+
| Formerly-proposed idea | Status |
|
|
491
|
+
|---|---|
|
|
492
|
+
| A blessed loop pattern for invoking an akm run with structured per-step results | **Shipped**, generalized: an invoking agent or orchestrator invokes the native `akm workflow run`, which dispatches to any of ten configured harnesses (Part B.4) — not Claude-Code-specific |
|
|
493
|
+
| Machine-readable, near-live progress instead of polling `status` | **Shipped**: `akm log --run <id> --since '@offset:<id>'` (Part B.6) |
|
|
494
|
+
| Structural (schema) validation of step output, not just an LLM prose judge | **Shipped**: per-unit `output` JSON Schema + typed step artifacts, validated before a gate ever runs (Part B.4/B.7) |
|
|
495
|
+
| An explicit fan-out step type | **Shipped**: `map` steps with `over`/`concurrency`/`reducer` (Part B.4) |
|
|
496
|
+
|
|
497
|
+
What's genuinely still not built, as of this writing:
|
|
498
|
+
|
|
499
|
+
### F.1 Correlate an akm run with the invoking harness's own run/session id
|
|
500
|
+
|
|
501
|
+
`agent-identity.ts` captures `agent_harness`/`agent_session_id` from
|
|
502
|
+
environment hints, and each unit records its harness-native session id
|
|
503
|
+
(Part B.3). Neither captures a *workflow-
|
|
504
|
+
level* id from an external orchestrator (e.g. a Claude Code `Workflow` tool's
|
|
505
|
+
own `runId`) when it invokes an akm run — there is no env-hint or CLI flag for
|
|
506
|
+
it today, so correlating an akm run with its invoking Claude Code workflow
|
|
507
|
+
invocation still has to be done by hand (matching timestamps, logs).
|
|
508
|
+
|
|
509
|
+
### F.2 Notify a supervising observer instead of waiting for its next poll
|
|
510
|
+
|
|
511
|
+
The check-in (Part B.8) is still exactly as passive as the ADR mandates: it
|
|
512
|
+
surfaces only on the next `workflow status` poll, never proactively.
|
|
513
|
+
There is no signal-file or other out-of-band mechanism that lets a stalled
|
|
514
|
+
run *notify* a supervising or polling observer capable of receiving one (the ADR's own design
|
|
515
|
+
contemplates "a best-effort checkin signal file under the run scope" as a
|
|
516
|
+
future extension) — this remains unbuilt.
|
|
517
|
+
|
|
518
|
+
### F.3 Two-way compilation between the artifacts
|
|
519
|
+
|
|
520
|
+
Neither direction exists: there is no `akm workflow export` that emits a
|
|
521
|
+
script/pattern specialized to a given workflow for a specific harness, and
|
|
522
|
+
there is no `akm workflow ingest-journal` (or equivalent) that converts an
|
|
523
|
+
external orchestration journal into an akm run. The former live
|
|
524
|
+
external-driver protocol is retired; an external agent can invoke
|
|
525
|
+
`akm workflow run` and inspect `status` or `akm log`, but it does not drive an
|
|
526
|
+
alternate akm execution surface.
|
|
527
|
+
|
|
528
|
+
### F.4 Suppress the check-in when an observer already provides liveness
|
|
529
|
+
|
|
530
|
+
The check-in fires uniformly for any active run past the stall window
|
|
531
|
+
(`evaluateCheckin`, Part B.8) — it does not special-case a recorded
|
|
532
|
+
invoking harness/session context when an external observer already supplies
|
|
533
|
+
its own liveness/orchestration (so it would not stall the way a free-form chat
|
|
534
|
+
agent can). This is a small, purely local trim on top of the existing check-in
|
|
535
|
+
logic, not a new subsystem, but it is not implemented today.
|
|
536
|
+
|
|
537
|
+
### F.5 Distribute non-akm executable workflow scripts as akm assets
|
|
538
|
+
|
|
539
|
+
The `workflow` asset type carries akm's peer `.md` and `.yml` source formats.
|
|
540
|
+
There is no mechanism for it to carry or reference an
|
|
541
|
+
externally-executable script (a Claude Code `Workflow` tool script, or
|
|
542
|
+
anything else) as an alternate form alongside the runbook, which would let
|
|
543
|
+
akm's package-manager strengths (`add`, search, `curate`, version pinning,
|
|
544
|
+
feedback/improve) apply to *that* artifact too. This is the largest and most
|
|
545
|
+
speculative item on this list.
|
|
546
|
+
|
|
547
|
+
The guiding principle is to preserve each layer's strength. The native engine
|
|
548
|
+
gives akm concurrency without discarding its durable, verified spine, and it
|
|
549
|
+
dispatches the work itself to whichever harness the operator configured —
|
|
550
|
+
Claude Code included — instead of either side reimplementing the other.
|
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
# 0001 — No interpolation: data reaches units as attached structured context
|
|
2
|
+
|
|
3
|
+
## Context
|
|
4
|
+
|
|
5
|
+
The pre-unification engine spliced data into a unit's prompt by string
|
|
6
|
+
substitution (`${{ item }}`-style interpolation). That created an injection
|
|
7
|
+
class: a producer's output could itself contain `${{ ... }}`-shaped text,
|
|
8
|
+
and a re-scan of the already-substituted prose would interpret it as more
|
|
9
|
+
grammar (the "P1 `{{item}}` re-scan injection" the essay below names). The
|
|
10
|
+
workflow-format-unification work (spec §2.3) closed that class structurally
|
|
11
|
+
rather than by escaping or sanitizing.
|
|
12
|
+
|
|
13
|
+
## Decision
|
|
14
|
+
|
|
15
|
+
The essay, moved verbatim from `src/workflows/exec/native-executor.ts`'s
|
|
16
|
+
module header:
|
|
17
|
+
|
|
18
|
+
> Data flow (workflow-format-unification, spec §2.3): there is NO
|
|
19
|
+
> interpolation language. A unit's instructions are the step's body prose
|
|
20
|
+
> BYTE-EXACT — prose is never scanned for reference syntax, so a literal
|
|
21
|
+
> `${{` in a body is content, not grammar. Data reaches the unit as ATTACHED
|
|
22
|
+
> STRUCTURED CONTEXT rather than string splices: `buildUnitPrompt`
|
|
23
|
+
> (`exec/step-work.ts`) wraps the verbatim instructions with JSON blocks for
|
|
24
|
+
> the run params, a map unit's item + index, and the artifacts named by the
|
|
25
|
+
> step's `inputs:`. Because nothing is ever substituted INTO the prose, the
|
|
26
|
+
> P1 `{{item}}` re-scan injection class is structurally impossible.
|
|
27
|
+
>
|
|
28
|
+
> References survive only in the three whole-value FRONTMATTER positions the
|
|
29
|
+
> closed two-root grammar occupies (`program/expressions.ts`): `map.over`,
|
|
30
|
+
> `route.input`, and each `inputs[]` entry. They resolve ONCE per step
|
|
31
|
+
> against `{ params, stepOutputs }`. There is NO ambient key search: a
|
|
32
|
+
> `steps.<id>.output.<path>` reference addresses INTO that step's recorded
|
|
33
|
+
> output explicitly.
|
|
34
|
+
>
|
|
35
|
+
> Step outputs (`steps.<id>.output…`): every engine-executed step journals a
|
|
36
|
+
> promoted ARTIFACT under `evidence.output` — the solo unit's result/text,
|
|
37
|
+
> the collect reducer's per-item array, or the vote reducer's winner — and
|
|
38
|
+
> that artifact is what the reference scope exposes
|
|
39
|
+
> (`projectStepOutput`). The documented addressing
|
|
40
|
+
> (`steps.discover.output.files`) therefore resolves against real step
|
|
41
|
+
> results, never the raw evidence envelope (peer review R1). Steps completed
|
|
42
|
+
> manually (no `output` key in their evidence) expose their recorded
|
|
43
|
+
> evidence object as-is.
|
|
44
|
+
|
|
45
|
+
## Consequences
|
|
46
|
+
|
|
47
|
+
- A workflow author can never accidentally (or deliberately) inject
|
|
48
|
+
reference grammar through step output content — the prose a unit receives
|
|
49
|
+
is always byte-exact, and the only place reference syntax is EVER parsed
|
|
50
|
+
is the three closed frontmatter positions, resolved once, before dispatch.
|
|
51
|
+
- `steps.<id>.output` addressing is stable and explicit: it always resolves
|
|
52
|
+
against the PROMOTED artifact a step recorded, never against an internal
|
|
53
|
+
evidence shape that could change without the reference contract changing
|
|
54
|
+
with it.
|
|
55
|
+
- Peer review R1 flagged an earlier draft where the documented addressing
|
|
56
|
+
risked resolving against the raw evidence envelope instead of the
|
|
57
|
+
promoted artifact; the fix (route addressing through `projectStepOutput`)
|
|
58
|
+
is what the essay above describes as settled behavior, not a proposal.
|
|
59
|
+
- Because references resolve ONCE per step (not per unit, not lazily),
|
|
60
|
+
resuming a run against the same frozen plan recomputes the identical
|
|
61
|
+
resolution — this is part of what makes `computeStepWorkList`
|
|
62
|
+
(`step-work.ts`, ADR 0002) a pure function of (plan, params, journaled
|
|
63
|
+
results).
|
|
64
|
+
|
|
65
|
+
## Provenance
|
|
66
|
+
|
|
67
|
+
- Source: `src/workflows/exec/native-executor.ts`, module header (the "Data
|
|
68
|
+
flow" / "References" / "Step outputs" paragraphs).
|
|
69
|
+
- Spec: workflow-format-unification, spec §2.3.
|
|
70
|
+
- Review round: peer review R1 (addressing must resolve against the
|
|
71
|
+
promoted artifact, not the raw evidence envelope).
|
|
72
|
+
- Extracted: P4 (`docs/plans/specs/p4-deletions-closeout.md` §4.2), 2026-08-27.
|