# Migrating from akm 0.8.x to 0.9.0

0.9.0 removes the CLI deprecation aliases introduced during the 0.8 stabilization
window **and** the deprecated `vault` asset type. Everything below keeps working
in 0.8.x (the old spellings warn on stderr and delegate); 0.9.0 removes them.
Move your scripts to the canonical spellings before upgrading.

## CLI rename table (old → new, removed 0.9.0)

Every old spelling prints a stderr deprecation warning in 0.8.x (suppressed
under `--quiet`) and delegates to the canonical form. JSON stdout is unaffected.

| Old spelling (0.8, deprecated) | Canonical (use this) | Notes |
| --- | --- | --- |
| `akm proposals` | `akm proposal list` | bare `akm proposal` also lists |
| `akm show proposal <id>` | `akm proposal show <id>` | |
| `akm diff <id>` | `akm proposal diff <id>` | |
| `akm accept <id>` | `akm proposal accept <id>` | |
| `akm reject <id>` | `akm proposal reject <id>` | |
| `akm revert <id>` | `akm proposal revert <id>` | |
| `--detail summary` | `--shape summary` | `--detail` is now verbosity only (`brief\|normal\|full`) |
| `--detail agent` | `--shape agent` | |
| `--for-agent` | `--shape agent` | |
| `--source` (on `accept`/`reject`/`history`) | `--generator` | unchanged on `search`/`curate`/`graph`/`remember` |
| `akm save` | `akm sync` | `sync` = commit + optional push; adds `--no-push` |
| `akm enable <component>` | `akm config enable <component>` | |
| `akm disable <component>` | `akm config disable <component>` | |
| `akm events` | `akm log` | `log` is primary in 0.9.0; `history` is a different (asset-scoped) surface |
| `akm wiki remove --force` | `akm wiki remove -y` | `--force` skips the new confirmation prompt; use `-y`/`--yes` |
| `akm feedback --note <text>` | `akm feedback --reason <text>` | |
| `akm workflow next --dry-run` | (removed) | the flag is gone; `next` never supported a dry run |

Additive (non-breaking) singular aliases also shipped in 0.8: `akm task`
(= `akm tasks`) and `akm lesson` (= `akm lessons`).

## Safety guards added in 0.8 (behavior change for non-interactive callers)

Two previously-unguarded destructive paths now confirm before acting. **Scripts
that invoked these non-interactively must add `-y` / `--yes`:**

- `akm registry remove <name>` — prompts before removing the registry; pass `-y`
  to skip. Non-interactive use without `-y` aborts.
- `akm proposal accept --generator <g>` (the **bulk** form) — prompts before
  promoting every matching proposal. Single-id accept is unchanged (revertable).

## Proposal triage replaces the `process-proposals` prompt task

0.8.x ships **proposal-queue triage** as a first-class, config-driven improve
process (`profiles.improve.<name>.processes.triage`). It drains the standing
pending-proposal backlog deterministically (with an optional `llm`/`agent`/`sdk`
judgment tier), replacing the bespoke `process-proposals` cron task that
dispatched a full agent session running the `manage-akm-proposals` skill.

Two supported replacements, both additive — the old `process-proposals.yml`
keeps working in 0.8.x:

- **Folded pre-pass (recommended).** Enable triage in your improve profile and
  it runs as a PRE-pass inside every whole-stash `akm improve` (before
  `ensureIndex`, under `improve.lock`, gated on `scope.mode !== "ref"`). No
  separate task is needed — the standing `:20` `process-proposals` task can be
  **retired**:

  ```yaml
  profiles:
    improve:
      default:
        processes:
          triage:
            enabled: true
            applyMode: queue       # queue (safe default) | promote
            policy: personal-stash
  ```

- **Standalone drain task.** Regenerate `process-proposals.yml` as a
  `command:` task that calls the new verb directly, instead of the
  `manage-akm-proposals` prompt:

  ```yaml
  # tasks/process-proposals.yml (regenerated)
  schedule: 20 * * * *
  command: akm proposal drain --policy personal-stash --yes
  enabled: true
  name: Drain AKM proposal queue
  ```

Task files live in your stash (`~/akm/tasks/…`), which is user data — akm does
**not** rewrite them for you. Edit or regenerate them yourself when you adopt
triage. **0.9.0** removes the `manage-akm-proposals`-driven prompt-task guidance
from setup/docs; the deterministic `akm proposal drain` verb plus the optional
judgment tier (or the folded pre-pass) is the supported path.

## Single batch-at-boundary git commit (`options.pushOnCommit` deprecated)

