akm-cli
Version:
akm (Agent Knowledge Manager) — a portable, local-first capability library for AI agents. Discover, load, share, and improve reusable skills, scripts, workflows, and knowledge across any shell-capable coding agent, including Claude Code, OpenCode, and Cur
153 lines (125 loc) • 9.91 kB
Markdown
# Consolidated findings — three independent Opus reviews of spec v7 + codebase
Sources: review-1.md (758 lines), review-2.md (645), review-3.md (602).
"R1/R2/R3" = which reviewers found it independently. 3/3 = unanimous.
## Critical (all confirmed by ≥2 reviewers)
C1. 3/3 — **`output:` is NOT a duplicate; the flatten collides two different schemas.**
`unit.output` = per-dispatch structured-result schema (LLM responseSchema, structured
retry, driver --result-file); `step.output` = promoted artifact / reducer result feeding
the gate loop. Deleting the bag merges them and makes per-item schemas on map steps
unexpressible. (compile.ts:204,229; step-work.ts:555-561; native-executor.ts:663,928,1092)
C2. 3/3 — **`run:` shell units are unrepresentable in IR v3 / the dispatch seam.**
UnitDispatchRequest.engine and IrInvocation.engine are required; FrozenEngineSnapshot is
a closed 2-kind union; freeze hard-errors with no engine; IR rejects empty instructions;
no shell runner exists; brief/report behavior undefined. §10's "no IR/freeze changes" is
false. (unit-dispatch.ts:15-19; ir/schema.ts:29-85,434-441,481-497; freeze.ts:49-75)
C3. 3/3 — **The unit input-hash preimage is unaddressed → silent replay-reuse bugs.**
hashVersion 4 hashes template instructions/item/inputs/params/dispatch/env NAMES only.
run: text, uses: refs, cwd, shell, appended prose, and env literal values are all
outside the preimage; editing any of them would reuse completed journal rows. The
prose-append also can't happen "at one seam" — prompts are assembled per-unit at
dispatch (item/inputs/gate blocks), so the append must be a freeze-time concat plus a
hash-version bump. (step-work.ts:333-393,473-500)
C4. 3/3 — **The cascade's far layers don't exist; the worked example is a no-op today.**
DefaultsSchema has no `model` (passthrough swallows it silently); `defaults.llm` is
explicitly hard-rejected as retired; freeze's layer list is only [documentDefaults,
unit]; there is no persona layer. The spec presents "exposing an existing mechanism";
reality is a 2-layer mechanism that must be extended to 5.
(config-schema.ts:102-108,234-241; freeze.ts:50,163-209)
C5. 2/3 — **`uses: scripts/*` reverses a documented security boundary.**
Script run/setup/cwd frontmatter is advisory/display-only today; "registering a bundle
never activates code" is an explicit invariant; executing a third-party bundle's script
at cron time is net-new RCE surface with no activation-policy rule.
(indexer/passes/metadata.ts:140-144; renderers.ts:118-142; core/activation-policy.ts)
## Major
M1. 3/3 — **`params` acquires 3-4 conflicting meanings** (frontmatter schema declarations,
workflow run-args, command placeholder fills, cascade value field) — the same
indictment the spec levels at `prompt:`. Cascaded params also violate the params-are-
non-secret/un-redactable contract and inject undeclared names the flag validators
reject. (ir/params.ts:34-72; param-secrets.ts:9-19; akm-workflow.json:22-32)
M2. 3/3 — **Command-template filling contradicts the real placeholder contract.**
Placeholders are $ARGUMENTS/$1-$9 positional (+ {{name}} advertised, but
fillPlaceholders only substitutes positional {{0}} leniently — a live bug); named
`params:` has no projection onto positionals; the proposed fill-time hard error makes
every $ARGUMENTS command unusable as a target. (renderers.ts:164-186;
matchers.ts:126; agent-dispatch.ts:63-72)
M3. 3/3 — **`env:` list shape is NOT additive.** IR types env as string[]; the input hash
deliberately carries env NAMES ONLY (values never durable); literal values would be
frozen into plan_json, hashed, and surfaced by brief; every resolved value joins
sensitiveValues with no floor, so `LOG_LEVEL: debug` over-redacts "debug" everywhere;
AKM_ITEM fan-out needs per-unit env (env resolves once per step today).
(ir/compile.ts:46; step-work.ts:353-357,387; native-executor.ts:449-457,1163-1193)
M4. 3/3 — **Alias portability claim unimplemented.** BUILTIN_ALIASES has only
claude/opencode columns; `model: sonnet` passes through verbatim to LLM endpoints and
opencode-sdk. (model-aliases.ts:45-76,103-109)
M5. 2/3 — **`agent:` selector needs plumbing + a provenance decision.** systemPrompt and
toolPolicy cannot reach a workflow unit; the self-declared `tools:` policy is gated by
a writable-vs-third-party provenance ceiling applied at the show layer that freeze
knows nothing about. ALSO: the spec's corroboration "command assets already read an
agent: field" is FALSE — it's a type-classification signal; nothing consumes the value.
(native-executor.ts:1026-1035; renderers.ts:250-262; matchers.ts:234-236)
M6. 2/3 — **The kind-gated llm hard error §3.4 argues against is dead code in freeze** —
computed only when kind==="llm", then guarded kind!=="llm" && llm!==undefined
(unreachable); workflows silently DROP llm overrides on agent engines; the live error
exists only on the task path. (freeze.ts:67-73; runner.ts:497-502)
M7. 3/3 — **Migration section overclaims.** Not a byte-rewrite (existing migrator rewrites
scalars in place, journals byte diffs, refuses read-only bundles; .yml→.md renames
break that model); `name:` silently dropped; llm.supportsJsonSchema/contextLength/
enableThinking have no home in the nine value fields; command ARRAYS have no shell
spelling; "strays named by path" is not structural — refusals key on config/DB shape,
not leftover files, and placement/sync filter by extension, so a stray .yml is invisible
or wrongly installed; `<id>.yml` + `<id>.md` collide on one conceptId.
(task-target-ref-migration.ts:27,44-66; asset-placement.ts:159-170; tasks.ts:155-164,414)
M8. 2/3 — **Markdown-task recognition collides with the .md-claiming workflow adapter**
(ordered first; claims any .md without a different type:), so `type: task` is mandatory
and "tasks/ residence" recognition is unimplementable as written; contradicts
okf-support.md's current text; akm-task format-family goldens are marked immutable.
(adapters/index.ts:73-90; akm-workflow-adapter.ts:60-76)
M9. 2/3 — **`run:` shell text defeats akm-bin resolution.** resolveNestedAkmCommand
rewrites bare `akm` in pre-split argv to the current installation; all ten shipped
templates depend on it; shell text makes it impossible without sniffing.
(runner.ts:277-289,343-348; command-executable.ts:10-22)
M10. 2/3 — **Optional prose contradicts two live invariants** — the parser errors on a
missing/empty step section and the IR rejects empty instructions.
(workflows/parser.ts:281-296; ir/schema.ts:434-441)
M11. 1/3 — **uses: tasks/<id> makes freeze IO-dependent, and lint never freezes** — all
composition errors escape lint (freeze has exactly one caller: run start); cross-bundle
ref scoping undefined. (freeze.ts:39-43; runs.ts:241)
M12. 2/3 — **`defaults.on_error` already exists at document level** — "graph keys are
steps-only" silently deletes it; no §8 migration row. (program/schema.ts:210; compile.ts:231)
M13. 1/3 — **Markdown tasks inherit the lint/OKF/improve surface** — the akm adapter
special-cases type:"task" as pure YAML so markdown checks never fire; an
improve-eligible body is now the literal executable prompt of a scheduled agent.
(akm-adapter.ts:388-434)
## Notable minors (deduped)
- `status: draft` overloads OKF lifecycle with scheduler activation — second gate beside
`enabled:`, no precedence, `deprecated` undefined. (2/3)
- `prompt: agents/x` today ships RAW file bytes incl. frontmatter to the model — live bug,
worse than §3.5 states. (2/3)
- §3.5 embodiment sentence reads backwards (--model wins over hint). (1/3)
- Engine nodes are .passthrough() with blacklists — "strict schema" claim overstated;
cwd/shell/env/params would be silently accepted. (2/3)
- Trigger-keys claim inaccurate: the runner also gates `enabled` at fire time. (1/3)
- Value fields on `uses: workflows/*` tasks are structurally inert (run-workflow takes
ref+params only). (1/3)
- Subdir task ids: placement indexes `sub/x` but validateTaskId rejects `/` — inherited
bug; `.yml` hardcoded in id length budget. (1/3)
- Embedded templates: enumeration filters .yml, drops non-command targets; setup's YAML
round-trip would destroy markdown bodies. (2/3)
- Model aliases: gate judgeEngine sits outside the cascade. (1/3)
- Composition can breach the 256 KiB instruction cap; single-file provenance lost. (1/3)
- task_history.target_kind is a persisted 3-value enum the vocabulary outgrows. (1/3)
- cwd × isolation:worktree × llm-runner interaction unspecified. (1/3)
- uses: table covers 5 of ~15 placement types; no rule for the rest. (1/3)
## Verified accurate (all three ran verification passes)
The §2 freeze-cascade description, the UnitDispatchRequest field inventory, the
resolveModel 4-tier chain, engines.ts kind-split (as blacklists), the 368-line parser and
87-line orphan schema counts, the scheduler-ABI stability claim, and the journaled
0.9.0 migration extension point all check out.
## Convergent verdict
Direction (markdown tasks, uses:/run:, steps-are-tasks, cascade *shape*) survives.
The spec's §10 non-goals are false: this IS an IR + freeze + hash-version change and must
be costed as one. Required redesigns before implementation: keep two output concepts;
specify the shell unit kind in IR + dispatch; freeze-time prose concat + hash bump; build
(not "expose") the 5-layer cascade; split params' meanings (GHA's inputs/with split exists
precisely for this); a security/activation-policy design for script execution; rewrite §8
migration against the real migrator mechanics.