akm-cli 0.9.29 → 0.9.31

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (162) hide show
  1. package/CHANGELOG.md +41 -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/commands/sources/plugin-upgrade.js +46 -23
  6. package/dist/core/file-lock.js +10 -1
  7. package/dist/llm/client.js +33 -3
  8. package/dist/scripts/akm-migrate-node.js +5 -1
  9. package/dist/scripts/akm-migrate.js +5 -1
  10. package/docs/README.md +42 -41
  11. package/docs/agents/README.md +15 -0
  12. package/docs/agents/agent-install.md +14 -0
  13. package/docs/architecture/README.md +67 -0
  14. package/docs/architecture/adapters.md +272 -0
  15. package/docs/architecture/akm-architecture-decision-history.md +2055 -0
  16. package/docs/architecture/akm-core-principles.md +126 -0
  17. package/docs/architecture/architecture.md +579 -0
  18. package/docs/architecture/comparisons/claude-code-vs-akm-workflows-full.md +550 -0
  19. package/docs/architecture/decisions/0001-no-interpolation-attached-structured-context.md +72 -0
  20. package/docs/architecture/decisions/0002-unit-reuse-and-input-hash-scope.md +142 -0
  21. package/docs/architecture/decisions/0003-child-env-allowlist-and-provenance.md +189 -0
  22. package/docs/architecture/decisions/0004-task-input-contract-and-flag-coercion.md +97 -0
  23. package/docs/architecture/decisions/0005-task-result-vocabulary-and-legacy-read-mapping.md +108 -0
  24. package/docs/architecture/decisions/0006-task-source-version-routing.md +124 -0
  25. package/docs/architecture/decisions/0007-workflow-composition-bounds.md +83 -0
  26. package/docs/architecture/decisions/0008-task-binding-normalization.md +88 -0
  27. package/docs/architecture/decisions/0009-child-run-publication-column-parity.md +80 -0
  28. package/docs/architecture/decisions/0010-driver-protocol-cut.md +36 -0
  29. package/docs/architecture/decisions/0011-engine-run-loop-invariants.md +125 -0
  30. package/docs/architecture/decisions/README.md +62 -0
  31. package/docs/architecture/improvement.md +440 -0
  32. package/docs/architecture/internals/classification.md +154 -0
  33. package/docs/architecture/internals/fresh-host-rebuild-runbook.md +40 -0
  34. package/docs/architecture/internals/functional-contract-patterns.md +340 -0
  35. package/docs/architecture/internals/health-advisories.md +110 -0
  36. package/docs/architecture/internals/improve-workflow.md +713 -0
  37. package/docs/architecture/internals/indexing.md +498 -0
  38. package/docs/architecture/internals/registry-network-boundary.md +57 -0
  39. package/docs/architecture/internals/search.md +249 -0
  40. package/docs/architecture/internals/storage-locations.md +943 -0
  41. package/docs/architecture/persisted-data-compat.md +59 -0
  42. package/docs/architecture/reviews/env-secret-access.md +319 -0
  43. package/docs/architecture/runtime-boundary-design.md +153 -0
  44. package/docs/architecture/specs/0.9.0-close-out-plan.md +361 -0
  45. package/docs/architecture/specs/0.9.0-decisions.md +528 -0
  46. package/docs/architecture/specs/0.9.0-docs-code-drift-register.md +745 -0
  47. package/docs/architecture/specs/0.9.0-open-items-register.md +135 -0
  48. package/docs/architecture/specs/0.9.0-public-api-issue-backlog.md +740 -0
  49. package/docs/architecture/specs/0.9.0-release-surface-review.md +636 -0
  50. package/docs/architecture/specs/agent-command-engine-model-design.md +513 -0
  51. package/docs/architecture/specs/akm-0.9.0-bundle-adapter-spec.md +501 -0
  52. package/docs/architecture/specs/akm-0.9.0-ref-grammar-decision.md +291 -0
  53. package/docs/architecture/specs/akm-format-neutral-bundle-workspace-spec.md +2059 -0
  54. package/docs/architecture/specs/di-seams-plan.md +539 -0
  55. package/docs/architecture/specs/driver-protocol-keep-or-cut.md +275 -0
  56. package/docs/architecture/specs/fact-asset-type.md +142 -0
  57. package/docs/architecture/specs/improve-collapse-churn-detector-design.md +634 -0
  58. package/docs/architecture/specs/okf-support.md +148 -0
  59. package/docs/architecture/specs/ref.md +329 -0
  60. package/docs/architecture/specs/reviews/task-workflow-unification/README.md +72 -0
  61. package/docs/architecture/specs/reviews/task-workflow-unification/findings-consolidated.md +152 -0
  62. package/docs/architecture/specs/reviews/task-workflow-unification/library-eval-alternatives.md +352 -0
  63. package/docs/architecture/specs/reviews/task-workflow-unification/library-eval-bunqueue.md +300 -0
  64. package/docs/architecture/specs/reviews/task-workflow-unification/library-eval-openworkflow.md +402 -0
  65. package/docs/architecture/specs/reviews/task-workflow-unification/opus-review-1.md +758 -0
  66. package/docs/architecture/specs/reviews/task-workflow-unification/opus-review-2.md +645 -0
  67. package/docs/architecture/specs/reviews/task-workflow-unification/opus-review-3.md +602 -0
  68. package/docs/architecture/specs/reviews/task-workflow-unification/panel-r1-adversarial.md +27 -0
  69. package/docs/architecture/specs/reviews/task-workflow-unification/panel-r1-architecture.md +27 -0
  70. package/docs/architecture/specs/reviews/task-workflow-unification/panel-r1-coverage.md +74 -0
  71. package/docs/architecture/specs/reviews/task-workflow-unification/panel-r1-security.md +15 -0
  72. package/docs/architecture/specs/reviews/task-workflow-unification/panel-r2-adversarial.md +132 -0
  73. package/docs/architecture/specs/reviews/task-workflow-unification/panel-r2-verification.md +147 -0
  74. package/docs/architecture/specs/stash-conventions-code-spec.md +275 -0
  75. package/docs/architecture/specs/stash-organization-conventions.md +377 -0
  76. package/docs/architecture/specs/task-workflow-format-unification.md +721 -0
  77. package/docs/architecture/specs/workflow-engine-buy-vs-build.md +231 -0
  78. package/docs/architecture/specs/workflow-format-unification.md +437 -0
  79. package/docs/architecture/testing/manual-testing-checklist.md +3420 -0
  80. package/docs/architecture/testing/okf-v0.2-conformance-runbook.md +924 -0
  81. package/docs/architecture/testing/task-workflow-manual-tests.md +1817 -0
  82. package/docs/architecture/testing/testing-workflow.md +446 -0
  83. package/docs/architecture/workflow-engine.md +274 -0
  84. package/docs/guides/README.md +33 -0
  85. package/docs/guides/author-bundles.md +310 -0
  86. package/docs/guides/author-workflows.md +520 -0
  87. package/docs/guides/bundles.md +321 -0
  88. package/docs/guides/capture-knowledge.md +136 -0
  89. package/docs/guides/claude-code-vs-akm-workflows.md +30 -0
  90. package/docs/guides/concepts.md +93 -0
  91. package/docs/guides/discover-and-load.md +158 -0
  92. package/docs/guides/getting-started.md +240 -0
  93. package/docs/guides/improve-the-library.md +246 -0
  94. package/docs/guides/knowledge-management.md +18 -0
  95. package/docs/guides/recipes/headless-install.md +97 -0
  96. package/docs/guides/recipes/website-source.md +28 -0
  97. package/docs/guides/run-workflows.md +279 -0
  98. package/docs/guides/scheduling.md +123 -0
  99. package/docs/guides/use-with-any-agent.md +129 -0
  100. package/docs/guides/wikis.md +81 -0
  101. package/docs/maintainers/README.md +19 -0
  102. package/docs/maintainers/curate-workmap.md +336 -0
  103. package/docs/maintainers/eval.md +3 -0
  104. package/docs/maintainers/local-development.md +108 -0
  105. package/docs/maintainers/release-checklist.md +103 -0
  106. package/docs/migration/README.md +2 -2
  107. package/docs/migration/release-notes/0.0.13.md +1 -1
  108. package/docs/migration/release-notes/0.1.0.md +1 -1
  109. package/docs/migration/release-notes/0.2.0.md +1 -1
  110. package/docs/migration/release-notes/0.3.0.md +1 -1
  111. package/docs/migration/release-notes/0.5.0.md +1 -1
  112. package/docs/migration/release-notes/0.6.0.md +1 -1
  113. package/docs/migration/release-notes/0.7.0.md +1 -1
  114. package/docs/migration/release-notes/0.7.3.md +2 -2
  115. package/docs/migration/release-notes/0.7.4.md +1 -1
  116. package/docs/migration/release-notes/0.7.5.md +1 -1
  117. package/docs/migration/release-notes/0.8.0.md +1 -1
  118. package/docs/migration/release-notes/0.9.0.md +1 -1
  119. package/docs/migration/release-notes/0.9.14.md +1 -1
  120. package/docs/migration/release-notes/0.9.15.md +1 -1
  121. package/docs/migration/release-notes/0.9.16.md +1 -1
  122. package/docs/migration/release-notes/0.9.17.md +1 -1
  123. package/docs/migration/release-notes/0.9.19.md +1 -1
  124. package/docs/migration/release-notes/0.9.2.md +1 -1
  125. package/docs/migration/release-notes/README.md +27 -31
  126. package/docs/migration/v0.5-to-v0.6.md +759 -0
  127. package/docs/migration/v0.8-to-v0.9.md +1 -1
  128. package/docs/plans/0.9.2-agent-command-workflow-plan.md +423 -0
  129. package/docs/posts/README.md +30 -0
  130. package/docs/posts/akm-command-tour.md +495 -0
  131. package/docs/posts/akm-logo-sized.webp +0 -0
  132. package/docs/posts/belief-aware-memory-12.md +139 -0
  133. package/docs/posts/config-v2-lessons-16.md +191 -0
  134. package/docs/posts/improve-loop-autonomous-10.md +225 -0
  135. package/docs/posts/improve-pipeline-debugging-0803.md +179 -0
  136. package/docs/posts/intro-part-01.md +76 -0
  137. package/docs/posts/intro-part-02.md +221 -0
  138. package/docs/posts/intro-part-03.md +190 -0
  139. package/docs/posts/intro-part-04.md +221 -0
  140. package/docs/posts/llm-mode-reflect-13.md +166 -0
  141. package/docs/posts/openpalm-built-on-akm.md +86 -0
  142. package/docs/posts/progressive-disclosure-06.md +129 -0
  143. package/docs/posts/proposal-safety-net-15.md +112 -0
  144. package/docs/posts/release-0.5.0.md +247 -0
  145. package/docs/posts/release-0.6.0.md +117 -0
  146. package/docs/posts/release-0.7.0.md +159 -0
  147. package/docs/posts/release-0.8.0.md +332 -0
  148. package/docs/posts/scattered-skills-05.md +100 -0
  149. package/docs/posts/task-assets-persistent-workflows-11.md +222 -0
  150. package/docs/posts/team-skills-07.md +198 -0
  151. package/docs/posts/wiki-agent-knowledge-08.md +223 -0
  152. package/docs/posts/workflows-vaults-09.md +321 -0
  153. package/docs/product-surface.md +54 -0
  154. package/docs/reference/README.md +1 -2
  155. package/docs/reference/asset-types.md +168 -0
  156. package/docs/reference/cli.md +5 -6
  157. package/docs/reference/env-and-secrets.md +126 -0
  158. package/docs/reference/memory.md +124 -0
  159. package/docs/reference/refs.md +156 -0
  160. package/docs/reference/registry.md +489 -0
  161. package/docs/reference/website-sources.md +141 -0
  162. package/package.json +12 -15
