@kontextmind/kxm 0.7.97 → 0.7.98
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.
- package/.claude-plugin/marketplace.json +1 -1
- package/.kxm/workflows/default.yaml +2 -0
- package/CHANGELOG.md +39 -2
- package/docs/concepts/architecture.md +1 -1
- package/docs/concepts/data-and-storage.md +1 -1
- package/docs/contracts/routing.md +1 -1
- package/docs/contributing/test-matrix.md +6 -5
- package/docs/operations/backup-and-restore.md +43 -24
- package/docs/operations/deploy.md +1 -1
- package/docs/reference/cli-reference.md +59 -24
- package/docs/reference/config-reference.md +24 -13
- package/docs/reference/harness-routing.md +3 -3
- package/docs/reference/http-api.md +1 -1
- package/docs/start/first-workflow.md +4 -4
- package/docs/start/quickstart-claude-code.md +2 -2
- package/package.json +1 -1
- package/plugins/kxm/.claude-plugin/plugin.json +1 -1
- package/plugins/kxm/dist/cli.js +393 -154
- package/plugins/kxm/dist/core.js +5 -2
- package/plugins/kxm/dist/mcp-server.js +1 -1
- package/plugins/kxm/dist/runtime-supervisor.js +231 -48
- package/plugins/kxm/dist/runtime.js +415 -92
- package/plugins/kxm/dist/server.js +7 -0
- package/plugins/kxm/package.json +1 -1
- package/plugins/kxm/skills/kxm-hub-ops/SKILL.md +4 -3
- package/plugins/kxm/skills/kxm-project-setup/SKILL.md +3 -2
- package/plugins/kxm/skills/kxm-routing-improve/SKILL.md +3 -0
- package/plugins/kxm/skills/kxm-runs/SKILL.md +8 -7
- package/plugins/kxm/src/cli/project.ts +22 -13
- package/plugins/kxm/src/cli/system.ts +22 -1
- package/plugins/kxm/src/cli.ts +11 -3
- package/plugins/kxm/src/database.ts +210 -36
- package/plugins/kxm/src/engine.ts +117 -2
- package/plugins/kxm/src/harness.ts +29 -0
- package/plugins/kxm/src/init-guide-setup.ts +43 -28
- package/plugins/kxm/src/mcp-server.ts +1 -1
- package/plugins/kxm/src/oneshot-producer.ts +16 -8
- package/plugins/kxm/src/prices.ts +33 -2
- package/plugins/kxm/src/routing.ts +13 -7
- package/plugins/kxm/src/studio-layout.ts +5 -4
- package/plugins/kxm/src/template.ts +31 -0
- package/plugins/kxm/src/worktree-witness.ts +71 -0
- package/schemas/backup-manifest.schema.json +33 -0
package/CHANGELOG.md
CHANGED
|
@@ -69,6 +69,18 @@ All notable user-facing changes are documented here. The project follows [Semant
|
|
|
69
69
|
|
|
70
70
|
### Changed
|
|
71
71
|
|
|
72
|
+
- **The rotation surface drive uses is the one setup writes.** Guide setup appends only
|
|
73
|
+
reviewed selectors to `.kxm/routes.yaml` and routes Google through Pi `antigravity`.
|
|
74
|
+
|
|
75
|
+
- **CLI and Studio stop reporting work they did not do.** Drive help no longer
|
|
76
|
+
describes the default as a model-free simulation. A Studio mutation with no handler returns 501 and `mappedToCli: false`.
|
|
77
|
+
|
|
78
|
+
- **Missing cost stays unknown.** `kxm prices acknowledge` restamps the local catalog
|
|
79
|
+
as today without fetching vendor rates, which is what an estimate will accept.
|
|
80
|
+
Routing totals are null when any attempt has no cost, and those rows sort after
|
|
81
|
+
complete-cost rows. `kxm improve` stays proposal-only. Wiki compile writes a file
|
|
82
|
+
only with `--out` and does not ingest.
|
|
83
|
+
|
|
72
84
|
- **The workflow loader refuses a gate step that can never settle
|
|
73
85
|
(`gate_outcome_impossible`).** A gate step settles only on `passed` or
|
|
74
86
|
`implementation-failure` when `expect` is `pass`, and only on `passed` or `repro-missing`
|
|
@@ -90,8 +102,8 @@ All notable user-facing changes are documented here. The project follows [Semant
|
|
|
90
102
|
execute until the run engine lands. The JSON result and its
|
|
91
103
|
`phase` are unchanged. A `run_handoff_required` refusal from `kxm runs drive` now ends
|
|
92
104
|
with `(handoff reason …; field …; detail …)`, each part capped at 200 characters; a run of
|
|
93
|
-
|
|
94
|
-
on
|
|
105
|
+
a workflow that declares `limits.maxAgentTimeMs`, for example, reports `limit_unsupported`
|
|
106
|
+
on that field. Top-level help names the product KXM instead of KontextMind,
|
|
95
107
|
and `kxm init` text output lists each validation issue as `file: code: message`.
|
|
96
108
|
- **`kxm suggest` recommends only KXM command skills.** Suggested skills come from the
|
|
97
109
|
command skills shipped in `plugins/kxm/skills` (such as `kxm-workflow`, `kxm-runs`,
|
|
@@ -307,6 +319,31 @@ All notable user-facing changes are documented here. The project follows [Semant
|
|
|
307
319
|
|
|
308
320
|
### Fixed
|
|
309
321
|
|
|
322
|
+
- **Live `kxm runs drive` can author on an audited writer profile.** A write-repository
|
|
323
|
+
step on pi (`-a`, with extensions, skills, and the session off) or grok
|
|
324
|
+
(`--always-approve`, with subagents and web search off) runs against the checkout.
|
|
325
|
+
A live write that leaves the tree unchanged settles `failed` with `authored: false`.
|
|
326
|
+
A read-only step that changes the tree cannot settle `passed`. Harnesses without a
|
|
327
|
+
writer profile still hand off. Simulated drive does not require a diff. The witness
|
|
328
|
+
fingerprints the one checkout, so a live write step must be a single assignment
|
|
329
|
+
(`assignments.maximum: 1`) in a project whose `limits.maxConcurrentRuns` is 1; a
|
|
330
|
+
panel of writers or a project that admits concurrent runs hands off with
|
|
331
|
+
`step_unsupported` instead of crediting one writer's change to another.
|
|
332
|
+
|
|
333
|
+
- **Fresh `kxm init` can be driven.** The current template drops `limits.maxAgentTimeMs`,
|
|
334
|
+
names coordinator `claude` / `anthropic/fable` and implementer `grok` / `xai/grok-4.6`,
|
|
335
|
+
and admits those two routes. One-shot production no longer falls through to an
|
|
336
|
+
unadmitted `claude-3-7-sonnet`.
|
|
337
|
+
|
|
338
|
+
- **`kxm backup` includes the Runtime stores under the user-state root.** Discovery
|
|
339
|
+
copies `$S/runtime/registry.db` and `$S/runtime/projects/<projectKey>/run-events.db`,
|
|
340
|
+
plus each `run-events.db.run-prompts.json` sidecar. A copy that misses a discovered
|
|
341
|
+
store is `complete: false`: `kxm backup` exits 1 with `ok: false`, and restore
|
|
342
|
+
refuses that manifest. Cross-box remap of absolute `$S` paths is still the file recipe.
|
|
343
|
+
The Runtime stores are machine-wide, so a backup holds every project's run store and a
|
|
344
|
+
restore rolls all of them back. `kxm backup --help` no longer says Runtime stores are
|
|
345
|
+
left out.
|
|
346
|
+
|
|
310
347
|
- **Signed webhooks cannot be replayed.** KXM's own webhook senders now sign the
|
|
311
348
|
timestamp, delivery ID, definition, run and signal key along with the body
|
|
312
349
|
(`x-kxm-signature`, `x-kxm-timestamp`, `x-kxm-delivery-id`; see
|
|
@@ -204,7 +204,7 @@ The package ships a directory of `SKILL.md` suites that Pi and Claude Code both
|
|
|
204
204
|
|
|
205
205
|
`kxm dash` draws live terminal screens (agents, tasks, workflows, plans, inbox, processes, and spend, which is always empty today) from the admin-only operations stream, a presence-only fallback, and a read-only snapshot of `kxm.db`. Treat it as an observer: its action keys `a`, `r`, `s` and `c` post to hub routes that do not exist, so they change nothing even though the status line reports success, and `d` creates a git branch and worktree.
|
|
206
206
|
|
|
207
|
-
`kxm studio layout` renders a workflow as DAG, stepper, and swimlane JSON, and `kxm studio serve` hosts a local viewer on `127.0.0.1:4242`. The Studio mutation endpoint is not wired to commands: it
|
|
207
|
+
`kxm studio layout` renders a workflow as DAG, stepper, and swimlane JSON, and `kxm studio serve` hosts a local viewer on `127.0.0.1:4242`. The Studio mutation endpoint is not wired to commands: it answers every allowed request with HTTP 501 `mutation_handler_missing` and runs nothing.
|
|
208
208
|
|
|
209
209
|
## Configuration is reviewed Git YAML
|
|
210
210
|
|
|
@@ -169,7 +169,7 @@ Every store opens in WAL mode with a 5-second busy timeout, `synchronous=NORMAL`
|
|
|
169
169
|
## Reset a store
|
|
170
170
|
|
|
171
171
|
> [!CAUTION]
|
|
172
|
-
> Deleting a store erases its history for good. Back it up first if you might need it. `kxm backup` copies the hub store
|
|
172
|
+
> Deleting a store erases its history for good. Back it up first if you might need it. `kxm backup` copies the hub store and the Runtime stores in the user state root, for every project on the machine.
|
|
173
173
|
|
|
174
174
|
Stop every writer first, then delete each database together with its sidecars:
|
|
175
175
|
|
|
@@ -227,7 +227,7 @@ backfilled: they still resolve an outcome, but they group per run.
|
|
|
227
227
|
- **Price catalog:** `.kxm/prices.yaml` (`kxm.prices.v1`, dated and hashed) defines input, output, cache-read, cache-write rates, and context tiers for active models. Missing rows or uncataloged models evaluate to `costBasis: "unknown"`.
|
|
228
228
|
- **Ranked report:** `kxm routing report` (`plugins/kxm/src/routing.ts`, CLI command `kxm routing report`) groups records by `(harness, model, thinking, role)`.
|
|
229
229
|
- **Ranking order:** Quality first (`verifyPassRate` descending, then `reworkRate` ascending where rework measures back-edge re-entries `transitions > 0`), followed by `costPerAcceptedUsd` ascending.
|
|
230
|
-
- **Underquote prevention:** Routes with unknown cost are flagged (`*`). Any unknown-cost attempt makes a route's cost a
|
|
230
|
+
- **Underquote prevention:** Routes with unknown cost are flagged (`*`). Any unknown-cost attempt makes a route's cost unknown: `costPerAcceptedUsd` is `null` (displayed `-`), never a partial sum, and the route ranks after every route of equal quality that has no unknown-cost attempt. Per-configuration comparisons likewise report `totalCostUsd: null` with a `missingCostRuns` count when any record lacks a cost.
|
|
231
231
|
- **Population separation:** Reports metered cost, unmetered attempt counts, unknown-cost attempt counts, and quota-exhausted attempt counts as separate metrics rather than a single misleading total.
|
|
232
232
|
- **List prices flag:** Supports `--equivalent-list-cost` / `--list-prices` to display estimated list rates for comparison alongside actual recorded spend.
|
|
233
233
|
- **Rework column:** reads `transitions`, which Runtime records never set, so Runtime rework shows up only as a resolved `reworked` outcome, which the report does not count as a pass.
|
|
@@ -133,16 +133,17 @@ The context suites in detail:
|
|
|
133
133
|
| Metadata-only dashboard: ops mode, presence-only fallback, observer filtering, keys, body-free local projection | `tui.test.ts`, `hub-api.test.ts` |
|
|
134
134
|
| Restricted loader, deterministic bundle hash, init classification, atomic creation, three-way repair, crash resumption | `project-config.test.ts`, `cli.test.ts`, `package-install.test.ts` |
|
|
135
135
|
| Legacy `.kxm/config` JSON is refused with `legacy_state_unsupported`; `kxm migrate` is unknown; older stores are refused | `project-config.test.ts`, `cli.test.ts`, `e6-backup-restore-migrations.test.ts` |
|
|
136
|
-
| `kxm backup` and `kxm restore` round-trip the hub store and Runtime stores seeded under `.kxm/runtime/`; tampered or
|
|
136
|
+
| `kxm backup` and `kxm restore` round-trip the hub store and Runtime stores seeded under `.kxm/runtime/`; Runtime stores and prompt sidecars under `KXM_STATE_HOME` are discovered; tampered, newer or incomplete (`complete: false`) backups are refused | `e6-backup-restore-migrations.test.ts` |
|
|
137
137
|
| Permission-diff trust: authority lattice, prose neutrality, Git base shadowing, CLI diff and check | `permission.test.ts`, `cli.test.ts`, `contracts.test.ts`, `package-install.test.ts` |
|
|
138
138
|
| KXM schemas, restricted YAML fixtures, cross-resource semantics and sync-safe rejection | `contracts.test.ts`, `restricted-yaml.test.ts` |
|
|
139
139
|
| Harness detection, auth and dispatch for the built-in catalog, including Windows launch rules | `harness.test.ts` |
|
|
140
140
|
| `kxm update`: `update.yaml` validation, GitHub or npm version checks, install-kind detection, `kxm-<v>.tgz` asset selection | `kxm-update.test.ts`, `kxm-update-cli.test.ts`, `kxm-install-kind.test.ts` |
|
|
141
141
|
|
|
142
|
-
The
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
142
|
+
The round trip seeds its Runtime stores in a project-local `.kxm/runtime/`
|
|
143
|
+
layout that the Runtime never writes. A separate test seeds a registry, an event
|
|
144
|
+
store and its prompt sidecar under a temporary `KXM_STATE_HOME` and checks that
|
|
145
|
+
`kxm backup` finds them and that a partial manifest is refused. No test backs up
|
|
146
|
+
stores a running supervisor wrote.
|
|
146
147
|
|
|
147
148
|
## Packaging, release and repository gates
|
|
148
149
|
|
|
@@ -59,17 +59,17 @@ Record every override with the backup. A restore that lands where the running se
|
|
|
59
59
|
|
|
60
60
|
## Know what each backup covers
|
|
61
61
|
|
|
62
|
-
`kxm backup`
|
|
62
|
+
`kxm backup` copies the SQLite stores it can find: the hub store under the current directory, and the Runtime stores and their prompt sidecars under the user state root. Everything else needs the stopped-state copy described below.
|
|
63
63
|
|
|
64
64
|
> [!WARNING]
|
|
65
|
-
>
|
|
65
|
+
> The Runtime stores are shared by every project on the machine. `kxm backup` copies `$S/runtime/registry.db` and the `run-events.db` of every project under `$S/runtime/projects/`, not only the project you run it from, and `kxm restore` writes all of them back. Restoring one project's backup returns every project's runs to the moment of that backup.
|
|
66
66
|
|
|
67
67
|
| Path | Holds | In `kxm backup` |
|
|
68
68
|
|---|---|---|
|
|
69
69
|
| `$W/kxm.db` | Hub store: agents, messages, workflow runs, journals, context items, leases, synced run facts | Yes, at `<current directory>/.kxm/state/kxm.db` only |
|
|
70
|
-
| `$S/runtime/registry.db` | Runtime registry: projects, their roots, the supervisor identity and claim |
|
|
71
|
-
| `$S/runtime/projects/<key>/run-events.db` | Event-sourced runs, drive receipts, gate evidence, the sync outbox |
|
|
72
|
-
| `$S/runtime/projects/<key>/run-events.db.run-prompts.json` | Run prompt text; restoring a store without it loses every prompt |
|
|
70
|
+
| `$S/runtime/registry.db` | Runtime registry: projects, their roots, the supervisor identity and claim | Yes (`registry`) |
|
|
71
|
+
| `$S/runtime/projects/<key>/run-events.db` | Event-sourced runs, drive receipts, gate evidence, the sync outbox | Yes, for every project (`events:<key>`) |
|
|
72
|
+
| `$S/runtime/projects/<key>/run-events.db.run-prompts.json` | Run prompt text; restoring a store without it loses every prompt | Yes, as a plain file (`events:<key>:run-prompts`) |
|
|
73
73
|
| `$S/projects/<hash>/repository-bindings.json`, `$S/update.yaml` | Member repository paths; updater settings | No |
|
|
74
74
|
| `$S/hub-env.json`, `$S/hub-binding.json`, `$C/session.token` | Credentials and the machine's hub binding | No; prefer regenerating secrets to copying them |
|
|
75
75
|
| `$R/.kxm/` definition files and durable records | Project, roles, routes, prices, roster, memory, skills, goals, tasks, candidates | No; commit them to Git or copy the checkout |
|
|
@@ -82,9 +82,9 @@ A restore without `roster.yaml`, `routes.yaml` or `prices.yaml` comes back healt
|
|
|
82
82
|
|
|
83
83
|
These files are disposable and need no backup: `hub.pid`, `hub.stop`, `worker-*.pid`, `session-brief.json`, `update-check.json`, `runtime/supervisor.token`, `runtime/supervisor.error`.
|
|
84
84
|
|
|
85
|
-
## Back up the
|
|
85
|
+
## Back up the SQLite stores with `kxm backup`
|
|
86
86
|
|
|
87
|
-
Run it from the checkout root. It
|
|
87
|
+
Run it from the checkout root. It finds the hub store from the current directory and ignores `--workspace`, `KXM_WORKDIR`, `KXM_STATE_DIR` and `KXM_DATA_PATH`, so a relocated hub database is not found. It finds the Runtime stores under the user state root, including one moved with `KXM_STATE_HOME`.
|
|
88
88
|
|
|
89
89
|
Preview first. A dry run lists what it would write, opens no store and writes nothing:
|
|
90
90
|
|
|
@@ -96,31 +96,40 @@ kxm backup --dry-run
|
|
|
96
96
|
Expected output:
|
|
97
97
|
|
|
98
98
|
```text
|
|
99
|
-
dry run: back up
|
|
99
|
+
dry run: back up 3 store(s) and 1 file(s) to /srv/kxm/product/.kxm/backups/backup-2026-09-23T18-29-09-107Z (sources are not opened, so their WAL is not checkpointed)
|
|
100
100
|
would write /srv/kxm/product/.kxm/backups/backup-2026-09-23T18-29-09-107Z/kxm.db
|
|
101
|
+
would write /srv/kxm/product/.kxm/backups/backup-2026-09-23T18-29-09-107Z/registry.db
|
|
102
|
+
would write /srv/kxm/product/.kxm/backups/backup-2026-09-23T18-29-09-107Z/run-events.db
|
|
103
|
+
would write /srv/kxm/product/.kxm/backups/backup-2026-09-23T18-29-09-107Z/run-events.db.run-prompts.json
|
|
101
104
|
would write /srv/kxm/product/.kxm/backups/backup-2026-09-23T18-29-09-107Z/manifest.json
|
|
102
105
|
```
|
|
103
106
|
|
|
104
107
|
Then write the backup outside the checkout:
|
|
105
108
|
|
|
106
109
|
```bash
|
|
107
|
-
kxm backup --out /backups/kxm/2026-09-23/
|
|
110
|
+
kxm backup --out /backups/kxm/2026-09-23/sqlite
|
|
108
111
|
```
|
|
109
112
|
|
|
110
113
|
Expected output:
|
|
111
114
|
|
|
112
115
|
```text
|
|
113
|
-
Created SQLite backup with
|
|
116
|
+
Created SQLite backup with 3 store(s):
|
|
114
117
|
- hub-store: /srv/kxm/product/.kxm/state/kxm.db -> kxm.db (schema v5, 110592 bytes, sha256 sha256:86549...)
|
|
115
|
-
|
|
118
|
+
- registry: /home/kxm/.local/state/kxm/runtime/registry.db -> registry.db (schema v1, 20480 bytes, sha256 sha256:a5bde...)
|
|
119
|
+
- events:38ed26cb8eeaa297f3b0b452: /home/kxm/.local/state/kxm/runtime/projects/38ed26cb8eeaa297f3b0b452/run-events.db -> run-events.db (schema v7, 348160 bytes, sha256 sha256:27c13...)
|
|
120
|
+
Manifest: /backups/kxm/2026-09-23/sqlite/manifest.json
|
|
116
121
|
```
|
|
117
122
|
|
|
118
|
-
|
|
123
|
+
The summary lists stores only; the prompt sidecar is in the manifest's `files`. When two projects' event stores share a file name, the second copy is prefixed with its store id.
|
|
124
|
+
|
|
125
|
+
For each store, `kxm backup` checkpoints the write-ahead log, runs `PRAGMA integrity_check`, copies the database with `VACUUM INTO`, checks the copy's integrity, sets mode `0600`, and records its schema version and SHA-256 in `manifest.json` (`kxm.backup-manifest.v1`). `VACUUM INTO` reads one consistent snapshot, so the hub and the Runtime can keep running during this step. Each prompt sidecar is copied as a regular file with mode `0600` and its SHA-256 recorded.
|
|
126
|
+
|
|
127
|
+
A backup is complete only when it copied everything it found. If a store or sidecar cannot be copied, or one appears while the backup runs, the manifest lists it under `omitted` and records `complete: false`, the command prints `Backup is incomplete (<n> omitted); not ok:` and exits 1, and `kxm restore` refuses that manifest.
|
|
119
128
|
|
|
120
129
|
> [!NOTE]
|
|
121
130
|
> Without `--out`, backups go to `.kxm/backups/` inside the checkout. Keep that directory out of Git, or always pass `--out`.
|
|
122
131
|
|
|
123
|
-
It fails with `backup_no_stores` when `.kxm/state/kxm.db`
|
|
132
|
+
It fails with `backup_no_stores` when it finds no store at all, neither `.kxm/state/kxm.db` under the current directory nor a Runtime store under the user state root, and with `database_corrupted` when an integrity check fails.
|
|
124
133
|
|
|
125
134
|
## Back up everything else
|
|
126
135
|
|
|
@@ -142,11 +151,11 @@ Copy the remaining state with both services stopped. SQLite runs in write-ahead-
|
|
|
142
151
|
S="${KXM_STATE_HOME:-$HOME/.local/state/kxm}" # macOS: "$HOME/Library/Application Support/KXM"
|
|
143
152
|
B=/backups/kxm/2026-09-23
|
|
144
153
|
mkdir -p "$B"
|
|
145
|
-
kxm backup --out "$B/
|
|
154
|
+
kxm backup --out "$B/sqlite"
|
|
146
155
|
tar -C "$S" --exclude 'supervisor.token' --exclude 'hub-env.json' -czf "$B/user-state.tgz" .
|
|
147
156
|
```
|
|
148
157
|
|
|
149
|
-
|
|
158
|
+
`kxm backup` already holds the Runtime stores and sidecars. The archive holds them again, as files, together with what `kxm backup` does not copy: the repository bindings, the hub binding and `update.yaml`. It leaves out the credential file; keep tokens in your secret store, or include `hub-env.json` and protect the archive as a secret.
|
|
150
159
|
|
|
151
160
|
If `KXM_STATE_DIR` or `KXM_DATA_PATH` moved the hub database, `kxm backup` fails with `backup_no_stores`. With both services stopped, copy that database file and any `-wal` and `-shm` files instead.
|
|
152
161
|
4. Copy the other roots your recovery needs: the checkout's untracked `.kxm/` records, `$D/assets/`, `$W/worker-*.json` (and `$W/pi-sessions/` only if your policy keeps model history), and `$C`.
|
|
@@ -157,40 +166,45 @@ Keep at least one previous backup, and bound retention: run events and prompt si
|
|
|
157
166
|
|
|
158
167
|
## Restore with `kxm restore`
|
|
159
168
|
|
|
160
|
-
`kxm restore` overwrites
|
|
169
|
+
`kxm restore` overwrites every live database in the manifest, including the Runtime stores of every project on the machine, and deletes their `-wal` and `-shm` files. It does not check whether the hub or the Runtime is running. Stop the hub and the Runtime first, and move the current state aside rather than deleting it.
|
|
161
170
|
|
|
162
171
|
Preview the restore. The dry run performs every check below and lists what it would overwrite:
|
|
163
172
|
|
|
164
173
|
```bash
|
|
165
174
|
cd /srv/kxm/product
|
|
166
|
-
kxm restore /backups/kxm/2026-09-23/
|
|
175
|
+
kxm restore /backups/kxm/2026-09-23/sqlite/manifest.json --dry-run
|
|
167
176
|
```
|
|
168
177
|
|
|
169
178
|
Expected output:
|
|
170
179
|
|
|
171
180
|
```text
|
|
172
|
-
dry run: restore
|
|
181
|
+
dry run: restore 3 store(s) and 1 file(s) from /backups/kxm/2026-09-23/sqlite/manifest.json; digests verified against the manifest
|
|
173
182
|
would write /srv/kxm/product/.kxm/state/kxm.db
|
|
174
183
|
would delete /srv/kxm/product/.kxm/state/kxm.db-wal
|
|
175
184
|
would delete /srv/kxm/product/.kxm/state/kxm.db-shm
|
|
185
|
+
would write /home/kxm/.local/state/kxm/runtime/registry.db
|
|
186
|
+
would write /home/kxm/.local/state/kxm/runtime/projects/38ed26cb8eeaa297f3b0b452/run-events.db
|
|
187
|
+
would write /home/kxm/.local/state/kxm/runtime/projects/38ed26cb8eeaa297f3b0b452/run-events.db.run-prompts.json
|
|
176
188
|
```
|
|
177
189
|
|
|
178
190
|
Then run it without `--dry-run`:
|
|
179
191
|
|
|
180
192
|
```bash
|
|
181
|
-
kxm restore /backups/kxm/2026-09-23/
|
|
193
|
+
kxm restore /backups/kxm/2026-09-23/sqlite/manifest.json
|
|
182
194
|
```
|
|
183
195
|
|
|
184
196
|
Expected output:
|
|
185
197
|
|
|
186
198
|
```text
|
|
187
|
-
Restored
|
|
199
|
+
Restored 3 SQLite store(s) from /backups/kxm/2026-09-23/sqlite/manifest.json:
|
|
188
200
|
- hub-store: -> /srv/kxm/product/.kxm/state/kxm.db (schema v5, integrity ok)
|
|
201
|
+
- registry: -> /home/kxm/.local/state/kxm/runtime/registry.db (schema v1, integrity ok)
|
|
202
|
+
- events:38ed26cb8eeaa297f3b0b452: -> /home/kxm/.local/state/kxm/runtime/projects/38ed26cb8eeaa297f3b0b452/run-events.db (schema v7, integrity ok)
|
|
189
203
|
```
|
|
190
204
|
|
|
191
|
-
Before it overwrites anything, `kxm restore` checks that the manifest is a `kxm.backup-manifest.v1` document, that every listed file exists, that each file's SHA-256 matches the manifest, and that no store is newer than this build supports. The manifest's own `manifestSha256` is not checked.
|
|
205
|
+
Before it overwrites anything, `kxm restore` checks that the manifest is a `kxm.backup-manifest.v1` document that does not record `complete: false` (`restore_incomplete`), that every listed file exists, that each file's SHA-256 matches the manifest, and that no store is newer than this build supports. The manifest's own `manifestSha256` is not checked. A manifest from an older build that has no `complete` field still restores.
|
|
192
206
|
|
|
193
|
-
It then checks each backup's integrity and schema version again, copies it into place with mode `0600`, and
|
|
207
|
+
It then checks each backup's integrity and schema version again, copies it into place with mode `0600`, checks the result, and then copies the prompt sidecars back. A store under the checkout is rebased onto the current directory when the manifest came from another checkout. The Runtime stores and sidecars return to their recorded absolute paths under the user state root; restore does not move them to a different machine's or a different `KXM_STATE_HOME`.
|
|
194
208
|
|
|
195
209
|
### Restore ceilings
|
|
196
210
|
|
|
@@ -207,6 +221,8 @@ The ceilings come from `KXM_BACKUP_CEILINGS` in `plugins/kxm/src/database.ts` an
|
|
|
207
221
|
|
|
208
222
|
### Restore the Runtime stores by hand
|
|
209
223
|
|
|
224
|
+
`kxm restore` puts the Runtime stores back at the paths they came from. Use the archive instead when the user state root moved, or when you also need the bindings and `update.yaml`.
|
|
225
|
+
|
|
210
226
|
1. Stop the Runtime and the hub, as in the backup procedure.
|
|
211
227
|
2. Move `$S/runtime/registry.db` and `$S/runtime/projects/` aside.
|
|
212
228
|
3. Extract the archive into the user state root:
|
|
@@ -232,8 +248,11 @@ Test a full restore on a spare machine before you rely on it, and repeat the tes
|
|
|
232
248
|
|
|
233
249
|
| Symptom | Cause | Fix |
|
|
234
250
|
|---|---|---|
|
|
235
|
-
| `backup_no_stores` | No `.kxm/state/kxm.db` under the current directory
|
|
236
|
-
|
|
|
251
|
+
| `backup_no_stores` | No `.kxm/state/kxm.db` under the current directory (or it was moved with `KXM_STATE_DIR` or `KXM_DATA_PATH`) and no Runtime store under the user state root | Run from the checkout root; copy a relocated database with the stopped-state procedure |
|
|
252
|
+
| `Backup is incomplete (<n> omitted); not ok:`, exit 1 | A store or sidecar could not be copied, or appeared while the backup ran | Fix the source named after `omitted`, then run `kxm backup` again |
|
|
253
|
+
| `restore_incomplete` | The manifest records `complete: false` | Restore a complete backup; the partial one is not restorable |
|
|
254
|
+
| Runs are missing after a restore | The checkout moved to another absolute path, so the Runtime derives a different store key, or the user state root changed | Keep the checkout at its original path and restore under the same `KXM_STATE_HOME` |
|
|
255
|
+
| Another project's recent runs disappeared after a restore | `kxm restore` rolled back every project's Runtime store to the backup's moment | Restore that project's store from a newer backup or from the moved-aside copy |
|
|
237
256
|
| `restore_manifest_digest_mismatch` | A backup file changed after the manifest was written | Use another backup; do not edit files in a backup set |
|
|
238
257
|
| `restore_file_missing` | A file listed in the manifest is not beside it | Copy the whole backup directory, not only `manifest.json` |
|
|
239
258
|
| `runtime_schema_mismatch` | A backup file's schema version differs from the one its manifest records | Use another backup set; never mix files between sets |
|
|
@@ -237,7 +237,7 @@ flowchart LR
|
|
|
237
237
|
Hold these invariants on every tenant box:
|
|
238
238
|
|
|
239
239
|
1. **A service account, not root.** Session isolation routes model context; it is not a sandbox against a hostile process under the same account. Give untrusted workers separate accounts or containers.
|
|
240
|
-
2. **Explicit, stable paths.** Set `KXM_WORKSPACE_DIR`, `KXM_STATE_DIR`, `KXM_DATA_PATH`, `KXM_LOG_PATH` and `KXM_STATE_HOME` instead of inheriting a home directory, and pin `KXM_HOST=127.0.0.1`. `kxm backup` ignores `KXM_STATE_DIR` and `KXM_DATA_PATH`, so on such a box it finds no hub store
|
|
240
|
+
2. **Explicit, stable paths.** Set `KXM_WORKSPACE_DIR`, `KXM_STATE_DIR`, `KXM_DATA_PATH`, `KXM_LOG_PATH` and `KXM_STATE_HOME` instead of inheriting a home directory, and pin `KXM_HOST=127.0.0.1`. `kxm backup` ignores `KXM_STATE_DIR` and `KXM_DATA_PATH`, so on such a box it finds no hub store: it copies only the Runtime stores under `KXM_STATE_HOME`, or fails with `backup_no_stores` when there are none. Copy the hub database with the [stopped-state backup](backup-and-restore.md#back-up-everything-else).
|
|
241
241
|
3. **Loopback listeners only.** Neither the hub nor the supervisor has a public port, and nothing is load-balanced across hubs.
|
|
242
242
|
4. **Edge authentication.** The proxy authenticates browsers (for example with Authentik forward auth) on the portal's routes only. The hub never interprets browser identity; see [ADR-0004](../adr/ADR-0004-edge-identity-authentik.md).
|
|
243
243
|
5. **Server-side machine credentials.** The portal backend keeps the hub credentials and calls the hub itself. `kxm tenant status`, which uses the admin token, gives it one labelled view of hub metadata and Runtime runs.
|
|
@@ -15,7 +15,7 @@ Output shown under examples was captured from a source checkout, inside a throwa
|
|
|
15
15
|
- Hub and sessions: [`hub`](#kxm-hub-view), [`session`](#kxm-session), [`dash`](#kxm-dash), [`studio`](#kxm-studio)
|
|
16
16
|
- Harnesses, models, and roles: [`harness`](#kxm-harness), [`auth`](#kxm-auth), [`update`](#kxm-update), [`models`](#kxm-models), [`routes`](#kxm-routes), [`role`](#kxm-role)
|
|
17
17
|
- Running work: [`run`](#kxm-run), [`runs`](#kxm-runs), [`runtime`](#kxm-runtime), [`agent`](#kxm-agent), [`workflow`](#kxm-workflow), [`gate`](#kxm-gate), [`peer`](#kxm-peer), [`task`](#kxm-task), [`goal`](#kxm-goal), [`suggest`](#kxm-suggest), [`explain`](#kxm-explain)
|
|
18
|
-
- Context and learning: [`context`](#kxm-context), [`memory`](#kxm-memory), [`skills`](#kxm-skills), [`improve`](#kxm-improve), [`routing`](#kxm-routing)
|
|
18
|
+
- Context and learning: [`context`](#kxm-context), [`memory`](#kxm-memory), [`skills`](#kxm-skills), [`improve`](#kxm-improve), [`routing`](#kxm-routing), [`prices`](#kxm-prices)
|
|
19
19
|
- Operations: [`backup`](#kxm-backup), [`restore`](#kxm-restore), [`tenant`](#kxm-tenant), [`ssh`](#kxm-ssh), [`help`](#kxm-help)
|
|
20
20
|
- [Known behavior gaps](#known-behavior-gaps)
|
|
21
21
|
|
|
@@ -102,7 +102,7 @@ kxm -V
|
|
|
102
102
|
- Plan with `planned`: `backup`, `restore`, `config set`, `role add|remove|modify|set-host|resume`, `workflow add|remove|modify`, `goal create`, `task create|run|sync`, `memory note|sync`, `skills evaluate|promote|reject`, `context promote`, `context wiki-compile --out`, `auth token`, `session token`, `session brief`, and `ssh run|file|close`. Each prints its normal result plus `dryRun: true` and `planned`, a list of `{action, target}` entries whose `action` is `write`, `delete`, `move`, `request`, or `ssh`. Text mode prints `dry run: <summary>` and one indented `would <action> <target>` line per entry (`session brief` appends `dry run: would write <file>` lines to the brief instead).
|
|
103
103
|
- Plan in their own shape (described in each section): `init`, `run`, `runs drive|cancel`, `runtime start|stop|sync-retry`, `hub start|stop|bind|unbind`, `session start|stop`, `agent worker`, `dash`, `studio serve`, `models inventory-refresh`, `routes admit|disable`, `update`, `completion install`, `workflow start|signal|export|checkpoint|record|wait`, every `peer` subcommand, `gate degrade|signal`, `skills create`, and `improve report`. `gate github watch --dry-run` still polls GitHub but does not post the signal.
|
|
104
104
|
- Read-only commands run as usual, without leaving a trace: a local SQLite store is opened without creating `-wal` or `-shm` files, and `runs status|list|receipt` only attach to a running Runtime supervisor. With no supervisor running they exit 2 with `dry_run_unsupported` instead of starting one. `context wiki-compile --dry-run` still asks the hub to compile, which is a read.
|
|
105
|
-
- Refused: `kxm models` (the interactive screen)
|
|
105
|
+
- Refused: `kxm models` (the interactive screen) and `kxm prices acknowledge` exit 2 with `dry_run_unsupported`. The CLI keeps one list of the commands that answer `--dry-run` and refuses every other command the same way before it runs, so a command added without dry-run support fails closed.
|
|
106
106
|
|
|
107
107
|
```bash
|
|
108
108
|
kxm config set user.theme light --dry-run
|
|
@@ -161,7 +161,7 @@ Exit 2 covers an unknown command or option, a missing argument or required optio
|
|
|
161
161
|
| Message peers | [`kxm peer list`](#kxm-peer-list), [`kxm peer send`](#kxm-peer-send), [`kxm peer await`](#kxm-peer-await), [`kxm peer fanout`](#kxm-peer-fanout) |
|
|
162
162
|
| Operate gates and evidence | [`kxm gate validate`](#kxm-gate-validate), [`kxm gate artifacts-exist`](#kxm-gate-artifacts-exist), [`kxm gate signal`](#kxm-gate-signal), [`kxm workflow checkpoint`](#kxm-workflow-checkpoint) |
|
|
163
163
|
| Query context and memory | [`kxm context get`](#kxm-context-get), [`kxm context recall`](#kxm-context-recall), [`kxm memory brief`](#kxm-memory-brief), [`kxm memory note`](#kxm-memory-note) |
|
|
164
|
-
| Read improvement and routing reports | [`kxm improve report`](#kxm-improve-report), [`kxm routing report`](#kxm-routing-report) |
|
|
164
|
+
| Read improvement and routing reports | [`kxm improve report`](#kxm-improve-report), [`kxm routing report`](#kxm-routing-report), [`kxm prices acknowledge`](#kxm-prices-acknowledge) |
|
|
165
165
|
| Back up and restore | [`kxm backup`](#kxm-backup), [`kxm restore`](#kxm-restore) |
|
|
166
166
|
| Update KXM and harnesses | [`kxm update --check`](#kxm-update), [`kxm update --kxm`](#kxm-update) |
|
|
167
167
|
|
|
@@ -183,7 +183,7 @@ Creates, validates, repairs, resumes, or joins a KXM project at the Git root tha
|
|
|
183
183
|
- `--project-id` must match `prj_` followed by 6 to 128 letters, digits, `_`, or `-`. `--repository` is repeatable; each value must be `<id>=<absolute path>`, and a repeated ID fails with `repository_binding_argument_duplicate`.
|
|
184
184
|
- Needs a Git repository. Does not need a hub or the Runtime.
|
|
185
185
|
- Writes `.kxm/project.yaml`, `.kxm/agents/coordinator.yaml`, `.kxm/agents/implementer.yaml`, `.kxm/gates.yaml`, `.kxm/repo/repo.yaml`, `.kxm/workflows/default.yaml`, and `.kxm/template-provenance.yaml`, using a `.kxm-init-transaction` directory at the Git root while a create or repair is in flight. Repository bindings are written under the user state root, never into Git. `--dry-run` writes nothing.
|
|
186
|
-
- On an interactive terminal without `--json` or `--dry-run`, a successful create or join offers to install shell completion (suppress with `KXM_SKIP_COMPLETION_PROMPT=1`) and to write workflow-guide agents
|
|
186
|
+
- On an interactive terminal without `--json` or `--dry-run`, a successful create or join offers to install shell completion (suppress with `KXM_SKIP_COMPLETION_PROMPT=1`) and to write workflow-guide agents (suppress with `KXM_SKIP_GUIDE_SETUP_PROMPT=1`). Guided setup keeps a role only when one of its guide candidates is on a fixed map of reviewed harness/model pairs and that harness is authenticated; it writes the agent and workflow files, appends those selectors to `.kxm/routes.yaml`, and skips every other candidate. Google candidates map to the Pi `antigravity` provider, which the Runtime's Pi one-shot cannot reach yet (see [Harness routing](harness-routing.md#google-through-the-antigravity-pi-provider)).
|
|
187
187
|
- JSON keys: `action` (`planned`, `created`, `joined`, `repaired`, `resumed`, or `validated`), `mode`, `inspectedFrom`, `projectRoot`, `changesRequired`, `legacyInputs`, `issues`, `configRevision`, `files`, `plannedOnly`, and, when relevant, `localBindingFile`, `bindingsChanged`, `repairPlan`, `resumePending`, `transactionKind`.
|
|
188
188
|
- Exit 0 for every completed action and every dry-run plan. Exit 1 when the result is planning-only (legacy state, blocked repair, partial state without provenance) or for `initialization_failed` (with `issues`) and `initialization_io_failed`. A planning-only text result prints the reason and then one `<file>: <code>: <message>` line per validation issue, for example `.kxm/workflows/first.yaml: gate_outcome_impossible: ...`.
|
|
189
189
|
|
|
@@ -846,7 +846,7 @@ kxm studio serve [-p <port>] [--host <host>] [--token <token>]
|
|
|
846
846
|
Serves the Web Studio on `http://127.0.0.1:4242` until interrupted. It serves `/`, `/health`, `GET /api/layout`, and `POST /api/mutate`. The plan comes from `.kxm/workflows/default.yaml` in the current directory, or the first YAML file in `.kxm/workflows/`.
|
|
847
847
|
|
|
848
848
|
> [!WARNING]
|
|
849
|
-
> Every response carries `Access-Control-Allow-Origin: *`, so any web page open in a browser on this machine can read `/api/layout` (your workflow plan). `POST /api/mutate` requires `Authorization: Bearer <session token>` only when a session token resolves (`--token`, `KXM_SESSION_TOKEN`, or the on-disk token); with none, it accepts any caller.
|
|
849
|
+
> Every response carries `Access-Control-Allow-Origin: *`, so any web page open in a browser on this machine can read `/api/layout` (your workflow plan). `POST /api/mutate` requires `Authorization: Bearer <session token>` only when a session token resolves (`--token`, `KXM_SESSION_TOKEN`, or the on-disk token); with none, it accepts any caller. An allowlisted command name gets HTTP 501 with `ok: false`, `executed: false`, `mappedToCli: false` and `error: "mutation_handler_missing"`, because `kxm studio serve` wires no mutation handler; it executes nothing. Keep the default loopback `--host`.
|
|
850
850
|
|
|
851
851
|
| Option | Argument | Default | Description |
|
|
852
852
|
|---|---|---|---|
|
|
@@ -1388,7 +1388,7 @@ Create a KXM run (offline-first; `kxm runs drive <runId> --simulated` executes i
|
|
|
1388
1388
|
- Needs a KXM project. Starts and uses the Runtime; no hub needed. Honors `--dry-run`, which validates the project and prints the plan without starting the supervisor.
|
|
1389
1389
|
- JSON keys: `phase`, `idempotent`, `run` (`runId`, `homeRuntimeId`, `status`, `configRevision`), `supervisor` (`runtimeId`, `port`, `started`). Dry run: `projectRoot`, `workflowId`, `configRevision`. The JSON result does not carry the drive command.
|
|
1390
1390
|
- Errors: `workflow_required` (exit 2), `project_required`, `run_workflow_unknown`, `run_failed` with `issues` (any invalid file in the project fails the load, for example `gate_outcome_impossible`), `run_io_failed` (exit 1).
|
|
1391
|
-
- The `default` workflow that `kxm init` writes
|
|
1391
|
+
- The `default` workflow that `kxm init` writes does not set `limits.maxAgentTimeMs`, so it can be driven. A workflow that sets that limit is created, but `kxm runs drive` refuses it with `run_handoff_required` (`limit_unsupported`); projects from older `kxm init` templates carry it on `default`.
|
|
1392
1392
|
|
|
1393
1393
|
```bash
|
|
1394
1394
|
kxm run default "Fix the flaky login test" --dry-run
|
|
@@ -1458,7 +1458,7 @@ kxm runs status --dry-run refused: the Runtime supervisor is not running and --d
|
|
|
1458
1458
|
kxm runs drive <runId> [--simulated] [--wait] [--timeout-ms <n>]
|
|
1459
1459
|
```
|
|
1460
1460
|
|
|
1461
|
-
Opens a drive of the run. With `--simulated`, a model-free producer reports every agent step as passed. Without `--simulated` the drive runs in live mode: each agent step invokes its harness through a one-shot producer, and the agent's model must be an admitted route (otherwise `producer_route_not_admitted`).
|
|
1461
|
+
Opens a drive of the run. With `--simulated`, a model-free producer reports every agent step as passed. Without `--simulated` the drive runs in live mode: each agent step invokes its harness through a one-shot producer, and the agent's model must be an admitted route (otherwise `producer_route_not_admitted`; there is no fallback model). A read-only step runs with the harness's read-only flags. A step with `write` access runs with an audited writer profile, which only `pi` and `grok` have; it must be a single assignment in a project whose `limits.maxConcurrentRuns` is 1, and, when `.kxm/roster.yaml` exists, its route must be on the roster's writer lineup. Otherwise the drive hands the run off with `step_unsupported`. Around each live attempt the Runtime fingerprints the checkout with `git status` and `git diff`: a write step settles `passed` only when the checkout changed (routing metadata `authored: true`), and a read-only step that changed it settles `failed` (`authoringWitness: readonly_mutated`).
|
|
1462
1462
|
|
|
1463
1463
|
| Option | Argument | Default | Description |
|
|
1464
1464
|
|---|---|---|---|
|
|
@@ -1480,7 +1480,7 @@ kxm runs drive run_0123456789abcdef0123456789abcdef --simulated --dry-run
|
|
|
1480
1480
|
drive plan: run run_0123456789abcdef0123456789abcdef in simulated mode (no events written)
|
|
1481
1481
|
```
|
|
1482
1482
|
|
|
1483
|
-
|
|
1483
|
+
A workflow that declares `limits.maxAgentTimeMs`, such as `default` in a project from an older `kxm init` template, is handed off (see [`kxm run`](#kxm-run)):
|
|
1484
1484
|
|
|
1485
1485
|
```bash
|
|
1486
1486
|
kxm runs drive run_a80e84c98f514299b82f0157f4537ea3 --simulated
|
|
@@ -3245,10 +3245,10 @@ Without `--file` it reads the same sources as [`kxm improve report`](#kxm-improv
|
|
|
3245
3245
|
- Reads only. A price catalog that cannot be loaded is skipped silently.
|
|
3246
3246
|
- `--equivalent-list-cost` loads the catalog without the freshness check the producers apply, so it prices with a catalog of any date, including one the producers treat as stale. Check the catalog `date` before you rely on `ListEquiv($)`.
|
|
3247
3247
|
- A Runtime store that exists but cannot be read exits 1 with `improve_source_unreadable`.
|
|
3248
|
-
- Ranking: quality first (Pass%, then Rwk%), then cost per accepted attempt. Among routes of equal quality, any route with an unknown-cost attempt ranks after every route without one.
|
|
3248
|
+
- Ranking: quality first (Pass%, then Rwk%), then cost per accepted attempt. Among routes of equal quality, any route with an unknown-cost attempt ranks after every route without one. Such a route's cost is unknown, not a partial sum: `$/Acc` prints `-` (JSON `costPerAcceptedUsd: null`) and the `*` in `Unk` marks it.
|
|
3249
3249
|
- The `Quota` column counts attempts whose metadata looks quota-exhausted (a quota failure class, a `quota` flag, or text such as `rate limit` or `HTTP 429`). It is a count only: nothing fails over to another route.
|
|
3250
3250
|
- The text output does not list the sources, and prints `no routing records in telemetry` when no source holds a record. The Rwk% column counts records with `transitions` greater than 0, which Runtime records never set.
|
|
3251
|
-
- JSON keys: `file` (the telemetry path, also when the Runtime store was read), `sources` (without `--file`; the same shape as in `kxm improve`), `configurations` (per behavioral hash for v1 records), `report` (`schema`, `generatedAt`, `totalAttempts`, `rows`).
|
|
3251
|
+
- JSON keys: `file` (the telemetry path, also when the Runtime store was read), `sources` (without `--file`; the same shape as in `kxm improve`), `configurations` (per behavioral hash for v1 records; `totalCostUsd` is `null` when any record lacks a cost, and `missingCostRuns` counts those records), `report` (`schema`, `generatedAt`, `totalAttempts`, `rows`).
|
|
3252
3252
|
|
|
3253
3253
|
With no routing records yet:
|
|
3254
3254
|
|
|
@@ -3306,45 +3306,78 @@ claude fable 680 1200 450 $0.4
|
|
|
3306
3306
|
pi qwen3-coder-plus 560 1200 450 $0.12 passed
|
|
3307
3307
|
```
|
|
3308
3308
|
|
|
3309
|
+
## `kxm prices`
|
|
3310
|
+
|
|
3311
|
+
### `kxm prices acknowledge`
|
|
3312
|
+
|
|
3313
|
+
```text
|
|
3314
|
+
kxm prices acknowledge
|
|
3315
|
+
```
|
|
3316
|
+
|
|
3317
|
+
Stamps the existing `.kxm/prices.yaml` list as today's estimate. It rewrites the file with today's date and a new `sha256` over the same `models` rows, then reads it back and checks both. It does not fetch vendor rates or change any price. Cost estimates treat a catalog whose date is not today as stale and stay unknown, so run this only after you have checked the list against the vendors' current rates.
|
|
3318
|
+
|
|
3319
|
+
No command-specific options.
|
|
3320
|
+
|
|
3321
|
+
- Reads and rewrites `.kxm/prices.yaml` in the current directory. Needs no hub and no Runtime.
|
|
3322
|
+
- Refuses `--dry-run` with `dry_run_unsupported` (exit 2).
|
|
3323
|
+
- JSON keys: `date`, `sha256`, `note`.
|
|
3324
|
+
- Exit 1 with `prices_acknowledge_failed` and a `message`, for example `price catalog missing` when there is no `.kxm/prices.yaml`.
|
|
3325
|
+
|
|
3326
|
+
```bash
|
|
3327
|
+
kxm prices acknowledge
|
|
3328
|
+
```
|
|
3329
|
+
|
|
3330
|
+
```text
|
|
3331
|
+
price catalog stamped 2026-09-23 (list estimate only; vendor rates were not fetched)
|
|
3332
|
+
```
|
|
3333
|
+
|
|
3309
3334
|
## `kxm backup`
|
|
3310
3335
|
|
|
3311
3336
|
```text
|
|
3312
3337
|
kxm backup [--out <dir>]
|
|
3313
3338
|
```
|
|
3314
3339
|
|
|
3315
|
-
Creates a verified SQLite backup of the project hub store
|
|
3340
|
+
Creates a verified SQLite backup of the project hub store and the Runtime stores under the user state root, with a hashed `kxm.backup-manifest.v1` manifest. Relative to the current directory it discovers the hub store `.kxm/state/kxm.db`, and any `registry.db`, `bindings.db`, and `events/*.db` under `.kxm/runtime/`. Under the user state root (`KXM_STATE_HOME` or the platform default) it discovers `runtime/registry.db`, the `run-events.db` of every project under `runtime/projects/`, and each store's `run-events.db.run-prompts.json` prompt sidecar, which it copies as a plain file. Those Runtime stores belong to every project on the machine, not only this one. It ignores `--workspace` and `KXM_DATA_PATH`. Bindings, `update.yaml` and the other state roots are not included; see [Backup and restore](../operations/backup-and-restore.md).
|
|
3316
3341
|
|
|
3317
3342
|
| Option | Argument | Default | Description |
|
|
3318
3343
|
|---|---|---|---|
|
|
3319
3344
|
| `--out` | `<dir>` | `.kxm/backups/backup-<timestamp>` | Directory to write backup and manifest |
|
|
3320
3345
|
|
|
3321
|
-
- Writes a copy of each store and `manifest.json`. `--dry-run` lists the stores it found and the files it would write without opening any store, so no WAL is checkpointed (JSON: `outDir`, `stores` with `storeId`, `sourcePath`, `backupFile`, plus `dryRun` and `planned`).
|
|
3322
|
-
- JSON keys: `backupId`, `outDir`, `manifest` (`schema`, `backupId`, `createdAt`, `projectRoot`, `stores` with `storeId`, `sourcePath`, `backupFile`, `schemaVersion`, `sha256`, `bytes`, `integrity`; `manifestSha256`).
|
|
3323
|
-
-
|
|
3346
|
+
- Writes a copy of each store and sidecar and `manifest.json`. `--dry-run` lists the stores and files it found and the files it would write without opening any store, so no WAL is checkpointed (JSON: `outDir`, `stores` with `storeId`, `sourcePath`, `backupFile`, `files` with `id`, `sourcePath`, `backupFile`, plus `dryRun` and `planned`).
|
|
3347
|
+
- JSON keys: `ok`, `backupId`, `outDir`, `manifest` (`schema`, `backupId`, `createdAt`, `projectRoot`, `stores` with `storeId`, `sourcePath`, `backupFile`, `schemaVersion`, `sha256`, `bytes`, `integrity`; `files` with `id`, `sourcePath`, `backupFile`, `sha256`, `bytes`; `complete`; `omitted`; `manifestSha256`).
|
|
3348
|
+
- A store or sidecar that cannot be copied, or that appears while the backup runs, is listed in `omitted` and the manifest records `complete: false`. The command then prints `Backup is incomplete (<n> omitted); not ok:`, sets `ok: false`, and exits 1; `kxm restore` refuses that manifest.
|
|
3349
|
+
- Exit 1 with `backup_failed`; `issues` carry codes such as `backup_no_stores` (no store found, or none could be copied) and `database_corrupted`.
|
|
3324
3350
|
|
|
3325
3351
|
```bash
|
|
3326
3352
|
kxm backup --out ../bk
|
|
3327
3353
|
```
|
|
3328
3354
|
|
|
3329
3355
|
```text
|
|
3330
|
-
Created SQLite backup with
|
|
3356
|
+
Created SQLite backup with 3 store(s):
|
|
3331
3357
|
- hub-store: /work/proj/.kxm/state/kxm.db -> kxm.db (schema v5, 110592 bytes, sha256 sha256:56c6d...)
|
|
3358
|
+
- registry: /home/me/.local/state/kxm/runtime/registry.db -> registry.db (schema v1, 20480 bytes, sha256 sha256:a5bde...)
|
|
3359
|
+
- events:38ed26cb8eeaa297f3b0b452: /home/me/.local/state/kxm/runtime/projects/38ed26cb8eeaa297f3b0b452/run-events.db -> run-events.db (schema v7, 348160 bytes, sha256 sha256:27c13...)
|
|
3332
3360
|
Manifest: /work/bk/manifest.json
|
|
3333
3361
|
```
|
|
3334
3362
|
|
|
3335
|
-
|
|
3363
|
+
The summary lists stores; the prompt sidecar appears under `files` in the manifest.
|
|
3364
|
+
|
|
3365
|
+
In a project with a hub store and one Runtime project:
|
|
3336
3366
|
|
|
3337
3367
|
```bash
|
|
3338
3368
|
kxm backup --dry-run
|
|
3339
3369
|
```
|
|
3340
3370
|
|
|
3341
3371
|
```text
|
|
3342
|
-
dry run: back up
|
|
3372
|
+
dry run: back up 3 store(s) and 1 file(s) to /work/proj/.kxm/backups/backup-2026-09-23T17-44-51-277Z (sources are not opened, so their WAL is not checkpointed)
|
|
3343
3373
|
would write /work/proj/.kxm/backups/backup-2026-09-23T17-44-51-277Z/kxm.db
|
|
3374
|
+
would write /work/proj/.kxm/backups/backup-2026-09-23T17-44-51-277Z/registry.db
|
|
3375
|
+
would write /work/proj/.kxm/backups/backup-2026-09-23T17-44-51-277Z/run-events.db
|
|
3376
|
+
would write /work/proj/.kxm/backups/backup-2026-09-23T17-44-51-277Z/run-events.db.run-prompts.json
|
|
3344
3377
|
would write /work/proj/.kxm/backups/backup-2026-09-23T17-44-51-277Z/manifest.json
|
|
3345
3378
|
```
|
|
3346
3379
|
|
|
3347
|
-
|
|
3380
|
+
On a machine with no hub store and no Runtime stores yet:
|
|
3348
3381
|
|
|
3349
3382
|
```bash
|
|
3350
3383
|
kxm backup --json
|
|
@@ -3360,14 +3393,14 @@ kxm backup --json
|
|
|
3360
3393
|
kxm restore <manifest>
|
|
3361
3394
|
```
|
|
3362
3395
|
|
|
3363
|
-
Restores SQLite stores from a verified backup manifest. It checks the manifest schema, that every backup file is present
|
|
3396
|
+
Restores SQLite stores and prompt sidecars from a verified backup manifest. It checks the manifest schema, refuses a manifest that records `complete: false` (`restore_incomplete`), checks that every backup file is present and each file's digest, then restores each store to its recorded source path, then each sidecar. A path under the manifest's project root is rebased onto the current directory when the manifest came from another project root; Runtime stores under the user state root go back to their recorded absolute paths. A manifest without a `complete` field, from an older build, still restores.
|
|
3364
3397
|
|
|
3365
3398
|
- Arguments: `<manifest>`, path to `manifest.json`.
|
|
3366
3399
|
- No command-specific options.
|
|
3367
|
-
- Overwrites live stores
|
|
3368
|
-
- Before overwriting anything, a restore checks every store's recorded schema version against the ceiling for that store, so a store newer than this build is refused (`runtime_schema_newer`) before the first file is replaced. `--dry-run` runs the same manifest, file, digest, and schema checks and plans each target it would overwrite (and any `-wal` or `-shm` sidecar it would delete) without touching them. Dry-run JSON keys: `backupId`, `manifestPath`, `stores` (`storeId`, `targetPath`, `schemaVersion`), `dryRun`, `planned`.
|
|
3400
|
+
- Overwrites live stores, including the Runtime stores of every project the backup holds; it does not check whether the hub or the Runtime is running. Stop both before restoring.
|
|
3401
|
+
- Before overwriting anything, a restore checks every store's recorded schema version against the ceiling for that store, so a store newer than this build is refused (`runtime_schema_newer`) before the first file is replaced. `--dry-run` runs the same manifest, file, digest, and schema checks and plans each target it would overwrite (and any `-wal` or `-shm` sidecar it would delete) without touching them. Dry-run JSON keys: `backupId`, `manifestPath`, `stores` (`storeId`, `targetPath`, `schemaVersion`), `files` (`id`, `targetPath`), `dryRun`, `planned`.
|
|
3369
3402
|
- JSON keys: `backupId`, `manifestPath`, `restoredStores` (`storeId`, `sourcePath`, `backupFile`, `schemaVersion`, `integrity`).
|
|
3370
|
-
- Exit 1 with `restore_failed`; `issues` carry codes such as `runtime_path_invalid`, `restore_manifest_invalid`, `restore_file_missing`, `restore_manifest_digest_mismatch`, and `runtime_schema_newer`.
|
|
3403
|
+
- Exit 1 with `restore_failed`; `issues` carry codes such as `runtime_path_invalid`, `restore_manifest_invalid`, `restore_incomplete`, `restore_file_missing`, `restore_manifest_digest_mismatch`, and `runtime_schema_newer`.
|
|
3371
3404
|
- Two more checks run per store while restoring, after the plan checks: a backup file whose schema version differs from the version the manifest records is refused with `runtime_schema_mismatch`, and one that fails its SQLite integrity check with `database_corrupted`. In a multi-store restore, stores restored before the refused one stay restored.
|
|
3372
3405
|
|
|
3373
3406
|
```bash
|
|
@@ -3375,8 +3408,11 @@ kxm restore ../bk/manifest.json --dry-run
|
|
|
3375
3408
|
```
|
|
3376
3409
|
|
|
3377
3410
|
```text
|
|
3378
|
-
dry run: restore
|
|
3411
|
+
dry run: restore 3 store(s) and 1 file(s) from /work/bk/manifest.json; digests verified against the manifest
|
|
3379
3412
|
would write /work/proj/.kxm/state/kxm.db
|
|
3413
|
+
would write /home/me/.local/state/kxm/runtime/registry.db
|
|
3414
|
+
would write /home/me/.local/state/kxm/runtime/projects/38ed26cb8eeaa297f3b0b452/run-events.db
|
|
3415
|
+
would write /home/me/.local/state/kxm/runtime/projects/38ed26cb8eeaa297f3b0b452/run-events.db.run-prompts.json
|
|
3380
3416
|
```
|
|
3381
3417
|
|
|
3382
3418
|
A missing manifest:
|
|
@@ -3552,7 +3588,6 @@ Inspect KXM runs
|
|
|
3552
3588
|
|
|
3553
3589
|
These are behaviors of the current build that differ from what the help text or the flag names suggest. Each is also noted in the command's section.
|
|
3554
3590
|
|
|
3555
|
-
- The `default` workflow that `kxm init` writes sets `limits.maxAgentTimeMs`, so `kxm runs drive` hands every run of it off with `run_handoff_required` (`limit_unsupported`). Use a `kxm workflow add --template` workflow, or remove the limit, to drive a first run.
|
|
3556
3591
|
- `kxm routing benchmark` prints constant placeholder figures.
|
|
3557
3592
|
- `kxm task sync` does not contact GitHub or Jira.
|
|
3558
3593
|
- `kxm peer inbox` always returns an empty list from the CLI.
|