# Migrating to akm v1

> **Audience.** This guide is for users on a 0.6.x or 0.7.x **pre-release**
> upgrading toward v1.0. If you are still on 0.5.x, read
> [`v0.5-to-v0.6.md`](v0.5-to-v0.6.md) first — it covers the older refactor
> and the `stashes[]` → `sources[]` rename.
>
> The v1 contract itself lives in
> [`../technical/v1-architecture-spec.md`](../technical/v1-architecture-spec.md)
> §9. This page describes only the **delta** from the current pre-release
> baseline to v1.0.

## What is changing between pre-release and v1.0

The v1 freeze is split across milestones 0.7 – 1.0. Each row below is a
locked surface (or surface change) that v1.0 must ship. Items marked
**Planned** are part of the v1 contract but not yet live in the
pre-release.

| Area | v0.6 baseline | v1.0 | Status |
| --- | --- | --- | --- |
| Source providers | `filesystem`, `git`, `website`, `npm` | unchanged | Shipped |
| `SourceProvider` interface | `{ name, kind, init, path, sync? }` | unchanged | Shipped |
| Asset *type* | closed-ish union with `registerAssetType()` escape hatch | open string set; renderer registry is the authority | Shipped (codified in spec §4.1) |
| Asset *quality* | implicit; legacy registry `curated` boolean | open string set; `"proposed"` excluded from default search | Planned |
| `SearchHit` | `{ ref, origin, kind, score, snippet?, updatedAt? }` | adds optional `quality`, `warnings` | Planned |
| Registry boolean `curated` | present in v2 JSON | removed; legacy JSON parses-and-ignores | Planned |
| In-tree LLM | ad-hoc opt-in on individual flags (`--enrich`) | gated by `llm.features.*` map; defaults all `false` | Planned |
| External agent integration | none | first-class `agent.*` config + `akm agent`, `akm improve`, `akm proposal list`, `akm proposal show <id>`, `akm proposal diff <id>`, `akm proposal accept <id>`, `akm proposal reject <id>` | Planned |
| `lesson` asset type | absent | first-class with required `description` and `when_to_use` frontmatter | Planned |
| Proposal queue | absent | durable SQLite queue (`proposals` table in state.db); all reflection/generation/distill paths go through it | Planned |
| Usage events | shipped baseline | adds `improve_invoked`, `promoted`, `rejected` | Planned |
| CLI command surface | shipped baseline | adds `agent`, `improve`, and the `proposal` family: `list`, `show`, `diff`, `accept`, `reject` (§9.4) | Planned |

You do not need to act on any **Planned** row before its milestone lands.
The loader treats unknown top-level config keys as warn-and-ignore, so
adding `agent` or `llm.features` to your config now is harmless even on a
binary that does not yet implement the feature.

---

## Pre-release migration baseline

If you are upgrading from a pre-0.6 version (or a very early 0.6.x), do
these once:

### Metadata authoring guidance during pre-release

`.stash.json` still loads for backward compatibility in 0.7.x, but treat it as
a deprecated legacy format while the v1 contract settles. In line with the
current aggressive pre-release phase-out posture, `.stash.json` will be removed
in v0.8.0 rather than carried forward toward 1.0. New assets should prefer
frontmatter for markdown and structured script header comments for
descriptions, `@param`, and execution hints. If you maintain an older curated
stash, finish moving per-asset metadata inline before upgrading to v0.8.0.

### 1. Remove `openviking`

`openviking` was a remote-only API source provider in v0.5.x. It is gone
in v0.6+ and not coming back as a `SourceProvider`. Remote API-backed
sources will return as a separate `QuerySource` tier post-v1.

A config that still contains an `openviking`-typed source fails to load
with a `ConfigError`. Remediation:

```sh
akm config list                       # find the OpenViking source name
akm config sources remove <name>      # remove it
```

### 2. Drop `writable: true` from `website` / `npm` sources

`writable: true` is rejected at config load on `website` and `npm` because
their `sync()` step would clobber local edits on the next refresh. If you
need to author into a checked-out npm package, add the same path as a
separate `filesystem` source.

### 3. Rename `stashes[]` → `sources[]` (auto-migrated)