@@ -0,0 +1,27 @@
1
+ # v8 spec architecture review
2
+
3
+ major | task path has no freeze stage today, and the spec never says whether it gets one | `runPromptTask`/`runCommandTask` in `src/tasks/runner.ts` resolve engine/model/cascade against *live* `loadConfig()` synchronously inline (no compile/resolve/freeze staging, no persisted plan), while §5.4's two-stage freeze is specified purely in workflow terms (`compileResolveFreezeWorkflow`) — the spec asserts cascade.ts is "consumed at freeze (workflows) and dispatch (tasks)" but never states whether task dispatch runs compile→resolve→freeze in-memory-only or calls cascade.ts directly, skipping the asset-loader injection seam that persona/`uses:` resolution depends on. | spec §4.3, §5.4; `src/tasks/runner.ts:461-567`; `src/workflows/ir/freeze.ts:43`
4
+
5
+ major | AgentUnitExecutor seam glosses over two structurally different dispatch surfaces | Workflow dispatch already flows through `UnitDispatchRequest` (frozen `FrozenEngineSnapshot`, `IrInvocation`, `env: Record<string,string>`, journaled via `workflow_run_units`) in `src/workflows/exec/unit-dispatch.ts`, but the task-prompt path (`runPromptTask`) builds a `RunnerSpec` from live config via `resolveEngine`/`resolveLlmEngineUse` and calls `executeRunner()` directly, then writes to `task_history`/`logs.db` — a completely different type and persistence shape; "one AgentUnitExecutor, two callers" needs an adapter layer the spec doesn't name, not just a shared strategy interface. | `src/workflows/exec/unit-dispatch.ts:9-31`; `src/tasks/runner.ts:479-539`
6
+
7
+ major | ShellUnitExecutor claim ignores that the task runner's shell path is a distinct, already-hardened subsystem | `runCommandTask` already owns process-group kill-ladders, `AKM_EVENT_SOURCE` stamping, `resolveNestedAkmCommand`, and its own log/history sinks (`src/tasks/runner.ts:250-341`); folding this into one `ShellUnitExecutor` shared with the orchestrator's `Bun.spawn()` path is plausible but the spec treats it as "just" a strategy swap (§5.1) when it actually requires reconciling two independent timeout/kill/logging contracts — not costed anywhere in §9. | `src/tasks/runner.ts:273-296`; spec §5.1, §9
8
+
9
+ major | cascade module's two consumption times are not obviously bitemporally safe for `uses: tasks/<ref>` composition | A workflow step referencing `tasks/<id>` resolves that task's cascade layers at the workflow's freeze time (baked into the frozen plan), but the *same* task run standalone via `akm task run <id>` re-resolves cascade against config as of the actual run — the spec never states which config snapshot a step's `uses: tasks/<ref>` composition should read (config at workflow-freeze time vs. config at task-file's own authoring), so two runs of "the same" referenced task can diverge without either being wrong per the spec's own text. | spec §4 layers table, §5.4; `src/workflows/ir/freeze.ts:41` ("only source-to-runtime boundary")
10
+
11
+ major | persona snapshot / "show layer" reuse is directionally sound but the show layer isn't a resolver today, it's a CLI-output formatter | The provenance ceiling the spec cites is real (`src/commands/read/show.ts:433-445`, `isPrimaryStash` gate deleting `toolPolicy` for non-writable stashes) but it lives inside `akm show`'s response-shaping code, not a reusable resolution function; extracting a `{systemPrompt, toolPolicy, model, …}` snapshot for freeze means carving a pure resolver out of a command handler that currently also does display formatting — a real but unscoped refactor, understated as "consumes the show layer's verdict." | `src/commands/read/show.ts:433-445`; spec §4 item 2
12
+
13
+ minor | IrInvocation as currently typed (`engine: string` required, `model`, `timeoutMs`, `llm?`) is agent/llm-shaped only — extending it to a `{kind:"agent",…}|{kind:"shell",…}` discriminated union is the right shape given `IrRuntimeKind` already exists as `"llm"\|"agent"\|"sdk"`, but the spec should say explicitly whether `IrRuntimeKind` gets a `"shell"` member or whether shell units carry no `runner` field at all — `decodeWorkflowPlanV3`'s `validateInvocation`/`assertUnitEngineCompatibility` (`src/workflows/ir/schema.ts:481-527`) hard-requires `invocation.engine` on every unit today, so the decoder rewrite is nontrivial, not a narrow "instructions become optional" patch. | `src/workflows/ir/schema.ts:78-84, 383-452, 517-527`; spec §5.2
14
+
15
+ minor | hashVersion 5 preimage table is a clean, additive extension of the existing pattern | `step-work.ts`'s `computeStepWorkList` already documents its hash preimage exhaustively (hashVersion 4, lines 375-392) and explicitly walks through why each field is in/out; the v5 additions (target kind + ref + content hash, shell text, env literal values) fit the same discipline and the existing code's own doc comments make clear this is a well-understood seam to extend, not a risky one. | `src/workflows/exec/step-work.ts:333-392`; spec §5.3
16
+
17
+ minor | "one cascade module" name is right, but engine/model resolution logic that would feed it is currently duplicated three ways with subtly different fallback rules (`exactModel`/`effectiveTimeout` in freeze.ts, `resolveEngine`+inline overrides in runner.ts, `resolveLlmEngineUse` in engine-resolution.ts) — each has bespoke opencode-sdk-fallback handling; cascade.ts absorbing all three without losing the sdk-fallback edge cases (`freeze.ts:174-186, 199-208`) is the real engineering cost, understated by calling it "one cascade module." | `src/workflows/ir/freeze.ts:163-209`; `src/tasks/runner.ts:479-514`; spec §4 item 3
18
+
19
+ minor | env literal/ref provenance split matches an idiom the codebase already uses elsewhere (names-only hashing, resolved-value redaction sets) | `step-work.ts` already hashes `env: template.env ?? null` as ref *names* only and documents exactly the "hashing a resolved secret would leak it" rationale the spec restates in §5.5 — this is not a new pattern, it's naming an existing invariant, which is the correct minimal move. | `src/workflows/exec/step-work.ts:353-358`; spec §5.5
20
+
21
+ minor | `type: task` adapter-ordering claim is correct and lint-checkable today | `BUILTIN_ADAPTERS` in `src/core/adapter/adapters/index.ts:73-90` does put `akmWorkflowAdapter` before `akmTaskAdapter`, both before the loose `akmAdapter`, confirming the spec's M8 claim that "workflow adapter is ordered first and claims `.md` files" is grounded, not asserted. | `src/core/adapter/adapters/index.ts:73-90`; spec §3
22
+
23
+ minor | GHA-alignment naming (`uses:`/`run:`/`with:`) is clean and the mutual-exclusion rule maps directly onto a single discriminated `Target` union — no simpler alternative stands out; the one soft spot is overloading `uses: tasks/<n>` to mean "compose this task's fields into me" while `uses: workflows/<r>` means "invoke and return," two different composition semantics under one keyword, which the spec acknowledges (no nesting rule) but doesn't name as an asymmetry worth flagging to authors beyond a doc note. | spec §2.2, §5.1
24
+
25
+ minor | over-engineered: threading `llm` overrides validation (`assertUnitEngineCompatibility`, `validateLlmOverrides`) through a *shell* invocation union member that never carries them is dead branch surface — the discriminated union (§5.1 `{kind:"shell",...}`) should structurally exclude `llm`/`engine` fields rather than relying on decoder-time cross-checks the way today's single-shape `IrInvocation` must; the spec doesn't say the shell variant drops the whole `invocation` wrapper, so decoder complexity may grow rather than shrink under v4. | `src/workflows/ir/schema.ts:481-498, 517-527`; spec §5.2
26
+
27
+ minor | under-specified: scheduler-id mapping (`/` → `--` with collision lint) and per-engine capability-declaration shape are both explicitly deferred to the implementer (§11 "Open items") — reasonable to defer, but subdir task ids interacting with `validateTaskId`'s current rejection of `/` (cited in §9) means the migration step and the new id grammar must land atomically, which isn't sequenced anywhere in §8's migration steps. | spec §8, §11; `src/tasks/task-id.ts` (not read in depth but referenced by spec)
@@ -0,0 +1,74 @@
1
+ # Audit of v8 §11 findings cross-reference
2
+
3
+ Spec: /home/user/akm/docs/architecture/specs/task-workflow-format-unification.md (v8)
4
+ Baseline: claude/akm-markdown-tasks-history-75l6du @ current HEAD (d8095a0)
5
+ Consolidated findings: scratchpad/review-consolidated.md
6
+
7
+ Method: for each finding, located v8's claimed resolution via §11's table, read the
8
+ referenced section in full, and where v8 makes a factual claim about current code
9
+ (to justify the design), re-verified that claim by reading the cited file/lines
10
+ directly. Flagged anything that only restates the finding, that is logically
11
+ incomplete relative to the finding's actual scope, or that conflicts with another
12
+ part of v8 or with a documented architectural decision elsewhere in the codebase.
13
+
14
+ ## Verified genuinely resolved (design-level; spec, not code, so "resolved" = coherent design that actually addresses the mechanism, not just names it)
15
+
16
+ - C1 (output collision): compile.ts:204 (`step.output` → `outputSchema`) and
17
+ compile.ts:229 (`unit?.output` → `schema`) confirm the two real, distinct
18
+ schema slots v8's §7 two-homes design targets. Accurate and complete.
19
+ - C2 (shell units in IR): unit-dispatch.ts:19 confirms `engine: FrozenEngineSnapshot`
20
+ is non-optional today, matching v8's premise; §5.2's discriminated-union +
21
+ per-kind engine resolution is a real fix, not a rename.
22
+ - C3 (hash preimage): hashVersion 5 table in §5.3 covers every named gap (run: text,
23
+ shell/cwd, uses: ref + content hash, env literals) and the freeze-time-append
24
+ design in §2.3 correctly resolves the "no single seam" objection.
25
+ - C4 (cascade layers): config-schema.ts:102-108 confirms `DefaultsSchema` is
26
+ `.passthrough()` with no `model` field, matching v8's claim; §4's "build, not
27
+ expose" inventory (6 concrete numbered work items) is substantive, not hand-wavy.
28
+ - M2 (placeholder grammar): agent-dispatch.ts:67-72 confirms `fillPlaceholders`
29
+ only substitutes positional `{{\d+}}` (matches v7-era `{{0}}` bug claim); named
30
+ `{{name}}` is recognized only by `extractParameters` (renderers.ts) for metadata,
31
+ never filled — confirms the bug is real and v8's with:/with.arguments design
32
+ addresses it.
33
+ - M3 (env additive): compile.ts:46 confirms `env?: string[]` — IR literally cannot
34
+ carry literal values today. §5.5's provenance split is a real fix.
35
+ - M4 (alias portability): model-aliases.ts confirms `BUILTIN_ALIASES` has only
36
+ `claude`/`opencode` columns (no `"*"`), while the *global* config-level alias
37
+ table already supports a `"*"` fallback (config-schema.ts / model-aliases.ts
38
+ doc comment) — so v8's proposed "give builtins a `*` column too" is consistent
39
+ with an existing pattern, not novel invention.
40
+ - M6 (dead kind-gate): freeze.ts:67-73 confirms the exact dead-code shape
41
+ (`llm` computed only when `kind==="llm"`, then guarded on `kind!=="llm"`).
42
+ Accurate; the capability-notice replacement in §4 item 4 is coherent.
43
+ - M9 (akm-bin resolution) — see PARTIAL below.
44
+ - M11 (freeze IO / lint never freezes): grep confirms `compileResolveFreezeWorkflow`
45
+ has exactly one call site (runs.ts:241). The two-stage compile/resolve/freeze +
46
+ lint dry-freeze design in §5.4 is a real structural fix.
47
+ - M12 (defaults.on_error exists today): program/schema.ts:204-211 confirms
48
+ `ProgramDefaults.onError` already exists at document level, exactly as the
49
+ finding states. §4's decision to keep on_error/retry as value fields is a
50
+ direct, correct response.
51
+ - M8 (adapter ordering): akm-workflow-adapter.ts:66-70 confirms `isWorkflowFile`
52
+ returns true whenever `type` is undefined or `"workflow"` — any `.md` without a
53
+ contrary `type:` is claimed by the workflow adapter, confirming the collision.
54
+ Making `type: task` mandatory (§3) is a workable, not hand-waved, fix.
55
+
56
+ ## Problems found
57
+
58
+ C5 | contradicts | v8 §5.6 proposes a new persisted per-bundle trust grant (`bundles.<name>.allowScriptExecution`, settable via `akm setup`/`akm config set`) to unlock third-party script execution — but src/core/activation-policy.ts explicitly documents a 2026-07-14 decision that the "installation is not activation" work "ships no new trust / approval / security machinery: no labeling, action clamps, confirm prompts, digests, trust records, or persisted `workspace_bindings`," and the existing analogous `tools:` provenance ceiling (show.ts:433-445) has no override at all — it unconditionally strips `toolPolicy` for non-primary-stash assets. v8's claimed "same ceiling philosophy" is therefore inaccurate: the tools: ceiling is a hard, ungrantable ceiling, while the new mechanism is an invertible, persisted, named per-bundle opt-in — a materially bigger and different kind of security surface than the finding's own comparison implies, and one that runs directly against a decision already on record in the codebase.
59
+
60
+ M13 | partial | v8's §11 row points only to "§3 (improve excluded from task bodies)," which resolves the improve-pipeline half of the finding, but the finding's other half — "the akm adapter special-cases type:'task' as pure YAML so markdown checks never fire" (akm-adapter.ts:404-412, confirmed: `parsed = { data, content: raw, frontmatter: null }` for `type==="task"`, explicitly to suppress `missing-updated`) — is never revisited. v8's own §3 examples give tasks substantial free-form prose bodies that are now the literal executable prompt, but no change to the lint/OKF parse path is proposed to give that prose the same base-check coverage other markdown assets get (frontmatter-shaped checks like `unquoted-colon`/`missing-updated` still never fire on tasks); only `stale-path`/`missing-ref` (body-scoped, frontmatter-independent) survive.
61
+
62
+ M9 | partial | §5.7's fix ("PATH prepend... replacing the argv-rewrite that shell text would defeat") glosses over why `resolveNestedAkmCommand` was built as an argv splice in the first place: `resolveAkmInvocation()` (resolve-akm-bin.ts) can return a *multi-element* invocation (e.g. a node/bun launcher + script path), not always a single standalone binary. A bare PATH prepend only works when there is one real executable file to point `PATH` at; the multi-arg-launcher case needs a synthesized shim (a generated `akm` script that re-invokes `node <path>`), which is materially more than "PATH prepend" and isn't mentioned.
63
+
64
+ Minor findings with no resolution anywhere in v8 (contradicts §11's blanket "all resolved or explicitly costed" claim and the Minors row's non-exhaustive listing):
65
+
66
+ - "§3.5 embodiment sentence reads backwards (--model wins over hint)" (1/3) — no trace of this in v8; the section was restructured away but the underlying precedence claim about `--model` vs. an alias/tier hint is never restated or corrected anywhere in v8's aliasing text (§4 item 5).
67
+ - "Value fields on `uses: workflows/*` tasks are structurally inert (run-workflow takes ref+params only)" (1/3) — not mentioned; §2.2's table gives `with:` → declared param flags for `uses: workflows/<r>` but never addresses whether cascade value fields (timeout, model, etc.) set on such a task do anything, which was the actual complaint.
68
+ - "uses: table covers 5 of ~15 placement types; no rule for the rest" (1/3) — not mentioned anywhere in v8.
69
+ - "single-file provenance lost" (part of the 256 KiB composition finding, 1/3) — §5.4 addresses the 256 KiB cap-breach half ("carry the existing... cap with a lint warning at 80%") but not the provenance-loss half of the same finding.
70
+
71
+ ## Notes on things that check out but are worth flagging as "costed, not fixed" (v8 is honest about these, no complaint)
72
+
73
+ - M4/§4 item 6: gate judge (`judgeEngine`) explicitly stays outside the cascade in 0.9.0 — correctly flagged as deferred rather than claimed resolved.
74
+ - M7/§8: migration section is a genuine rewrite against the real migrator, spot-checked one mapping (`name:` → body H1) is plausible; not exhaustively re-verified against `task-target-ref-migration.ts` line-by-line given time budget.
@@ -0,0 +1,15 @@
1
+ # Security/migration review — task-workflow-format-unification.md v8 (d8095a0), baseline 71bb686
2
+
3
+ - critical | §5.6 grant mechanism does not exist anywhere in the codebase | `allowScriptExecution` / `bundles.<name>.allowScriptExecution` has zero hits in `src/`, so today "third-party bundle → script asset" is unconditionally executable (script `run:`/`setup:` is already advisory-only per `src/core/activation-policy.ts` comments) — the whole §5.6 line is unbuilt, not just "resolve-time enforceable," and no code path currently gates it | grep across `src/` for `allowScriptExecution`/`ScriptExecution`; `src/core/activation-policy.ts` rules 1-4 (env/task/write-activation only, no script-execution rule).
4
+ - critical | §5.6's reused "ceiling philosophy" (tools: provenance gate, M5) is unverified against code | The spec says script-execution enforcement reuses "the writable-vs-third-party provenance ceiling for self-declared `tools:`" that "already lives" at the show layer, but no such ceiling/gate keyed on writable-vs-registryId for `tools:` was found anywhere under `src/integrations/agent/`, `src/core/adapter/`, or `src/core/asset/` — if M5's premise is wrong, §5.6's plan to "consume the show layer's verdict" has nothing to consume and must build the primitive from scratch, changing the cost estimate | grep for `provenance ceiling`/`toolPolicy` in agent/adapter trees turned up no writable/registryId-keyed logic; only `activation-policy.ts` rule 4 (`isSourceWriteActivated`) and rule 1 (env dangerous-key) exist as analogous patterns.
5
+ - major | §5.6 composition-chain bypass is not addressed by the per-bundle grant design | A writable task composing `uses: tasks/<ref>` where the referenced task (in a third-party bundle) itself targets `uses: scripts/<n>` resolves recursively (§5.1 `{ kind: task, ref }` "resolves recursively once") — the spec never states whose bundle-grant applies (the composing writable task's bundle, which has no grant, or the referenced task's own third-party bundle, which needs one) or how §5.4's resolve stage attributes the grant when task-A (bundle X, writable) pulls task-B (bundle Y, third-party, ungranted) that pulls script-C — this is exactly the "composition chains" bypass route named in the audit brief and v8 gives it no explicit resolution rule | §5.1 recursive task resolution; §5.6 (grant keyed by bundle, not by resolved-asset chain); §2.3 "no nesting" only forbids workflow-in-step, not task-in-task depth-1 recursion through third-party bundles.
6
+ - major | §5.6 grant granularity is per-bundle, not per-asset, so one grant silently activates every future script in that bundle including ones added after the grant (bundle-update bypass) | The grant text says "settable... by `akm config set`" against `bundles.<name>.allowScriptExecution: true` — a boolean on the whole bundle. §5.6 does not pin the grant to a content hash or a specific script set, so `akm update` on a third-party bundle can swap in new/modified script content (or a modified `run:`/`cwd:`) that inherits the prior grant with no re-confirmation — the audit brief's "bundle update swapping script content after grant" bypass is not closed by the design as stated | §5.6 grant text (`bundles.<name>.allowScriptExecution: true`); no mention of grant invalidation on content change; contrast with §5.3's hash-preimage discipline for *dispatch* identity, which has no counterpart for *authorization* identity.
7
+ - major | §5.6 cron-fire-time vs sync-time grant evaluation is unspecified, and §5.4 says resolution happens at "resolve time," which for the task path is `task sync`/lint, not `task run` fire time | The audit brief specifically asks whether grants are evaluated at sync time (when the OS entry is installed) or fire time (when cron actually invokes `akm task run <id>`); §5.6 says "enforced at both executors" and "applied at resolve time (§5.4)" but §5.4's two-stage compile/resolve/freeze pipeline is described for workflows, and the task path's own freeze/resolve moment relative to `sync` vs the scheduler's `task run` invocation is never pinned — if the grant is checked at `sync` time only, revoking `allowScriptExecution` after sync leaves the OS-level cron entry still firing the script, since §5.2 says cron enable/disable is a separate `enabled:` gate, not a script-authorization gate | §5.6; §2.3 ("Trigger keys ... consumed only by `akm task sync` and, at fire time, the task runner's enabled gate `runner.ts:169`" — this passage explicitly documents `enabled` as fire-time-checked but says nothing analogous for the script-execution grant).
8
+ - major | §5.5 "literal entries ... never redacted" collides with `param-secrets.ts`'s own heuristic scanner for exactly this shape of leak, and the spec does not reconcile the two | `param-secrets.ts` exists specifically because author-written values (there: workflow params) that "look like credentials" are dangerous when they cannot be redacted — it scans key names and value entropy and warns. §5.5 freezes `env:` literal values into the plan with the identical non-redactable contract ("frozen into the plan, hashed, shown by `brief`, never redacted") but the spec's decision log gives no equivalent heuristic-warning obligation for literal env entries the way `param-secrets.ts` already provides for params — an author who writes `env: [{ API_KEY: "sk-live-..." }]` (a plausible mistake, not even malicious) gets zero warning at freeze/lint time despite the codebase already having the exact detector (`detectSecretShapedParams`) that could be pointed at literal env values too | `src/workflows/exec/param-secrets.ts` (rationale + `detectSecretShapedParams`); spec §5.5 (no mention of applying the existing secret-shape heuristic to literal env entries); §5.4 (lint's dry-freeze would be the natural hook and isn't used for this).
9
+ - major | §5.5's `AKM_ITEM` (JSON) env injection has no shell-escaping/quoting story and no code precedent to check against | `AKM_ITEM` does not exist anywhere in `src/` yet (grep confirms zero hits) — this is wholly new plumbing proposed by §5.5 ("a `map:` step with a shell target receives `AKM_ITEM` (JSON) and `AKM_ITEM_INDEX` per unit"). JSON serialized into a process env var is safe from *shell metacharacter* injection only if the executor sets it via the `env` object passed to `spawn`/`Bun.spawn` (not string-interpolated into a shell command line); the spec doesn't say which, and existing shell-invoking code in this codebase (`src/tasks/backends/exec-utils.ts` `spawnCommand`) passes argv arrays with no shell, which is the safe pattern, but nothing pins §5.5's `AKM_ITEM` assembly to that pattern rather than to a `run:` string that a script might `eval`/interpolate itself (e.g., a script doing `node -e "$AKM_ITEM"` would be JSON-injection-shaped if the JSON contains `"`/backtick sequences and the script author naively string-interpolates it into a nested command) | §5.5 "Env assembly moves per-unit ... receives `AKM_ITEM` (JSON)"; no code precedent (`grep AKM_ITEM src/` → 0 hits); `src/tasks/backends/exec-utils.ts` `spawnCommand` shows the codebase's existing safe argv-array convention that §5.5 should but does not explicitly commit to.
10
+ - major | §8 migration plan does not fit the actual 71bb686 backup/journal model for the .yml→.md file-level transform | `migration-backup.ts`'s manifest/journal machinery (`ARTIFACT_NAMES`, `MigrationBackupManifest`, apply/restore journal + sentinel) covers exactly four opaque artifacts — `config.json`, `state.db`, `workflow.db`, `index.db` — and has no concept of per-bundle-file changes; the *actual* hardened per-file transform pattern in this codebase is `task-target-ref-migration.ts`'s plan/apply split (read-before-write byte-fencing via `current.equals(rewrite.before)`, single atomic rename per file, no cross-file transaction). §8's `.yml`→`.md` conversion is a two-file operation (create the new `.md`, remove the old `.yml`) that this per-file model does not support atomically: an interruption between writing the new `.md` and removing the old `.yml` leaves both present, which the spec's own "`<id>.yml` + `<id>.md` collide on one conceptId → same named error" rule would then fire on the *migrator's own* half-applied state, turning an interrupted migration into a self-inflicted hard error rather than a resumable step | `scripts/akm-migrate/migration-backup.ts` (ARTIFACT_NAMES, manifest scope); `scripts/akm-migrate/migrate/legacy/task-target-ref-migration.ts` (per-file plan/apply, single-file atomicity only); spec §8 tombstone-collision rule.
11
+ - major | §8's "vendored, frozen copy of the v2 task parser" fix is not yet applied at 71bb686 — the cited defect is confirmed live, not already resolved | `scripts/akm-migrate/migrate/legacy/task-target-ref-migration.ts` line 25 currently does `import { parseTaskDocument } from "../../../../src/tasks/parser"` — the live, non-frozen parser — exactly the M7 finding the spec describes as needing a fix "as part of this work." This is consistent with the spec's own claim (not a contradiction), but it means the "frozen-migrator principle" is presently violated in the baseline the spec is built on, and v8 gives no interim mitigation for the window between baseline and when the vendoring lands — any `src/tasks/parser` change merged before the vendoring work starts will retroactively break `akm-migrate` silently, since nothing in the migrator's test suite pins the parser's frozen behavior independent of `src/tasks/parser` | `scripts/akm-migrate/migrate/legacy/task-target-ref-migration.ts:25`; spec §8 first bullet and §11 M7 row.
12
+ - major | §8 read-only bundle handling for the .yml tombstone/collision rule doesn't address the same read-only-bundle carve-out already coded for the *existing* migration | `task-target-ref-migration.ts`'s `planTaskTargetRefMigration` explicitly skips non-writable bundles and instead *warns* that their v1 `.yml` tasks "will not run after upgrade" (never rewritten, never deleted) — this is the precedent §8's new `.yml`→`.md` conversion must follow for read-only bundles, but §8 states only "task discovery keeps matching `tasks/*.yml` permanently, as a diagnostic" and "sync/doctor/lint each hard-error" without saying whether that hard-error applies to a read-only third-party bundle the operator cannot fix (contradicting the existing precedent of warn-and-continue for exactly that case) — operators with a read-only bundle carrying legacy `.yml` tasks would move from "warned, keeps working" (current) to "hard-errors sync/doctor/lint" (v8), a behavior regression not costed anywhere in §9 | §8 tombstone rule; `task-target-ref-migration.ts` lines 253-271 (existing read-only warn-and-skip precedent); spec §9 cost inventory has no line for this regression.
13
+ - minor | §2.4 lenient placeholder filling is prompt-injection-adjacent but bounded by scope, not fully safe | Leaving unmatched `{{name}}`/`$ARGUMENTS`/`$1`-`$9` verbatim (rather than erroring) means an operator who edits `with:` and mistypes a key, or pulls a shared command asset whose placeholder grammar drifted, gets a raw `{{typo}}` or literal `$3` byte-string injected into the agent prompt with no runtime signal beyond a lint warning that a human must have already run and read — this is "silent garbage into an agent prompt" as the audit brief frames it; it's a correctness/quality risk more than an injection vector since the text is author-controlled, not attacker-controlled, but shared/imported command templates make the author and the operator different trust boundaries, and §2.4 doesn't add any freeze-time notice surfaced to the *operator* (only lint, which is opt-in) | §2.4 ("An unmatched placeholder or unused `with:` key is a lint warning and is left verbatim — not a runtime hard error"); §5.4 (lint dry-freeze exists but running lint is not mandatory before dispatch).
14
+ - minor | §5.7 PATH-prepend hijack risk is named but Windows semantics are not actually specified | §5.7 says bare `akm` resolves "by PATH prepend in the child environment" replacing the argv-rewrite, and separately flags Windows via `schtasks`/`PATHEXT`/`COMSPEC` handling elsewhere in the codebase (`redaction.ts` allowlists `PATHEXT`, `COMSPEC`, `WINDIR` explicitly, suggesting Windows env quirks are already a known sharp edge) — but §5.7 never states whether the prepend uses `;`-joined `Path`/`PATH` (Windows env vars are case-insensitive and there can be both `Path` and `PATH` keys colliding) nor whether a child script that does its own `where akm`/`akm.cmd` shim resolution could still pick up a stale/hijacked entry earlier in the *original* PATH the prepend is supposed to shadow (prepending only shadows if the child's PATH lookup honors first-match order, which is true for `cmd.exe` but not guaranteed for every shell/script interpreter a `scripts/` asset might invoke) | §5.7; `src/core/redaction.ts` PATHEXT/COMSPEC/WINDIR allowlist entries (evidence Windows env handling is already fragile in this codebase); no cross-reference to `src/tasks/resolve-akm-bin.ts`'s existing Windows-aware absolute-path resolution, which §5.7 could reuse instead of a PATH string prepend.
15
+ - minor | §5.5 env-literal redaction carve-out has no cross-check against `redactSensitiveValue`'s recursive, boundary-agnostic redaction, creating a silent divergence between two redaction call sites | `src/core/redaction.ts`'s `redactSensitiveValue` recursively redacts every string leaf of a structured value using `sensitiveValues` before it "crosses a durable/output boundary" — a general-purpose function with no knowledge of the env literal/ref split §5.5 introduces; if any caller applies `redactSensitiveValue` to a frozen plan (or its env-literal entries) using a `sensitiveValues` set built only from ref-sourced secrets (per §5.5's "never redacted" contract for literals), literals are correctly preserved — but if a *different* caller (e.g. a generic log-sanitization pass) instead builds `sensitiveValues` more broadly and includes literal values, §5.5's "never redacted" guarantee silently breaks for that surface with no test/contract in the spec pinning which call sites must respect the split | `src/core/redaction.ts:385-398` (`redactSensitiveValue`, generic/recursive, no literal/ref awareness); spec §5.5 (states the contract but does not name which call sites must special-case literals vs which already handle the split correctly).
@@ -0,0 +1,132 @@
1
+ # Adversarial review — task-workflow-format-unification.md v9 (round 2, cold read)
2
+
3
+ Scope: read the spec cold, then verified its file:line evidence against the actual
4
+ tree at HEAD (`ec5d062`, branch `claude/akm-markdown-tasks-history-75l6du`) for
5
+ src/workflows, src/tasks, src/core, src/integrations/agent, scripts/akm-migrate.
6
+ Most citations were spot-checked directly; two rounds of prior Sonnet panel
7
+ review already landed on v8→v9, so this pass hunts specifically for what those
8
+ missed rather than re-litigating settled findings (all of which I re-verified
9
+ and found accurate: freeze.ts:50/67-73 dead llm guard, config-schema.ts
10
+ DefaultsSchema passthrough + no model field + defaults.llm/agent/improve
11
+ hard-rejected at line ~234-241, show.ts:433-445 tools: ceiling, runner.ts:592-593
12
+ raw-frontmatter bug, activation-policy.ts's no-new-trust-machinery decision,
13
+ task-target-ref-migration.ts:25 importing the live `src/tasks/parser`,
14
+ compile.ts:204/229 output vs outputSchema split, param-secrets.ts's
15
+ detectSecretShapedParams, model-aliases.ts's claude/opencode-only columns,
16
+ step-work.ts hashVersion 4 preimage, WORKFLOW_IR_VERSION currently 3).
17
+
18
+ ## Findings
19
+
20
+ ### MAJOR — §2.4's cited "current filler" evidence points at the wrong function; the actual bug lives in a different, unmentioned call site
21
+
22
+ §2.4 states: "the current filler substitutes only positional `{{0}}` forms
23
+ leniently (`src/output/renderers.ts:164-186` — a live bug fixed by this work)."
24
+
25
+ `src/output/renderers.ts:164-186` is `extractParameters()`, called only from
26
+ `commandMdRenderer.buildShowResponse` (line 235) to populate the `parameters`
27
+ field shown by `akm show`. It does not fill or substitute anything — it just
28
+ regexes the template for `$ARGUMENTS`/`$1-9`/`{{name}}` names for display.
29
+
30
+ The actual filler with exactly the described `{{0}}`/`{{1}}`-only lenient
31
+ behavior is `fillPlaceholders()` in `src/commands/agent/agent-dispatch.ts:67-72`,
32
+ used by `akm agent --command <ref>` dispatch (a CLI entry point the spec never
33
+ mentions). This is a load-bearing citation — it's the concrete evidence
34
+ anchoring §2.4's entire contract and the §9 "inherited bugs fixed en route"
35
+ line — and it misdirects an implementer to a display-only function while
36
+ leaving the actual buggy code, and its actual call site, uncited. It also
37
+ hides a real open question the spec doesn't address: does the new
38
+ cascade-driven `with:`/`$ARGUMENTS`/`{{name}}` filler specified in §2.4
39
+ replace `agent-dispatch.ts`'s `fillPlaceholders`, or do the two now coexist
40
+ with different (old lenient-`{{0}}`-only vs new full-grammar) semantics for
41
+ the same command asset type depending on dispatch path? The spec is silent.
42
+
43
+ Refs: `docs/architecture/specs/task-workflow-format-unification.md:113-114`,
44
+ `src/output/renderers.ts:164-186,220-238`, `src/commands/agent/agent-dispatch.ts:14-16,63-72`.
45
+
46
+ ### MAJOR — §3's "recognition requires `type: task`" evidence covers only bundle-root probing; the actual per-file indexer classifier that types ordinary `tasks/*.md` files has no task branch at all, and has an ordering hazard the spec's own example would trip
47
+
48
+ §3 argues residence-only recognition is unimplementable "because the workflow
49
+ adapter is ordered first and claims `.md` files without a contrary `type:`"
50
+ citing `src/core/adapter/adapters/index.ts:73-90`. That citation (plus
51
+ `akm-workflow-adapter.ts`'s `isWorkflowFile`, which returns true when
52
+ `type === undefined`) is accurate, but it is evidence about **single-file
53
+ bundle-root detection** (`akmWorkflowAdapter`/`akmTaskAdapter` `looksLikeRoot`,
54
+ i.e. "is this repo root itself one task/workflow file") — a different code
55
+ path from the one that actually assigns a `type` to every ordinary file inside
56
+ a ordinary akm stash during `akm index`, which is `src/indexer/walk/matchers.ts`
57
+ (consumed directly by `akmAdapter`, per `akm-adapter.ts:27`).
58
+
59
+ In that classifier:
60
+ - `classifyBySmartMd` (matchers.ts:199-248) special-cases `fm.type === "workflow"`
61
+ (line 223-225) but has **no `fm.type === "task"` branch at all** today.
62
+ - The `tasks/` directory rule (`DIR_TYPE_MAP`, matchers.ts:98-105) only tests
63
+ `ext === ".yml"` — a `tasks/<id>.md` file gets **zero** directory-based
64
+ specificity from either `classifyByDirectory` or `classifyByParentDirHint`
65
+ today, contrary to what "residence is a lint expectation" implies is already
66
+ meaningfully wired.
67
+ - Absent a new, correctly-ordered `type === "task"` branch, the classifier's
68
+ existing `"agent" in fm → { type: "command" }` rule (matchers.ts:234-236)
69
+ fires first and would misclassify the spec's *own* first example task (§3,
70
+ `agent: agents/reviewer` top-level key) as a command, exactly the kind of
71
+ silent miswiring the workflow branch at line 223 was positioned early to
72
+ avoid.
73
+
74
+ §9's cost-inventory line ("markdown task recognition (`type: task`)") doesn't
75
+ name matchers.ts, the DIR_TYPE_MAP fix, or this ordering hazard, so an
76
+ implementer following only the cited evidence can miss the actual mechanism
77
+ that has to change and can reproduce the ordering bug live in the spec's own
78
+ worked example.
79
+
80
+ Refs: spec `docs/architecture/specs/task-workflow-format-unification.md:135-139`;
81
+ `src/indexer/walk/matchers.ts:98-105,199-248`;
82
+ `src/core/adapter/adapters/akm-adapter.ts:27`;
83
+ `src/core/adapter/adapters/akm-workflow-adapter.ts:69`.
84
+
85
+ ### MINOR — "document defaults" layer's applicability to a bare (non-referenced) task is left to inference
86
+
87
+ §4's six-layer cascade table lists `document defaults:` as a layer distinct
88
+ from `uses: tasks/<ref>` and `step/task keys`, but §3's task schema shows no
89
+ `defaults:` sub-block for a standalone task file — only workflows have
90
+ `asset.document.defaults` today (`freeze.ts:52`). It's a reasonable inference
91
+ that "document defaults" is workflow-only and collapses/no-ops for a bare
92
+ task, but the spec never says so explicitly, and a task that both composes
93
+ another task (`uses: tasks/<ref>`) *and* wants its own "defaults for this
94
+ document" layer (as opposed to its own directly-set fields) has no described
95
+ home. Low risk — an implementer would likely get this right by analogy to the
96
+ existing `documentDefaults` special-casing in freeze.ts — but it's a genuine
97
+ gap in an otherwise very precisely specified cascade.
98
+
99
+ Refs: spec §4 layers table (line 238); `src/workflows/ir/freeze.ts:52`.
100
+
101
+ ### MINOR — script/task "resolved content hash" for the tools: ceiling doesn't state whether frozen workflow plans re-read script bytes at dispatch
102
+
103
+ §5.3's hash preimage records "target kind + `uses:` ref + resolved content
104
+ hash" for re-dispatch invalidation, implying script *bytes* are not made
105
+ durable in the plan the way command templates are (§2.4 says command
106
+ templates are filled and frozen). If a script referenced by a frozen workflow
107
+ plan is edited between freeze and (possibly much later, queued) execution,
108
+ the plan's content hash goes stale for journal/idempotency bookkeeping, but
109
+ the spec doesn't say whether execution reads the live file (current bytes) or
110
+ a frozen copy, and doesn't note this as a deliberate asymmetry vs. commands.
111
+ Not a security issue (the ceiling only gates non-primary-stash sources, and
112
+ editing your own primary-stash file is expected), but worth one sentence so
113
+ an implementer doesn't have to guess whether "resolved content hash" implies
114
+ plan-embedded bytes.
115
+
116
+ Refs: spec §5.3 preimage table (line 368); §2.4 (lines 126-128, "filling
117
+ happens at freeze... producing the prompt string").
118
+
119
+ ## Verdict
120
+
121
+ No criticals found. The two majors above are citation/evidence gaps rather
122
+ than design contradictions — the underlying design decisions (§2.4's contract,
123
+ §3's `type: task` requirement) are themselves sound and match the codebase's
124
+ real needs; what's wrong is that the evidence anchoring them points at the
125
+ wrong or incomplete code, which is exactly the kind of thing this spec has
126
+ otherwise been extremely disciplined about getting right (nearly every other
127
+ citation checked out verbatim, including several with exact line numbers).
128
+ An implementer working strictly from the cited evidence would fix the wrong
129
+ placeholder-filler function and would very plausibly ship a task-recognition
130
+ patch that misclassifies its own worked example. Both are cheap to fix in the
131
+ spec (retarget the citations, name matchers.ts + DIR_TYPE_MAP + the ordering
132
+ requirement) before implementation starts.
@@ -0,0 +1,147 @@
1
+ # Round-2 verification of v9 against round-1 Sonnet panel (coverage/architecture/adversarial/security)
2
+
3
+ Spec: docs/architecture/specs/task-workflow-format-unification.md, v9 (ec5d062), branch
4
+ claude/akm-markdown-tasks-history-75l6du. Baseline for code claims: same checkout.
5
+
6
+ Method: for every critical/major in the four r1 reports, located the v9 text via §11's
7
+ cross-reference table, read the cited section in full, and where the fix leans on a
8
+ factual code claim, re-verified the claim by reading the cited file. Then checked the
9
+ new text against the rest of v9 for reintroduced contradictions, with extra scrutiny on
10
+ the five areas called out (§5.6 ceiling, persona-snapshot hash, `with:` merge, tombstone
11
+ + crash-window ordering, shim vs resolve-akm-bin.ts).
12
+
13
+ ## Criticals — all four verified fixed, no reintroduced contradiction
14
+
15
+ - **coverage C5** (§5.6 grant contradicts activation-policy's no-new-trust decision):
16
+ v9 deletes the `bundles.<name>.allowScriptExecution` grant entirely and replaces it
17
+ with exact inheritance of the existing `tools:` provenance ceiling (primary-stash-only,
18
+ no override, fail-closed) — verified `show.ts:433-445` really has no override
19
+ (`if (response.toolPolicy !== undefined && !isPrimaryStash) delete ...`), matching v9's
20
+ description exactly. `activation-policy.ts`'s "no new trust machinery" decision (header
21
+ comment, 2026-07-14) is respected: zero new config, zero persisted state. Fixed.
22
+ - **security "grant mechanism doesn't exist" / "ceiling philosophy unverified"**: moot —
23
+ v8's grant is withdrawn. The second half of this finding (reviewer claimed the `tools:`
24
+ ceiling "was not found" in code) is factually wrong — it exists at `show.ts:433-445` —
25
+ and v9 §11 correctly flags this as the one rejected panel claim. Confirmed correct.
26
+ - **adversarial "`with:` merge/replace semantics undefined"**: §2.2 now states the rule
27
+ explicitly — per-key shallow merge on a step→task→(command|workflow) composition chain,
28
+ referencing step wins, `with.arguments` replaced whole. Checked against §2.3 ("no
29
+ nesting: a task referencing a task is an error") — the chain is capped at depth 1
30
+ exactly where the merge rule assumes it stops, no gap. Checked against §4 ("`with:` is
31
+ not a value field... merges per-key at its target, outside the cascade") — value-field
32
+ cascade (§4's layer list, which includes `uses: tasks/<ref>` as a layer) and `with:`
33
+ merge are two disjoint mechanisms over two disjoint key sets; no overlap, no
34
+ contradiction. Fixed.
35
+ - **adversarial "hashVersion 5 omits persona snapshot"**: §5.3's table now has a
36
+ `persona snapshot hash` row (system prompt, tool policy, value fields). Matches §4.2's
37
+ persona-snapshot shape (`{systemPrompt, toolPolicy, model, …valueFields}`) verbatim —
38
+ same field list on both sides. Fixed, consistent.
39
+ - **adversarial "`.yml` tombstone hard-error has no remedy for read-only bundles"**: §8
40
+ now carves out read-only/third-party bundles to a warning instead of a hard error,
41
+ explicitly citing it as matching the existing migrator precedent. Verified against
42
+ `scripts/akm-migrate/migrate/legacy/task-target-ref-migration.ts:246-266` —
43
+ `planTaskTargetRefMigration` really does skip non-writable bundles and warn-and-continue
44
+ for legacy files it can't rewrite, word-for-word the precedent v9 claims. Fixed.
45
+ > **Editor's note (archival).** The cosmetic stage-name mismatch this
46
+ > report flags in §5.6 was fixed in the same revision that followed this
47
+ > panel: v9 now says the ceiling is evaluated in the **resolve stage** for
48
+ > both surfaces. The finding is retained as the historical record of what
49
+ > the panel saw, not as an open issue.
50
+
51
+
52
+ ## Majors — verified fixed unless noted
53
+
54
+ All of the following were checked and are substantively addressed with no new
55
+ contradiction: architecture's five majors (task-path staging named in §5.1 as
56
+ "resolve → execute"; `RunnerSpec`-vs-`UnitDispatchRequest` split pushed to the sink
57
+ per §5.1; `ShellUnitExecutor` explicitly extracted from the task runner's hardened path,
58
+ not rebuilt; monotemporal composition rule pins config-snapshot timing for `uses:
59
+ tasks/<ref>`; persona resolver extraction named and costed in §9); adversarial's
60
+ `extra_params` deep-merge preservation (§4 merge-classes, matches `freeze.ts:211-216`
61
+ `mergedLlmOverrides`/`deepMergeConfig` exactly), shell `timeoutMs` slot (§5.2), shell
62
+ crash/lease semantics (§5.2), persona-toolPolicy-vs-engine-capability reconciliation
63
+ (§4.2's "unconsumed-field notice" line folds it into §4 item 4's capability-notice
64
+ mechanism); security's composition-chain bypass (§5.6 bullet 3, "inherit from the
65
+ defining asset's own source"), grant-granularity/bundle-update bypass (moot — grant
66
+ removed), sync-vs-fire-time gap (§5.6 bullet 2, evaluated at resolution not at
67
+ sync/enable time), `env:` literal-secret-heuristic reuse (§5.5, points at
68
+ `param-secrets.ts`'s `detectSecretShapedParams` directly), `AKM_ITEM` shell-injection
69
+ story (§5.5 pins it to the `exec-utils` env-object pattern, never shell-text splice),
70
+ and the two-file migration atomicity gap (§8 states journal-then-verify-then-delete
71
+ ordering explicitly as new work, costed in §9, rather than claiming the existing
72
+ 4-artifact `migration-backup.ts` model already covers it — checked, that model really
73
+ is scoped to `config.json`/`state.db`/`workflow.db`/`index.db` only, so v9's honesty
74
+ about this being an extension is accurate, not a claim contradicted by code).
75
+
76
+ - **coverage M13 (improve-exclusion mechanism)**: §3 now removes the `type:"task"`
77
+ pure-YAML special-case (markdown base checks fire) and separately declares tasks
78
+ improve-ineligible via a per-type adapter capability. The capability-declaration
79
+ *pattern* genuinely exists today (`bundle-adapter.ts:9`, "one interface, optional
80
+ capability methods"), so naming that seam is not fabricated — but the concrete
81
+ eligibility check in `src/commands/improve/eligibility.ts` is currently a hardcoded
82
+ `indexed.entry.type === "..."` string comparison, not adapter-capability-driven; v9
83
+ doesn't say eligibility.ts itself gets refactored to consult the new capability
84
+ rather than its own type-string list. Not a contradiction, but thinner than "names a
85
+ mechanism" suggests — residual gap, not blocking.
86
+ - **coverage M9 / adversarial-adjacent (akm-bin shim)**: §5.7's synthesized-shim design
87
+ (write a temp-dir `sh`/`.cmd` shim invoking the exact resolved multi-element
88
+ invocation, PATH-prepend the shim's directory) matches `resolve-akm-bin.ts`'s actual
89
+ return shape (`argv: string[]`, up to two elements for the npm/checkout launcher
90
+ cases) — the shim approach correctly generalizes to the multi-element case a bare PATH
91
+ prepend cannot. Fixed.
92
+
93
+ ## Special-attention items — findings
94
+
95
+ 1. **§5.6 ceiling enforceability at both resolve points**: the mechanism itself
96
+ (primary-stash-only, checked at the point the target asset is resolved, no override)
97
+ is sound and symmetric for workflows and tasks. However, v9 states the enforcement
98
+ point as "freeze for workflows, dispatch for tasks" — but §5.1 (this same v9 revision)
99
+ explicitly renames task staging to **"resolve → execute"** (no stage named "dispatch"),
100
+ and §5.4 explicitly splits workflow freeze into **compile → resolve → freeze**, with
101
+ asset loading (the thing the ceiling needs to inspect) happening in **resolve**, not
102
+ in the now-narrower **freeze** ("snapshot + hash" only). So §5.6's own enforcement-point
103
+ language doesn't line up with the stage names v9 itself just introduced elsewhere
104
+ (§5.1, §5.4) to fix the architecture panel's "staging is unstated" finding. This is
105
+ cosmetic (both "resolve" and "freeze"/"dispatch" as used here occur before any
106
+ shell/agent actually spawns, so no execution-order security gap results), but it is a
107
+ real terminology inconsistency introduced by the v9 redesign, not present as such
108
+ before the two staging fixes existed to be inconsistent with. Not blocking; a
109
+ copy-edit item.
110
+ 2. **"scope: non-AI exec surface only" loophole for third-party `run:` tasks scheduled
111
+ via sync review**: no loophole found. §5.6's own definition of "shell-class work"
112
+ explicitly includes "the `run:` text of a task," so a third-party task whose target is
113
+ `run:` is covered by the ceiling and refuses regardless of whether the operator
114
+ reviewed and enabled it via `sync` (activation-policy rule 3 is a separate, orthogonal
115
+ gate that only concerns *whether the task fires at all*, not *whether its shell target
116
+ is trusted*). The "agent-dispatched third-party tasks... unchanged" carve-out only
117
+ exempts prose/agent-dispatch tasks (which have no direct shell surface here), not
118
+ `run:`/script tasks. No new hole.
119
+ 3. **Persona-snapshot hash vs §4.2 resolver extraction**: consistent — §5.3's new row
120
+ lists exactly the fields §4.2's snapshot shape defines (systemPrompt, toolPolicy,
121
+ value fields), and both are attributed to the same extracted resolver.
122
+ 4. **`with:` merge rule vs §4 cascade and §2.3's override bullet**: consistent — verified
123
+ above, disjoint mechanisms, no double-counting.
124
+ 5. **Tombstone carve-out + crash-window ordering vs §8's journal claims and the real
125
+ migrator**: consistent — the read-only carve-out matches
126
+ `task-target-ref-migration.ts`'s existing warn-and-skip behavior; the create-verify-
127
+ delete ordering is honestly presented as new work extending (not already covered by)
128
+ `migration-backup.ts`'s 4-artifact model, and it's costed in §9's "Named refactors" row.
129
+ 6. **Shim design vs `resolve-akm-bin.ts`**: consistent — the shim generalizes correctly
130
+ to the multi-element launcher case `resolveAkmInvocation()` can return.
131
+
132
+ ## Verdict
133
+
134
+ ALL FIXES HOLD.
135
+
136
+ Residual (non-blocking) notes:
137
+ - §5.6's "freeze for workflows, dispatch for tasks" enforcement-point language uses stage
138
+ names that don't match the stage vocabulary §5.1/§5.4 just introduced in this same
139
+ revision ("resolve → execute" for tasks; compile→resolve→freeze for workflows, with
140
+ asset resolution living in "resolve" not "freeze"). Suggest wording as "resolve for
141
+ workflows, resolve for tasks" (or just "at resolve, for both") to align with §5.1/§5.4.
142
+ - §3's improve-ineligibility fix correctly names an existing capability-declaration
143
+ *pattern* in the adapter interface, but doesn't state that
144
+ `src/commands/improve/eligibility.ts`'s current hardcoded `entry.type === "..."` checks
145
+ need to be rewired to consult that capability — worth a one-line addition if precision
146
+ matters, but doesn't reopen the original finding (a mechanism is now named, whereas
147
+ before none was).