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.
Files changed (161) hide show
  1. package/CHANGELOG.md +29 -0
  2. package/README.md +10 -10
  3. package/ROADMAP.md +112 -0
  4. package/dist/commands/improve/improve-cli.js +33 -12
  5. package/dist/core/file-lock.js +10 -1
  6. package/dist/llm/client.js +33 -3
  7. package/dist/scripts/akm-migrate-node.js +5 -1
  8. package/dist/scripts/akm-migrate.js +5 -1
  9. package/docs/README.md +42 -41
  10. package/docs/agents/README.md +15 -0
  11. package/docs/agents/agent-install.md +14 -0
  12. package/docs/architecture/README.md +67 -0
  13. package/docs/architecture/adapters.md +272 -0
  14. package/docs/architecture/akm-architecture-decision-history.md +2055 -0
  15. package/docs/architecture/akm-core-principles.md +126 -0
  16. package/docs/architecture/architecture.md +579 -0
  17. package/docs/architecture/comparisons/claude-code-vs-akm-workflows-full.md +550 -0
  18. package/docs/architecture/decisions/0001-no-interpolation-attached-structured-context.md +72 -0
  19. package/docs/architecture/decisions/0002-unit-reuse-and-input-hash-scope.md +142 -0
  20. package/docs/architecture/decisions/0003-child-env-allowlist-and-provenance.md +189 -0
  21. package/docs/architecture/decisions/0004-task-input-contract-and-flag-coercion.md +97 -0
  22. package/docs/architecture/decisions/0005-task-result-vocabulary-and-legacy-read-mapping.md +108 -0
  23. package/docs/architecture/decisions/0006-task-source-version-routing.md +124 -0
  24. package/docs/architecture/decisions/0007-workflow-composition-bounds.md +83 -0
  25. package/docs/architecture/decisions/0008-task-binding-normalization.md +88 -0
  26. package/docs/architecture/decisions/0009-child-run-publication-column-parity.md +80 -0
  27. package/docs/architecture/decisions/0010-driver-protocol-cut.md +36 -0
  28. package/docs/architecture/decisions/0011-engine-run-loop-invariants.md +125 -0
  29. package/docs/architecture/decisions/README.md +62 -0
  30. package/docs/architecture/improvement.md +440 -0
  31. package/docs/architecture/internals/classification.md +154 -0
  32. package/docs/architecture/internals/fresh-host-rebuild-runbook.md +40 -0
  33. package/docs/architecture/internals/functional-contract-patterns.md +340 -0
  34. package/docs/architecture/internals/health-advisories.md +110 -0
  35. package/docs/architecture/internals/improve-workflow.md +713 -0
  36. package/docs/architecture/internals/indexing.md +498 -0
  37. package/docs/architecture/internals/registry-network-boundary.md +57 -0
  38. package/docs/architecture/internals/search.md +249 -0
  39. package/docs/architecture/internals/storage-locations.md +943 -0
  40. package/docs/architecture/persisted-data-compat.md +59 -0
  41. package/docs/architecture/reviews/env-secret-access.md +319 -0
  42. package/docs/architecture/runtime-boundary-design.md +153 -0
  43. package/docs/architecture/specs/0.9.0-close-out-plan.md +361 -0
  44. package/docs/architecture/specs/0.9.0-decisions.md +528 -0
  45. package/docs/architecture/specs/0.9.0-docs-code-drift-register.md +745 -0
  46. package/docs/architecture/specs/0.9.0-open-items-register.md +135 -0
  47. package/docs/architecture/specs/0.9.0-public-api-issue-backlog.md +740 -0
  48. package/docs/architecture/specs/0.9.0-release-surface-review.md +636 -0
  49. package/docs/architecture/specs/agent-command-engine-model-design.md +513 -0
  50. package/docs/architecture/specs/akm-0.9.0-bundle-adapter-spec.md +501 -0
  51. package/docs/architecture/specs/akm-0.9.0-ref-grammar-decision.md +291 -0
  52. package/docs/architecture/specs/akm-format-neutral-bundle-workspace-spec.md +2059 -0
  53. package/docs/architecture/specs/di-seams-plan.md +539 -0
  54. package/docs/architecture/specs/driver-protocol-keep-or-cut.md +275 -0
  55. package/docs/architecture/specs/fact-asset-type.md +142 -0
  56. package/docs/architecture/specs/improve-collapse-churn-detector-design.md +634 -0
  57. package/docs/architecture/specs/okf-support.md +148 -0
  58. package/docs/architecture/specs/ref.md +329 -0
  59. package/docs/architecture/specs/reviews/task-workflow-unification/README.md +72 -0
  60. package/docs/architecture/specs/reviews/task-workflow-unification/findings-consolidated.md +152 -0
  61. package/docs/architecture/specs/reviews/task-workflow-unification/library-eval-alternatives.md +352 -0
  62. package/docs/architecture/specs/reviews/task-workflow-unification/library-eval-bunqueue.md +300 -0
  63. package/docs/architecture/specs/reviews/task-workflow-unification/library-eval-openworkflow.md +402 -0
  64. package/docs/architecture/specs/reviews/task-workflow-unification/opus-review-1.md +758 -0
  65. package/docs/architecture/specs/reviews/task-workflow-unification/opus-review-2.md +645 -0
  66. package/docs/architecture/specs/reviews/task-workflow-unification/opus-review-3.md +602 -0
  67. package/docs/architecture/specs/reviews/task-workflow-unification/panel-r1-adversarial.md +27 -0
  68. package/docs/architecture/specs/reviews/task-workflow-unification/panel-r1-architecture.md +27 -0
  69. package/docs/architecture/specs/reviews/task-workflow-unification/panel-r1-coverage.md +74 -0
  70. package/docs/architecture/specs/reviews/task-workflow-unification/panel-r1-security.md +15 -0
  71. package/docs/architecture/specs/reviews/task-workflow-unification/panel-r2-adversarial.md +132 -0
  72. package/docs/architecture/specs/reviews/task-workflow-unification/panel-r2-verification.md +147 -0
  73. package/docs/architecture/specs/stash-conventions-code-spec.md +275 -0
  74. package/docs/architecture/specs/stash-organization-conventions.md +377 -0
  75. package/docs/architecture/specs/task-workflow-format-unification.md +721 -0
  76. package/docs/architecture/specs/workflow-engine-buy-vs-build.md +231 -0
  77. package/docs/architecture/specs/workflow-format-unification.md +437 -0
  78. package/docs/architecture/testing/manual-testing-checklist.md +3420 -0
  79. package/docs/architecture/testing/okf-v0.2-conformance-runbook.md +924 -0
  80. package/docs/architecture/testing/task-workflow-manual-tests.md +1817 -0
  81. package/docs/architecture/testing/testing-workflow.md +446 -0
  82. package/docs/architecture/workflow-engine.md +274 -0
  83. package/docs/guides/README.md +33 -0
  84. package/docs/guides/author-bundles.md +310 -0
  85. package/docs/guides/author-workflows.md +520 -0
  86. package/docs/guides/bundles.md +321 -0
  87. package/docs/guides/capture-knowledge.md +136 -0
  88. package/docs/guides/claude-code-vs-akm-workflows.md +30 -0
  89. package/docs/guides/concepts.md +93 -0
  90. package/docs/guides/discover-and-load.md +158 -0
  91. package/docs/guides/getting-started.md +240 -0
  92. package/docs/guides/improve-the-library.md +246 -0
  93. package/docs/guides/knowledge-management.md +18 -0
  94. package/docs/guides/recipes/headless-install.md +97 -0
  95. package/docs/guides/recipes/website-source.md +28 -0
  96. package/docs/guides/run-workflows.md +279 -0
  97. package/docs/guides/scheduling.md +123 -0
  98. package/docs/guides/use-with-any-agent.md +129 -0
  99. package/docs/guides/wikis.md +81 -0
  100. package/docs/maintainers/README.md +19 -0
  101. package/docs/maintainers/curate-workmap.md +336 -0
  102. package/docs/maintainers/eval.md +3 -0
  103. package/docs/maintainers/local-development.md +108 -0
  104. package/docs/maintainers/release-checklist.md +103 -0
  105. package/docs/migration/README.md +2 -2
  106. package/docs/migration/release-notes/0.0.13.md +1 -1
  107. package/docs/migration/release-notes/0.1.0.md +1 -1
  108. package/docs/migration/release-notes/0.2.0.md +1 -1
  109. package/docs/migration/release-notes/0.3.0.md +1 -1
  110. package/docs/migration/release-notes/0.5.0.md +1 -1
  111. package/docs/migration/release-notes/0.6.0.md +1 -1
  112. package/docs/migration/release-notes/0.7.0.md +1 -1
  113. package/docs/migration/release-notes/0.7.3.md +2 -2
  114. package/docs/migration/release-notes/0.7.4.md +1 -1
  115. package/docs/migration/release-notes/0.7.5.md +1 -1
  116. package/docs/migration/release-notes/0.8.0.md +1 -1
  117. package/docs/migration/release-notes/0.9.0.md +1 -1
  118. package/docs/migration/release-notes/0.9.14.md +1 -1
  119. package/docs/migration/release-notes/0.9.15.md +1 -1
  120. package/docs/migration/release-notes/0.9.16.md +1 -1
  121. package/docs/migration/release-notes/0.9.17.md +1 -1
  122. package/docs/migration/release-notes/0.9.19.md +1 -1
  123. package/docs/migration/release-notes/0.9.2.md +1 -1
  124. package/docs/migration/release-notes/README.md +27 -31
  125. package/docs/migration/v0.5-to-v0.6.md +759 -0
  126. package/docs/migration/v0.8-to-v0.9.md +1 -1
  127. package/docs/plans/0.9.2-agent-command-workflow-plan.md +423 -0
  128. package/docs/posts/README.md +30 -0
  129. package/docs/posts/akm-command-tour.md +495 -0
  130. package/docs/posts/akm-logo-sized.webp +0 -0
  131. package/docs/posts/belief-aware-memory-12.md +139 -0
  132. package/docs/posts/config-v2-lessons-16.md +191 -0
  133. package/docs/posts/improve-loop-autonomous-10.md +225 -0
  134. package/docs/posts/improve-pipeline-debugging-0803.md +179 -0
  135. package/docs/posts/intro-part-01.md +76 -0
  136. package/docs/posts/intro-part-02.md +221 -0
  137. package/docs/posts/intro-part-03.md +190 -0
  138. package/docs/posts/intro-part-04.md +221 -0
  139. package/docs/posts/llm-mode-reflect-13.md +166 -0
  140. package/docs/posts/openpalm-built-on-akm.md +86 -0
  141. package/docs/posts/progressive-disclosure-06.md +129 -0
  142. package/docs/posts/proposal-safety-net-15.md +112 -0
  143. package/docs/posts/release-0.5.0.md +247 -0
  144. package/docs/posts/release-0.6.0.md +117 -0
  145. package/docs/posts/release-0.7.0.md +159 -0
  146. package/docs/posts/release-0.8.0.md +332 -0
  147. package/docs/posts/scattered-skills-05.md +100 -0
  148. package/docs/posts/task-assets-persistent-workflows-11.md +222 -0
  149. package/docs/posts/team-skills-07.md +198 -0
  150. package/docs/posts/wiki-agent-knowledge-08.md +223 -0
  151. package/docs/posts/workflows-vaults-09.md +321 -0
  152. package/docs/product-surface.md +54 -0
  153. package/docs/reference/README.md +1 -2
  154. package/docs/reference/asset-types.md +168 -0
  155. package/docs/reference/cli.md +5 -6
  156. package/docs/reference/env-and-secrets.md +126 -0
  157. package/docs/reference/memory.md +124 -0
  158. package/docs/reference/refs.md +156 -0
  159. package/docs/reference/registry.md +489 -0
  160. package/docs/reference/website-sources.md +141 -0
  161. package/package.json +12 -15
