@kontextmind/kxm 0.7.180 → 0.7.182

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.
@@ -11,7 +11,7 @@
11
11
  "name": "kxm",
12
12
  "source": "./plugins/kxm",
13
13
  "description": "Durable workflows, peer agents, and kxm tui",
14
- "version": "0.7.180",
14
+ "version": "0.7.182",
15
15
  "category": "development",
16
16
  "tags": ["kxm", "multi-agent", "workflows", "mcp"]
17
17
  }
package/CHANGELOG.md CHANGED
@@ -6,6 +6,20 @@ All notable user-facing changes are documented here. The project follows [Semant
6
6
 
7
7
  ### Added
8
8
 
9
+ - **The witnessed MG1 Python control-plane foundation is now reproducible from this repository.**
10
+ `python/kxm-control` provides the pinned Python 3.13 uv workspace, fixed
11
+ `kxmd` Postgres/Temporal caller, Authentik user and runner verification
12
+ that requires token expiry, tenant/project/repository registration,
13
+ owner/admin tenant rename, revision and artifact APIs, and a probe entry
14
+ point. OpenAPI and the Studio path inventory are derived from the FastAPI
15
+ app. The checked generator emits the revision helper. Studio exposes the
16
+ bounded `/api/control/account` relay with a three-second bound and explicit
17
+ failure states. Removing registration leaves source state untouched and
18
+ later registry calls return `registration_unavailable` until the schema is
19
+ installed again. The package does not change host or guest installs. The
20
+ required CI aggregate runs the Python contracts against PostgreSQL 17. This
21
+ does not move the hub or Runtime from their existing Node and SQLite owners.
22
+
9
23
  - **KXM configuration now has one owner-validated terminal editor.**
10
24
  `kxm config edit --scope project|user` and OMP/Pi `/kxm config` publish the
11
25
  same `kxm.config.v1` surface. Accepted writes are saved and re-read; rejected
@@ -651,6 +665,15 @@ All notable user-facing changes are documented here. The project follows [Semant
651
665
 
652
666
  ### Fixed
653
667
 
668
+ - **`kxm improve report` and `kxm routing report` include attempts settled in a registered lane.**
669
+ Inside a KXM project, a present user-state `registry.db` is opened read-only at schema version 2. One query loads the home row and every lane row for that project id. Event-store paths come from the stored project keys, so a stale checkout path cannot hide a lane. A missing store stays a zero-record source. An absent registry, or a checkout that is not registered, still reads only that checkout's own store. `--file` still reads only the named file. See
670
+ [kxm improve report](docs/reference/cli-reference.md#kxm-improve-report).
671
+
672
+ - **Browser integration tests isolate the Steel credential environment.**
673
+ Each test restores the operator's variables afterward, so ambient Authentik
674
+ credentials cannot override a case that deliberately exercises the legacy
675
+ API-key path.
676
+
654
677
  - **`kxm supervise` keeps supervisor state across a restart.**
655
678
  `status`, `record`, and `tick` store in-flight lanes, the last-seen pull
656
679
  request and CI state, and backoff timers in `.kxm/state/supervisor.json`.
@@ -1,7 +1,7 @@
1
1
  # Authentication
2
2
 
3
- Read from `plugins/kxm/src/harness.ts` and `plugins/kxm/src/cli.ts`.
4
-
3
+ Read from `plugins/kxm/src/harness.ts`, `plugins/kxm/src/cli.ts`, and
4
+ `python/kxm-control/src/kxm_control/auth.py`.
5
5
  `kxm harness list` runs `probeHarnesses` (`cmdHarnessList` in
6
6
  `plugins/kxm/src/cli/project.ts`). For each detected harness that has
7
7
  `authArgs`, `probeEntry` runs that command and `interpretAuth` reads the
@@ -23,6 +23,15 @@ the harness command's own stdout.
23
23
  issues, or clears the local session token file. That token is a KXM session
24
24
  token, not a provider key. Provider logins stay in each harness.
25
25
 
26
+ The MG1 Python control API uses a separate Authentik boundary. It accepts only
27
+ RS256 tokens whose issuer and audience match its configured values, whose
28
+ `exp` is present and unexpired, and whose `kxm_actor` is `user` or `runner`.
29
+ A token without `exp` is rejected.
30
+ The signed subject and actor kind must match
31
+ an explicit tenant membership; a user membership never authorizes a runner
32
+ token with the same subject. Only a user can create a tenant. The API does not
33
+ store the bearer token.
34
+
26
35
  ## Related
27
36
 
28
37
  - [Access](access.md)
@@ -52,11 +52,13 @@ roadmap question. The tracker wins.
52
52
  proposal is not in this repository. Its delivery position is unchanged:
53
53
  none of the delivery is scheduled there.
54
54
 
55
- The tracker names that file. The sentences that can be repeated without a
56
- hostname, an account name, or a secret are: a first move is recorded as
57
- installed, Postgres and Temporal are described as loopback only, KXM does not
58
- write those stores, Redis Streams, NATS, raw WebSockets, and the A2A Python
59
- SDK stay backlog, there is no S3, and the SQLite write path is unchanged.
55
+ Today the Node hub and Runtime still write local SQLite and local files. The
56
+ checked-in MG1 Python control API is the narrow exception: it owns only central
57
+ registration and bounded tenant artifacts in the fixed `kxmd` PostgreSQL
58
+ account. Temporal is provisioned for `kxmd`, but no production workflow cutover
59
+ is selected. Redis Streams, NATS, raw WebSockets, the A2A Python SDK, and S3
60
+ remain backlog. The rest of this page remains a target, not a live deployment
61
+ description.
60
62
 
61
63
  ## Related
62
64
 
@@ -6,7 +6,8 @@ Read from `plugins/kxm/src/cli.ts`, `plugins/kxm/src/config-editor.ts`,
6
6
  `plugins/kxm/src/runtime-paths.ts`, `plugins/kxm/src/mcp-server.ts`,
7
7
  `plugins/kxm/src/oneshot-producer.ts`, `plugins/kxm/src/studio-layout.ts`,
8
8
  `plugins/kxm/src/cli/types.ts` (`workspaceDirs`),
9
- `plugins/kxm/src/local-snapshot.ts`, and `scripts/kxm-worker.mjs`.
9
+ `plugins/kxm/src/local-snapshot.ts`, `scripts/kxm-worker.mjs`, and
10
+ `python/kxm-control/src/kxm_control/{api,registry}.py`.
10
11
 
11
12
  | Surface | Started by | Listener or path | Store it owns | Defining file |
12
13
  | --- | --- | --- | --- | --- |
@@ -17,11 +18,12 @@ Read from `plugins/kxm/src/cli.ts`, `plugins/kxm/src/config-editor.ts`,
17
18
  | Hub cloud binding | `kxm hub bind <url> --cloud --token-env/--token-command` | no listener; writes the binding record and never stores the token | binding record; token stays in the named env or command | `plugins/kxm/src/cli/hub.ts` |
18
19
  | Runtime supervisor | `kxm runtime` / `scripts/kxm-runtime-supervisor.mjs` | `127.0.0.1` and the requested port, or ephemeral | registry and event stores below | `plugins/kxm/src/runtime-supervisor.ts` |
19
20
  | Runtime registry | opened by the supervisor | `runtime/registry.db` under the user state root | `registry.db`. A lane worktree registers under its project: same project id, its own control root, `lane_of` set. A registering root is the primary worktree when its git common dir is `<root>/.git`; if that primary registers while the live home row is not primary, the primary becomes the home row and every other live row of that repository becomes its lane. | `plugins/kxm/src/runtime-paths.ts` |
20
- | Runtime event store | opened by the supervisor | `runtime/projects/<key>/run-events.db` | `run-events.db` | `plugins/kxm/src/runtime-paths.ts` |
21
+ | Runtime event store | opened by the supervisor | `runtime/projects/<key>/run-events.db` | `run-events.db`. `kxm improve report` and `kxm routing report` read the home store and every lane store for the registered project id through one read-only query of `registry.db` (`plugins/kxm/src/improve-sources.ts`). An absent registry, or a checkout that is not registered, reads only that checkout's own store. Paths come from the stored project keys. | `plugins/kxm/src/runtime-paths.ts` |
21
22
  | MCP server | Claude plugin or stdio launch | stdio | none | `plugins/kxm/src/mcp-server.ts` |
22
23
  | One-shot harness | supervisor producer | child process, no listener | none | `plugins/kxm/src/oneshot-producer.ts` |
23
24
  | Pi worker | `kxm agent worker` | no HTTP listener in `scripts/kxm-worker.mjs` | none of its own | `scripts/kxm-worker.mjs` |
24
- | Studio | `kxm studio serve` | `127.0.0.1:4242` | none | `plugins/kxm/src/studio-layout.ts` |
25
+ | Studio | `kxm studio serve` | `127.0.0.1:4242` | none; `/api/control/account` is a bounded relay to the configured control API | `plugins/kxm/src/studio-layout.ts` |
26
+ | Python control API | ASGI server loading `kxm_control.api:app` | deployment-selected; the package declares no network default | `kxm_registry` in the fixed `kxmd` PostgreSQL database and the configured tenant artifact root | `python/kxm-control/src/kxm_control/api.py` |
25
27
  | Workspace config | `workspaceDirs` | `.kxm/config`, or `KXM_CONFIG_DIR` | files in that directory | `plugins/kxm/src/cli/types.ts` |
26
28
  | Workspace logs | `workspaceDirs` | `.kxm/logs`, or `KXM_LOGS_DIR` | log files | `plugins/kxm/src/cli/types.ts` |
27
29
  | Workspace assets | `workspaceDirs` | `.kxm/assets`, or `KXM_ASSETS_DIR` | asset files | `plugins/kxm/src/cli/types.ts` |
@@ -2,7 +2,8 @@
2
2
 
3
3
  Read from `plugins/kxm/src/cli.ts`, `plugins/kxm/src/runtime-supervisor.ts`,
4
4
  `scripts/kxm-hub.mjs`, `plugins/kxm/src/hub.ts`, `plugins/kxm/src/mcp-server.ts`,
5
- and `plugins/kxm/src/oneshot-producer.ts`.
5
+ `plugins/kxm/src/oneshot-producer.ts`, and
6
+ `python/kxm-control/src/kxm_control/api.py`.
6
7
 
7
8
  KXM on this checkout is local-first. The `kxm` CLI is the operator entry
8
9
  (`plugins/kxm/src/cli.ts`, launched through `scripts/kxm.mjs`). The hub is an
@@ -18,17 +19,29 @@ requested. The MCP server (`plugins/kxm/src/mcp-server.ts`) uses
18
19
  spawned by the producer registered in `plugins/kxm/src/oneshot-producer.ts`.
19
20
  That child process is not a listener.
20
21
 
22
+ The checked-in MG1 Python control API is additive. It owns central registration
23
+ and bounded tenant artifacts through the fixed `kxmd` account. Only an owner
24
+ or admin membership can rename a tenant. Removing the registration schema
25
+ leaves source state in place, and later registry routes return
26
+ `registration_unavailable` until the schema is installed again. The API does
27
+ not install or change host or guest software. It does not replace the Node
28
+ hub, Runtime, CLI, or their SQLite stores. Studio can relay
29
+ `GET /api/control/account` to this service when `KXM_CONTROL_URL` is configured.
30
+
21
31
  ```mermaid
22
32
  flowchart LR
23
33
  cli["kxm CLI"]
24
- hub["hub"]
25
- sup["Runtime supervisor"]
34
+ hub["Node hub"]
35
+ sup["Node Runtime supervisor"]
26
36
  mcp["MCP server"]
27
37
  shot["one-shot harness"]
38
+ studio["Studio"]
39
+ control["MG1 Python control API"]
28
40
  cli -->|"kxm hub start"| hub
29
41
  mcp -->|"HTTP to hub"| hub
30
42
  cli -->|"kxm run"| sup
31
43
  sup -->|"spawn producer"| shot
44
+ studio -->|"bounded account read"| control
32
45
  ```
33
46
 
34
47
  ## Listeners
@@ -39,7 +52,8 @@ flowchart LR
39
52
  | Supervisor HTTP | `127.0.0.1` and an ephemeral port when the requested port is `0` |
40
53
  | MCP | stdio, no TCP port |
41
54
  | CLI and one-shot harness | no listener |
42
- | Studio, when served | `127.0.0.1:4242` from `createStudioServer` in `plugins/kxm/src/studio-layout.ts` |
55
+ | Studio, when served | `127.0.0.1:4242` from `createStudioServer` in `plugins/kxm/src/studio-layout.ts`; its optional control relay targets `KXM_CONTROL_URL` |
56
+ | Python control API | ASGI listener selected by deployment; the package declares no host or port default |
43
57
  | Antigravity OAuth callback | loopback port `51121` only during login (`plugins/kxm/src/providers/antigravity/auth/oauth.ts`) |
44
58
 
45
59
  ## Related
@@ -1,7 +1,8 @@
1
1
  # CI and release
2
2
 
3
- Pull requests run lint, typecheck, and the unit suite on Linux Node 24.
4
- Pushes to `main` also run that suite and the full OS × Node matrix.
3
+ Pull requests run lint, typecheck, the Node unit suite on Linux Node 24, and
4
+ the Python control-plane suite against PostgreSQL 17. Pushes to `main` also run
5
+ the Node suite and the full OS × Node matrix.
5
6
  The complete suite with coverage floors runs nightly, every merge to `main`
6
7
  cuts a patch release, and every release is verified before it reaches npm.
7
8
  This page explains which checks run where, how a merge becomes a published
@@ -15,7 +16,7 @@ release and an npm publish; the nightly job adds the slower suites.
15
16
 
16
17
  ```mermaid
17
18
  flowchart LR
18
- PR[Pull request] -->|"unit shards, docs lint, plugin validation"| REQ["CI / required"]
19
+ PR[Pull request] -->|"Node shards, Python/Postgres, docs lint, plugin validation"| REQ["CI / required"]
19
20
  REQ --> MERGE{Merged?}
20
21
  MERGE -->|yes| MAIN[main]
21
22
  MAIN -->|"unit shards plus validate:pr matrix"| PUSH[Push CI]
@@ -40,6 +41,7 @@ together.
40
41
  | Pull request, push to `main`, or manual | `ci.yml` (`required`) | Aggregates the lanes below into one pass/fail check named `CI / required` |
41
42
  | Pull request and push | `ci.yml` (Docs lint) | `lint:docs` and `check:versions`, always |
42
43
  | Code pull request and code push | `ci.yml` (Unit engine and Unit, Linux Node 24) | `engine.test.ts` split by test name; `permission.test.ts`, `runtime.test.ts`, and `package-install.test.ts` one file at a time; every other unit file in two light shards. Typecheck and `check-generated` run on `light-1` |
44
+ | Code pull request and code push | `ci.yml` (Python control plane) | Python 3.13 `unittest` contracts from the frozen uv workspace against PostgreSQL 17 started by `scripts/ci-postgres-17.sh` |
43
45
  | Platform-sensitive pull request | `ci.yml` (Unit, Windows Node 24) | The same unit lanes on `windows-latest` |
44
46
  | Push to `main`, or manual | `ci.yml` (Validate matrix) | `validate:pr` on Linux and Windows for Node 22.19.0 and Node 24; not on a pull request |
45
47
  | Code pull request and code push | `ci.yml` (Plugin validation) | `claude plugin validate --strict` on the marketplace and the plugin |
@@ -73,7 +75,8 @@ What moved off the pull-request lane, and what did not:
73
75
  runs `validate:pr` on Windows.
74
76
  - Coverage, `test/simulations`, and `npm pack --dry-run` stay in the nightly
75
77
  complete suite. They were not part of pull-request CI before this split.
76
- - Plugin validation still runs on code pull requests and code pushes.
78
+ - Python control-plane validation and Plugin validation still run on code pull
79
+ requests and code pushes.
77
80
 
78
81
  Run `npm run verify` locally before every push. A nightly failure is a release
79
82
  blocker: it is the only place the simulation suite and coverage floors run.
@@ -82,8 +85,13 @@ blocker: it is the only place the simulation suite and coverage floors run.
82
85
 
83
86
  Code pull requests run Docs lint, two Linux Node 24 engine shards, a serial
84
87
  lane (`permission.test.ts`, then `runtime.test.ts`, then
85
- `package-install.test.ts`), two light shards for every other unit file, and
86
- Plugin validation. `light-1` typechecks and checks generated bundles.
88
+ `package-install.test.ts`), two light shards for every other Node unit file,
89
+ the Python 3.13 control-plane suite against PostgreSQL 17, and Plugin
90
+ validation. The Python lane stays on `kontextmind-doks`. That scale set has
91
+ no container-engine socket, so the job starts PostgreSQL 17 with
92
+ `scripts/ci-postgres-17.sh` on `127.0.0.1:5432` and uses the same admin and
93
+ test DSNs as the contributor procedure in [Develop KXM](development.md).
94
+ `light-1` typechecks and checks generated bundles.
87
95
  `engine.test.ts` is split by test name because that file alone was 174
88
96
  seconds; the serial lane keeps the next longest files off the light pool,
89
97
  which was 268 seconds when every non-engine file shared one job.
@@ -165,9 +173,10 @@ workaround.
165
173
  The first job, Classify changes, lists the changed paths and sets `code=false`
166
174
  when every path matches `*.md` (including `plans/**/*.md`), `docs/**`,
167
175
  `.kxm/assets/**`, `LICENSE`, the issue and PR templates, or `dependabot.yml`.
168
- A non-markdown file under `plans/` is code. Unit lanes, Plugin validation,
169
- and the Validate matrix are skipped. Docs lint runs, and `CI / required`
170
- passes. `scripts/ci-classify.mjs` and `ci-contract.test.ts` pin this behavior.
176
+ A non-markdown file under `plans/` is code. Unit lanes, the Python control-plane
177
+ lane, Plugin validation, and the Validate matrix are skipped. Docs lint runs,
178
+ and `CI / required` passes. `scripts/ci-classify.mjs` and
179
+ `ci-contract.test.ts` pin this behavior.
171
180
 
172
181
  `platform=true` when a path is a package manifest, the lockfile, a file under
173
182
  `scripts/` or `.github/workflows/`, or a filename that names path, process,
@@ -198,3 +198,34 @@ Evidence: Evidence: `git diff HEAD origin/main` on the stale branch showed upstr
198
198
  - Interim recommendation: use the opt-in installer until OMP exposes a
199
199
  first-class transactional status-preset API.
200
200
  Evidence: `plugins/kxm/src/cli/plugins.ts`, `plugins/kxm/src/session-work.ts`, and `test/core/cli.test.ts`.
201
+
202
+ ## 2026-10-11 — Bring the witnessed MG1 Python foundation onto main (migration)
203
+
204
+ - Context: the tracker records MG1 as passed off-repo while the operator's
205
+ primary checkout holds the matching untracked Python workspace, MG0 evidence,
206
+ and Studio control-account proxy. Leaving the implementation outside Git
207
+ makes the recorded gate irreproducible and blocks MG2 from a reviewed base.
208
+ - Options:
209
+ - Keep MG1 off-repo and start the roadmap's role-authority work — weight 0.2.
210
+ Avoids a CI change, but preserves the tracker/source contradiction and risks
211
+ losing the only implementation matching the deployed witness.
212
+ - Import MG1 at `python/kxm-control`, keep Node as the current product owner,
213
+ and add a required Python/PostgreSQL contract lane — weight 0.9. Makes the
214
+ accepted foundation reproducible without claiming a Python cutover.
215
+ - Replace the Node CLI and Runtime with the Python package now — weight 0.1.
216
+ MG2 through MG8 are still open, so this would skip import, bridge, runner,
217
+ parity, and retirement gates.
218
+ - Selected: import the bounded MG1 foundation and its evidence, harden the
219
+ Studio proxy, and make the Python unit/contract suite part of `CI / required`.
220
+ SQLite and the Node Runtime remain authoritative. OpenAPI and the Studio
221
+ path inventory are derived from the FastAPI app; the checked generator emits
222
+ the revision helper. The package does not change host or guest installs.
223
+ After registration removal, registry routes return `registration_unavailable`
224
+ until the schema is installed again.
225
+ - Solid recommendation: keep every accepted migration wave in Git with its
226
+ generated contracts and a required service-backed test lane.
227
+ - Interim recommendation: if the service lane is unavailable, do not merge the
228
+ Python source; retain the off-repo tracker wording rather than claiming an
229
+ unverified gate.
230
+ Evidence: `plans/implementation-plan.md`, `plans/plan-python-migration.md`,
231
+ `python/kxm-control/tests/test_mg1.py`, and `.github/workflows/ci.yml`.
@@ -23,7 +23,10 @@ extension, the Claude Code plugin, or the bundled skills.
23
23
  `kxm lane run <unit> --brief <file> --workflow implement-only`.
24
24
  The read-only critic workflows on the same command are
25
25
  `review-arch-only` and `review-cli-only`.
26
- - Docker for the clean-container install smoke.
26
+ - Docker for the clean-container install smoke and the Python control-plane
27
+ contract suite.
28
+ - Python 3.13 and [`uv`](https://docs.astral.sh/uv/) for the Python
29
+ control-plane contract suite.
27
30
 
28
31
  ## Set up a checkout
29
32
 
@@ -255,6 +258,41 @@ It runs three scripts in order:
255
258
  | 2 | `npm run check` | `tsc --noEmit`, `markdownlint-cli2`, and version surfaces |
256
259
  | 3 | `npm run check:generated` | Generated artifacts are tracked and match the staged copy |
257
260
 
261
+ Changes to the Python control plane also run its frozen-workspace contract
262
+ suite before the npm gate. The suite drops and recreates `kxm_mg1_test`, so
263
+ the admin DSN account needs database create and drop privilege. The `postgres`
264
+ superuser in the container below is that account. Run PostgreSQL 17 in a
265
+ disposable container published only on loopback `127.0.0.1:5432`, with
266
+ `POSTGRES_PASSWORD=postgres`. Leave port `5432` on loopback free. Wait until
267
+ `pg_isready` succeeds, export the two DSNs CI uses, run the frozen suite, and
268
+ remove the container afterward:
269
+
270
+ ```bash
271
+ docker rm -f kxm-mg1-pg >/dev/null 2>&1 || true
272
+ docker run --detach --name kxm-mg1-pg \
273
+ --publish 127.0.0.1:5432:5432 \
274
+ --env POSTGRES_PASSWORD=postgres \
275
+ postgres:17
276
+ for _ in 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30; do
277
+ docker exec kxm-mg1-pg pg_isready -U postgres && break
278
+ sleep 1
279
+ done
280
+ docker exec kxm-mg1-pg pg_isready -U postgres
281
+ export KXM_MG1_ADMIN_DSN='host=127.0.0.1 port=5432 dbname=postgres user=postgres password=postgres'
282
+ export KXM_MG1_TEST_DSN='host=127.0.0.1 port=5432 dbname=kxm_mg1_test user=postgres password=postgres'
283
+ uv run --project python --frozen python -m unittest discover -s python/kxm-control/tests
284
+ suite_status=$?
285
+ docker rm -f kxm-mg1-pg
286
+ (exit "$suite_status")
287
+ ```
288
+
289
+ The required CI aggregate repeats that suite against PostgreSQL 17 with the
290
+ same two DSNs and the same `postgres` superuser. On the `kontextmind-doks`
291
+ runners, `scripts/ci-postgres-17.sh` starts that server on `127.0.0.1:5432`
292
+ from a pinned PostgreSQL 17 binary. Contributors use the `postgres:17`
293
+ container procedure above, which publishes the same port and DSNs. The suite
294
+ remains a CI lane rather than a third root gate script.
295
+
258
296
  Every suite script passes `--test-force-exit`, and `scripts/run-bounded.mjs` stops the run if the suite is still going after its wall-clock limit (20 minutes, or 40 for the complete and coverage suites). The bound is that wall clock per script. A per-test timeout is not used because under `node --test` each file is itself a test, so a per-test timeout also bounds each file.
259
297
 
260
298
  Other scripts you will use:
@@ -84,6 +84,7 @@ to run one file is in [Develop KXM](development.md#run-one-file-or-one-test).
84
84
  | Promotion needs the configured admin token with no loopback bypass; proposed items never reach a packet | `e5-memory-floor.test.ts` |
85
85
  | `kxm memory`: candidates, marker-delimited sync blocks, and one brief across CLI, Pi and the Claude hook | `e5b-harness-agnostic-memory.test.ts` |
86
86
  | `kxm improve report` reads Runtime-settled attempts, drops simulated and duplicate ones, and flags only same-ask repeats | `improve.test.ts` ("kxm improve report resolves Runtime-settled attempts from the event log and flags only same-ask cross-run repeats"), `cli.test.ts`, `cli-experience.test.ts`, `commands-policy.test.ts` |
87
+ | A registered lane's attempt appears in `kxm improve report` and `kxm routing report` run from the primary checkout; a duplicate still drops and `--file` stays isolated | `improve.test.ts` ("kxm improve report and kxm routing report from the primary include an attempt recorded only in a registered lane") |
87
88
  | Improvement candidates exclude write steps; promotion readiness never authorizes | `improve.test.ts` |
88
89
  | Telemetry classification and JSONL recovery | `telemetry.test.ts`, `cli.test.ts` |
89
90
 
@@ -100,7 +101,7 @@ The context suites in detail:
100
101
  | `wiki.test.ts` | Wiki compilation determinism, lifecycle preservation, contradiction visibility, lint |
101
102
  | `skills.test.ts` | Skill candidate lifecycle, quarantine, immutability, CLI |
102
103
  | `routing.test.ts` | Behavioral hash, record parsing, comparisons, `kxm routing report` |
103
- | `improve.test.ts` | Routing-record sources, event-log outcomes, same-ask candidacy, candidate files, promotion readiness |
104
+ | `improve.test.ts` | Routing-record sources, registered lane stores, event-log outcomes, same-ask candidacy, candidate files, promotion readiness |
104
105
 
105
106
  ## Claude Code plugin and MCP
106
107
 
@@ -3499,21 +3499,21 @@ Proposes coded-repeat candidates from this project's Runtime routing records and
3499
3499
  kxm improve report [--file <path>] [--out-dir <path>]
3500
3500
  ```
3501
3501
 
3502
- Generate the improvement report and candidates from routing records. Inside a KXM project (the current directory's Git root holds `.kxm/project.yaml`) it reads the project's Runtime event store, then `telemetry.jsonl` in the workspace logs directory; `--file` reads only the named file. Routing records (`kxm.routing-record.v1` and `v2`, bare or nested under `routing`, `envelope.routing`, or a `routing.attempt.recorded` event) are grouped by workflow, step, agent role, and ask. Groups that qualify become proposed candidates, each written as a `.diff` and a `.json` file. Nothing is applied.
3502
+ Generate the improvement report and candidates from routing records. Inside a KXM project (the current directory's Git root holds `.kxm/project.yaml`) it reads that project's Runtime event stores, then `telemetry.jsonl` in the workspace logs directory; `--file` reads only the named file. Routing records (`kxm.routing-record.v1` and `v2`, bare or nested under `routing`, `envelope.routing`, or a `routing.attempt.recorded` event) are grouped by workflow, step, agent role, and ask. Groups that qualify become proposed candidates, each written as a `.diff` and a `.json` file. Nothing is applied.
3503
3503
 
3504
3504
  | Option | Argument | Default | Description |
3505
3505
  |---|---|---|---|
3506
- | `--file` | `<path>` | the Runtime store, then `.kxm/logs/telemetry.jsonl` | Read only this routing-record JSONL instead of the project's Runtime store and telemetry |
3506
+ | `--file` | `<path>` | the Runtime stores, then `.kxm/logs/telemetry.jsonl` | Read only this routing-record JSONL instead of the project's Runtime stores and telemetry |
3507
3507
  | `--out-dir` | `<path>` | `<project>/.kxm/candidates` | Directory for candidates (default .kxm/candidates) |
3508
3508
 
3509
- - The Runtime store is `<state root>/runtime/projects/<key>/run-events.db`, with the key derived from the checkout's real path, so each checkout and worktree reads only its own. It is opened read-only for one query over its events table and is never created, written, or migrated. Outside a KXM project only telemetry is read, and the text output says `Runtime store not read`.
3510
- - A telemetry record whose `attemptId` the store already supplied is dropped (`duplicatesDropped`). Attempts from simulated drives are excluded (`excludedSimulated`). Each Runtime attempt's outcome is resolved from the event log and never written back: `accepted` when the run completed and the step was not re-entered, `reworked` when the step was entered again, `failed` when the run failed, undecided (`undecided`) when the run was cancelled or is still running.
3509
+ - Each Runtime store is `<state root>/runtime/projects/<key>/run-events.db`. When `runtime/registry.db` is absent, or this checkout's project key is not in it, the command reads only the store derived from this checkout's real path and does not create the registry. When the registry is present and this checkout is registered, it is opened read-only with no migration and no write lock, requires schema version 2, and one query returns the home row and every lane row for that project id. The home store is listed first, then lanes by registration time and key. Store paths are built from those stored keys and `runtime/projects`, not from the checkout path saved on the row, so a stale path cannot change the key. A missing store is an engine source with `exists=false` and zero records. A registry that is not a regular database file, is not schema version 2, or does not have exactly one home row and 24-character lowercase hex keys exits 1 with `improve_source_unreadable`. Each store is opened read-only for one query over its events table and is never created, written, or migrated. Outside a KXM project only telemetry is read, and the text output says `Runtime store not read`. The example below is an unregistered checkout, so it lists one engine source.
3510
+ - A telemetry record whose `attemptId` an earlier source already supplied is dropped (`duplicatesDropped`). The same drop applies between the home store and a lane store: the first source wins. Attempts from simulated drives are excluded (`excludedSimulated`). Each Runtime attempt's outcome is resolved from the event log and never written back: `accepted` when the run completed and the step was not re-entered, `reworked` when the step was entered again, `failed` when the run failed, undecided (`undecided`) when the run was cancelled or is still running.
3511
3511
  - A group becomes a candidate only when the same objective was decided in at least 2 runs, at least 0.75 of its decided records were accepted, and its step writes no repository. A group that passes but misses shows `no (writes-repository)` or `no (ask-not-repeated)`. `Weighted` is the recency-weighted record count (`improvement.telemetryHalfLifeDays`); it orders rows and never decides candidacy.
3512
3512
  - Reads `improvement.*` from the project and user configuration (see [`kxm.config.v1`](config-reference.md#personalization-settings-kxmconfigv1)) and reports each candidate's promotion readiness under `improvement.promotionPolicy`. Readiness never authorizes anything. Under `critic_quorum` every candidate reports not ready, because this command cites no critic receipts. A configuration that cannot be loaded exits 1 with `config_invalid`.
3513
- - A Runtime store that exists but cannot be read exits 1 with `improve_source_unreadable`; `detail` names the path.
3513
+ - A Runtime store or registry that exists but cannot be read, and a registry whose identity is corrupt, exits 1 with `improve_source_unreadable`; `detail` names the path.
3514
3514
  - Writes candidate files under `--out-dir` (relative to the current directory) and the report at `.kxm/assets/improvements/<timestamp>.json`. Honors `--dry-run` (writes neither). No hub needed.
3515
3515
  - JSON keys: `path`, `events`, `recordsCount`, `groupsCount`, `candidatesCount`, `candidates`, `report` (`schema`, `createdAt`, `reviewDecision`, `recordsCount`, `groups`, `candidates`, `promotionPolicy`, `promotion`, and `routeSwitches` when the store recorded any), `sources`, `projectRoot` (`null` outside a project). `routeSwitches` lists opted-in mid-attempt walks (`from`, `to`, `reason`, optional `effort`, `attemptId`, `stepId`). It is omitted when there are none.
3516
- - Each `sources` entry has `kind` (`engine`, `telemetry`, or `file`), `path`, `exists`, `records`, and `duplicatesDropped`; the `engine` entry also has `skippedInvalid`, `excludedSimulated`, and `undecided`.
3516
+ - Each `sources` entry has `kind` (`engine`, `telemetry`, or `file`), `path`, `exists`, `records`, and `duplicatesDropped`; each `engine` entry also has `skippedInvalid`, `excludedSimulated`, and `undecided`. A registered project has one `engine` entry for the home store and one for each lane store.
3517
3517
  - Each `report.groups` row has `workflowHash`, `workflowId` (Runtime records), `stepId`, `agentRole`, `promptHash`, `recurrence`, `distinctRuns`, `askRecurrence`, `undecidedRecords`, `meanCost`, `meanLatency`, `verifyPassRate` (accepted share of decided records), `rework`, `weightedRecurrence`, `undatedRecords`, `costSamples`, `writesRepository`, `evidenceRefs`, `isCandidate`, and, when they apply, `excludedReason`, `candidateKind`, and `candidateId`.
3518
3518
  - Each `report.promotion` entry has `candidateId`, `policy`, `readyForReview`, and `reason`.
3519
3519
 
@@ -3565,18 +3565,18 @@ kxm routing report [-f <path>] [-l] [--prices <path>]
3565
3565
 
3566
3566
  Compare verified completion, cost, and rework per behavioral configuration. The table ranks routes quality-first with columns Harness, Model, Effort, Role, Att, Pass%, Rwk%, p50(ms), p95(ms), CtxTok, Metered($), $/Acc, Unm, Unk, Quota, and optionally ListEquiv($).
3567
3567
 
3568
- Without `--file` it reads the same sources as [`kxm improve report`](#kxm-improve-report): the current project's Runtime event store (read-only), then the workspace `telemetry.jsonl`, dropping a telemetry copy of an attempt the store already supplied and excluding simulated drives. A Runtime attempt counts toward Pass% only when the event log shows its run completed without the step being re-entered.
3568
+ Without `--file` it reads the same sources as [`kxm improve report`](#kxm-improve-report): the registered home store and every lane store for that project (read-only), or only this checkout's store when the registry is absent or this checkout is unregistered, then the workspace `telemetry.jsonl`, dropping a later copy of an attempt an earlier source already supplied and excluding simulated drives. A Runtime attempt counts toward Pass% only when the event log shows its run completed without the step being re-entered.
3569
3569
 
3570
3570
  | Option | Argument | Default | Description |
3571
3571
  |---|---|---|---|
3572
- | `-f`, `--file` | `<path>` | the Runtime store, then workspace telemetry | Read only this telemetry or event log JSONL file (default: this project's Runtime event store plus workspace telemetry) |
3572
+ | `-f`, `--file` | `<path>` | the Runtime stores, then workspace telemetry | Read only this telemetry or event log JSONL file (default: this project's Runtime event stores plus workspace telemetry) |
3573
3573
  | `-l`, `--equivalent-list-cost` | none | off | Include equivalent list price column using price catalog |
3574
3574
  | `--list-prices` | none | off | Alias for --equivalent-list-cost |
3575
3575
  | `--prices` | `<path>` | `.kxm/prices.yaml` | Path to price catalog (default: .kxm/prices.yaml) |
3576
3576
 
3577
3577
  - Reads only. A price catalog that cannot be loaded is skipped silently.
3578
3578
  - `--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($)`.
3579
- - A Runtime store that exists but cannot be read exits 1 with `improve_source_unreadable`.
3579
+ - A Runtime store or registry that exists but cannot be read exits 1 with `improve_source_unreadable`. `--file` does not read the registry or any event store.
3580
3580
  - 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.
3581
3581
  - 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. Quota is not an `onError` class, and the column does not choose a route. Walks a role opted into are listed under Route switches when the store has `routing.route_switched` events (`from`, `to`, `reason`, optional `effort`, `stepId`).
3582
3582
  - 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.
@@ -30,6 +30,7 @@ Names that start with `KXM_` are not all operator settings. This map covers ever
30
30
  | Agents | `KXM_SERVER_URL`, `KXM_AUTH_TOKEN`, `KXM_PROJECT`, `KXM_AGENT_NAME`, `KXM_AGENT_PURPOSE` | [Agent settings](#agent-settings) |
31
31
  | Long-lived Pi workers | `KXM_PI_COMMAND`, `KXM_WORKER_*` (operator settings), `KXM_AGENT_LOG_PATH` | [Long-lived worker settings](#long-lived-worker-settings) |
32
32
  | Runtime supervisor | `KXM_STATE_HOME`, `KXM_RUNTIME_SYNC_INTERVAL_MS`, `KXM_RUNTIME_STOP_GRACE_MS` | [Runtime supervisor settings](#runtime-supervisor-settings) |
33
+ | Studio and Python control plane | `KXM_CONTROL_URL`, `KXM_KXMD_*`, `KXM_AUTHENTIK_*`, `KXM_POSTGRES_*`, `KXM_DATABASE`, `KXM_DB_USER`, `KXM_TEMPORAL_*` | [Studio and Python control-plane settings](#studio-and-python-control-plane-settings) |
33
34
  | Operator CLI and sessions | `KXM_USER_CONFIG_DIR`, `KXM_USER_TELEMETRY_DIR`, `KXM_SESSION_TOKEN`, `KXM_SESSION_BRIEF`, `KXM_WORKFLOW_*`, `GITHUB_TOKEN`, and others | [CLI and session settings](#cli-and-session-settings) |
34
35
  | Nous model providers | `KXM_NOUS_PROVIDERS`, `KXM_NOUS_PROXY_URL`, `KXM_NOUS_DISCOVERY_TIMEOUT_MS`, `KXM_NOUS_CATALOG_FILE`, `NOUS_API_KEY` | [Nous providers](../guides/nous-providers.md) |
35
36
  | Browser automation | `KXM_BROWSER`, `OBSCURA_CDP_URL`, `OBSCURA_PORT`, `STEEL_API_URL`, `STEEL_UI_URL`, `STEEL_AUTH_HEADER`, `STEEL_AUTH_BASIC`, `STEEL_AUTH_USER`, `STEEL_AUTH_TOKEN`, `STEEL_API_KEY` (legacy), `USE_PASS_CLI` | [Browser settings](#browser-automation) |
@@ -187,6 +188,27 @@ The Runtime supervisor runs `kxm run` workflows and syncs their summaries to the
187
188
  | `KXM_RUNTIME_SYNC_INTERVAL_MS` | `10000` | How often the supervisor heartbeats and pushes its outbox, clamped to `250` through `60000` |
188
189
  | `KXM_RUNTIME_STOP_GRACE_MS` | `30000` | How long a stopping supervisor waits for open drives, `0` to `600000`; an invalid value warns and uses the default |
189
190
 
191
+ ## Studio and Python control-plane settings
192
+
193
+ The MG1 Python service is additive: these values do not redirect the Node hub
194
+ or Runtime from SQLite. Requests cannot override its database host, database,
195
+ role, Temporal target, or namespace.
196
+
197
+ | Variable | Default | Effect |
198
+ |---|---|---|
199
+ | `KXM_CONTROL_URL` | Unset | Python control API base URL used by Studio's `GET /api/control/account` relay. The relay returns `control_unconfigured` when absent, and aborts a nonresponding upstream after three seconds with `control_unreachable` |
200
+ | `KXM_KXMD_PASSWORD` | None; required | PostgreSQL password for the fixed `kxmd` role. Keep it in the deployment secret manager |
201
+ | `KXM_POSTGRES_HOST` | `127.0.0.1` | PostgreSQL host selected by the service deployment, including an operator-owned tunnel |
202
+ | `KXM_POSTGRES_PORT` | `5432` | PostgreSQL port, from 1 through 65535 |
203
+ | `KXM_DATABASE` | `kxmd` | Fixed database assertion. Any other value refuses startup-time account resolution |
204
+ | `KXM_DB_USER` | `kxmd` | Fixed PostgreSQL role assertion. Any other value is refused |
205
+ | `KXM_TEMPORAL_TARGET` | `127.0.0.1:7233` | Temporal endpoint selected by the service deployment |
206
+ | `KXM_TEMPORAL_NAMESPACE` | `kxmd` | Fixed Temporal namespace assertion. Any other value is refused |
207
+ | `KXM_KXMD_ARTIFACTS` | `/var/lib/kxm/artifacts/kxmd` | Root for bounded per-tenant artifacts |
208
+ | `KXM_AUTHENTIK_ISSUER` | None; required for authenticated routes | Exact token issuer |
209
+ | `KXM_AUTHENTIK_AUDIENCE` | None; required for authenticated routes | Exact token audience |
210
+ | `KXM_AUTHENTIK_JWKS_URL` | None; required for authenticated routes | JWKS endpoint used to verify RS256 user and runner tokens |
211
+
190
212
  ## CLI and session settings
191
213
 
192
214
  | Variable | Default | Effect |
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@kontextmind/kxm",
3
- "version": "0.7.180",
3
+ "version": "0.7.182",
4
4
  "description": "KXM local-first multi-agent orchestration and operator dashboard",
5
5
  "type": "module",
6
6
  "author": "KontextMind",
@@ -2,7 +2,7 @@
2
2
  "$schema": "https://json.schemastore.org/claude-code-plugin-manifest.json",
3
3
  "name": "kxm",
4
4
  "displayName": "KXM",
5
- "version": "0.7.180",
5
+ "version": "0.7.182",
6
6
  "description": "Headless multi-agent orchestration, durable workflows, and a live operator dashboard for Pi and Claude Code",
7
7
  "author": {
8
8
  "name": "KontextMind",