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
741 lines (648 loc) • 45.3 kB
Markdown
# Public API review — tracked issue backlog (v0.9.0-rc.10)
Status: proposed — reconciled 2026-07-26 against the maintainer rulings on
Q-01..Q-19. Items carrying a `> Ruled 2026-07-26 (Q-NN)` note below are
settled, re-scoped, or partially cancelled by a ruling; where a ruling bears on
an item, **the ruling is authoritative**.
Source: implementation-only review of the CLI surface and code (documentation claims
deliberately ignored). Line references are against the tree at the time of review
and may drift; each item names the file and the observable behavior so it can be
re-verified independently.
Note: items below cite `docs/agents/AGENTS.md` / `AGENTS.full.md`. Those files
were deleted during the 0.9.0 docs sweep — they fed nothing at runtime after
R-006 and kept re-diverging from the embedded copy. Their still-true content
now lives in `src/assets/hints/cli-hints-full.md` / `cli-hints-short.md`, which
is what `akm hints` prints and what `loadHints` reads.
Baseline: items were found against `a8464f2` and re-verified 2026-07-26 against
the base branch `claude/0-9-0-release-review-bcjxxu` @ `453a0ba`, which has
since fixed some of them. A further pass on 2026-07-27 @ `2d2edc1` (code
identical to `453a0ba`) re-checked the contested items individually and added
`> Status 2026-07-27:` notes where the tree disagrees with the item as
written — including two items this register had over- or under-reported
(R-004 fixed-in-surface; R-046 superseded by the D6 ruling). Items carrying a **Fixed / Reframed / Rejected on the
base branch** note have been re-checked; the rest were not re-verified
individually and may have been incidentally affected. See the
"Implementation status against the base branch" table in the companion drift
register for per-ruling status.
Companion: `0.9.0-docs-code-drift-register.md` covers the docs-vs-code
comparison this review deliberately excluded — including the intent questions
(Q-01..Q-19) where the decision record and the code conflict. All 19 were ruled
by the maintainer on 2026-07-26; see the **"Maintainer rulings — 2026-07-26"**
table in that register for the authoritative direction.
## Framing
This backlog evaluates akm as what it has grown into: **a self-contained agent
toolkit** — one CLI that gives AI agents the capabilities that normally require a
stack of tools and a database server (indexed knowledge retrieval, secret/env
management, harness-agnostic workflow execution and delegation, scheduling), while
staying agent-, harness-, and content-format-agnostic (e.g. OKF alongside
markdown).
Under that identity, the env/secret manager, the workflow engine, the agent
dispatcher, the OS-scheduler integration, and the improve loop are **in-scope
features, not scope creep**, and no issue below asks for their removal. The
identity instead raises the bar in three specific ways, and most of this backlog
follows from them:
1. **Agents are the primary consumers.** Machine-facing guidance (`akm hints`,
rendered command suggestions, help text, completions) is the product's API
documentation *and* its runtime behavior. Every place it misleads an agent is a
P0, not a docs nit.
2. **The abstraction claims must hold at the seams.** "Harness-agnostic" and
"format-agnostic" are testable properties; half-wired normalizers and
format special-cases are gaps in the core value proposition.
3. **An autonomous toolkit must be trustworthy by default.** Defaults that
schedule LLM rewrites, push git remotes, or mine other tools' session
transcripts need explicit opt-in or loud disclosure.
Severity legend:
- **P0** — the surface actively misleads its consumers, or an advertised path is
broken/unreachable.
- **P1** — incorrect or surprising behavior/semantics; wrong results or side
effects.
- **P2** — coherence, naming, and abstraction-seam debt.
- **P3** — cleanup: dead code, stale comments, duplication.
Counts: 9 × P0, 26 × P1, 21 × P2, 12 × P3 (68 tracked). The counts reflect the
tier labels as written; two items carry ruling-driven re-tier recommendations
not yet applied — R-045 (Q-18 makes it mandated feature work) and the `--format`
half of R-050 (Q-04 makes it Stable-tier contract work, and the item should be
split first). Recompute when those land.
---
## A. Agent-facing guidance correctness (P0)
- **R-001 (P0) — `akm hints` documents a `wiki` command that does not exist.**
The short and full hints corpora instruct agents to run `akm wiki list`,
`akm wiki show <name>`, `akm wiki ingest <name>`, use `--type wiki`, and
`akm show wiki:<name>`. No `wiki` subcommand is registered in
`src/cli.ts:509-550`; an agent following the tool's own onboarding issues
failing commands. Either ship the command or strip it from
`src/assets/hints/cli-hints-*.md` and `docs/agents/AGENTS*.md`.
> Note (Q-02, ruled 2026-07-26): the quoted `akm show wiki:<name>` example
> uses the colon grammar Q-02 removes — the hints sweep hits these lines
> either way; if the wiki content is kept (ship-the-command path), its ref
> examples must move to the slash form.
- **R-002 (P0) — `akm show --format text` renders copy-pasteable commands in the
retired `type:name` ref grammar.** `appendShowDirectives` builds
`` `${assetType}:${r.name}` `` and emits e.g.
`akm feedback 'knowledge:guide' --positive` (`src/output/show-directives.ts:26,57,75,87`),
which the 0.9 parser rejects. The same file renders `workflows/${name}`
correctly two lines down. Every APPLY footer suggestion currently fails when
executed.
> Ruled 2026-07-26 (Q-02): the colon grammar is removed entirely — no alias,
> no deprecation window — so re-accepting `type:name` in the parser is not a
> fix option. `appendShowDirectives` must emit slash refs (as it already does
> for `workflows/${name}`); include `show-directives.ts` in the Q-02 colon
> sweep alongside `search --help`, `akm hints`, and the ref-prefix tests.
> **Still open at base `0475870`** — and now strictly worse: Q-02 landed
> (the colon browse grammar is gone) while `show-directives.ts:26` still
> builds `` `${assetType}:${r.name}` ``. Raise with the Q-02 sweep.
> Status 2026-07-27: confirmed still open; the file now lives at
> `src/output/text/show-directives.ts` (`:26` builds the colon ref that
> `:37,57,75` interpolate into `akm feedback` suggestions). The fix is one
> line — build `assetRef` from the entry's conceptId (`workflows/${name}`
> two lines down shows the pattern) — plus refreshing the show-format text
> goldens that pin the footer.
- **R-003 (P0) — `akm feedback` help contradicts the implementation.** Help
(`src/cli.ts` command description; `src/commands/feedback-cli.ts:189-206`)
states negative feedback "does NOT immediately lower the asset's ranking — run
`akm index` after". The code applies the EMA utility update unconditionally
and immediately for both signals (`feedback-cli.ts:170-176`). Fix the text or
the behavior — currently agents are told to run an unnecessary reindex.
- **R-004 (P0) — feedback threshold hint references a nonexistent command.**
`feedback-cli.ts:365` prints "run `akm events list --type improve_review_needed`";
the command is `akm log`. (`events-list`/`events-tail` shape names and text
formatter headers carry the same retired name — see R-060.)
> Status 2026-07-27: ✅ **fixed in surface** — the printed hint no longer
> exists; `akm events list` survives only in two code comments
> (`feedback-cli.ts:341,365`). Downgrade to comment cleanup and fold into
> R-065; the R-060 shape-name half is unaffected.
- **R-005 (P0) — the shipped conventions fact contradicts `akm mv`.** The
scaffolded `facts/conventions/organization.md` asset teaches "A rename is
delete plus create. There is no command that preserves an asset's identity or
learned state across one" and demonstrates raw `mv` + manual grep — while
top-level `akm mv` exists precisely to move the file, rewrite inbound refs,
and re-key the index row plus usage/salience history in place
(`src/commands/mv-cli.ts`). Agents reading the stash's own guidance will avoid
the safe command and hand-roll the dangerous procedure it automates.
> Ruled 2026-07-26 (Q-01): `akm mv` stays; D3 is superseded. The fix is
> one-directional — rewrite `facts/conventions/organization.md` and the other
> D3-side docs to teach `mv`, and add the missing Experimental tier entry for
> `mv` to STABILITY.md. Removing `mv` is off the table.
- **R-006 (P0) — `akm hints` output depends on install method, and the copies
have drifted.** `loadHints` prefers `<pkgroot>/docs/agents/AGENTS[.full].md`
on disk and falls back to embedded `src/assets/hints/cli-hints-*.md`
(`src/commands/observability-cli.ts:240-257`). npm packs neither `docs/agents/`
nor `src/` (`package.json` `files`), so npm installs always get the embedded
copy while repo checkouts get `docs/` — and the two corpora already differ in
size and content. Single-source the corpus.
> Note (Q-02/Q-08, ruled 2026-07-26): both rulings mandate spelling sweeps of
> `akm hints` (colon→slash ref grammar; `env:`/`secret:`→slash) — land the
> single-sourcing first, or sweep both copies atomically, so the ruled
> changes don't re-drift the corpora.
- **R-007 (P0) — advertised `owner/repo` shorthand is unreachable.**
`add-cli.ts:234` and `git-install.ts:489` advertise the form, but
`isPathLikeRef` returns true for any string containing `/`
(`src/registry/resolve.ts:298`), so `tryParseLocalRef` throws
`Local path not found` before the `parseGithubShorthand` fallback at
`resolve.ts:86-90` can run. The fallback is dead code; `akm add itlackey/akm-stash`
fails. Reorder the dispatch (try shorthand when the local path doesn't exist).
> Note (Q-19, ruled 2026-07-26): the resolver fix (`resolveSourcesForOrigin`
> matching `github:owner/repo` install refs) confirms install-ref shorthand is
> intended surface, so the fix direction here — reorder the dispatch, don't
> retire the advertised form — is sanctioned. Land both together so
> `show //meta`'s "Run: akm add …" hint chain works end to end.
- **R-008 (P0) — `akm registry search` ignores registries added via
`akm registry add`.** `registry-cli.ts:128` calls `searchRegistry` without a
`registries` option; the fallback resolves `DEFAULT_CONFIG.registries`, never
the on-disk config (`src/commands/read/registry-search.ts:35,166`). Meanwhile
`akm search --source registry` passes `config.registries` and honors them.
Configured registries are listed by `registry list` but never searched by
`registry search`.
- **R-009 (P0) — `akm search` rejects the empty query its own help advertises.**
The positional's description says "omit to list all assets"
(`src/commands/read/search-cli.ts:36-39`) but the handler throws
`MISSING_REQUIRED_ARGUMENT` for an empty query (`:79-86`), making the
`enumerateEntries` browse path reachable only via `<type>:` prefix queries or
queries that sanitize to empty. Allow the bare listing or fix the description.
> Note (Q-02, ruled 2026-07-26): the `<type>:` escape hatch named here is
> removed with the colon grammar; post-ruling the browse path is reachable via
> the slash forms (`memories/`, `bundle//`). The empty-query question itself
> is unaffected — update the parenthetical when Q-02 lands.
---
## B. Broken or surprising command behavior (P1)
- **R-010 (P1) — the lockfile locks nothing.** `akm.lock` records
`resolvedVersion`/`resolvedRevision`/`integrity`, but the only field consumed
for behavior is `localRoot` (`src/integrations/lockfile.ts:122-130`).
`akm update` re-resolves the original ref (npm `latest`, git branch HEAD) and
overwrites the lock (`installed-stashes.ts:384`); nothing reproduces a locked
state; stored integrity is never re-verified. Either implement
lock-consumption (frozen installs, integrity re-check) or stop recording the
fields and drop the "lockfile" concept from the surface.
- **R-011 (P1) — git/github installs have no integrity verification at all.**
`verifyArchiveIntegrity` returns early for git sources
(`src/sources/providers/tar-utils.ts:42`); the sha256 computed at install time
is stored and never compared.
- **R-012 (P1) — corrupt lockfile fails silently open.** Unparseable JSON →
`return []`; malformed entries silently filtered (`lockfile.ts:99-106`). Every
managed bundle vanishes from `akm list` with no warning.
- **R-013 (P1) — `akm add --provider` combinations are broken.**
`providerType` is honored only when the target is a remote URL
(`source-manage.ts:71-100`): `akm add lodash --provider npm` silently creates
a *filesystem* bundle at `./lodash`; `akm add <url> --provider npm` stores the
URL as an npm path that later resolves as a filesystem path
(`config-sources.ts:102-104`, `npm.ts:54-59`).
- **R-014 (P1) — `akm add`'s two paths return two incompatible response
shapes.** Path A (`--provider`) returns `SourceAddResult`, Path B returns
`AddResponse`; the shared text renderer reads Path B fields and prints
`Installed undefined (0 directories scanned, 0 total assets indexed)` for
Path A (`output/text/command-format.ts:510-514`).
- **R-015 (P1) — `akm update --all` silently skips plain (non-lock-backed)
sources.** `selectManagedTargets` returns lock-backed installs only
(`installed-stashes.ts:519-522`); a configured plain `git`/`website` bundle is
never refreshed by `--all` and must be named individually. Skip should at
minimum be reported.
> Note (Q-09, ruled 2026-07-26): the user-facing kind axis stays
> `filesystem`/`git`/`npm`/`website`, so the lock-backed "managed" split stays
> behavioral-only with no taxonomy surface — making the skip report asked for
> here the sole disclosure of it.
- **R-016 (P1) — `akm setup --yes` silently skips 7 of 8 wizard steps.** Only
the `stash-dir` step sets `nonInteractive: true` (`setup/setup.ts:436`;
skip logic `setup/steps.ts:98`); embedding, llm, semantic-search, registries,
stash-sources, agent-cli, and output steps never run, while the call-site
comment claims defaults are applied (`setup.ts:776`). Also
`runSetupWithDefaults` hardcodes `online: false` yet still runs network
detection (`setup.ts:779,788`).
- **R-017 (P1) — `akm setup --config/--from` silently drops five valid config
keys.** `ALLOWED_KEYS` (`setup.ts:1013-1027`) omits `index`, `search`,
`feedback`, `workflow`, `archiveRetentionDays` — all present in
`AkmConfigShape` — deleting them with only a stderr warn.
- **R-018 (P1) — `akm curate --type` bypasses the entire curation algorithm.**
With an explicit type the result is `stashHits.slice(0, limit)` — no family
collapse, no intent nudges, no score floor (`curate.ts:255-262`). Two
qualitatively different products under one command name.
- **R-019 (P1) — curate hard-caps registry results at 2 regardless of
`--limit`** (`curate.ts:264-265`), and the fallback path can issue up to 7
registry searches per invocation (`curate.ts:549-566`).
- **R-020 (P1) — `akm show` drops the canonical `ref` from default and summary
output.** `showLocal` computes it, brief/summary builders include it, and the
shaper's `pickFields` allow-lists omit `"ref"` for human and summary shapes
(`output/shapes/helpers.ts:453-496`). Only `--shape agent` emits the one
identifier agents need for follow-up commands.
- **R-021 (P1) — `--detail brief` behaves differently as a flag vs as config
default.** `showCommand` detects only the literal CLI token via
`getFlagValue("--detail")` to trigger `buildBriefResponse`
(`search-cli.ts:223-227`); config `output.detail: "brief"` reaches only the
shaper, which treats brief and normal identically. Same nominal level, two
payloads.
- **R-022 (P1) — `akm index --dry-run` alone performs a full real index.** The
flag is only consulted inside the `--clean` branch (`indexer.ts:715-726`).
- **R-023 (P1) — `akm index --background` claims to manage a PID file; none
exists.** The flag only suppresses the spinner and the final output
(`stash-cli.ts:95-99,127,157`) — the command still runs in the foreground and
prints nothing.
- **R-024 (P1) — `akm graph update --source <bundle>` validates the name, then
extracts the primary stash anyway.** The validated result is discarded and
extraction always uses `sources[0]` (`graph.ts:529-538`,
`graph-extraction.ts:406`).
- **R-025 (P1) — `akm config get <scalar>` returns secrets unredacted.**
`redactConfigValue` only redacts inside objects (`config-cli.ts:66`):
`akm config get embedding.apiKey` prints the literal value while
`akm config get embedding` redacts it. (Mitigated by write-time
sanitization, but the read surface is inconsistent.)
- **R-026 (P1) — `akm env unset` argv workaround can silently drop keys.** The
positional filter removes any token equal to a global flag's *value*
(`env-cli.ts:474-482`), so unsetting a key literally named `json` while
`--format json` is set is a silent no-op; `--target` is wrongly included in
the global-flag list; the comment cites `cli.ts:1335` in a 666-line file.
> Status 2026-07-27: ✅ **fixed** (`0475870`) — the workaround is deleted
> outright. The root cause was citty parsing each command level against only
> its own declared args; `GLOBAL_OUTPUT_ARGS` now declares the global output
> flags on every leaf so their values are consumed by the parser, and keys
> literally named `json` unset correctly (verified end-to-end alongside the
> `sync --format json` case). See R-051's companion note.
- **R-027 (P1) — `akm secret path`/`remove` can resolve to different files than
the read path for the same ref.** Reads use `findEnvSource` (all sources);
mutations use `resolveMutationTarget` (`core/env-secret-ref.ts:64-85` vs
`198-255`).
- **R-028 (P1) — `akm import <url>`'s private-host allowance is gated on a
test-shaped predicate in production.** `allowPrivateHosts` defaults to
`shouldAllowPrivateWebsiteUrlForTests(source)` (`stash-cli.ts:256`,
`knowledge.ts:141-146`).
- **R-029 (P1) — `akm backup` does not back up the stash.** Artifact set is
`config.json`, `state.db`, `workflow.db`, `index.db`
(`scripts/akm-migrate/migration-backup.ts:57-61`) — the markdown assets, env
files, and secrets are excluded. Additionally `--for` accepts only the literal
`"0.9.0"` (`backup-cli.ts:9-16`), and both `backup` and `migrate` hard-require
Bun on PATH under the supported Node install (`migration-tool.ts:21`). Rename
the command to match its migration-snapshot scope, or extend it to cover the
knowledge.
> Note (Q-14, ruled 2026-07-26): `backup` must receive a stability tier and
> either a cli.md section or an explicit internal listing — make the
> rename-vs-extend decision as part of that follow-through.
> Ruled 2026-07-27: **removed from the CLI** — neither renamed nor extended.
> `backup-cli.ts` deleted and the `backup` registration dropped; the
> capability remains at `akm-migrate backup` / `akm-migrate restore` (already
> present in that binary's surface). A cleaner backup story is deferred past
> 0.9.0. Migration guide + 0.9.0 release notes swept to the new spelling; the
> Q-14 tier question for `backup` is moot. ✅ landed same day.
- **R-030 (P1) — `akm health` writes to the append-only event stream on every
run.** The state-db round-trip probe appends a `health_probe` event
(`health/metrics.ts:35-38`); CI polling accumulates rows indefinitely. Use a
transaction-rollback probe or a dedicated probe table.
- **R-031 (P1) — `akm health --format html` overrides `--group-by`** (forces
`groupBy: "run"`, `cli.ts:341-360`) and runs the health computation twice with
a second window read — undocumented and inconsistent with the other formats.
> **Fixed on the base branch (`0475870`).** `akm health` no longer branches on
> `--format` anywhere: `--report` is now a data flag carrying runs/deltas/the
> pending-proposal queue in the envelope, the md/html renderers are pure
> functions of the result, `setHealthHtmlContext` is gone, and the html-only
> `--compare` was removed in favor of `--window-compare`. Breaking: bare
> `akm health --format html` renders the plain check generically — use
> `--report`. Verify and close.
> Follow-up in `453a0ba`: `--report` had been seeding `windowCompare` from
> `--since` unconditionally, so an absolute date/ISO/epoch `--since` reached a
> duration-only parser and threw, and an explicit `--windows` tripped the
> mutual-exclusivity guard. Precedence is now `--window-compare`, then a
> duration-valued `--since`, and nothing alongside `--windows`.
- **R-032 (P1) — unknown-command handling is inconsistent with the exit-code
contract.** `akm totally-bogus` exits 1; `akm wiki list` prints top-level
usage and exits **0**; the CLI's own table says usage errors are exit 2.
Verified against the running CLI.
- **R-033 (P1) — `akm feedback --tag` reads raw argv, including tokens after
`--`.** The declared citty arg is decorative; `parseAllFlagValues("--tag")`
scans the whole argv (`feedback-cli.ts:220,288`). Also `--applied-to` is a
silent no-op for non-lesson refs (`:397`).
- **R-034 (P1) — feedback silently skips the ranking update for non-`user`
event sources.** With `AKM_EVENT_SOURCE` set to anything else — including
invalid values coerced to `"unknown"` — the usage event is recorded but the
utility score is untouched, with no output signal (`feedback-cli.ts:170`,
`usage-events.ts:31-35`).
- **R-035 (P1) — the `AKM_NPM_REGISTRY` hint is false.** The env var only
widens the tarball-host allowlist (`resolve.ts:332-344`); metadata fetches are
hardcoded to `registry.npmjs.org` (`resolve.ts:375`), so installs from a
private mirror do not work despite the error hint saying they do
(`resolve.ts:322`).
---
## C. Trust and safety defaults (P1)
- **R-036 (P1) — `akm setup` installs five cron/launchd/schtasks entries by
default** (hourly/4-hourly/nightly `akm improve` variants,
`commands/tasks/default-tasks.ts:51-92`), and embedded task templates add
`akm extract --auto` every 30 minutes. An OS-scheduler mutation deserves an
explicit, itemized opt-in step in the wizard and a flag in `--yes` mode.
> Ruled 2026-07-26 (Q-05/Q-14): `experimental.improveAutonomy` ships as
> STABILITY.md documents it, and `extract` is tiered Experimental. The
> scheduled `improve` entries must sit behind that off-by-default gate, and
> the embedded `akm extract --auto` template schedules an Experimental-tier
> command — implement the opt-in asked for here as the gate plus an explicit
> wizard step (and a flag in `--yes` mode).
- **R-037 (P1) — `akm improve` commits and pushes the stash by default** for
git-backed stashes (`improve-cli.ts:145-154`). Combined with R-036 this means
a default setup schedules unattended LLM rewrites that push to a remote.
Default should be commit-only (or off) with push as explicit opt-in.
> **Rejected on the base branch (`84bba7a`, 2026-07-26)** — supersedes this
> item's recommendation and the earlier Q-05 note here. `sync.push` stays
> default-on and *outside* the autonomy gate: it publishes already-committed
> content to a remote configured for that purpose rather than mutating or
> deleting assets without review, and it already has two documented opt-outs
> (`sync.push: false`, `--no-push`). D8 gates five lanes — consolidate
> merge/delete/contradict, memory-cleanup and contradiction passes,
> memory-inference writes, and triage `applyMode: "promote"` — and `push`
> joins extract's session indexing as an ungated-by-design write. The R-036
> half (scheduling unattended runs) is unaffected and still stands.
- **R-038 (P1) — `akm extract` mines other harnesses' private session
transcripts (claude-code JSONL, opencode storage) and sends content to the
configured LLM** (`improve/extract.ts`, `integrations/session-logs/`), with a
`--watch` mode that tails those directories indefinitely
(`extract-cli.ts:267-319`). Legitimate feature; needs first-run consent and
prominent disclosure given it reads data the user may not consider akm's.
> Ruled 2026-07-26 (Q-14): `extract` is tiered **Experimental** and must be
> documented (or explicitly listed internal) in cli.md. Land the first-run
> consent and prominent-disclosure requirements in that new STABILITY.md tier
> entry and reference section rather than as a standalone fix.
- **R-039 (P1) — default `akm index` attempts an implicit ~130 MB embedding
model download.** With `semanticSearchMode: "auto"` and no endpoint,
`embedBatch` falls through to `@huggingface/transformers` →
`Xenova/bge-small-en-v1.5` (`llm/embedder.ts:158-176`). Failure downgrades
gracefully, but the download itself should be prompted.
> Ruled (owner ruling 9, X-RULING-9, `f04932f`): `semanticSearchMode` now
> defaults to `"off"` — a bare/headless install never attempts the
> download; the interactive `akm setup` wizard still pre-selects it on,
> with the download warning moved ahead of the prompt.
- **R-040 (P1) — dynamic code loading from the stash.** Wiki snapshot fetchers
`import()` arbitrary `.ts`/`.js` from `<stashDir>/scripts/wiki-fetchers/`
(`sources/snapshot-fetchers/registry.ts:34-46`) — a code-execution surface
reachable from synced content. Should be gated by the same
third-party/first-party activation policy used for env keys. Related: the
built-in YouTube fetcher calls the private InnerTube API with a hardcoded key
impersonating the Android client (`snapshot-fetchers/youtube.ts:35-42`) —
fragile and worth an explicit support decision.
- **R-041 (P1) — `akm graph export --out` writes anywhere with default 0644**
(`graph.ts:302-315`) while `akm env export` enforces 0600. Graph contents are
LLM-extracted from the user's knowledge; align the posture.
---
## D. Abstraction-seam gaps (P2) — where the agnosticism claims leak
- **R-042 (P2) — harness result normalization is half-wired.** All harnesses
ship `result-extractor.ts` normalizers, but only the workflow native engine
consumes them (`workflows/exec/native-executor.ts:1259-1267`); `akm agent`,
`akm propose`, and reflect return raw harness stdout. Harness-agnosticism
currently holds only inside `workflow run`.
- **R-043 (P2) — OKF is special-cased in `akm show`.** OKF-adapter entries
bypass the renderer registry for a hand-rolled markdown path
(`show.ts:369-370,545-586`). If format-agnosticism is the goal, OKF should go
through the same adapter/renderer seam as markdown assets.
> Ruled 2026-07-26 (Q-07): the parser half of this special-casing is settled
> — the ref-parser seam is fixed centrally so opaque conceptIds are accepted
> across graph, tasks, improve, proposals, the utility repo, and the indexer
> walk, removing `show`'s need to bypass `parseRefInput`. The
> renderer-registry half remains this item's ask and aligns with the ruling.
> **Largely fixed on the base branch (`453a0ba`).** `show`'s
> `indexedEntry?.adapterId === "okf"` branch became `usesIndexedProjection`
> (`read/show.ts:536-559`): OKF plus any indexed item whose type AKM does not
> place keeps its adapter's projection, so a `website` entry no longer comes
> back typed as AKM placement and a generic `file` no longer loses its
> content. OKF is still named explicitly — deliberately, since its `type` is
> open frontmatter and may collide with an AKM placement type — and
> `env`/`secret` stay pinned to the dotenv renderer so values are never
> dumped. What remains of this item is only that residual OKF name-check;
> the general seam now exists.
- **R-044 (P2) — two disjoint secret systems; the toolkit doesn't dogfood its
own.** akm's LLM/embedding keys come from env vars or `$VAR` config refs
(`config.ts:437-444`) and cannot be supplied from `akm secret`. Letting config
reference `secrets/<name>` would close the loop and demonstrate the feature.
> Ruled 2026-07-26 (Q-08): slash spelling only, no colon alias — if config
> learns to reference managed secrets, the spelling must be the slash form
> `secrets/<name>` (e.g. `secrets/OPENAI_KEY`), not the `secret:NAME` form
> this item originally proposed.
- **R-045 (P2) — the `instruction` asset type is half-registered.** It exists
in `KNOWN_TYPES`/`TYPE_PRESENTATION` but not `PLACEMENT_SPECS`
(`core/asset/asset-placement.ts:89-166` vs `core/recognition-util.ts:92-106`),
so it is invisible to `akm info`, unbrowsable via prefix queries, and its refs
don't round-trip. Register or remove it; add a compile-time exhaustiveness
check tying the two registries together.
> Ruled 2026-07-26 (Q-18): "register or remove" is settled — **register**.
> The two-registry split stays the core truth (`KNOWN_TYPES` = presentation
> vocabulary, `PLACEMENT_SPECS` = stash-resident subset), so the
> exhaustiveness check is a **subset** assertion (placement keys ⊆ known
> types), not equality. `instruction` gains a `PLACEMENT_SPECS` entry with an
> `instructions/` stash dir on the markdown spec, becoming creatable,
> browsable, and listed by `akm info` like knowledge; adapter-emitted
> `instruction` docs round-trip via the Q-07 opaque-conceptId fix. This is now
> mandated 0.9.0 feature work — **raise to P1**.
- **R-046 (P2) — `--type` is unvalidated everywhere, and the help enumerates 11
of the 13 real types** (missing `fact`, `task`, `session`;
`search-cli.ts:41-43,125-127`). `--type bogus` silently returns zero hits.
Validate against `placementTypes()` and generate the help list from it.
> Note (Q-18, ruled 2026-07-26): `instruction` becomes a placement type,
> shifting the real-type count from 13 to 14 — a further reason to generate
> the help list and validation from `placementTypes()` rather than
> re-hardcoding.
> Status 2026-07-27: **superseded by the D6 decision** — the validation half
> of this item conflicts with a maintainer ruling this register missed.
> `type` is a free-form open string BY DESIGN (adapters emit `website`,
> `wiki-source`, OKF frontmatter types outside any placement list), so
> validating against `placementTypes()` would reject valid filters; D6 and
> `STABILITY.md`'s Stable-tier bullet both pin "deliberately not validated:
> an unrecognized type returns zero hits, not an error". The help half is
> already fixed: `search`/`curate` `--type` help now enumerates all 14
> built-ins **and states the set is open** (`search-cli.ts:43`). Nothing
> remains except keeping that string current — do not implement validation.
- **R-047 (P2) — same concept, two flag names: `search --filter` vs
`show --scope`** for the identical `user=/agent=/run=/channel=` narrowing.
Pick one (keep the other as a hidden alias for one release).
> Ruled 2026-07-27: **unify on `--filter`, remove `--scope` with no alias.**
> ✅ landed same day: `show` declares `filter`, parses repeatable `--filter`
> through the same `parseScopeFilterFlags` helper as `search`; cli.md swept.
> The removed spelling fails loudly rather than silently un-narrowing —
> citty drops the unknown flag and its value lands in the positional slot,
> where `rejectExtraShowPositionals` raises exit 2 (pinned by a new CLI test
> in `scope-flags.test.ts`).
- **R-048 (P2) — package identity: the npm description still says "package
manager".** Under the accepted toolkit framing this misstates the product to
its distribution channel and sets the wrong expectations for the verbs in §E.
Reposition the one-liner (`package.json:5`).
---
## E. Verb semantics and surface coherence (P2)
- **R-049 (P2) — `update` / `upgrade` / `sync` are three unrelated operations
wearing package-manager names.** Refresh sources / self-update the akm binary
/ git-commit-and-push the stash. Given the toolkit identity, consider
`akm self update` (or `self-upgrade`) and `akm stash sync` (or `save`) to
free the ecosystem-conventional meanings; `sync` still emits
`eventType: "save"` and `output("save", …)` from its retired name
(`sources-cli.ts:181-185`).
- **R-050 (P2) — global flags that are mostly not global.** `--detail` is an
identity no-op for `init`/`index`/`info`/`import`/`remember` (and
brief==normal on `show`); `--format md|html` is accepted globally but valid
only for `health`; `--shape summary` only for `show`. Either scope the flags
to the commands that honor them in help output, or implement them uniformly.
> Ruled 2026-07-26 (Q-04): for `--format` the either/or is settled —
> implement basic support on every non-exempt command, with `text` and `md`
> collapsed into a single format, plus alignment of the persisted
> `output.format` schema and the `INVALID_FORMAT_VALUE` hint. Only the
> `--detail`/`--shape` halves of this item remain an open choice. Split the
> ruled `--format` work into its own item and **raise it to P1**: it is
> mandated Stable-tier contract work, not coherence debt.
> Status 2026-07-27: the "implement on every non-exempt command" half
> **landed** (D7: renderer registries, generic md/html fallbacks, declared
> exemptions in `format-exempt.ts`, `output.format` schema widened to six),
> but as SIX formats — the text/md collapse was not applied, and the
> `INVALID_FORMAT_VALUE` hint still lists four values (`errors.ts:111`).
> The Q-04 conflict in the drift register is the open decision; the hint
> fix is deferred until that call so it is corrected once, to the final set.
- **R-051 (P2) — per-command `--format/--detail/--shape` declarations are
decorative.** Declared on `search`/`curate`/`show` but never read from
`args`; values come from the process-wide singleton (`search-cli.ts:63-64,
131-136`). Users see help entries for flags the handler ignores.
> **Reframed by the base branch (`0475870`).** The redundant declarations turn
> out to be load-bearing: citty parses each command level against only its own
> declared args, so a root-declared global was unknown at the leaf and its
> space-separated value fell through as a positional (`akm sync --format json`
> synced a bundle named "json"). `GLOBAL_OUTPUT_ARGS` now redeclares
> `--format`/`--detail`/`--shape`/`--output` on **every** leaf, parse-only,
> with the mode still read once from the invocation singleton
> (`cli/shared.ts:138-159`). The declarations are no longer decorative — this
> item is closed as fixed-by-design; only the help-text clarity ask remains.
> **Completed in `453a0ba`:** the first pass covered only `defineJsonCommand`
> leaves, so `improve`, `propose`, and `tasks run` (plain `defineCommand`)
> still had citty parse `--format` as a boolean and spill its value into the
> next positional — `improve --dry-run --format md` read "md" as the scope,
> `tasks run --format md nightly` lost the task id. Each leaf now declares
> the flags.
- **R-052 (P2) — bash completions advertise wrong values.** `FLAG_VALUES`
offers `--detail … summary` (a `--shape` value that errors) and omits
`md`/`html` from `--format` (see the Q-04 note below); the table is keyed globally by flag name, so
`graph --source <TAB>` suggests search's `stash registry both`
(`commands/completions.ts:15-21`). The `completions` handler is also the only
one not wrapped in `runWithJsonErrors`, so a non-bash shell error surfaces as
`UNHANDLED_REJECTION`/exit 1 instead of a usage error/exit 2 (`cli.ts:449-452`).
> Ruled 2026-07-26 (Q-04): `text` and `md` collapse into a single format, so
> the accepted `--format` value set is changing — fix the completions table
> against the post-Q-04 set rather than adding `md`/`html` to today's list.
- **R-053 (P2) — bare-group behavior is inconsistent three ways.**
`akm log`/`akm lessons` print help and exit 0; `akm backup`/`akm migrate`
throw usage errors (exit 2); `akm env`/`akm config`/`akm graph` run a default
action. Pick one convention.
- **R-054 (P2) — `akm lessons` advertises "strength queries" that don't
exist.** The group description promises them (`observability-cli.ts:205`);
`lessonStrength` is written by feedback and read by ranking but not queryable
from any command. Implement `lessons strength <ref>` or fix the description.
- **R-055 (P2) — reads are writes, undisclosed.** `search`/`show`/`curate`
append events, insert usage rows, and search bumps EMA utility scores —
running a search changes future rankings (`search.ts:393`). Legitimate design
for a learning index, but it should be stated on the command surface and
offer an opt-out (the internal `skipLogging` already exists).
- **R-056 (P2) — `akm index` writes to `config.json`** (persisting detected
adapters via `mutateConfig`, `indexer.ts:600-622`). Surprising for an "build
the index" command; disclose or relocate to `add`/`setup`.
- **R-057 (P2) — `akm info` omits the most basic facts.** No `stashDir`, no
`defaultBundle`, no per-type asset counts (`info.ts:64-78`); `searchModes`
reports semantic readiness from a cached status file, not a live probe; its
stats warning writes raw `process.stderr`, bypassing `--quiet`
(`info.ts:103`).
- **R-058 (P2) — remove/update destructive-action asymmetry.** `akm remove`
prompts for confirmation; `akm update`, which can `rm -rf` the prior
materialized root (`installed-stashes.ts:421-428`), does not. Also removing a
plain git/website bundle leaves its mirror cache behind, and `akm clone` of an
unregistered origin downloads a full package into the cache with no bundle or
lock record (orphaned disk).
- **R-059 (P2) — two parallel cache schemes for the same upstream.** Managed
installs key `getRegistryCacheDir()` by version/revision; plain git/website
mirrors key `getRegistryIndexCacheDir()` by URL hash — adding the same repo
both ways stores two copies.
- **R-060 (P2) — `akm log` is still `akm events` internally.** Output shapes
`events-list`/`events-tail`, text headers, and the R-004 hint all use the old
name. Rename the shapes at the next schemaVersion bump.
- **R-061 (P2) — argv is parsed in three layers with four workaround
patterns.** citty; the pre-citty `normalizeShowArgv` rewrite; the
`ParsedInvocation` raw-argv singleton for repeatable flags; plus ad-hoc
`process.argv.includes("--no-init")` and the `remember` positional-swallowing
heuristic that covers `--format`/`--detail` but not `--shape`/`--output`
(`remember.ts:303-370`). Consolidate on one parse or replace the framework —
each workaround has already produced user-visible bugs (R-026, R-033).
> Ruled 2026-07-26 (Q-03): `normalizeShowArgv` and the hidden `--akm*` flags
> it feeds are deleted with the show view modes, removing one of the four
> workaround patterns outright. Consolidate the remaining three; do not invest
> further in the pre-citty rewrite layer.
> Status 2026-07-27: two of the four are gone — `normalizeShowArgv` (Q-03)
> and the env-unset value filter (R-026, root-caused by
> `GLOBAL_OUTPUT_ARGS`). Remaining: the `ParsedInvocation` singleton (now
> load-bearing by design — the one-parse rule), ad-hoc
> `process.argv.includes` probes, and the `remember` positional-swallowing
> heuristic, which `GLOBAL_OUTPUT_ARGS` on the `remember` leaf may make
> deletable — verify against `remember.ts:303-370` before consolidating.
- **R-062 (P2) — `--showSimilar` is the CLI's only camelCase flag**
(`remember-cli.ts:141-144`); everything else is kebab-case, and
`migrate apply` declares `dryRun` where the codebase convention is
`"dry-run"`.
---
## F. Cleanup batch (P3)
- **R-063 (P3) — confirmed dead code.** `parseGithubShorthand` branch
(unreachable, see R-007); `injectIntoEnv` (`env.ts:115` — mutates
`process.env`, forbidden by its own module docstring); secret `listNames`
(`secret.ts:96`); `loadGraphMetaOnly` / `loadGraphEntitiesByPath`
(`graph-db.ts:343,403`); `parseConfigValue` shim and the unreachable
`toggleComponent` throw and dead `--list` flag (`config-cli.ts:44,129,136`);
`listStashes`/`SourceListResult` (`source-manage.ts:36-39`);
`resolveRegistries` on the search path (`registry-search.ts:35`); the
`semanticSearch` projection branch (`helpers.ts:290`); curate's
colon-grammar family regexes (`curate.ts:769,779`); the
`session-log-failures` advisory hardwired to pass (`checks.ts:464-482`);
`setup.taskSchedules` schema key nothing reads or writes.
> Note (Q-02, ruled 2026-07-26): curate's colon-grammar family regexes
> (`curate.ts:769,779`) are now part of the mandated colon-removal sweep —
> schedule their deletion with the Q-02 work, not as standalone cleanup.
- **R-064 (P3) — dead fields threaded through live signatures.**
`IndexOptions.reEnrich` (flag removed, parameter still threaded three layers;
`stash-cli.ts:103-112`, `indexer.ts:179-183`); `IndexResponse.graphQuality`
(declared, never assigned); `acceptedActions` "always 0 since the 0.9.0
confidence-gate deletion" (`loop-stages.ts:746-750`); `durationMs` computed
then `void`-ed (`improve-cli.ts:359-360`); `SourceEntry.updatable` and
`.provider` set-but-never-read; `installedKitCount` (retired "Agent Kit
Manager" naming) still in the public JSON envelope of add/remove/update;
`workflow.db` still in the backup artifact set after the 0.9 state.db merge.
- **R-065 (P3) — stale comments that assert the opposite of adjacent code.**
`init.ts:9` ("ensures ripgrep is available" — nothing uses ripgrep);
`info.ts:57-61` (claims to avoid the `getDbPath()` call it makes);
`graph-extraction.ts:34-37` (describes retired fs.writeFile storage);
`history.ts:11-15` (`events.jsonl` vs actual `state.db`); the four
`TODO(R2)` markers in `workflows/program/{parser,schema}.ts` and
`ir/compile.ts` describing gate loops / retry / isolation / schema validation
as unimplemented — all four are implemented in `native-executor.ts`;
`reflect.ts:6` / `distill.ts` / `consolidate.ts` headers documenting
`akm reflect` etc. as CLI commands that were never registered;
`metrics.ts:270-274` documenting a deleted metric; `setup.ts:776` (R-016).
- **R-066 (P3) — duplicated logic.** Two secret-directory walkers
(`secret.ts:96-115` vs `secret-cli.ts:50-74`); `ACTIVE_RUN_WARN_MS` defined
twice (`health/types-result.ts:37`, `health/checks.ts:20`); the `--limit`
guard copy-pasted five times in `graph.ts`; `assertSetupSandbox` invoked
2–3× per setup path; `getEffectiveRegistries()` exists but `registry list`
re-implements it inline.
- **R-067 (P3) — `process.exit()` mid-command bypasses cleanup.**
`env run`, `secret run` (`env-cli.ts:305`, `secret-cli.ts:257`), and
`runMigrationTool` exit directly, skipping the `disposeDispatchResources()`
finally in `cli.ts:663-665`.
- **R-068 (P3) — misc small fixes.** `show lines` coerces `start` with
`Number` and `end` with `parseInt` (NaN accepted, `search-cli.ts:206-212`);
`init` never checks `git init`'s exit status (`init.ts:183-188`);
`.akm-include` referenced in seven comments but the real mechanism is
`package.json` `akm.include`; the `semver` npm package is a runtime
dependency imported nowhere in `src/` while a hand-rolled subset rejects
valid ranges (`registry/semver.ts:58-99`); `graphPath` in every graph
command's output is the index.db path (field name fossil,
`graph-db.ts:471`); graph read commands open/close index.db twice per
invocation; `--belief` is described as memory-only but filters any entry
with a `beliefState` (`db-search.ts:770-787`).
> Ruled 2026-07-26 (Q-03): drop the `show lines` coercion sub-item — the
> `lines` view mode and the `normalizeShowArgv`/`--akmStart`/`--akmEnd`
> plumbing it lives in are deleted per D2, with fragment refs as the
> successor. The remaining sub-items stand.
> Status 2026-07-27: D2 has landed, so the `show lines` sub-item is now moot
> in code, not just ruled away. The remaining sub-items were not
> individually re-verified.
---
## Explicitly reclassified as in-scope (no issue filed)
Under the agent-toolkit identity, the following were reviewed and are **not**
defects of scope, only subject to the coherence/safety issues above:
`akm env`/`akm secret` (including run-injection and `${secret:}` interpolation),
`akm agent` and the ten harness integrations, the workflow engine including the
`brief`/`report` external-driver protocol, `akm tasks` and its three OS
backends, the improve/proposal/collapse-detector loop, and the health
observability surface.
Also noted as genuinely strong and worth preserving: the journaled
fs-transaction promotion/revert machinery, tar-extraction and website-crawl
SSRF hardening, git-URL scheme allowlisting, the exact-path git staging in
stash sync, the values-never-in-stdout secret discipline, and the uniform
`{ok, error, code, hint}` error envelope.