akm-cli 0.9.17 → 0.9.19-alpha.1

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 (57) hide show
  1. package/CHANGELOG.md +157 -0
  2. package/STABILITY.md +2 -1
  3. package/dist/assets/hints/cli-hints-full.md +4 -2
  4. package/dist/assets/hints/cli-hints-short.md +5 -3
  5. package/dist/assets/prompts/reflect-feedback-framing.md +1 -1
  6. package/dist/commands/feedback-cli.js +1 -1
  7. package/dist/commands/health/checks.js +6 -6
  8. package/dist/commands/health.js +3 -3
  9. package/dist/commands/improve/consolidate/coverage.js +132 -0
  10. package/dist/commands/improve/consolidate/pair-pass.js +31 -21
  11. package/dist/commands/improve/consolidate.js +46 -13
  12. package/dist/commands/improve/distill.js +8 -4
  13. package/dist/commands/improve/eligibility.js +39 -9
  14. package/dist/commands/improve/improve-cli.js +9 -6
  15. package/dist/commands/improve/improve.js +13 -4
  16. package/dist/commands/improve/ledger.js +2 -2
  17. package/dist/commands/improve/loop-stages.js +2 -0
  18. package/dist/commands/improve/preparation.js +3 -1
  19. package/dist/commands/improve/reflect.js +2 -2
  20. package/dist/commands/improve/stage.js +43 -12
  21. package/dist/commands/proposal/diff-format.js +21 -0
  22. package/dist/commands/proposal/proposal-cli.js +48 -10
  23. package/dist/commands/proposal/proposal-types.js +11 -0
  24. package/dist/commands/proposal/proposal.js +60 -5
  25. package/dist/commands/proposal/repository.js +250 -18
  26. package/dist/commands/read/knowledge.js +13 -11
  27. package/dist/commands/read/remember-cli.js +7 -3
  28. package/dist/commands/sources/source-clone.js +1 -1
  29. package/dist/commands/tasks/tasks-cli.js +1 -1
  30. package/dist/commands/tasks/tasks.js +10 -3
  31. package/dist/core/mutation-target.js +8 -3
  32. package/dist/core/write-source.js +3 -2
  33. package/dist/indexer/usage/usage-events.js +2 -1
  34. package/dist/output/shapes/helpers.js +7 -0
  35. package/dist/output/shapes/passthrough.js +1 -0
  36. package/dist/output/shapes/proposal/reopen.js +14 -0
  37. package/dist/output/shapes.js +2 -0
  38. package/dist/output/text/helpers.js +1 -1
  39. package/dist/output/text/proposal/proposal.js +3 -1
  40. package/dist/output/text/proposal-format.js +87 -32
  41. package/dist/scripts/akm-migrate-node.js +102 -25
  42. package/dist/scripts/akm-migrate.js +102 -25
  43. package/dist/storage/repositories/improve-ledger-repository.js +65 -6
  44. package/dist/storage/repositories/index-vec-repository.js +13 -8
  45. package/dist/storage/repositories/proposals-repository.js +23 -0
  46. package/dist/storage/sqlite-read-snapshot.js +46 -2
  47. package/dist/storage/state-db-integrity.js +12 -9
  48. package/dist/tasks/run/load-task.js +5 -1
  49. package/docs/migration/README.md +1 -0
  50. package/docs/migration/release-notes/0.9.19.md +134 -0
  51. package/docs/migration/release-notes/README.md +5 -0
  52. package/docs/migration/v0.7-to-v0.8.md +2 -2
  53. package/docs/migration/v0.8-to-v0.9.md +5 -1
  54. package/docs/reference/cli.md +189 -28
  55. package/docs/reference/configuration.md +9 -8
  56. package/docs/reference/data-and-telemetry.md +24 -16
  57. package/package.json +1 -1
@@ -1610,7 +1610,7 @@ akm feedback skills/code-review --negative --reason "flaky" --tag slice:train --
1610
1610
  | --- | --- |
1611
1611
  | `--positive` | Record positive feedback (use when an asset was helpful) |
1612
1612
  | `--negative` | Record negative feedback (use when an asset was not useful) |
1613
- | `--reason` | Optional text reason to attach to the feedback event (required for negative feedback by default) |
1613
+ | `--reason` | What was wrong with (or right about) the asset's content; not for `akm` command errors. Attached to the feedback event (required for negative feedback by default) |
1614
1614
  | `--failure-mode` | Structured failure-mode taxonomy for negative feedback: `incorrect`, `outdated`, `dangerous`, `incomplete`, `redundant`. Stored alongside `--reason` in event metadata for the distill pipeline. |
1615
1615
  | `--tag` | Tag to attach to the feedback (repeatable, e.g. `--tag slice:train --tag team:platform`) |
1616
1616
  | `--applied-to <ref>` | Credit a `lessons/<name>` lesson that helped resolve this task. When combined with `--positive`, appends this feedback ref to the target lesson's `lessonStrength[]` frontmatter array (dedup, idempotent). A non-lesson target, or a missing `--positive`, produces a warning rather than silently doing nothing. |
@@ -2429,10 +2429,10 @@ akm improve report --since 7d # ...aggregated over every real run start
2429
2429
  | `--task` | Optional extra guidance for this improvement pass |