0.9.0 unifies the two commit models for git-backed sources onto a single
**batch-at-boundary** model (issue #507). Previously, writing an asset to a
writable git `--target` committed (and optionally pushed) **per asset**, gated
on `options.pushOnCommit`. That staged only the single asset file (leaving
`.akm/` state dirty) and produced one noisy commit per asset.

Now every write/delete to a source is a plain filesystem operation with **no**
per-asset commit. Git-backed targets are committed **once** at the end of the
operation (e.g. `akm remember --target <git-source>`, proposal accept/revert,
consolidate) as a single complete commit (`git add -A` staging `.akm/` + assets
together), pushed under the same `writable + remote` gate as `akm save`/`akm sync`.

**Migration:** `options.pushOnCommit` is **deprecated**. Existing configs still
parse — its push intent is honored via the batch push gate, and akm prints a
one-time deprecation warning when it encounters the option. Remove
`pushOnCommit` from your source config and rely on `writable: true` (plus a
configured remote) to push. No behavior is lost: a writable git target with a
remote is still pushed; a target without a remote (or with push disabled)
commits only.

## `vault` → `env` / `secret`

0.9.0 also removes the deprecated `vault` asset type. Its replacement, the `env`
asset type, shipped in 0.8.0 alongside a deprecation shim and an automatic
`vaults/` → `env/` migration. This guide explains what changed, how to migrate,
and what 0.9.0 removes.

> **TL;DR:** In 0.8.0, run the migration (`akm-migrate-storage --yes`) to copy
> `vaults/` → `env/`, then switch your scripts from `akm vault …` to
> `akm env …` and from `source "$(akm vault path …)"` to
> `akm env run env:<name> -- <command>` (or `-- $SHELL` for an interactive
> session). Everything keeps working through 0.8.x; the `vault` verb and
> `vault:` refs are removed in 0.9.0.

## Table of contents

- [Why `vault` → `env`](#why-vault--env)
- [What changed in 0.8.0](#what-changed-in-080)
- [Running the migration](#running-the-migration)
- [Command mapping](#command-mapping)
- [The safe shell-load path](#the-safe-shell-load-path)
- [Single values are now secrets](#single-values-are-now-secrets)
- [What 0.9.0 removes](#what-090-removes)
- [Verifying the migration](#verifying-the-migration)
- [Rolling back](#rolling-back)

## Why `vault` → `env`

The old `vault` type managed individual `KEY=value` entries: `vault set`,
`vault unset`, comment management, and bespoke value quoting. That hand-rolled
write surface was the riskiest part of the feature. 0.8.0 simplifies the model
and splits it by **purpose**:

- **`env`** — a group of related **configuration** for an app/service (URLs,
  flags, and any credentials it needs) in one `.env` file, sourced or injected
  **wholesale**. Values may or may not be sensitive — all are protected. akm no
  longer edits entries; you edit the file with your own editor and akm loads it.
- **`secret`** — a single **sensitive value** used on its own for authentication
  (one file = one value: a token, key, or cert), for the cases where
  `vault set <ref> <KEY>` was used to store one credential.

Both protect values identically (never written to stdout, the index, or any
structured output); env additionally surfaces key names + comments for
discoverability. Pick `env` for configuration, `secret` for a standalone
authentication credential.

## What changed in 0.8.0

| Area | 0.7.x / early 0.8 | 0.8.0+ |
| --- | --- | --- |
| Asset type | `vault` | `env` (`vault` is a deprecated alias) |
| Directory | `vaults/` | `env/` (`vaults/` frozen after migration) |
| Ref prefix | `vault:prod` | `env:prod` (also accepts `environment:`; `vault:` still resolves) |
| Shell load | `source "$(akm vault path …)"` | `akm env run env:prod -- $SHELL` (or `export --out <file>` then source) |
| Run | `akm vault run vault:prod[/KEY] -- …` | `akm env run env:prod [--only K] -- …` |
| Set one value | `akm vault set vault:prod KEY` | `akm secret set secret:key` (or edit the `.env`) |
| Ingest a `.env` | (hand-copy into `vaults/`) | `akm env create prod --from-file ./.env` |
| Delete | (hand-delete the file) | `akm env remove env:prod` |
| Renderer | `vault-env` | `env-file` |
| Audit event | `vault_access` | `env_access` (`metadata.deprecatedAlias` when reached via the `vault` shim) |

The `akm vault` verb still works in 0.8.x: it prints a stderr deprecation
warning and delegates `list` / `path` / `export` / `run` / `create` to the
`env` handlers. `vault set` / `vault unset` and the single-key
`vault run <ref>/KEY` form are **hard-errors** with a signpost — silent changes
to secret-handling behaviour are unacceptable.

## Running the migration

The migration copies `<stash>/vaults/` → `<stash>/env/`. It is **copy, never
move**: the legacy `vaults/` tree is left intact as a frozen copy and a
`vaults/.migrated` marker is written so re-runs are no-ops.

```sh
# Preview (no changes written)
akm-migrate-storage --dry-run

# Apply
akm-migrate-storage --yes

# From a source clone:
bun scripts/migrate-storage.ts --yes
```

What the `vaults/ → env/` step does:

1. Skips entirely if there is no `vaults/` directory, if the `.migrated` marker
   already exists, or if `vaults/` contains no `.env` files (e.g. a fresh
   install).
2. Copies every file under `vaults/` into `env/` as **opaque bytes** (`.env`,
   `.sensitive`, and `.lock` sidecars alike) — contents are never read or
   re-serialised.
3. **Never overwrites** an `env/` file you already authored (those are skipped
   and preserved).
4. Tightens permissions on the copied tree: `0600` files, `0700` directories,
   then verifies the mode. (The generic copy helper checks size only, so this
   pass guarantees migrated secret material does not land at the umask default.)
5. Verifies the post-copy `.env` count is at least the source count, then writes
   the `vaults/.migrated` marker.

After migrating, run `akm index` to refresh search so entries surface under
`env:` rather than `vault:`.

## Command mapping

```sh
# List
akm vault list                          →  akm env list

# Inspect keys (values never shown)
akm show vault:prod                     →  akm show env:prod

# Load values into a shell (use a subshell — safe, nothing on disk)
source "$(akm vault path vault:prod)"   →  akm env run env:prod -- $SHELL

# Run a command with the env injected
akm vault run vault:prod -- ./deploy.sh →  akm env run env:prod -- ./deploy.sh

# Create / ingest an existing .env
akm vault create prod                   →  akm env create prod
                                            # or: akm env create prod --from-file ./.env

# Edit (akm no longer manages entries)
akm vault set vault:prod DB_URL         →  $EDITOR "$(akm env path env:prod --quiet)"
                                            # or: akm secret set secret:db-url
```

Existing `vault:` refs embedded in your own assets are **not** rewritten (akm
never mutates your content). They keep resolving through 0.8.x: the resolver
prefers `env/` and falls back to the frozen `vaults/` copy.

## The safe load paths

`env path` prints the **raw** file path. Do **not** `source` it: a hand-edited
or migrated `.env` containing `X=$(rm -rf ~)` would execute on `source`.

- **Processes / agents / interactive** — `akm env run env:prod -- <cmd>` (or
  `-- $SHELL`). Values go straight into the child process, never through a shell
  and never onto stdout. **This is the only path safe for AI agents** —
  `env export`/`env path` put value-bearing data where a captured context would
  ingest it.
- **A sourceable file** (a tool that must `source` a script) — `akm env export
  env:prod --out <file>` writes single-quote-escaped `export KEY='value'` lines
  to a file (mode 0600); the values are re-serialised so sourcing it can never
  execute a substitution. `export` never prints values to stdout, so it requires
  `--out`.
- **Docker `_FILE` / `--env-file`** — `akm env path env:prod --quiet` prints the
  raw file path for tools that read it themselves.

## Single values are now secrets

If you used `vault set <ref> <KEY>` to store a single credential, store it as a
[secret](../cli.md#secret) instead:

```sh
printf '%s' "$TOKEN" | akm secret set secret:deploy-token
akm secret run secret:deploy-token GITHUB_TOKEN -- gh release create v1.0.0
```

`akm env run` injects the **whole** file; the single-key `vault run <ref>/KEY`
form was removed because silently changing which variables a child process sees
is a security-relevant behaviour change.

## What 0.9.0 removes

- The entire `akm vault` verb and its subcommands.
- The `vault:` ref alias (parsing a `vault:` ref becomes an unknown-type error
  pointing at `env:`).
- The `vault` asset-spec entry, renderer (`vault-env`), and the `vault_access`
  audit-event alias.
- The frozen `vaults/` directory is deleted **only** after explicit per-path
  confirmation — the migration never auto-removes it.

Switch to `akm env` / `akm secret` and the `akm env run env:<name> -- <cmd>`
idiom before upgrading to 0.9.0.

### If you upgraded straight to 0.9.0 without migrating

Because 0.9.0 removed the `vault` asset type, the indexer **no longer scans
`vaults/` at all**. If you jumped from 0.7/0.8 to 0.9.0 and never ran
`akm-migrate-storage`, the `.env` data still sitting in `vaults/` was never
copied to `env/` and will **not** appear under `env:` — it is silently
un-indexed (the files themselves are untouched on disk).

To make this non-silent, 0.9.0 adds a **one-time, read-only guard**: whenever
`akm index` (or any auto-index path) runs against a stash whose `vaults/`
directory holds `.env` files but has no `vaults/.migrated` marker, akm prints a
single warning pointing here and at `akm-migrate-storage`. The guard never
reads secret contents, and never writes, moves, or deletes anything — it only
detects the un-migrated state. The fix is the same idempotent, non-destructive
command as on 0.8:

```sh
akm-migrate-storage --yes   # copies vaults/ -> env/, leaving vaults/ intact
akm index                   # refresh search so entries surface under env:
```

The `vaults/ → env/` migration step still ships in 0.9.0's
`akm-migrate-storage` (it is part of the `0.8 → 0.9` migration) precisely so a
late migration on a 0.9.0 install still works.

## Verifying the migration

```sh
# env/ now contains your former vault files
akm env list

# The frozen copy + marker are present
ls -la "$(akm config get stashDir)/vaults/.migrated"

# Values still never leak
akm show env:prod          # keys + comments only
akm search <a-secret-value> # no hits
```

## Rolling back

The migration is non-destructive — `vaults/` is untouched. To roll back, delete
the generated `env/` directory and remove the `vaults/.migrated` marker, then
downgrade akm. Because `env/` is a copy, no data is lost either way.