@@ -0,0 +1,645 @@
1
+ # Review #2 — `task-workflow-format-unification.md` (v7) vs. the implementation
2
+
3
+ Reviewer: independent reviewer #2 of 3.
4
+ Repo: `/home/user/akm` @ `claude/akm-markdown-tasks-history-75l6du` (baselined on
5
+ `claude/release-0-9-0-polish-d6sycl`, HEAD `3c5fadb`).
6
+ Scope: the spec in full, plus `src/tasks/`, `src/workflows/{ir,exec,program}`,
7
+ `schemas/*.json`, config engine schema, model aliases, the task adapter, and the
8
+ 0.9.0 migrator.
9
+
10
+ **Overall.** The *direction* is right and several of the spec's diagnoses are
11
+ accurate (the `unit:` bag is awkward; `resolvePromptSource()` sniffing is bad;
12
+ `akm-task.json` really is dead weight; freeze really does resolve by layers).
13
+ But the document repeatedly presents **new mechanism as existing mechanism**.
14
+ The three load-bearing "this already exists" claims — the configuration cascade,
15
+ the single dispatch seam, and "no IR/freeze/journal changes" — do not survive
16
+ contact with the code. `run:` shell units cannot be represented in IR v3 at all;
17
+ the cascade that "already exists" is two layers deep with no persona layer and
18
+ no `defaults.model`; and the `output:` "duplicate" the flatten deletes is two
19
+ semantically distinct schemas that the *sibling spec it cites* explicitly
20
+ distinguishes. The migration section's losslessness claim is false in at least
21
+ four concrete ways.
22
+
23
+ Verdict: **do not sign off decision 14 (or §3.6/§10) as written.** The design is
24
+ salvageable, but §10's non-goals must be withdrawn and the hidden cost
25
+ (IR v4, plan-hash/journal migration, a persona-layer freeze path, a placeholder
26
+ grammar decision, a redaction-policy change) has to be priced in before this is
27
+ scoped as "authoring-surface changes".
28
+
29
+ ---
30
+
31
+ ## Findings table
32
+
33
+ | # | Sev | Title | Key refs |
34
+ |---|---|---|---|
35
+ | C1 | critical | `unit.output` and `step.output` are not duplicates; the flatten collides them | `src/workflows/ir/compile.ts:204,229`; `src/workflows/exec/step-work.ts:560`; `src/workflows/exec/report.ts:1621`; `workflow-format-unification.md` §2.3b |
36
+ | C2 | critical | `run:` shell units are unrepresentable in IR v3 / dispatch; §10 non-goals are false | `src/workflows/exec/unit-dispatch.ts:19`; `src/workflows/ir/schema.ts:29,48,65,171`; `src/workflows/exec/native-executor.ts:638-645`; `src/workflows/ir/freeze.ts:52-62` |
37
+ | C3 | critical | Prose-append rule can change the prompt without changing the input hash | `src/workflows/exec/step-work.ts:376-392,362-368`; spec §3.3 bullet 3 |
38
+ | C4 | critical | `params` as a cascade value field collides with `params:` = param *declarations* | `schemas/akm-workflow.json:22-32`; `src/workflows/ir/params.ts:39-53`; spec §3.4 |
39
+ | M1 | major | The "existing cascade" is 2 layers; `defaults.model` and the persona layer don't exist | `src/core/config/config-schema.ts:102-108`; `src/workflows/ir/freeze.ts:50,163-191`; `schemas/akm-config.json` defaults |
40
+ | M2 | major | The kind-gated `llm` error is already dead code in workflows (silent drop, not error) | `src/workflows/ir/freeze.ts:67-73`; `src/tasks/runner.ts:497-502` |
41
+ | M3 | major | Model aliases are not portable to LLM endpoints — §3.5's portability claim is wrong | `src/integrations/agent/model-aliases.ts:45-76,107-109` |
42
+ | M4 | major | `agent:` as a cascade selector needs systemPrompt + toolPolicy in the frozen plan, incl. the provenance ceiling | `src/output/renderers.ts:250-262`; `src/workflows/exec/native-executor.ts:1026`; `src/workflows/exec/unit-dispatch.ts:16` |
43
+ | M5 | major | Command-template filling: four placeholder conventions, one existing bug, silent behavior change | `src/output/renderers.ts:164-186`; `src/commands/agent/agent-dispatch.ts:63-72,125-133` |
44
+ | M6 | major | `uses: scripts/<name>` is net-new remote-code-execution surface, not plumbing reuse | `src/output/renderers.ts:118-142`; `src/core/activation-policy.ts` |
45
+ | M7 | major | Literal `env:` values will be over-redacted through the existing seam | `src/workflows/exec/native-executor.ts:1163-1193`; `src/core/redaction.ts:193-202` |
46
+ | M8 | major | `env:` list shape is an IR + input-hash + per-unit-env change | `src/workflows/ir/compile.ts:46`; `src/workflows/exec/step-work.ts:353-357,387`; `src/workflows/exec/native-executor.ts:449-457` |
47
+ | M9 | major | Markdown-task recognition collides with the `.md`-claiming workflow adapter; "DO NOT modify" goldens | `src/core/adapter/adapters/index.ts:73-90`; `akm-workflow-adapter.ts:60-76`; `akm-task-adapter.ts:24-26` |
48
+ | M10 | major | Migration is neither a byte-rewrite nor lossless; stray `.yml` becomes silently invisible | `scripts/akm-migrate/migrate/legacy/task-target-ref-migration.ts:44-66`; `src/core/asset/asset-placement.ts:159-170`; `src/tasks/parser.ts:331-338` |
49
+ | M11 | major | `defaults.on_error` is deleted by "graph keys are steps-only" with no migration row | `src/workflows/parser.ts:104`; `src/workflows/ir/compile.ts:231` |
50
+ | m1 | minor | "Trigger keys consumed only by `task sync`" — the runner gates on `enabled` at fire time | `src/tasks/runner.ts:169`; `src/core/activation-policy.ts` rule 3 |
51
+ | m2 | minor | §3.5's `akm agent` embodiment sentence is backwards | `src/commands/agent/contribute-cli.ts:87-88` |
52
+ | m3 | minor | "The `.yml` task path drops the model preference" understates: frontmatter is injected into the prompt | `src/tasks/runner.ts:582-593` |
53
+ | m4 | minor | "The engine node keeps its strict schema" — both engine schemas are `.passthrough()` | `src/core/config/schema/engines.ts:54,82,104` |
54
+ | m5 | minor | `status: draft` overloads OKF lifecycle with scheduler activation; a second unordered gate | `schemas/akm-asset-envelope.json` `status`; spec §4 |
55
+ | m6 | minor | `cwd:` × `isolation: worktree` interaction unspecified | `src/workflows/exec/native-executor.ts:1063-1075` |
56
+ | m7 | minor | `uses:` table covers 5 of ~15 asset types; the rest are unspecified | `src/core/asset/asset-placement.ts:89-178` |
57
+ | m8 | minor | Task id / placement inconsistency the plan inherits (subdir tasks index but cannot run) | `src/tasks/task-id.ts:7`; `src/core/asset/asset-placement.ts:162-169` |
58
+ | m9 | minor | Embedded-template plumbing is `.yml`+command-only and must change too | `src/tasks/embedded.ts:82,97` |
59
+ | ✓ | — | Claims that *do* check out (see §"Verified claims") | — |
60
+
61
+ ---
62
+
63
+ ## Critical
64
+
65
+ ### C1. `unit.output` and `step.output` are two different schemas; the flatten collides them
66
+
67
+ Spec §3.6: *"`output:` is declared **both** on the step and inside the bag — a
68
+ live wart in `schemas/akm-workflow.json`"*, and *"The duplicate `output`
69
+ declaration disappears with the bag."* §6 repeats: *"separate `unit` defs with a
70
+ duplicated `output`"*.
71
+
72
+ They are not duplicates.
73
+
74
+ - `src/workflows/ir/compile.ts:229` — `unit.output` → `WorkflowUnitDraft.schema`.
75
+ - `src/workflows/ir/compile.ts:204` — `step.output` → `WorkflowStepDraft.outputSchema`.
76
+ - `schema` validates **one unit's structured result**, per dispatch attempt:
77
+ `src/workflows/exec/report.ts:1621` (`Unit "…" result failed validation against
78
+ its declared output schema`), threaded to harness native structured output at
79
+ `native-executor.ts:1038,1092`.
80
+ - `outputSchema` validates the **promoted step artifact** after reduction:
81
+ `src/workflows/exec/step-work.ts:558-561`, `native-executor.ts:55`,
82
+ `ir/schema.ts:217`.
83
+
84
+ And the spec this one claims consistency with says so in as many words —
85
+ `docs/architecture/specs/workflow-format-unification.md` §2.3b: *"For a map step,
86
+ `output:` validates the **promoted artifact**, i.e. what the reducer produced —
87
+ not one unit's result."*
88
+
89
+ Consequence: on a `map:` step, `map.unit.output` (per-item schema, which drives
90
+ each harness's native structured-output mode) and `step.output` (the collected
91
+ array schema) are **necessarily different documents**. Deleting the bag deletes
92
+ the ability to type a map step's per-item result at all — silently, since both
93
+ spellings are `output:`. Existing workflows that set both will migrate to a
94
+ conflict with no defined winner.
95
+
96
+ Recommendation: keep two spellings on the flattened step (e.g. `result:` for the
97
+ per-unit schema and `output:` for the step artifact), or explicitly declare
98
+ per-item schemas removed and accept losing harness-native structured output on
99
+ map steps. Either way, remove the "duplicate" framing from §3.6/§6 — it is the
100
+ justification for the change and it is wrong.
101
+
102
+ ### C2. `run:` shell units cannot be represented in IR v3; §10's non-goals are false
103
+
104
+ Spec §3.6: *"A shell unit inside a workflow journals like any unit
105
+ (`workflow_run_units`): no tokens, but status, timing, attempts, and
106
+ `failure_reason` behave normally."* §2: *"`unit-dispatch.ts` is the one dispatch
107
+ seam."* §10: *"no changes to … IR, freeze, journal, leases, or replay."*
108
+
109
+ The code:
110
+
111
+ - `src/workflows/exec/unit-dispatch.ts:19` — `UnitDispatchRequest.engine:
112
+ FrozenEngineSnapshot` is **required**, as is `invocation: IrInvocation`.
113
+ - `src/workflows/ir/schema.ts:48,65,76` — `FrozenEngineSnapshot` is a closed
114
+ two-member union (`llm` | `agent`), validated by `decodeWorkflowPlanV3`
115
+ (`ir/schema.ts:171` — `irVersion must be 3`; `:341-374` field allowlists;
116
+ `:517` `assertUnitEngineCompatibility`).
117
+ - `src/workflows/exec/native-executor.ts:638-645` — a unit with no frozen engine
118
+ snapshot hard-fails `dispatch_error` before dispatch.
119
+ - `src/workflows/ir/freeze.ts:49-62` — **every** unit runs `freezeInvocation`,
120
+ which throws `ConfigError("No workflow engine is selected…")` when no engine
121
+ resolves. A workflow made entirely of `run:` steps therefore cannot even be
122
+ frozen on a machine with no agent CLI configured — the single most obvious
123
+ use case for inline shell.
124
+
125
+ Adding a shell unit kind requires: a third `FrozenEngineSnapshot` variant or a
126
+ new IR node kind, `decodeWorkflowPlanV3` changes, an `irVersion` bump (persisted
127
+ `plan_json`/`plan_hash` on run rows), a `hashVersion` bump in `step-work.ts:379`,
128
+ and a `frozenUnitRunner` branch. That is an IR + freeze + journal + replay
129
+ change, i.e. the exact four things §10 says are untouched. Nothing in the
130
+ codebase currently executes a shell string at all — `runCommandTask` spawns
131
+ pre-split argv with no shell (`src/tasks/runner.ts:277-289`,
132
+ `src/tasks/parser.ts:300-306`), which the spec itself notes in §6.
133
+
134
+ Recommendation: withdraw the §10 non-goal, state IR v4 explicitly, and decide
135
+ whether in-flight v3 runs are drained or migrated. Also state what
136
+ `freezeInvocation` does for a shell unit (it must be skipped, not defaulted —
137
+ otherwise every shell-only workflow requires an engine).
138
+
139
+ ### C3. The prose-append rule can change the prompt without changing the input hash
140
+
141
+ Spec §3.3: *"the assembled prompt is a frozen string produced at one seam, so
142
+ appending is a deterministic concat — easy"*, and the appended section is
143
+ *"appended to the assembled prompt after a blank line, byte-exact."*
144
+
145
+ There are two seams, and the spec names the wrong one. `step-work.ts:322-332`
146
+ assembles the prompt via `buildUnitPrompt`; `step-work.ts:376-392` computes the
147
+ `inputHash` over **`template.instructions`** — the frozen template bytes — *not*
148
+ over the assembled prompt. The comment at `:342-350` states this deliberately.
149
+ The codebase has already been bitten by exactly this: `gateFeedback` had to be
150
+ added to the hash preimage (`:362-368`) because *"omitting it made loop 1 and
151
+ loop 2 journal identical hashes for different prompts, breaking the 'changed
152
+ inputs ⇒ changed hash' audit contract."*
153
+
154
+ If the `## <step-id>` section of a `uses: tasks/<id>` step is appended at
155
+ prompt-assembly time, two materially different prompts hash identically →
156
+ `classifyUnitReuse` (`native-executor.ts:690`) reuses a completed row for a
157
+ different ask, and editing only the step's section produces no re-dispatch.
158
+
159
+ Recommendation: the append must happen at **compile** time, folded into
160
+ `WorkflowUnitDraft.instructions` before freeze, so it is inside the hash
161
+ preimage by construction. Say so in §3.3. (This also makes the "byte-exact" and
162
+ "blank line" rule a compile-time, testable property.)
163
+
164
+ Secondary: the rule is conditioned on *"when the resolved work is
165
+ agent-dispatched"* — but for a `uses: tasks/<id>` step, whether the task
166
+ resolves to agent work or shell work is known only after resolving the task.
167
+ The same authored step therefore silently changes whether its prose is
168
+ load-bearing or documentation depending on the referenced task's target — and a
169
+ later edit to the *task* (prose → `run:`) silently demotes the *workflow's*
170
+ prose to a comment. That is a footgun worth an explicit lint.
171
+
172
+ ### C4. `params` as a cascade value field collides with `params:` = declarations
173
+
174
+ §3.4 lists `params` in the flat value vocabulary, *"legal at every layer, merged
175
+ per-field with the nearest layer winning"*, and §3.3 says a referenced task is
176
+ *"one more layer"*.
177
+
178
+ But at the workflow-document layer, `params:` already means something else
179
+ entirely: the **declaration block**, name → JSON Schema
180
+ (`schemas/akm-workflow.json:22-32`; `compile.ts:132-139` → `plan.paramSchemas`).
181
+ A "document defaults" layer contributing `params` *values* into the same
182
+ frontmatter namespace is not expressible.
183
+
184
+ Worse, cascading param *values* down from config/engine/persona layers breaks
185
+ the declared-flag contract the spec cites in §2: `materializeWorkflowParameterFlags`
186
+ (`ir/params.ts:39-53`) rejects any name not in `plan.params`, and
187
+ `assertRunParamsSatisfyPlan` (`run-workflow.ts:531`) validates the journaled set.
188
+ A global `params` default would inject undeclared names into every run.
189
+
190
+ Also unspecified: `params` means three different things across §3.2 —
191
+ workflow param flags, command-template placeholder fills, and (per §3.4) a
192
+ cascading value field. Merging them per-field across five layers has no
193
+ coherent semantics (e.g. a persona node's `params.scope` leaking into an
194
+ unrelated workflow's param set).
195
+
196
+ Recommendation: remove `params` from the cascade vocabulary. Make it a
197
+ target-local input (`uses:`-adjacent only), and keep the document-level
198
+ `params:` declaration block as-is.
199
+
200
+ ---
201
+
202
+ ## Major
203
+
204
+ ### M1. The "cascade that already exists" is two layers, with no persona layer and no `defaults.model`
205
+
206
+ §2 and §3.4 lean hard on *"Freeze already implements a configuration cascade"*
207
+ and *"exposing this existing mechanism"*. What exists:
208
+
209
+ - `freeze.ts:50` — `layers = [documentDefaults?, unit?]`. **Two** layers, both
210
+ from the workflow document. Verified.
211
+ - `freeze.ts:151-155` `selectedEngine`, `:163-191` `exactModel`,
212
+ `:193-209` `effectiveTimeout`, `:211-216` `mergedLlmOverrides` — nearest-wins /
213
+ deep-merge. Verified.
214
+ - The **engine node is not a layer**: `engine.model` is consulted only as a
215
+ fallback *after* the layers produce nothing (`freeze.ts:171`), and
216
+ `engine.timeoutMs` likewise (`:197`). Same relative order the spec wants, but
217
+ it is a two-branch fallback chain, not a layer list — generalizing it to five
218
+ layers is a rewrite of these three functions, not an exposure.
219
+ - **There is no persona layer at all.** Nothing in `freeze.ts` loads an agent
220
+ asset. This is the largest new piece of work in the whole proposal (see M4)
221
+ and §3.4 describes it as already-mechanism.
222
+ - **`defaults.model` does not exist.** `src/core/config/config-schema.ts:102-108`:
223
+ `DefaultsSchema = { engine?, llmEngine?, improveStrategy? }.passthrough()`.
224
+ `schemas/akm-config.json` `properties.defaults` has the same three keys with
225
+ `additionalProperties: true`. So the spec's §3.4 worked example
226
+ (`"defaults": { "engine": "claude", "model": "sonnet" }`) is a config a user can
227
+ write **today**, that validates, and that is **silently ignored**. That is a
228
+ live drift bug the plan should fix explicitly, not a documented layer.
229
+
230
+ Recommendation: rewrite §2's fifth bullet and §3.4's "the mechanism already
231
+ exists" framing to name what is new (persona layer, config `defaults.model`,
232
+ `env`/`cwd`/`shell` layering, per-field merge across five layers) versus what is
233
+ reused (nearest-wins over two workflow-document layers). Otherwise the estimate
234
+ is wrong by a large factor.
235
+
236
+ ### M2. The kind-gated `llm` hard error is already dead code in the workflow path
237
+
238
+ §3.4: *"`llm:` … was a grouping whose only job was to scope a kind-gated hard
239
+ error"*; §6: *"a kind-gated hard error"* (singular).
240
+
241
+ There are two behaviors, and one is a bug:
242
+
243
+ ```ts
244
+ // src/workflows/ir/freeze.ts:67-73
245
+ const llm = engine.kind === "llm" ? mergedLlmOverrides(layers) : undefined;
246
+ if (engine.kind !== "llm" && llm !== undefined) { throw new ConfigError(...) }
247
+ ```
248
+
249
+ The guard is **unreachable** — `llm` is `undefined` whenever `kind !== "llm"`.
250
+ So a workflow that sets `defaults.llm.temperature` under an agent engine today
251
+ gets its overrides **silently discarded**, with no error and no warning. The
252
+ live hard error exists only on the task path
253
+ (`src/tasks/runner.ts:497-502`, `NotFoundError` — also the wrong error class for
254
+ a config problem).
255
+
256
+ This matters for the proposal in two ways: (a) the "convert the error to a
257
+ notice" work item is really "fix a silent-drop bug *and* convert a task-path
258
+ error", and (b) §3.4's argument that *"a global default `temperature` must not
259
+ brick every agent-engine dispatch"* is already true of workflows — the
260
+ motivating hazard is one path only.
261
+
262
+ Recommendation: fix `freeze.ts:67-73` independently (it is a bug on the current
263
+ branch), and correct §3.4/§6 to describe two divergent behaviors.
264
+
265
+ ### M3. Model aliases are not portable to LLM endpoints
266
+
267
+ §3.5: *"Aliases are **per-platform**, which is what makes a shared asset
268
+ portable: `model: sonnet` resolves to the right string under a claude engine, an
269
+ opencode engine, **or an LLM endpoint**."*
270
+
271
+ `src/integrations/agent/model-aliases.ts:45-76` — the built-in table has exactly
272
+ two platform columns: `claude` and `opencode`. `resolveLlmModel` (`:107-109`)
273
+ calls `resolveModel(model, engineName, undefined, global, ["llm"])`, i.e. it
274
+ passes the **engine name** where `platform` is expected, then falls back to a
275
+ `"llm"` column and `"*"`. A user-supplied `modelAliases` table can supply those
276
+ columns; the **built-ins cannot**. So under an LLM engine, `model: sonnet`
277
+ returns `"sonnet"` verbatim (`:103` `entry?.platforms[platform] ?? model`) and is
278
+ sent as the model id to an OpenAI-compatible endpoint.
279
+
280
+ Since §3.4/§3.5 make aliases legal at *every* layer (including a shared persona
281
+ node in a third-party bundle), this failure now occurs at more layers, not
282
+ fewer, and only at dispatch.
283
+
284
+ Recommendation: either add an `llm`/`*` column to `BUILTIN_ALIASES`, or drop the
285
+ portability claim and add a freeze-time notice when an alias fails to resolve
286
+ against the selected engine.
287
+
288
+ ### M4. The persona layer is not "a node whose fields join the cascade" — it carries a system prompt and a gated tool policy
289
+
290
+ §3.4 describes `agent:` as picking *"the persona node — who runs it (system
291
+ prompt, tool policy, model preference)"* and then only ever discusses `model`.
292
+ The other two are the hard part:
293
+
294
+ - Today an agent asset yields `{ prompt (system), modelHint, toolPolicy }`
295
+ (`src/output/renderers.ts:241-262`), consumed only by `akm agent`
296
+ (`contribute-cli.ts:72-90` → `AgentDispatchRequest`).
297
+ - The workflow path deliberately does **not** carry them:
298
+ `native-executor.ts:1026` — *"`systemPrompt`/`tools`/`cwd` come from the
299
+ profile/asset, not the workflow unit"*; `unit-dispatch.ts:16` documents
300
+ `systemPrompt` as *"used by frozen workflow gate judges"* only;
301
+ `buildAgentDispatchRequest` (`:1029-1039`) passes only prompt/model/schema.
302
+ `FrozenEngineSnapshot` has no tool-policy field (`ir/schema.ts:60-75`).
303
+ - **Security-relevant:** the agent asset's `tools:` is *self-declared
304
+ frontmatter*, and there is an explicit **provenance ceiling** applied at the
305
+ show layer that decides whether to honour it based on writable-own-stash vs
306
+ read-only third-party source (`renderers.ts:257-260`, tagged "07 P1-D").
307
+ Freeze has no notion of source provenance. Making `agent:` a cascade selector
308
+ resolved at freeze silently relocates that ceiling — or drops it.
309
+
310
+ Combined with §3.4's *"This widens agent assets from today's model-hint-only
311
+ frontmatter to the full value vocabulary"*, a third-party bundle's agent asset
312
+ would now be able to set `env`, `cwd`, `timeout` and a tool policy for a
313
+ workflow that merely names it. §10 says *"no multi-file config merging"* while
314
+ §3.4 makes bundle assets configuration layers; that tension should be resolved
315
+ explicitly with a provenance rule.
316
+
317
+ Recommendation: state that the persona layer contributes `systemPrompt` and
318
+ `toolPolicy` into the frozen plan (IR change), and carry the existing provenance
319
+ ceiling into freeze with a named test.
320
+
321
+ ### M5. Command-template filling: four conventions, one live bug, and a silent behavior change
322
+
323
+ §3.2: *"`uses: commands/<name>` … `params:` fill the placeholders"*, *"a
324
+ placeholder with no matching param is an error at fill time"*; §6: *"one
325
+ implementation, both surfaces"* (from "n/a").
326
+
327
+ It is not "n/a" today, and the existing surface is inconsistent:
328
+
329
+ - `src/output/renderers.ts:164-186` (`extractParameters`) advertises **three**
330
+ conventions to consumers: `$ARGUMENTS`, `$1`…`$9`, and `{{name}}`.
331
+ - `src/commands/agent/agent-dispatch.ts:63-72` (`fillPlaceholders`) substitutes
332
+ **only** `{{<digits>}}`, **positionally** from `--args`, and leaves unmatched
333
+ placeholders verbatim (`:70`).
334
+
335
+ So a command asset declaring `{{scope}}` is reported as having a `scope`
336
+ parameter and can never be filled — an existing bug the plan inherits. And the
337
+ spec's rule changes behavior on two axes at once: positional → named, and
338
+ lenient → hard error. `$ARGUMENTS`-style command assets imported from Claude
339
+ Code (which the `agent:` frontmatter support at `renderers.ts:234` implies akm
340
+ already ingests) would become **errors** under "a placeholder with no matching
341
+ param is an error at fill time".
342
+
343
+ Recommendation: pick one grammar in the spec, state what happens to the other
344
+ three, and state the inverse rule (extra `params:` with no placeholder — error
345
+ or notice?). Treat this as a real work item, not a table row.
346
+
347
+ ### M6. `uses: scripts/<name>` is new execution surface, not consolidation
348
+
349
+ §3.2: *"Execute the script asset per its own `run`/`setup`/`cwd` metadata — no
350
+ AI."* §6 files it under shared plumbing.
351
+
352
+ Today `run`/`setup`/`cwd` on a script asset are **display hints only**:
353
+ `resolveExecHints` (`src/output/renderers.ts:118-142`) merges indexed metadata,
354
+ `@run`/`@setup`/`@cwd` header comments, and — critically — **auto-detection from
355
+ extension + dependency files** (`detectExecHints`). Nothing executes them.
356
+
357
+ Making them executable means: a scheduled task or workflow step naming a script
358
+ from an *installed third-party bundle* runs (a) an auto-detected interpreter
359
+ line and (b) a `setup` command (typically a package install) at cron time.
360
+ `src/core/activation-policy.ts` enumerates four activation rules — dangerous env
361
+ keys, install-time key scan, task activation, write activation — **none** covers
362
+ executing a bundle-supplied script. The spec's §10 "no change to how script
363
+ assets are authored or stored — this proposal executes and embodies them" waves
364
+ past exactly the interesting part.
365
+
366
+ Recommendation: add an activation rule (fifth) for third-party script execution,
367
+ decide whether `setup` runs at all (I'd say no — it is an install-time concern),
368
+ and forbid auto-detected `run` for non-primary-bundle scripts.
369
+
370
+ ### M7. Literal `env:` values will be over-redacted
371
+
372
+ §3.7: *"Values sourced from `env/` and `secrets/` assets join the dispatch's
373
+ `sensitiveValues`"* — implying literals do not.
374
+
375
+ `collectWorkflowDispatchSensitiveValues` (`native-executor.ts:1163-1185`) starts
376
+ from `new Set(Object.values(env ?? {}))` — **every** resolved env value, no
377
+ provenance distinction. `collectSensitiveValues` (`src/core/redaction.ts:193-202`)
378
+ applies no length, entropy, or dictionary floor.
379
+
380
+ So `env: [{ LOG_LEVEL: debug }]` (the spec's own example, §3.7) adds the string
381
+ `"debug"` to `sensitiveValues`, and every occurrence of `debug` in the unit's
382
+ output, journaled result, and error text is replaced. Worse,
383
+ `redactUnitOutcome` (`:1187-1193`) collapses any redacted `failureReason` to
384
+ `"reported_failure"` — so a common literal value can silently destroy the
385
+ failure taxonomy `retry.on` keys off.
386
+
387
+ Recommendation: tag env entries with provenance at resolution
388
+ (`ref` vs `literal`) and feed only ref-sourced values to `sensitiveValues`; or
389
+ add a minimum-length/entropy floor. Say which in §3.7.
390
+
391
+ ### M8. The `env:` list shape is an IR change, a hash-version change, and a per-unit-env change
392
+
393
+ §3.7: *"This **extends the existing schema additively** — the workflow
394
+ `unit.env` is already a list of ref strings, so every currently-valid value stays
395
+ valid."* §10: no IR/journal changes.
396
+
397
+ - `compile.ts:46` / `ir/schema.ts` — `env?: string[]`. A list of
398
+ `string | Record<string,string>` is a **type change** through
399
+ `WorkflowUnitDraft` → `IrUnitNode` → `decodeWorkflowPlanV3` validation →
400
+ `plan_json`. Existing values stay *authorable*; the IR does not stay the same.
401
+ - `step-work.ts:353-357,387` — the input-hash preimage includes `env` and the
402
+ comment is explicit: *"`env` carries NAMES ONLY, never resolved values:
403
+ hashing a resolved secret would leak it into a durable hash oracle"*. A list
404
+ containing literal mappings puts literal values into the preimage (fine) but
405
+ mixes them with names, and `secrets/<name>` ref-valued entries must be hashed
406
+ as refs, not values. Either way `hashVersion: 4` (`:379`) has to become 5,
407
+ which invalidates every journaled `input_hash` and every replay golden.
408
+ - **Per-unit env is not supported.** `native-executor.ts:449-457` resolves env
409
+ **once per step**, before any dispatch, from `template.env`, and passes one
410
+ `Record<string,string>` to every unit (`RunUnitInput.env`, `:611`). §3.6's
411
+ `AKM_ITEM` / `AKM_ITEM_INDEX` for map+shell steps requires per-unit env
412
+ assembly, which also moves the `sensitiveValues` computation and the
413
+ "one binding error fails the whole step cleanly" property (`:446-448`).
414
+ - The cascade also requires env at layers that have no env today:
415
+ `ProgramDefaults` (`program/schema.ts:203-211`) has no `env`, and
416
+ `DEFAULTS_KEYS` (`parser.ts:104`) does not list it.
417
+
418
+ Recommendation: drop "additively" from §3.7, name the hash-version bump, and
419
+ specify per-unit env for map fan-out.
420
+
421
+ ### M9. Markdown-task recognition collides with the workflow adapter; the task goldens are declared immutable
422
+
423
+ §6: *"Adapter | `.yml`-only recognize/place | markdown task recognized by
424
+ `type: task` / `tasks/` residence"*.
425
+
426
+ - `src/core/adapter/adapters/index.ts:73-90` orders `akmWorkflowAdapter`
427
+ **before** `akmTaskAdapter`.
428
+ - `akm-workflow-adapter.ts:60-76` — *"any `.md` file in this bundle IS a workflow
429
+ … UNLESS its frontmatter declares a DIFFERENT non-empty `type:`"*.
430
+
431
+ So `type: task` is **mandatory** in practice for a markdown task in a
432
+ workflow-adapter bundle; the "or `tasks/` residence" alternative in §6 and the
433
+ schedule-only examples pattern would be claimed as workflows. Note also that
434
+ `recognize()` receives a component-relative path, and `akmTaskAdapter.directoryList()`
435
+ returns `["."]` (tasks live anywhere) — residence-based recognition is not
436
+ available in this adapter's contract as written.
437
+
438
+ Separately, `akm-task-adapter.ts:24-26` declares its fixture + four goldens
439
+ (`tests/fixtures/format-family-goldens/akm-task/{recognition,placement,lint,renderer}.json`)
440
+ a *"Conformance oracle (authored, DO NOT modify)"*. §3.6's "schema and golden
441
+ churn on a pre-release format — approved" does not acknowledge that these
442
+ specific goldens are marked immutable, nor `looksLikeRoot`'s `.yml`+`schedule`
443
+ probe (`:136-155`), which would need a disjoint `.md` probe that does not fire
444
+ on workflow bundles.
445
+
446
+ Recommendation: state that `type: task` is required (drop the residence
447
+ alternative), and get explicit sign-off to re-author the akm-task conformance
448
+ oracle.
449
+
450
+ ### M10. Migration is not a byte-rewrite, is not lossless, and re-creates the invisible-file failure
451
+
452
+ §8: *"Task conversion joins it as one more content step"*, *"No key survives with
453
+ a changed meaning"*, and *"The 0.7→0.8 lesson — leftover files must never be
454
+ **silently invisible** — is honored structurally."*
455
+
456
+ Four problems.
457
+
458
+ 1. **Shape.** The existing task migration phase
459
+ (`scripts/akm-migrate/migrate/legacy/task-target-ref-migration.ts`) rewrites
460
+ *YAML scalar bytes in place*: `TaskTargetRefRewrite = { filePath, from, to,
461
+ before, after, mode }` (`:44-52`) with an atomic same-path write. There is no
462
+ rename in the plan structure, no journal phase for a rename, and no
463
+ crash-resume story for a half-renamed set. `.yml → .md` plus a full
464
+ frontmatter reshape plus body synthesis is a new migrator, not a new step.
465
+ 2. **Read-only bundles.** `:53-66` — `readOnlyLegacyTasks` exists precisely
466
+ because *"The migration NEVER rewrites a read-only bundle … but the 0.9
467
+ runtime removed the v1 parser, so these tasks will fail after upgrade."*
468
+ Under this proposal every `.yml` task in every registry-installed bundle
469
+ becomes permanently unrunnable and unconvertible. "No key survives with a
470
+ changed meaning" is true; "lossless" is not.
471
+ 3. **Invisible files.** `src/core/asset/asset-placement.ts:159-170` makes `task`
472
+ `.yml`-only for `isRelevantFile` / `toCanonicalName` / `toAssetPath`.
473
+ Flipping it to `.md` makes stray `.yml` task files invisible to
474
+ `resolveAssetPath` — which is what `akm task run` uses
475
+ (`src/tasks/runner.ts:159`). The 0.7→0.8 lesson is **not** honored
476
+ "structurally"; it requires *new* code that deliberately scans for a
477
+ no-longer-relevant extension in `task sync` / `task doctor`. That is a work
478
+ item, not a property.
479
+ (`src/tasks/task-id.ts:8,15,41-46` also hardcodes `.yml`/`.yaml`, including
480
+ in the portable-length budget.)
481
+ 4. **Dropped fields.** The §8 table's `llm.*` row ends in an ellipsis. Three
482
+ `llm` sub-fields have **no home** in §3.4's nine value fields:
483
+ `supportsJsonSchema`, `contextLength`, `enableThinking`
484
+ (`src/tasks/parser.ts:331-338`; `src/tasks/schema.ts:61-68`;
485
+ `schemas/akm-task.json:46-56`; identical set on the workflow side,
486
+ `schemas/akm-workflow.json:76-87`, and all six are carried into
487
+ `FrozenLlmEngine`, `freeze.ts:231-236`). Either the vocabulary grows to
488
+ twelve fields or the migration is lossy — the spec should say which.
489
+
490
+ ### M11. `defaults.on_error` is silently deleted
491
+
492
+ §3.4: *"**graph keys** (`map`, `route`, `inputs`, `output`, `gate`, `retry`,
493
+ `on_error`, `isolation`) are steps-only"*.
494
+
495
+ `on_error` is legal at document level today: `src/workflows/parser.ts:104`
496
+ (`DEFAULTS_KEYS = ["engine","model","timeout","on_error","llm"]`),
497
+ `schemas/akm-workflow.json:200`, `program/schema.ts:210`, and it is *read*:
498
+ `compile.ts:231` — `unit?.onError ?? defaults?.onError ?? "fail"`. Making
499
+ `on_error` steps-only removes a working document-level default, and §8's
500
+ migration table has no row for it.
501
+
502
+ Recommendation: either keep `on_error` (and `retry`, arguably) as a document
503
+ default, or add a migration row that lifts `defaults.on_error` onto every step.
504
+
505
+ ---
506
+
507
+ ## Minor
508
+
509
+ **m1 — "Trigger keys are consumed *only* by `akm task sync`" (§3.3, decision 10)
510
+ is inaccurate.** `enabled` is also read at fire time by the runner:
511
+ `src/tasks/runner.ts:169` → `shouldSkipUnactivatedTask({ enabled, scheduled })`,
512
+ which is activation-policy rule 3 (`src/core/activation-policy.ts`, doc block
513
+ rule 3: *"Task activation (`tasks/runner.ts`)"*). This is a defense-in-depth
514
+ property worth preserving; the spec's phrasing invites removing it.
515
+
516
+ **m2 — §3.5's embodiment sentence is backwards.** *"`akm agent` embodiment
517
+ honors it under an explicit `--model` override"* — `contribute-cli.ts:87-88`:
518
+ `// --model flag wins over the asset's modelHint`. The asset's model is honored
519
+ in the *absence* of `--model`. The design conclusion drawn from it still stands,
520
+ but the sentence should be fixed.
521
+
522
+ **m3 — §3.5 understates the `.yml` prompt-asset bug.** *"`prompt: agents/x`
523
+ resolves the asset to prompt text only"*. `runner.ts:582-593` does
524
+ `fs.readFileSync(assetPath, "utf8")` with **no frontmatter stripping**, so the
525
+ YAML frontmatter (`---\nmodel: opus\ntools: …\n---`) is prepended to the prompt
526
+ sent to the agent. Same for `kind: "file"` (`:576-579`). This is a live bug on
527
+ the current branch, worth fixing independently of the proposal.
528
+
529
+ **m4 — "the engine node … keeps its strict schema" overstates.** Both
530
+ `LlmEngineSchema` and `AgentEngineSchema` are `.passthrough()`
531
+ (`src/core/config/schema/engines.ts:54,82,104`) with a *blacklist* superRefine
532
+ (`:83-88`, `:105-131`). `model` legal on both, LLM fields rejected on agent
533
+ kind — that part of §3.4 is verified. But the new value fields `cwd`, `shell`,
534
+ `env`, `params` are in neither list, so an engine node would silently accept and
535
+ silently ignore them. "Strict" needs to become strict (closed schema) if the
536
+ spec's asymmetry argument is to hold.
537
+
538
+ **m5 — `status: draft` overloads the OKF lifecycle family.**
539
+ `schemas/akm-asset-envelope.json` defines `status: ["draft","stable","deprecated"]`
540
+ as *"OKF v0.2 lifecycle status"*, shared across all asset types and stamped by
541
+ `okf-support.md`'s enhancement contract. §4 makes it load-bearing for scheduler
542
+ installation (*"draft tasks are never installed by `sync`"*), creating a second
543
+ activation gate alongside `enabled:` with no stated precedence, and leaving
544
+ `status: deprecated` undefined. Recommend: keep activation on `enabled:` only,
545
+ or define the truth table.
546
+
547
+ **m6 — `cwd:` × `isolation: worktree`.** `request.cwd` is currently set *only*
548
+ by worktree isolation, and `defaultUnitDispatcher` rejects `cwd` on the llm
549
+ runner (`native-executor.ts:1063-1075`). A user-set `cwd:` on an isolated step,
550
+ or on an llm-engine step, has no defined behavior in the spec.
551
+
552
+ **m7 — `uses:` coverage.** §3.2 defines 5 of ~15 placement types
553
+ (`asset-placement.ts:89-178`: skill, command, agent, script, memory, env,
554
+ secret, lesson, task, session, fact, workflow, knowledge…). `uses: lessons/x`,
555
+ `uses: skills/x`, `uses: env/x` need a stated rule (error with a hint, or
556
+ "anything not listed is an error").
557
+
558
+ **m8 — task id vs placement inconsistency inherited.**
559
+ `asset-placement.ts:162-169` derives a task's canonical name from the *relative
560
+ path*, so `tasks/sub/x.yml` indexes as `sub/x`; `validateTaskId`
561
+ (`task-id.ts:7`, `/^[A-Za-z0-9][A-Za-z0-9._-]*$/`) rejects `/`, so
562
+ `akm task run sub/x` throws. §4's "an ordinary akm markdown asset at
563
+ `<bundle>/tasks/<id>.md`" plus the adapter's `directoryList(): ["."]`
564
+ perpetuates it. Worth resolving in the same change.
565
+
566
+ **m9 — embedded templates.** `src/tasks/embedded.ts:82` filters `.yml` and
567
+ `:97` drops any task whose target is not `command` — so the setup wizard's whole
568
+ enumeration path changes with the format (and `EmbeddedTask.command: string`
569
+ becomes `run:`). Confirmed 10 templates (`src/assets/tasks/core/` ×5,
570
+ `src/assets/tasks/improve/` ×5), all `command:` targets, so §8's "ten embedded
571
+ templates" is accurate.
572
+
573
+ ---
574
+
575
+ ## Verified claims (the spec gets these right)
576
+
577
+ Recorded so the above is read as targeted, not blanket:
578
+
579
+ - §2 — `akm workflow run` is the canonical orchestrator; no `start`/`next`/`complete`
580
+ subcommands; `brief`/`report` gated on `experimental.workflowEngine`
581
+ (`src/commands/workflow-cli.ts:160,324,351,522-530`;
582
+ `src/workflows/exec/workflow-engine-gate.ts:39,56`).
583
+ - §2 — scheduled workflow tasks run end-to-end (`runner.ts:199-208,352-416`
584
+ → `runWorkflowSteps`).
585
+ - §2 — `UnitDispatchRequest` carries prompt / frozen engine / `timeoutMs` / `env`
586
+ / `cwd` / `sensitiveValues`, and results carry `failureReason`
587
+ (`unit-dispatch.ts:9-43`).
588
+ - §2 — freeze assembles `layers = [documentDefaults, unit]`, selects engine by
589
+ nearest layer, resolves model/timeout/llm nearest-wins/deep-merged
590
+ (`freeze.ts:50,151-216`) — accurate as far as it goes (see M1).
591
+ - §3.4 — `temperature` etc. are a config validation error on an agent-kind
592
+ engine, `model` legal on both (`engines.ts:99,105-120`).
593
+ - §3.4 — command assets already read an `agent:` frontmatter field
594
+ (`renderers.ts:234`).
595
+ - §3.5 — `resolveModel` is a four-tier chain (profile aliases → global table with
596
+ `"*"` fallback → built-ins `fable`/`opus`/`sonnet`/`haiku` → verbatim), one
597
+ level, no recursion (`model-aliases.ts:45-104`).
598
+ - §3.6 — `output:` is declared on both the step and the `unit` bag in
599
+ `schemas/akm-workflow.json` (`:234`, `:351`). *Present* — but not a duplicate
600
+ (C1).
601
+ - §3.7 — `unit.env` is currently `array of string` (`akm-workflow.json:237-244`).
602
+ - §6 — `parseTaskDocument` + `TASK_KEYS` + `rejectTargetFields` +
603
+ `resolvePromptSource` = exactly 368 lines (`src/tasks/parser.ts`).
604
+ - §6 — `akm-task.json` is 87 lines and referenced only by
605
+ `tests/integration/tasks-schema.test.ts:8` (no runtime loader).
606
+ - §6 — `runCommandTask` runs raw argv only, no shell
607
+ (`runner.ts:277-289`; `parser.ts:300-306`).
608
+ - §6 — lint lives in `akm-task-adapter.ts` as `invalid-task-yaml` (`:88-98`).
609
+ - §8 — task ids and the `akm task run <id>` ABI need not change; the scheduler
610
+ argv shape is `[…akm, --scheduler-context, <path>, task, run, <id>, [--bundle
611
+ <b>], --scheduled]` and depends on the id, not the file format
612
+ (`scheduler-invocation.ts:59-78,198-227`).
613
+ - §8 — 0.9.0 already refuses un-migrated installations at the config boundary
614
+ (`src/core/common.ts:228`).
615
+ - §8 — ten embedded templates.
616
+ - §10 / §3.6 alignment with `workflow-format-unification.md` §2.3's forward
617
+ reference: *"If a non-agent unit kind (raw shell/exec) is ever added, that unit
618
+ kind reintroduces substitution as its own need"* — §3.6's `AKM_ITEM` env
619
+ approach is consistent with that ruling.
620
+
621
+ ---
622
+
623
+ ## Recommended changes before sign-off
624
+
625
+ 1. **Withdraw §10's "no changes to IR, freeze, journal, replay."** Replace with
626
+ an explicit list: IR v4, `hashVersion` 5, a shell node kind or third engine
627
+ snapshot variant, a persona-layer freeze path, plan/journal migration for
628
+ in-flight runs.
629
+ 2. **Fix C1** — keep two output spellings, or state the map-step loss.
630
+ 3. **Fix C3** — move the prose append to compile time, into the hashed
631
+ `instructions`.
632
+ 4. **Remove `params` from the cascade vocabulary (C4).**
633
+ 5. **Rewrite §2 bullet 5 and §3.4's framing (M1)** to distinguish the two-layer
634
+ nearest-wins resolver that exists from the five-layer cascade being proposed,
635
+ and add `defaults.model` as a new config field (it is silently ignored today).
636
+ 6. **Add a provenance rule for bundle-asset configuration layers (M4, M6)** —
637
+ the tool-policy ceiling and script execution both need one.
638
+ 7. **Decide the placeholder grammar (M5)** and the fate of `$ARGUMENTS`.
639
+ 8. **Rewrite §8** — losslessness is false for read-only bundles and for the three
640
+ `llm` sub-fields; the migrator needs rename support; the "silently invisible"
641
+ guarantee is a work item, not a structural property.
642
+ 9. **Independently fix, on the current branch:** the dead `llm` guard
643
+ (`freeze.ts:67-73`), the frontmatter-in-prompt bug (`runner.ts:582-593`), and
644
+ the `{{name}}` vs `{{0}}` placeholder mismatch (`renderers.ts:178` vs
645
+ `agent-dispatch.ts:68`). All three are bugs today and the plan inherits them.