2430
2430
  | `--dry-run` | Show the schema-v2 result on stdout without creating config, data, state, cache, bundle, log, or result artifacts. Dry-run results are never persisted, including on errors or signals. |
2431
2431
  | `--plan` | Alias for `--dry-run` (#947). Sets the exact same internal flag; no separate code path. Prefer this spelling when the goal is previewing `plan.processes` (resolved process -> engine -> model routing) rather than checking what would be written. |
2432
- | `--bundle` | Select the proposal/write target; when the ref scope is bundle-qualified, it must name the same bundle |
2433
- | `--limit <n>` | Base cap for ordinary assets (highest utility first); configured replay slots are additive |
2432
+ | `--bundle` | Select the bundle the run improves and writes to (default: `defaultWriteTarget`, else the working bundle); only that bundle's assets are planned. When the ref scope is bundle-qualified, it must name the same bundle |
2433
+ | `--limit <n>` | Cap the refs the run processes, highest salience first (refs routed to distill only come last). Overrides the strategy's `processes.reflect.limit` and `limit` |
2434
2434
  | `--timeout-ms <ms>` | Wall-clock budget for the run (default: `7200000` = 2 hours) |
2435
- | `--require-feedback-signal` | Only process assets with recent feedback signals |
2435
+ | `--require-feedback-signal` | Only process assets with recent feedback signals: turns the fallback lanes (high salience, proactive maintenance) off for the run |
2436
2436
  | `--strategy <name>` | Override the active improve strategy (a built-in or entry under `improve.strategies`) |
2437
2437
  | `--json-to-stdout` | Also emit the full persisted JSON result on stdout for a live run. Without this flag, stdout stays empty. Dry-runs always emit their result and are never persisted. |
2438
2438
  | `--skip-if-locked` | If another improve run already holds the lock, skip gracefully (exit 0) instead of failing with "already running" (exit 75, `TransientError`, code `IMPROVE_LOCK_HELD` — field follow-up to #948: two legitimate `improve` invocations colliding on this lock is ordinary, retryable contention, not a broken config file). Use for high-frequency scheduled runs so they don't pile up failures while a longer run is in progress. |
@@ -2446,6 +2446,17 @@ ref-scoped improvement. It owns the memory-cleanup and lesson-distillation
2446
2446
  flow. A qualified scope such as `team//skills/code-review` selects that bundle;
2447
2447
  a different explicit `--bundle` is a usage error.
2448
2448
 
2449
+ A run improves one bundle, the one it writes to, and plans only that bundle's
2450
+ assets: an asset that lives in another bundle is left alone even when that
2451
+ bundle is writable, and a bare ref scope (`akm improve skills/x`) resolves
2452
+ inside the write target only. To improve another bundle, name it
2453
+ (`akm improve --bundle team`, or `akm improve team//skills/code-review`). A
2454
+ scheduled `akm improve` therefore covers only its write target: schedule one
2455
+ `akm improve --bundle <name>` run per other bundle. `--dry-run` and `--plan`
2456
+ resolve the bundle the way a live run does (the working bundle starts from
2457
+ `AKM_BUNDLE_DIR`, then `defaultBundle`), so they preview the bundle a live run
2458
+ improves.
2459
+
2449
2460
  Every stage records what it did with each asset in the improve ledger
2450
2461
  (`improve_ledger` in `state.db`) and reads it before any model call: an asset
2451
2462
  whose proposal was rejected waits 14 days (reflect), 30 days (distill) or 7
@@ -2453,10 +2464,25 @@ days (other stages) before it is tried again; an expired proposal waits one
2453
2464
  day; an asset a stage looked at and left unchanged is revisited after 7 days,
2454
2465
  or as soon as new feedback (or, for consolidation, an edit) arrives.
2455
2466
 
2456
- Built-in `default` and `frequent` leave the improve-stage extract process off,
2457
- and `default` plus `reflect-distill` leave proactive maintenance off. Use the
2458
- explicit `proactive-maintenance` strategy or set the selected strategy's
2459
- process `enabled: true` to opt in. The stage toggle does not disable a direct
2467
+ Consolidation's promotion of a memory into `knowledge/` is the exception to the
2468
+ 7-day rule: once a promotion is accepted or rejected, its memory is not offered
2469
+ to the model again until its body changes, however long that takes. The ledger
2470
+ records the body hash the promotion was decided against and compares it with
2471
+ the memory's current body (frontmatter edits do not count), the same
2472
+ content-driven rule the consolidate pair pass uses. A promotion decided by an
2473
+ older release, which recorded no hash, keeps the old windows. Consolidation
2474
+ also does not promote a memory that `knowledge/` already covers: before it
2475
+ queues a promotion it compares the memory with the 20 `knowledge/` docs in its
2476
+ bundle nearest to it by stored vector, and skips the memory when one of them
2477
+ holds at least half of its distinct 5-word shingles (skip reason
2478
+ `dedup_covered_by_knowledge` in the result's `consolidation.skipReasons`). A
2479
+ covering doc that ranks lower than the 20th nearest goes unseen. With no stored
2480
+ vector (semantic search off, or the memory not indexed yet) that check does
2481
+ nothing and the exact slug and whole-body checks still apply.
2482
+
2483
+ No built-in strategy turns the improve-stage extract process on, and only
2484
+ `proactive-maintenance` turns proactive maintenance on. Use that strategy or
2485
+ set the selected strategy's process `enabled: true` to opt in. The stage toggle does not disable a direct
2460
2486
  `akm proposal extract --type <harness>` or `akm proposal extract --auto`
2461
2487
  invocation.
2462
2488
 
@@ -2464,9 +2490,10 @@ The maintenance pass run by `improve` also expires stale proposals: any pending
2464
2490
  proposal older than the top-level `archiveRetentionDays` config key (default
2465
2491
  **90**, not `improve.archiveRetentionDays`) is moved to the archive with the
2466
2492
  reason `expired: no action within retention window` and a `proposal_expired`
2467
- event is emitted. Set `archiveRetentionDays` to `0` to disable expiration
2468
- entirely. The total expired count surfaces in the improve result as
2469
- `proposalsExpired`.
2493
+ event is emitted (a proposal put back by `akm proposal reopen` is counted
2494
+ from the reopen, not its original creation). Set `archiveRetentionDays` to `0`
2495
+ to disable expiration entirely. The total expired count surfaces in the improve
2496
+ result as `proposalsExpired`.
2470
2497
 
2471
2498
  `improve` never promotes proposals on its own — there is no confidence gate.
2472
2499
  Every generated proposal lands in the queue with a `pending` status
@@ -2475,9 +2502,17 @@ the drain engine. Reflect still emits a `confidence` score (0..1) in its JSON
2475
2502
  response schema; it is recorded on the proposal for triage and ranking, but no
2476
2503
  threshold auto-accepts anything.
2477
2504
 
2478
- Selection behavior defaults to recent feedback signals first, with a
2479
- zero-feedback retrieval fallback for high-traffic refs. Use
2480
- `--require-feedback-signal` to disable retrieval fallback for the run.
2505
+ Selection picks the refs with feedback (a signal or a note, in the last 30
2506
+ days) newer than the stage's last ledger attempt. Two fallback lanes add refs
2507
+ with no such feedback: high salience (content-scored refs at or above
2508
+ `improve.salience.salienceThreshold`, default `0.75`, that were never reflected,
2509
+ capped at 10% of the limit, at least one ref) and, in a strategy that enables
2510
+ `proactiveMaintenance`, refs due for a revisit. Both pick only refs in the
2511
+ [retrieval scope](https://github.com/itlackey/akm/blob/main/docs/architecture/improvement.md#retrieval-scope): returned by
2512
+ `search`, `curate` or `show`, or named by feedback, in the last 90 days, or new
2513
+ material no improve stage has processed. The picks are ranked by salience and
2514
+ cut to the limit; an explicit ref scope bypasses every gate. Use
2515
+ `--require-feedback-signal` to turn the fallback lanes off for the run.
2481
2516
 
2482
2517
  When the active strategy enables a process (or the triage judgment engine)
2483
2518
  whose engine or credential cannot be resolved in this process's environment,
@@ -2514,9 +2549,9 @@ ref in the requested scope. The `plan` object preserves both views: raw scope
2514
2549
  size and per-gate removals, configured and effective caps, final ranked refs
2515
2550
  and their selection lanes, proactive and consolidation statistics, stage
2516
2551
  decisions, triage mode/caps, and `snapshot.status`/`snapshot.reason` for the
2517
- read-side index boundary. `limits.effective` is the ordinary-ref base cap;
2518
- `limits.additiveReplayAllowance` is the separate replay budget, and
2519
- `limits.totalCeiling` is their finite sum (omitted when the base run is
2552
+ read-side index boundary. `limits.effective` is the cap on the refs the run
2553
+ dispatches; the replay lane is retired, so `limits.additiveReplayAllowance` is
2554
+ always `0` and `limits.totalCeiling` equals the cap (omitted when the run is
2520
2555
  unbounded). A missing or incompatible index is an explicit empty snapshot and
2521
2556
  is not created or migrated. `plan.mode` is `estimate` and `plan.dispatch` is
2522
2557
  `false`; live JSON results use the same projection with `mode: "execution"`.
@@ -2587,7 +2622,8 @@ which never make an attributable LLM call themselves) the active strategy
2587
2622
  enabled but that ended the run with zero calls, each with a `reason` drawn
2588
2623
  from the existing skip-reason vocabulary: `"engine_unavailable"` (also in
2589
2624
  `skippedProcesses`), `"autonomy_gated"`, `"strategy_filtered_all_passes"`, a
2590
- reflect/distill dominant skip reason (e.g. `"no_new_signal"`, `"cooldown"`),
2625
+ reflect/distill dominant skip reason (e.g. `"no_change"` for reflect,
2626
+ `"no new signal since last proposal"` for distill),
2591
2627
  or `"no_signal"` as the fallback — never a fabricated category. The field is
2592
2628
  omitted entirely when both would be empty. The same table is printed to
2593
2629
  stderr (`[improve] usage report ...`) after every real run, independent of
@@ -2607,14 +2643,14 @@ support.
2607
2643
  ### proposal
2608
2644
 
2609
2645
  Manage the proposal queue. The canonical grammar is `akm proposal <verb>`:
2610
- `extract`, `new`, `list`, `show`, `diff`, `accept`, `reject`, `revert`,
2611
- `drain`. Bare `akm proposal` is a usage error (exit 2) as of 0.9.0 — it used
2646
+ `extract`, `new`, `list`, `show`, `diff`, `accept`, `reject`, `reopen`,
2647
+ `revert`, `drain`. Bare `akm proposal` is a usage error (exit 2) as of 0.9.0 — it used
2612
2648
  to behave as `akm proposal list`; name the verb. There are no flat-verb
2613
2649
  spellings (`akm proposals`, `akm extract`, `akm propose`, `akm accept`, `akm
2614
2650
  reject`, `akm diff`, `akm revert`) — use the `akm proposal <verb>` form.
2615
2651
 
2616
- `list`, `show`, `diff`, `accept`, `reject`, and `revert` (and bulk accept/
2617
- reject) support `--queue <source>`. It selects the proposal queue stored for
2652
+ `list`, `show`, `diff`, `accept`, `reject`, `reopen`, and `revert` (and bulk
2653
+ accept/reject) support `--queue <source>`. It selects the proposal queue stored for
2618
2654
  that configured writable source root; without it, commands use the primary
2619
2655
  queue. Queue selection is not a destination override. `drain` does **not**
2620
2656
  take `--queue` — it operates on the standing backlog via a policy, not a
@@ -2732,8 +2768,8 @@ akm proposal list --generator consolidate-pair
2732
2768
  | `--generator <name>` | Filter by generator/source (e.g. `reflect`, `distill`, `consolidate-pair`) — the same value `accept`/`reject --generator` take |
2733
2769
 
2734
2770
  Each retire proposal's `retirement.continuityRisk`, when present, also shows
2735
- in the default listing (`⚠ continuity-risk` inline) and in `proposal show`'s
2736
- text output (the specific failing/unverified queries) — see
2771
+ in the default listing (`⚠ continuity-risk` inline) and in the text output of
2772
+ `proposal show` and `proposal diff` (the specific failing/unverified queries) — see
2737
2773
  [Retirement continuity](https://github.com/itlackey/akm/blob/main/docs/architecture/improvement.md#retirement-continuity).
2738
2774
 
2739
2775
  Each proposal record carries an optional `confidence` field (0..1) emitted by
@@ -2774,7 +2810,7 @@ akm proposal accept --generator reflect --older-than 7 --dry-run # Preview a bu
2774
2810
  | `--target <name>` | Write destination; must match the proposal's recorded target |
2775
2811
  | `--generator <name>` | Bulk-accept all pending proposals from this generator (e.g. `reflect`, `distill`). Requires no positional id. |
2776
2812
  | `--max-diff-lines` | When bulk-accepting, only accept proposals whose content is `<=` this many lines. Larger proposals are skipped. |
2777
- | `--older-than` | When bulk-accepting, only accept proposals created more than this many days ago |
2813
+ | `--older-than` | When bulk-accepting, only accept proposals created (or last reopened) more than this many days ago |
2778
2814
  | `--dry-run` | List proposals that would be bulk-accepted without accepting them |
2779
2815
  | `-y`, `--yes` | Skip confirmation (required in non-interactive mode for bulk accept) |
2780
2816
 
@@ -2785,7 +2821,8 @@ Bulk-accept all pending proposals from one generator with `--generator <name>`
2785
2821
  #### proposal reject
2786
2822
 
2787
2823
  Reject a proposal and archive the reason. Accepts a full UUID, an 8-character
2788
- UUID prefix, or an asset ref.
2824
+ UUID prefix, or an asset ref. [`akm proposal reopen`](#proposal-reopen) undoes a
2825
+ rejection.
2789
2826
 
2790
2827
  ```sh
2791
2828
  akm proposal reject <id> --reason "duplicates existing workflow"
@@ -2802,13 +2839,95 @@ akm proposal reject --generator reflect --reason "noisy" --max-diff-lines 50 -y
2802
2839
  | `--queue <source>` | Select the proposal queue by configured writable source name |
2803
2840
  | `--generator <name>` | Bulk-reject all pending proposals from this generator (e.g. `reflect`, `distill`). Requires no positional id. |
2804
2841
  | `--max-diff-lines` | When bulk-rejecting, only reject proposals whose content is `<=` this many lines. Larger proposals are skipped. |
2805
- | `--older-than` | When bulk-rejecting, only reject proposals created more than this many days ago |
2842
+ | `--older-than` | When bulk-rejecting, only reject proposals created (or last reopened) more than this many days ago |
2806
2843
  | `--dry-run` | List proposals that would be bulk-rejected without rejecting them |
2807
2844
  | `-y`, `--yes` | Skip confirmation (required in non-interactive mode for bulk reject) |
2808
2845
 
2809
2846
  Bulk-reject all pending proposals from one generator with `--generator <name>`
2810
2847
  and no positional id. Bulk reject requires `-y`/`--yes` in non-interactive shells.
2811
2848
 
2849
+ #### proposal reopen
2850
+
2851
+ Undo a rejection: move rejected proposals back to `pending` so they can be
2852
+ reviewed again (a proposal that retention expiry archived is a rejected one
2853
+ too). A rejection is otherwise final. `accept` refuses anything that is not
2854
+ pending, and a rejected consolidate pair-pass retire proposal also keeps the
2855
+ pair pass from proposing that retirement again while both documents are
2856
+ unchanged.
2857
+
2858
+ ```sh
2859
+ akm proposal reopen <id>
2860
+ akm proposal reopen <id> --reason "the diff was misrendered (#997)"
2861
+ akm proposal reopen <id> <id> <id> # several at once: all or none
2862
+ akm proposal reopen <id> --queue team-bundle
2863
+ # One id per call, and only the rejections whose reason says the diff was misread:
2864
+ akm proposal list --status rejected --generator consolidate-pair \
2865
+ --detail normal --format json \
2866
+ | jq -r '.proposals[] | select(.review.reason // "" | test("blank line"))
2867
+ | .id' \
2868
+ | xargs -r -n 1 akm proposal reopen --reason "diff was misrendered"
2869
+ ```
2870
+
2871
+ | Flag | Description |
2872
+ | --- | --- |
2873
+ | `--reason <text>` | Why the rejection is being undone. Kept in the proposal's review history and the `proposal_reopened` event, and in its ledger row's detail when it has a row |
2874
+ | `--queue <source>` | Select the proposal queue by configured writable source name |
2875
+
2876
+ Takes full proposal ids: a UUID prefix only matches pending proposals, so it
2877
+ cannot name a rejected one (`akm proposal list --status rejected` prints the
2878
+ ids; add `--generator consolidate-pair` for the retire backlog). An asset ref
2879
+ also resolves, to the newest proposal for that ref, but only while none is
2880
+ pending, and it never reaches a retire proposal, which is named by its id.
2881
+
2882
+ Check a rejection's reason before reopening it. The default brief output of
2883
+ `akm proposal list` leaves it out; `--detail normal --format json` shows it as
2884
+ `review.reason`. Reopen only the rejections you want back, since a deliberate
2885
+ rejection would otherwise be undone with the rest. The pattern in the example,
2886
+ `test("blank line")`, matches the reason given in the 0.9.19 upgrade note (the
2887
+ diff read as a blank-line replacement); change it to yours. Pass one id per
2888
+ call (`xargs -n 1`) so a refusal skips only that proposal, and use `xargs -r`
2889
+ so GNU xargs does not run `reopen` with no id when nothing matches. A retire
2890
+ proposal refused because another pending retire proposal involves the same
2891
+ document can be reopened once that one is decided.
2892
+
2893
+ Reopening is refused, with the reason, when:
2894
+
2895
+ - the proposal is not `rejected` (it is pending, accepted or reverted);
2896
+ - its target changed since it was created, by the same rule `accept` applies,
2897
+ so a reopened proposal is never one `accept` would then refuse as stale: an
2898
+ update needs its target unchanged, a create needs the target still absent,
2899
+ and a retire proposal needs the successor to exist and both documents' body
2900
+ hashes to match the ones recorded when the pair was judged;
2901
+ - it is a retire proposal and another pending retire proposal already involves
2902
+ either of its two documents (the pair pass never has two at once, since
2903
+ accepting one would strand the other): decide that one first;
2904
+ - it was recorded before proposals carried their change envelope (very old
2905
+ archived rows).
2906
+
2907
+ With several ids nothing is reopened unless every one can be: the error lists
2908
+ each refusal (exit 2).
2909
+
2910
+ A reopened proposal is `pending` again with its `review` cleared. The rejection
2911
+ (its review, and any gate verdict that came with it) is appended to the
2912
+ proposal's `reviewHistory`, which `akm proposal show` prints as one line per
2913
+ reopen: `reopened: <when> (<reopen reason>), undoing rejected: <why> (<when>)`.
2914
+ The gate verdict is cleared so the drain treats the proposal as undecided,
2915
+ except a `deferred` one, the quality gate's hand-off to a person, which stays
2916
+ so the drain keeps leaving the proposal for that person.
2917
+
2918
+ A reopened retire proposal no longer counts as a settled pair for the pair
2919
+ pass, and while it is pending that pair is not proposed a second time. The
2920
+ proposal's `improve_ledger` row goes back to what the mint wrote (a retire
2921
+ proposal's mint writes none, so the row its rejection created is dropped).
2922
+ The age that retention expiry and `--older-than` see restarts at the reopen
2923
+ (retire proposals never expire), and a `proposal_reopened` event is appended.
2924
+ Accepting a reopened retire proposal archives the retired file exactly as for
2925
+ any retire proposal, and `akm proposal revert` restores it byte-exactly.
2926
+
2927
+ Output: for one id, the envelope `reject` returns (`ok`, `id`, `ref`, the
2928
+ proposal, and `reason`, here the reopen reason); for several ids,
2929
+ `{ reopened, results }` with one such envelope per proposal.
2930
+
2812
2931
  #### proposal revert
2813
2932
 
2814
2933
  Revert an accepted proposal by restoring the prior asset content from the
@@ -2854,6 +2973,41 @@ akm proposal diff <id> --target team-bundle # Must match a recorded target
2854
2973
  `proposal accept` runs full validation before promoting. `proposal reject`
2855
2974
  requires `--reason`.
2856
2975
 
2976
+ **A retire proposal** (the consolidate pair pass's `consolidate-pair`
2977
+ retirements) writes no content: accepting it archives the retired file under
2978
+ `.akm/memory-cleanup/archive/` (nothing is deleted), and `akm proposal revert`
2979
+ restores it byte-for-byte. Its diff shows just that, the retired file's lines as
2980
+ removals and nothing added, under a `retire` header. It does not present the
2981
+ file as replaced by a blank one, which is how earlier releases rendered it:
2982
+
2983
+ ```
2984
+ $ akm proposal diff <id>
2985
+ # proposal <id> (retire: memories/old-note -> memories/new-note)
2986
+ retire.label: duplicate (cosine=0.986)
2987
+ retire.reason: Same durable facts, B adds nothing new.
2988
+ note: Accepting archives the retired file under .akm/memory-cleanup/archive/ (nothing is deleted); `akm proposal revert` restores it byte-exactly.
2989
+ --- stash//memories/old-note (existing)
2990
+ +++ /dev/null (retired: archived; successor memories/new-note)
2991
+ @@ 1,5 0,0 @@
2992
+ ----
2993
+ -description: an old note
2994
+ ----
2995
+ -The durable fact.
2996
+ -A second line.
2997
+ ```
2998
+
2999
+ The JSON result carries three more fields for a retire proposal, and none of
3000
+ them on any other proposal: `op` (`"delete"`), `retirement`, and `note` (what
3001
+ accept and revert do to the file). `retirement` uses the keys `proposal show`
3002
+ reports the pair under: `retiredRef`, `successorRef`, `judgeLabel`,
3003
+ `judgeReason` (the judge's own text; the stored block's `reason` is the
3004
+ tombstone vocabulary and is not repeated), `cosine`, and `continuityRisk` when
3005
+ the retirement continuity check flagged the pair. The text output prints the
3006
+ same verdict lines `show` does, `continuityRisk` and its failing queries
3007
+ included, above the diff. `isNew` is always `false` for a retire proposal; when
3008
+ the retired file is already gone (retired, or removed by something else), the
3009
+ diff is only its two header lines, `--- <ref> (missing)` and the `+++` line.
3010
+
2857
3011
  #### proposal drain
2858
3012
 
2859
3013
  Drain the standing pending-proposal backlog instead of adjudicating proposals
@@ -2877,7 +3031,7 @@ akm proposal drain --strategy default --promote -y # Read the triage block from
2877
3031
  | `--promote` | Promote (accept) judge-passed proposals. Default is queue mode — stage only, no writes to assets. |
2878
3032
  | `--dry-run` | List what would be accepted/rejected/deferred, without writing |
2879
3033
  | `--max-accepts` | Hard per-run accept ceiling; accepts beyond this are reported as `skippedByCap` |
2880
- | `--older-than` | Only consider proposals created more than this many days ago |
3034
+ | `--older-than` | Only consider proposals created (or last reopened) more than this many days ago |
2881
3035
  | `--judgment` | Explicitly enable the judgment tier for this standalone drain, including when the selected strategy says `judgment.enabled: false`; execution overrides still come from that strategy. Without this flag, strategy judgment config does not enable standalone drain judgment. A missing runner remains a no-op with a logged `triage_deferred` summary. |
2882
3036
  | `-y`, `--yes` | Skip the confirmation prompt (required in non-interactive mode for promotion) |
2883
3037
 
@@ -2887,6 +3041,13 @@ akm proposal drain --strategy default --promote -y # Read the triage block from
2887
3041
  forwarded into feedback metadata and consumed by improve/distill proposal
2888
3042
  prompts. Negative feedback requires a reason by default.
2889
3043
 
3044
+ Write the reason about the asset's content. Reflect treats it as an unverified
3045
+ report to investigate, not a fact to insert, and is told to leave the section
3046
+ unchanged when the reason asks for information the asset lacks. Distill's
3047
+ quality gate rejects a lesson that is off-subject for the asset it was
3048
+ distilled from. A command that failed (`akm show` erroring on the ref, say)
3049
+ says nothing about the asset, so it is not a reason to record against it.
3050
+
2890
3051
  ### task
2891
3052
 
2892
3053
  `akm task` is the scheduling surface for workflows, agent prompts, and
@@ -353,11 +353,11 @@ guidance. When enabled, engine selection is judgment → triage → strategy →
353
353
  }
354
354
  ```
355
355
 
356
- The shipped `default` and `frequent` strategies keep improve-stage session
357
- extraction off. `proactiveMaintenance` is off in `default` and
358
- `reflect-distill`; run `akm improve --strategy proactive-maintenance` to use the
359
- dedicated opt-in preset. Because strategies inherit from `default`, a preset
360
- that omits either process also inherits the off value. User strategy overrides
356
+ No shipped strategy turns improve-stage session extraction on.
357
+ `proactiveMaintenance` is on only in the `proactive-maintenance` preset; run
358
+ `akm improve --strategy proactive-maintenance` to use that opt-in preset.
359
+ Because strategies inherit from `default`, a preset that omits either process
360
+ also inherits the off value. User strategy overrides
361
361
  are applied last, so an explicit `enabled: true` still opts the selected
362
362
  strategy in.
363
363
 
@@ -608,9 +608,10 @@ independent of scope. `--scope` was removed in 0.9.0 with no alias; use
608
608
 
609
609
  `archiveRetentionDays` (default `90` when unset) controls how long a pending
610
610
  proposal is kept before `akm improve`'s maintenance pass archives it (status
611
- `rejected`, reason `"expired: no action within retention window"`) — `akm
612
- proposal` itself has no archive/expire verb. Setting it to `0` or less
613
- disables expiry entirely.
611
+ `rejected`, reason `"expired: no action within retention window"`; counted
612
+ from the last `akm proposal reopen`, if there was one) — `akm proposal` itself
613
+ has no archive/expire verb, though `akm proposal reopen` puts an expired
614
+ proposal back. Setting it to `0` or less disables expiry entirely.
614
615
 
615
616
  ## Registries
616
617
 
@@ -138,29 +138,29 @@ An append-only log of every mutating action you perform with AKM. Events are sto
138
138
  **Full event type list.** `EventType` (`src/core/events.ts`) is an open
139
139
  string union — new types can be added without a schema bump — so this is
140
140
  the set of types the code actually emits at HEAD (verified against every
141
- `appendEvent(...)` call site, 2026-07-27), grouped by area:
141
+ `appendEvent(...)` and `insertEventOnce(...)` call site, 2026-09-30), grouped by area:
142
142
 
143
143
  *Asset lifecycle*
144
144
 
145
145
  | Event type | When emitted | Key metadata fields |
146
146
  |---|---|---|
147
- | `add` | `akm bundle add <source>` | `ref`, `provider` |
147
+ | `add` | `akm bundle add <source>` | `target`, `name`, `writable`; `provider` when given |
148
148
  | `remove` | `akm bundle remove <source>` | `ref` |
149
- | `update` | `akm bundle update [source]` | `ref` |
150
- | `remember` | `akm remember <text>` | `ref` |
151
- | `import` | `akm import <file>` | `ref` |
149
+ | `update` | `akm bundle update [source]` | `target`, `all`, `processed` |
150
+ | `remember` | `akm remember <text>` | `ref`, `path`, `force` |
151
+ | `import` | `akm import <file>` | `ref`, `source`, `path`, `force` |
152
152
  | `rekey` | `scripts/rekey-asset-ref.ts` moved at least one row onto a renamed asset's new ref — nothing is emitted on a no-op re-run | `ref` (the new ref); metadata `{from, to, changed}` (row counts only) |
153
153
 
154
154
  *Search, retrieval, sync*
155
155
 
156
156
  | Event type | When emitted | Key metadata fields |
157
157
  |---|---|---|
158
- | `search` | `akm search <query>` | `query`, `source`, `signal` |
159
- | `curate` | `akm curate <prompt>` | `query`, `source` |
158
+ | `search` | `akm search <query>` | `query`, `hitCount`, `resultRefs`, `mode` |
159
+ | `curate` | `akm curate <prompt>` | `query`, `itemCount`, `itemRefs` |
160
160
  | `show` | `akm show <ref>` | `ref`, `type`, `name` |
161
- | `select` | `akm show` after a search returning the same ref | `ref`, `entryId` |
162
- | `feedback` | `akm feedback <ref>` | `signal` (positive/negative) |
163
- | `sync` | `akm sync` | `ref` |
161
+ | `select` | `akm show` after a search returning the same ref | `ref`, `query`, `searchTs`, `rankPosition` |
162
+ | `feedback` | `akm feedback <ref>` | `signal` (positive/negative), `reason`, `failureMode`, `tags` |
163
+ | `sync` | `akm sync` | `name`, `message`, `ok` |
164
164
  | `index_db_vacuumed` | `akm index` VACUUMed index.db, after an index layout migration or because more than half its pages were free | `pagesBefore`, `pagesAfter`, `freelistRatioBefore` |
165
165
  | `stash_synced` | `akm improve`'s internal auto-sync pass (the `sync.push` feature), **distinct from** the `akm sync` command above | `committed`, `pushed`, `skipped`, `reason`, `attributed` (paths the run wrote and staged), `unattributed` (in-scope paths that went dirty during the run without the run writing them — left for their author) |
166
166
  | `env_access` | `akm env run <name> -- <command>` (audit trail: key **names** only, values never recorded) | `ref`, `keys` |
@@ -172,6 +172,7 @@ the set of types the code actually emits at HEAD (verified against every
172
172
  |---|---|---|
173
173
  | `promoted` | `akm proposal accept <id>` | `ref` |
174
174
  | `rejected` | `akm proposal reject <id>` | `ref` |
175
+ | `proposal_reopened` | `akm proposal reopen <id>` (a rejected proposal goes back to pending) | `ref`, `proposalId`, `source`, `reason` (when given) |
175
176
  | `proposal_reverted` | `akm proposal revert <id>` (undoes a previously-accepted proposal, restores prior content) | `ref` |
176
177
  | `proposal_expired` | A pending proposal aged past the retention window and was auto-expired | `ref` |
177
178
  | `proposal_expiration_pass` | Summary emitted once per `akm improve` maintenance run after per-proposal `proposal_expired` events | expiry counts |
@@ -187,7 +188,7 @@ the set of types the code actually emits at HEAD (verified against every
187
188
  | `improve_invoked` | Start of an `akm improve` run | `ref` (scope); `strategy`, `scope`, `dryRun`, `eligibleCount` |
188
189
  | `improve_completed` | `akm improve` run finished | run stats |
189
190
  | `improve_failed` | `akm improve` run errored | error |
190
- | `improve_skipped` | Asset skipped by cooldown or budget | `ref`, `reason` |
191
+ | `improve_skipped` | `akm improve` left a ref, a lane, or a group of refs out | `reason` (`no_new_signal`, `not_retrieved`, `distill_no_new_signal`, `budget_exhausted`, `budget_exhausted_batch`, `asset_missing_on_disk`, `strategy_filtered_all_passes`, `autonomy_gated`, `engine_unavailable`, `pool_below_min_size`, `consolidation_no_memory_updates`, `below_min_new_sessions`, `derived_memory_reflect_skipped`, `memory_distill_requires_feedback`); `count`, `remaining`, `strategy`, `lane` or `configKey` where they apply |
191
192
  | `improve_lock_recovered` | Stale improve lock cleared at startup | |
192
193
  | `improve_review_needed` | `akm feedback` pushed a high-utility asset's utility below the review threshold — a review-needed escalation is recorded (not a proposal, so it can't accidentally overwrite the asset) | `ref`, `previousUtility`, `nextUtility` |
193
194
  | `reflect_invoked` | Start of reflect phase in `akm improve` | `ref`, engine |
@@ -201,6 +202,7 @@ the set of types the code actually emits at HEAD (verified against every
201
202
  | `proactive_selected` | The proactive-maintenance selector runs (once per `akm improve` run) | `count`, `dueTotal`, `neverReflected` (aggregated) |
202
203
  | `events_purged` | Old events deleted by improve maintenance (90-day default retention) | `purgedCount`, `retentionDays` |
203
204
  | `improve_runs_purged` | Old `improve_runs` rows deleted by improve maintenance (same retention window as events) | `purgedCount`, `retentionDays` |
205
+ | `asset_state_gc` | Improve maintenance found `asset_salience`/`asset_outcome` rows that no longer resolve against the index (`pending`) or deleted them (`improve.stateGc.collect`); a run with neither emits nothing | `pending`, `collected`, `byTable` |
204
206
  | `state_db_vacuumed` | state.db was VACUUMed after the retention purge because more than half its pages were free | `pagesBefore`, `pagesAfter`, `freelistRatioBefore` |
205
207
  | `task_logs_purged` | Old scheduled-task log files purged by improve maintenance | |
206
208
 
@@ -280,12 +282,12 @@ an identity that production indexing omitted.
280
282
 
281
283
  ### 3. Proposals Table
282
284
 
283
- The proposal queue: pending, accepted, and rejected improvement proposals for your bundle assets. Generated by `akm improve`, `akm proposal new`, and related proposal-producing flows.
285
+ The proposal queue: pending, accepted, rejected, and reverted improvement proposals for your bundle assets. Generated by `akm improve`, `akm proposal new`, and related proposal-producing flows.
284
286
 
285
287
  Contents:
286
288
  - Proposal UUID (primary key)
287
289
  - Target asset ref
288
- - Status (pending/accepted/rejected)
290
+ - Status (pending/accepted/rejected/reverted)
289
291
  - Source (which process generated it — e.g. `reflect`, `distill`)
290
292
  - Full proposal content (Markdown text)
291
293
  - Created/updated timestamps
@@ -295,7 +297,10 @@ with each asset — one row per bundle, asset ref and stage: the outcome
295
297
  (`proposed`, `accepted`, `rejected`, `quality_rejected`, `review_needed`,
296
298
  `expired`, `unchanged`, `failed`, `judged_no_action`), when it was attempted,
297
299
  and the earliest time the stage may try that asset again. It holds refs,
298
- timestamps, a proposal id and a short reason — never asset content.
300
+ timestamps, a proposal id and a short reason — never asset content. A row whose
301
+ next attempt depends on the asset changing rather than on a clock (the
302
+ consolidate pair pass, and a consolidate promotion once accepted or rejected)
303
+ also holds a hash of the asset's body, and no earliest-retry time.
299
304
 
300
305
  ### 4. Task History Table
301
306
 
@@ -344,10 +349,13 @@ rm -rf ~/.cache/akm/config-backups/
344
349
 
345
350
  # Delete the events log from state.db (non-reversible)
346
351
  # There is no akm CLI command to do this directly (`akm log` only exposes
347
- # `list`/`tail`, no delete/purge verb). Use SQLite directly:
352
+ # `list`/`tail`, no delete/purge verb). Use SQLite directly.
353
+ # Stop akm first (no `akm` process or scheduled task running): an older
354
+ # `sqlite3` (< 3.51) opened read-write alongside a running akm can corrupt
355
+ # the database.
348
356
  sqlite3 ~/.local/share/akm/state.db "DELETE FROM events;"
349
357
 
350
- # Delete all proposals
358
+ # Delete all proposals (same precondition: stop akm first)
351
359
  sqlite3 ~/.local/share/akm/state.db "DELETE FROM proposals;"
352
360
  ```
353
361
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "akm-cli",
3
- "version": "0.9.17",
3
+ "version": "0.9.19-alpha.1",
4
4
  "type": "module",
5
5
  "description": "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 Cursor.",
6
6
  "keywords": [