@kontextmind/kxm
Version:
KXM local-first multi-agent orchestration and operator dashboard
55 lines (46 loc) • 4.08 kB
Markdown
# Migration and compatibility
**KXM carries no migration path.** The product ships for a single operator, and
the operator decision (recorded in
[`plans/implementation-plan.md`](../../plans/implementation-plan.md)) is that
older state is replaced, not converted. Old names are not accepted; old state
is refused loudly. This page documents what that means at each boundary so no
one re-adds a compatibility lane by inference.
## What happens to older input
| Boundary | Behaviour |
|---|---|
| Project tree with legacy `.kxm/config` JSON | `loadKxmProject` fails closed with `legacy_state_unsupported`, one issue per legacy file. No receipt, plan, flag, or environment variable unlocks it. |
| `kxm init` on such a tree | Reports `mode: "legacy"`, lists `legacyInputs`, performs **no writes**. Recovery is a fresh project directory plus the YAML definitions worth keeping. |
| `kxm migrate` | Unknown command. It existed as `plan` / `apply` / `verify` for pre-KXM JSON and was deleted with this decision. |
| SQLite store stamped with an older `user_version` | Hub stores and Runtime event stores are refused with `runtime_schema_outdated`. That refusal never advances `user_version`. Delete the state file and let the process that owns it recreate the store (`kxm hub start` for hub state, the Runtime for event stores). `kxm init` is **project-only** and rebuilds no database. The Runtime registry is the exception: schema 1 is copied to schema 2 in place, existing rows stay, and `lane_of` is null on those rows. Forward-only stamping of any other store is prohibited, because it turns a clean failure into a later query against a column that does not exist. |
| Retired product and command names | Rejected by fail-closed brakes (lint + readiness tests), not aliased. |
| Shared/off-scope Pi history | Never imported into a narrower run scope. A new physical session starts clean; durable facts come from Git, workflow evidence, artifacts, or promoted context. |
| Historical run records | Never fabricated. Events whose real ordering was not observed stay absent rather than reconstructed into a finer-grained model. |
## What is *not* relaxed by this
Deleting conversion code does not soften the guarantees around the state that
does exist:
- **Fresh installs must be complete.** Every table, index, and default that a
store needs is declared in its current schema definition, so an empty store is
valid without any upgrade step.
- **Backups stay whole.** The hub state set is copied as documented in
[Back up and restore KXM](../operations/backup-and-restore.md); a single `kxm.db`
copy is not a backup.
- **Out-of-range versions refuse without touching the file.** The stamp is read
before anything that can modify the store, so a database this build refuses,
older or newer, comes back byte-identical: neither its schema nor its
`user_version` changes, and it is not converted to WAL as a side effect of being
rejected. A store ahead of this build is refused without downgrade. The
registry schema 1 to 2 copy is not a refusal: that file is rewritten, and
every other older store is still refused untouched.
- **Schema changes are additive-and-replace, not in-place,** except the
Runtime registry's `lane_of` column. For every other store, land the new
definition, delete the local state, and let the process that owns each store
recreate it. `kxm hub start` recreates hub state. The Runtime recreates event
stores. `kxm init` is project-only and rebuilds no database. Nothing else in
the runtime may rewrite an existing store's shape. Opening a schema 1
registry copies its rows into schema 2 and leaves `lane_of` null.
## If this ever changes
Re-introducing migration needs a written decision first, because the cost is
not the converter — it is the permanent dual-read surface (legacy names staying
loadable, receipts that unlock state, and a test matrix that must keep proving
both sides). Any such proposal has to name the state it protects and who owns
it; "someone might have an old directory" is not a reason on its own.