The loader migrates the legacy key in memory and emits a one-time
deprecation warning. The renamed key is persisted on the next
`akm config set/unset`. New configs should write `sources[]` directly.

### 4. Drop `enable context-hub` / `disable context-hub`

Context Hub is a recommended kit in the official registry, not a special
source kind. The legacy `type: "context-hub"` value normalizes to
`type: "git"` at load time. To install fresh:

```sh
akm add github:andrewyng/context-hub --name context-hub
```

If neither of the four cases above applies, you are already on the v1
pre-release baseline. Continue reading for the planned changes.

---

## TL;DR — locked decisions for v1

These decisions are part of the v1 contract and shape every command that
touches sources, search, or write paths.

1. **`writable` defaults to `true` on `filesystem`, `false` otherwise.**
   Read-only filesystem sources require an explicit `writable: false`.
2. **Registry results are off by default in `akm search`.** Pass
   `--include-registry` (or `--source registry|both`) to merge them.
   Registry hits never land in `hits`; they live under `registryHits`.
3. **`defaultWriteTarget` resolution order:** `--target` →
   `defaultWriteTarget` → working stash (`stashDir`) → `ConfigError`.
4. **`writable: true` is rejected on `website` and `npm`** at config load.
5. **`SearchHit.score` is `[0, 1]`, higher = better.** One scoring pipeline
   for all indexed content (FTS5 + boosts).
6. **Asset refs are `[origin//]type:name`.** No URI schemes. Source
   locators (`github:owner/repo`, `npm:pkg`) are for `akm add`, not for
   addressing assets.
7. **(Planned)** `quality: "proposed"` is excluded from default search.
   `"generated"` and `"curated"` are included. Unknown values
   parse-warn-include.
8. **(Planned)** External agents are invoked via CLI shell-out only. akm
   never imports a vendor SDK. The in-tree LLM stays bounded and stateless,
   gated per call site by `llm.features.*` (see v1 spec §9.7).

---

## Planned for v1 — proposal queue (state.db `proposals` table)

All proposal-producing paths (`akm improve`,
plus any plugin-emitted proposals) write through one durable queue.
Storage is the `proposals` table in `state.db` (SQLite, WAL mode), one
row per proposal keyed by UUID. Rejected / accepted / reverted proposals
stay in the table with their status flipped — archival is a status, not
a move. The queue survives `akm index --full` and binary upgrades.

Pre-0.9.0 stashes stored proposals as filesystem state under
`<stashRoot>/.akm/proposals/<id>/` (one `proposal.json` per directory,
archived entries under `<stashRoot>/.akm/proposals/archive/<id>/`). The
first proposal operation against such a stash imports those files into
the table automatically and idempotently; the legacy files are left in
place as inert artifacts you may delete at leisure.

User actions:

```sh
akm proposal list
akm proposal list --status pending
akm proposal show <id>
akm proposal diff <id>
akm proposal accept <id>
akm proposal reject <id> --reason "..."
```

