UNPKG

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

109 lines (97 loc) • 6.33 kB
# 0005 — The D8 result-vocabulary re-code and its legacy read mapping ## Context Before P1b's D8 vocabulary re-code, a task's result `target.kind` used `"prompt"` for a prepared command/agent-LLM result and one shared `"command"` for every native (shell/script) result, which conflated two materially different execution shapes under one label and mislabeled an LLM-routed dispatch as a literal "prompt". D8 renamed the written vocabulary; the harder problem was that `task_history` is a durable, already-populated table — existing rows on disk still carry the OLD labels, and `readTaskHistory` has to keep returning a coherent shape for both generations of rows forever. ## Decision Moved verbatim from `src/tasks/run/task-result.ts`'s module header: > `TaskRunResult` — the shape every dispatch arm returns — plus the small > cluster of helpers that build one directly: `preparedResultTarget` (the D8 > result-vocabulary projection of a freshly prepared execution), > `finishDisabledTask` (the disabled-task short-circuit), and > `exitCodeForStatus` (the OS-scheduler exit-code mapping). `RunTaskOptions` > — the public options bag `runTask()`, `load-task.ts`, and every dispatch > arm read from — lives here too, alongside the other public-surface types > the compat shim (`src/tasks/runner.ts`) re-exports. > > D8 (§5.3, §6 F-2): `preparedResultTarget`'s prepared-command arm now > returns `{kind:"command", engine}` (formerly `{kind:"prompt", engine}`); > its native arm now returns the bare `{kind:"shell"}` / `{kind:"script"}` > (formerly one shared `{kind:"command"}`) — the arm-specific `cmd` is > added by `run-native-task.ts` once it has actually built the argv, > mirroring the pre-P1b shape where the bare disabled-task projection never > carried `cmd` either. And from `src/tasks/run/task-history.ts`'s module header (the WRITE half — the READ half's mapping rule is kept in the code itself, not moved; see below): > The `task_history` read/write boundary: `appendHistory` (write) and > `readTaskHistory` / `taskHistoryRowToResult` (read). > > D8 (spec §5.3, §6 F-2) result-vocabulary re-code, implemented entirely at > this read/write boundary. WRITE: every row `appendHistory` writes now > carries `targetVocab: 2` in its metadata, and the new target_kind strings > ("command" for a prepared command/agent-LLM result, "shell", "script", > "workflow" unchanged). ## Consequences - Every NEW row written after D8 is unambiguous — `targetVocab: 2` plus the new `target_kind` strings mean a reader never has to guess which generation a row belongs to. - Every OLD row (written before D8, no `targetVocab` marker at all) must keep reading correctly forever — this is not a migration that runs once and finishes; there is no "convert task_history in place" step, because the table is an append-only historical log, not authoring state. The read-side mapping rule is therefore a PERMANENT part of the codebase (`docs/plans/specs/p4-deletions-closeout.md` row B-51: "it reads old rows forever. Deleting it is a review-blocking violation."). - The exact legacy mapping — kept in `src/tasks/run/task-history.ts` itself as a short invariant comment, not summarized here, because a maintainer reading `taskHistoryRowToResult` needs it right there — is: a legacy row (no `targetVocab` marker) maps `"prompt"` → `{kind:"command", engine}`, `"command"` → `{kind:"shell"}`, `"workflow"` unchanged, and anything else (including the new vocabulary's own strings written WITHOUT a marker, which no production writer ever does) → `"unknown"`. The P0-pinned null fallbacks survive: a workflow row's `ref` falls back to `""`, the command/prompt arm's `engine` falls back to `null`. - `SAFE_TASK_ATTEMPT_ERROR_CODES` (`src/tasks/run/attempt-lifecycle.ts`) is a related but separate allowlist — it decides which error CODES are safe to surface verbatim in a `detail.error` column, not which result-kind strings are legal. Do not conflate the two when reading either file. - **Removed 2026-08-28** (review finding against this phase's own close-out spec, `docs/plans/specs/p4-deletions-closeout.md`): `finishDisabledTask` and `preparedResultTarget`, quoted above as part of the moved essay, are no longer in `src/tasks/run/task-result.ts`. `finishDisabledTask`'s only caller — `run-task.ts`'s `shouldSkipUnactivatedTask` — was deleted by the SAME phase's P4-N6 (task source v4 has no document-level `enabled` to skip at fire time), which left `finishDisabledTask` fully dead; `preparedResultTarget` was in turn `finishDisabledTask`'s only caller, so it went dead with it. Deleting them also retired the `"disabled"` `TaskRunStatus` member `finishDisabledTask` was the sole producer of, and the now-unreachable `result.status === "disabled"` branch in `src/commands/tasks/tasks.ts`. This does not touch the D8 vocabulary or the legacy read mapping this ADR is about — every OTHER dispatch arm still builds its own `TaskRunResult.target` per the Decision above, and `task-history.ts`'s read side is unchanged. - **Superseded 2026-09-01**: the "PERMANENT part of the codebase" ruling above (row B-51) is overturned. `task_history` is DB-owned data, and the legacy->current remap is deterministic and total, so it belongs in a one-time schema migration, not three permanently-recurring read-side branches (the mapping had been independently re-implemented at `src/tasks/run/task-history.ts`, `src/commands/health/improve-metrics.ts`'s `isAgentTaskHistoryRow`, and `src/commands/health/windows.ts`). State migration `025-task-history-vocabulary-backfill` (`src/core/state/migrations.ts`) now rewrites every legacy row's `target_kind` (and stamps `targetVocab: 2`) the first time a pre-migration state.db is opened by this or a later release; all three read sites were deleted and now read `target_kind` directly in the current vocabulary. ## Provenance - Source: `src/tasks/run/task-result.ts` module header; `src/tasks/run/task-history.ts` module header (WRITE half only — the READ half's mapping rule stays in the code as a permanent invariant). - Spec: `docs/plans/specs/p1b-model-extraction.md` §5.3, §6 F-2. - Extracted: P4 (`docs/plans/specs/p4-deletions-closeout.md` §4.2), 2026-08-27.