# Current 0.9 upgrade troubleshooting

This page covers the supported upgrade boundaries in the current 0.9 line.
There is no general config/storage compatibility coordinator.

## `akm upgrade` succeeded but AKM rejects config

`akm upgrade` updates executable code; it does not translate an old config.
Move the incompatible config aside and run `akm setup`, then configure current
`bundles`, `defaultBundle`, engines, and credentials deliberately.

Do not restore an old config over the generated current file. Keep the archive
with the executable version that created it.

## An old database does not open

Managed current databases apply exact-prefix additive schema migrations
automatically. Released migration 002's `task_history` table rebuild is also
automatic and preserves every existing row. Released migration 018 is the
exception: it removes retired dead-lane state, so an ordinary command stops
before that migration and tells you to run either of:

```sh
akm upgrade        # applies pending state migrations first, then installs if a release is newer
akm migrate apply  # applies them alongside the task-source and config migrations
```

Both run on an already-current install and never need an install to reach
the migration, so either is safe as a container entrypoint: on a current
database the step is a no-op. `akm upgrade` installs a newer release first
when there is one, then runs `akm-migrate apply` — the migrator that shipped
with whatever is now installed; `akm migrate apply` is that step without the
release check. Immediately before
the migration, AKM takes a SQLite writer-exclusion lock, rechecks the exact
ledger, and writes a consistent SQLite snapshot beside the database as
`state.db.pre-018-drop-dead-lane-schema.<UTC-digits>.<UUID>.bak`. The randomized
path is reserved with exclusive creation, kept owner-only while it is written,
checked against its held file descriptor to reject symlink/inode replacement,
fsynced, and required to pass `PRAGMA quick_check`. Its final permissions are
never broader than the source database. Migration 018 and its ledger row commit
before that writer-exclusion window is released, so a concurrent WAL writer
cannot land between the recovery snapshot and the destructive DDL. AKM never
substitutes a raw copy of the main database file, which could omit committed
WAL content. Keep that safety copy until the upgraded installation has
completed its checks. This is a one-purpose historical ledger guard, not a
general storage migrator.

An existing database with no applied migration IDs is never treated as a fresh
install, whether its `schema_migrations` table is absent or empty. Ordinary
commands reject it without writing. The explicit `akm upgrade` / `akm migrate apply` path
takes and verifies a descriptor-bound snapshot named
`state.db.pre-001-initial-schema.<UTC-digits>.<UUID>.bak` before it creates the
ledger or applies any migration from 001 through 022. A truly new database is
distinguished only by the inode this invocation created atomically. The
pre-001 snapshot, ledger initialization, migration 001, and migration 002 all
run within one writer-exclusion transaction. A concurrent process therefore
cannot commit an old-schema row after the snapshot but before migration 002
rebuilds `task_history`.

Both the source reader and safety-copy target stay bound to held file
descriptors while SQLite snapshots and verifies them; a pathname replacement
cannot substitute a different database. AKM never unlinks a reserved backup
pathname after reservation. If creation or verification fails, the
error names that retained path for inspection; do not treat it as verified
recovery media.

Unknown or divergent ledgers still fail closed.

Do not delete `state.db` as a generic repair for a current installation. First
stop writers and preserve the database plus its `-wal` and `-shm` companions.
If it came from a 0.8 installation, keep it in the 0.8 archive and initialize
fresh current state. It is not a supported input to the current runtime.

`index.db` is regenerable and can be rebuilt with `akm index` after its live
file is moved aside. `state.db` is durable history and is not regenerable.

If migration 018 fails after the safety copy was reported, stop every AKM
writer and preserve the failed database plus its sidecars. Verify the sibling
copy with `sqlite3 /path/to/safety-copy.bak 'PRAGMA quick_check;'` and use it
only with the matching older executable in an isolated data directory. Do not
overwrite the failed database while diagnosing it.

## Task migration is blocked

Preview the task-only plan:

```sh
akm migrate status
akm migrate apply --dry-run
```

The migrator runs two generations in one pass — task-v2 to task-v3, then
task-v3 to task source v4 against the resulting files — and either
generation can block a file. A blocked file is intentionally unchanged.
Common causes at the v2-to-v3 stage are argv arrays, shell-sensitive command
forms, invalid YAML, unsupported fields, or a source that cannot be proven
writable; rewrite that file manually as task v3 and preview again. Common
causes at the v3-to-v4 stage are a GitHub Action `uses:` locator (no v4
equivalent) or a `with:` block on a non-command target (v4 wants declared
`inputs:` instead); rewrite that file manually as task source v4 and preview
again.

The task migrator does not repair config or databases.

## Task migration was interrupted

The migrator validates and backs up each changed task immediately before its
atomic replacement. Re-run the preview. Already-current task source v4 files
are skipped; remaining v2 and v3 files are planned again from their current
bytes. A changed input generation fails closed instead of applying a stale
plan.

Use the per-file backup only to reverse that file deliberately. Do not copy a
backup over a file while a task sync or scheduler process is running.

## A workflow will not resume

Only the durable plan v4 family's `irVersion: 5` executes. Pre-`irVersion`-5
stored plans are rejected rather than decoded by a compatibility runtime.
Start a new run from the current Markdown or YAML workflow source.

For an `irVersion: 5` run, a missing or changed authored source is not a resume
blocker: the run uses its frozen plan. A plan-hash or schema failure is
durable-state corruption and must fail closed.

## A stale transaction journal is reported

Current atomic source mutations may leave a transaction journal after a crash.
Stop AKM writers and inspect the named journal and its target files. Reconcile
or restore those current-source files from their recorded backups, then remove
the journal only after verifying the result.

This is source-transaction recovery, not the removed 0.8 config/storage
cutover protocol.

## Scheduler entries point at an old installation

Run:

```sh
akm task doctor
akm task sync
```

Inspect ownership and the planned command. Use `akm task sync --rebind` only
for a scheduler entry you deliberately want the current installation to adopt.
Unknown or foreign entries must remain fail-closed.

## Roll back executable code

Executable rollback and data rollback are separate:

1. stop tasks and running AKM processes;
2. preserve the current config/data/state/cache and authored bundles;
3. restore the previous executable;
4. restore the matching archived data set, if one is required.

Never combine a restored old executable with current state, or a current
executable with an archived old state layout.

See [Moving from 0.8 to current 0.9](v0.8-to-v0.9.md) and
[Migrating from 0.9.1 to 0.9.2](v0.9.1-to-v0.9.2.md).