`accept` runs full validation **before** promoting via
`writeAssetToSource()` — same write path as `akm remember` and
`akm import`. Failed validation keeps the proposal in `pending` and emits a
`warnings` array. See [CLI: improvement flow](../cli.md#improvement-flow-080) and v1 spec §11.

---

## Planned for v1 — `quality: "proposed"` and `--include-proposed`

`SearchHit.quality` is an open string set with three well-known values:
`"generated"`, `"curated"`, and `"proposed"`. The first two are included
in default search; `"proposed"` is **excluded from default search** and
surfaces only via:

```sh
akm search "<query>" --include-proposed     # merge proposed rows into hits
akm proposal list                            # operate the queue directly
```

Unknown quality values **parse, warn once, and remain searchable** — they
are treated as included-by-default so plugin authors can introduce new
values without crashing the indexer.

`SearchHit` gains two new optional fields in v1: `quality?: AssetQuality`
and `warnings?: readonly string[]`. Renderers in `src/output/` surface
both when present and omit them when absent — no migration required for
existing index rows.

---

## Planned for v1 — `agent.*` config block

`agent.default`, `agent.timeoutMs`, and `agent.profiles[<name>]` configure
the external coding-agent CLI integration. Built-in profiles ship for
`opencode`, `claude`, `codex`, `gemini`, and `aider`. See
[Configuration: agent](../configuration.md#planned-for-v1--agent-block) for
the full schema.

`akm setup` will detect installed agent CLIs and persist `agent.default`
when one is found.

 A missing `agent` block in a config that calls `akm agent` or `akm improve`
 produces a `ConfigError` with a hint pointing at the docs.

---

## Planned for v1 — `llm.features.*` gates

In-tree LLM features are off by default. Seven locked keys:

```jsonc
{
  "llm": {
    "features": {
      "curate_rerank":            false,
      "tag_dedup":                false,
      "memory_consolidation":     false,
      "feedback_distillation":    false,
      "embedding_fallback_score": false,
      "memory_inference":         false,
      "graph_extraction":         false
    }
  }
}
```

Each flag gates exactly one call site and degrades cleanly when disabled or
on failure. Every gated call site uses the
`tryLlmFeature(feature, config, fn, fallback)` wrapper from
`src/llm/feature-gate.ts`, which:

- never invokes `fn` when the gate is `false`,
- enforces a 30-second hard timeout (override via `timeoutMs`),
- swallows throws and returns `fallback`,
- surfaces fallback events through an optional `onFallback` sink for the
  caller to emit a structured `warnings` entry.

`memory_inference` and `graph_extraction` shipped first; the remaining five
landed alongside the lesson asset type in 0.9.x. The keys above are locked
and cannot be renamed after v1.0; new keys may be added but only via the
extension-points process. See v1 spec §14.

---

## Planned for v1 — `lesson` asset type

`lesson` is a first-class asset type with required frontmatter
`description` and `when_to_use`. Stored under `lessons/` in the working
stash, parallel to `memories/`. Normally produced by `akm improve <ref>`
as a `proposed` proposal and promoted via `akm proposal accept`. See v1
spec §13.

---

## Planned for v1 — registry `curated` removed

The legacy registry boolean `curated` is removed in v1. Legacy v2 index
JSON containing the field parses and silently ignores it. Renderers no
longer surface a `curated` column; the per-asset `quality` field replaces
it (§4.2 in the spec).

If you publish a registry index, you do **not** need to migrate existing
JSON — the parser ignores extras. New entries should set `"quality":
"curated"` instead.

---

## DB schema bumps

`index.db` is ephemeral. v0.7 bumps `DB_VERSION` from 9 to whichever value
finalises the proposal-queue lookups; on first run `ensureSchema()` drops +
recreates `index.db` tables, preserving `usage_events` via a typed backup.
The next `akm index` rebuilds the index. `workflow.db` and the proposal
queue at `<stashRoot>/.akm/proposals/` (Planned) are never touched by
this path.

No manual action is required. `akm upgrade` invokes `akm index`
automatically after a successful upgrade so the migration + rebuild happen
up front; pass `--skip-post-upgrade` to defer that step.

---

## Verifying the upgrade

```sh
akm info
```

Expect:

- `version` reports the pre-release you upgraded to.
- `sourceProviders` lists only `filesystem`, `git`, `website`, `npm`.
- No `openviking` provider entry.

```sh
akm config list           # sources[] populated; no warnings
akm list                  # all sources resolve
akm search "<query>"      # indexed content still matches
```

Once the planned milestones land, also expect:

- `akm proposal list` to return `[]` on a fresh install (Planned).
- `akm config get agent.default` to return the detected agent CLI
  (Planned, post-`akm setup`).
- `akm config get llm.features` to return the five-key map with all
  values `false` (Planned).

---

## Rolling back

The auto-migrations are non-destructive. `config.json` is rewritten only
when you run a write command (`akm config set`, `akm add`, etc.). To roll
back to a 0.5.x release that still understands `stashes[]`:

```sh
npm install -g akm-cli@0.5
```

Restore your `config.json` from a backup if it has been rewritten with
`sources[]` (0.5.x reads `stashes[]` only). Stash content itself is
unchanged across versions; only the metadata layer evolved.

If you find a regression, file an issue at
<https://github.com/itlackey/akm/issues> with the output of `akm info` and
a redacted copy of your `config.json`.
