@kontextmind/kxm 0.7.169 → 0.7.171

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.169",
14
+ "version": "0.7.171",
15
15
  "category": "development",
16
16
  "tags": ["kxm", "multi-agent", "workflows", "mcp"]
17
17
  }
package/CHANGELOG.md CHANGED
@@ -617,6 +617,24 @@ All notable user-facing changes are documented here. The project follows [Semant
617
617
 
618
618
  ### Fixed
619
619
 
620
+ - **`kxm supervise` keeps supervisor state across a restart.**
621
+ `status`, `record`, and `tick` store in-flight lanes, the last-seen pull
622
+ request and CI state, and backoff timers in `.kxm/state/supervisor.json`.
623
+ A later process reads that file. `tick` clears a backoff whose deadline
624
+ has passed and appends `.kxm/logs/supervisor.log`. An unknown schema is
625
+ refused and left unchanged. See the
626
+ [CLI reference](docs/reference/cli-reference.md#kxm-supervise).
627
+
628
+ - **`kxm land` names a verify failure and refuses an UNKNOWN merge state.**
629
+ A failed `verify` records the exit code, the failing step, and a short
630
+ excerpt on the stage line and in `.kxm/logs/land-verify-<tree>.json`, and
631
+ writes the full output to `.kxm/logs/land-verify-<tree>.log`. The rebase
632
+ and merge stages poll `mergeStateStatus` while it is `UNKNOWN` and refuse
633
+ `land_merge_state_unknown` if it does not resolve. `CLEAN` and `HAS_HOOKS`
634
+ proceed; `BEHIND` and `DIRTY` still rebase; any other known state refuses
635
+ `land_merge_not_ready`. See the
636
+ [CLI reference](docs/reference/cli-reference.md#kxm-land).
637
+
620
638
  - **`kxm hub view` and `kxm hub bind` require a kxm hub body.** HTTP 200 HTML
621
639
  (an Authentik login page) and 30x redirects are not healthy. The JSON
622
640
  `health` field is `{ "error": "auth_proxy" }`, not the page. Bind refuses
@@ -7,7 +7,7 @@ project: "kxm"
7
7
  status: "accepted"
8
8
  owner: "@operator"
9
9
  created: "2026-09-27"
10
- updated: "2026-09-27"
10
+ updated: "2026-09-28"
11
11
  authority: "decision"
12
12
  confidence: "verified"
13
13
  summary: "Playwright testing and verification connect to pinned Obscura v0.2.3 over CDP. Steel stays the browser for human takeover, MFA, and the live session viewer."
@@ -74,7 +74,7 @@ Obscura v0.2.3 is a headless Chromium build that speaks the Chrome DevTools Prot
74
74
 
75
75
  - Playwright tests and verification use Obscura unless the operator sets `KXM_BROWSER=steel`.
76
76
  - Steel session create, release, takeover, and the session viewer are unchanged.
77
- - CI for this smoke test is `.github/workflows/e2e.yml` on `ubuntu-latest`. It does not enable the paused CI, Nightly, or Real Pi smoke workflows.
77
+ - CI for this smoke test is `.github/workflows/e2e.yml` on `ubuntu-latest`. The `CI` workflow is on, and `CI / required` is a required check in `protect-main`. `Nightly` and `Real Pi smoke` stay disabled. This ADR does not enable them.
78
78
  - The `node --test` globs do not collect `test/e2e/`.
79
79
 
80
80
  ## Related
@@ -4,9 +4,9 @@ Target. This page is a direction, not a live deployment.
4
4
 
5
5
  Read from `plans/plan-per-tenant-hosting.md`.
6
6
  `plans/plan-greenfield-infra.md` is in this checkout as a `kxm.doc.v1`
7
- stub (status draft, authority instruction): the long-form proposal was
8
- never committed, and the canonical facts below come from the
9
- implementation-plan greenfield bullets.
7
+ stub committed in #373 (status draft, authority instruction). The
8
+ long-form proposal is not in this repository. The canonical facts below
9
+ come from the implementation-plan greenfield bullets.
10
10
 
11
11
  ```mermaid
12
12
  flowchart LR
@@ -47,10 +47,10 @@ roadmap question. The tracker wins.
47
47
 
48
48
  ## Greenfield infra plan
49
49
 
50
- `plans/plan-greenfield-infra.md` is observed from this checkout as a
51
- `kxm.doc.v1` stub (status draft, authority instruction, updated
52
- 2026-09-28). Its delivery position is unchanged: none of the delivery is
53
- scheduled there.
50
+ `plans/plan-greenfield-infra.md` is a `kxm.doc.v1` stub committed in #373
51
+ (status draft, authority instruction, updated 2026-09-28). The long-form
52
+ proposal is not in this repository. Its delivery position is unchanged:
53
+ none of the delivery is scheduled there.
54
54
 
55
55
  The tracker names that file. The sentences that can be repeated without a
56
56
  hostname, an account name, or a secret are: a first move is recorded as
@@ -402,20 +402,21 @@ regenerates documentation (`docs`), then runs the GitHub stages: `push`,
402
402
 
403
403
  | Stage | Role |
404
404
  |---|---|
405
- | `verify` | Clean tree, then `npm run verify`, skipped when a receipt for this tree is younger than 30 minutes |
405
+ | `verify` | Clean tree, then `npm run verify`, skipped when an `ok: true` receipt for this tree is younger than 30 minutes. A failure names the exit code, the failing step, and a short excerpt, and writes the full log |
406
406
  | `docs` | Regenerate the roadmap after verify and before the merge. Absent generator: pass, skipped |
407
407
  | `push` | `git push -u origin <branch>`, with `--force-with-lease` after a rebase in this run |
408
408
  | `pr` | Reuse the open pull request, or create one from `--body-file` |
409
- | `rebase` | Up to five rounds onto `origin/main`. Only the dist rebuild, the CHANGELOG Unreleased union, and the tracker "Landed in this tree" union are resolved automatically |
409
+ | `rebase` | Polls `UNKNOWN` until it resolves. `CLEAN` and `HAS_HOOKS` proceed. `BEHIND` and `DIRTY` rebase, up to five rounds onto `origin/main`. Only the dist rebuild, the CHANGELOG Unreleased union, and the tracker "Landed in this tree" union are resolved automatically. Any other known state blocks |
410
410
  | `unblock` | Rerun one failed check. A required review is reported |
411
- | `merge` | Squash only. Poll until `MERGED`, up to 20 minutes |
411
+ | `merge` | Squash only after `CLEAN` or `HAS_HOOKS`. Poll until `MERGED`, up to 20 minutes. `UNKNOWN` does not merge |
412
412
  | `release` | Auto-release tag, Release workflow, then npm for 10 minutes. Prints `PUBLISHED <version>` |
413
413
  | `milestone` | Report `deep_review_required` when a phase completes or the body has a `Milestone:` line. Does not run the review |
414
414
 
415
415
  Refusals (exit 1): `land_dirty_tree`, `land_verify_failed`, `land_docs_failed`,
416
416
  `land_push_rejected`, `land_pr_body_missing`, `land_conflict_manual`,
417
- `land_blocked`, `land_merge_failed`, `land_release_failed`,
418
- `land_publish_timeout`, `land_milestone_failed`. Usage errors exit 2.
417
+ `land_merge_state_unknown`, `land_merge_not_ready`, `land_blocked`,
418
+ `land_merge_failed`, `land_release_failed`, `land_publish_timeout`,
419
+ `land_milestone_failed`. Usage errors exit 2.
419
420
  `--stage <name>` runs one stage. `--dry-run` prints the plan and does not mutate.
420
421
 
421
422
  The same stages are command gates in `.kxm/gates.yaml` (`land-verify`,
@@ -2,7 +2,7 @@
2
2
  title: "Decisions"
3
3
  description: "Decisions of medium significance or higher made by automated KXM work in this repository. One entry per decision: the options evaluated with their weights, and why the selected option won. Recorded the turn the decision is taken."
4
4
  audience: "agents and maintainers"
5
- updated: "2026-09-27"
5
+ updated: "2026-09-28"
6
6
  ---
7
7
 
8
8
  # Decisions
@@ -2,7 +2,7 @@
2
2
  title: "Learnings"
3
3
  description: "Durable lessons from running the writer, critic, and landing loop on this repository. One entry per lesson, with the evidence and where it applies. Pruned when a lesson stops being true."
4
4
  audience: "agents and maintainers"
5
- updated: "2026-09-26"
5
+ updated: "2026-09-28"
6
6
  ---
7
7
 
8
8
  # Learnings
@@ -97,9 +97,14 @@ entry whose fix has landed is deleted, not archived.
97
97
 
98
98
  - **Auto-merge cannot be enabled on a PR that is already `CLEAN`**; the
99
99
  mutation answers "clean status" and the REST squash merge is the path.
100
- While CI is paused every PR is clean, so this is the normal path.
101
- - **A verify failure inside `kxm land` shows only the child's last output
102
- line.** Re-run verify by hand to see the cause until backlog S19 lands.
100
+ `CI` is on and `CI / required` is a required check in `protect-main`, so
101
+ a pull request is not clean until that check passes.
102
+ - **A verify failure inside `kxm land` names the exit code, the failing
103
+ step, and a short excerpt, and keeps the full output in
104
+ `.kxm/logs/land-verify-<tree>.log`.** The receipt
105
+ `.kxm/logs/land-verify-<tree>.json` records the same fields with
106
+ `ok: false`. `mergeStateStatus: UNKNOWN` is polled and refused at the
107
+ bound; it is not treated as ready to merge.
103
108
 
104
109
  ## Reviews
105
110
 
@@ -160,6 +160,7 @@ Exit 2 covers an unknown command or option, a missing argument or required optio
160
160
  | Create and drive a run | [`kxm run`](#kxm-run), [`kxm runs drive`](#kxm-runs-drive), [`kxm runtime status`](#kxm-runtime-status) |
161
161
  | Work in an isolated checkout | [`kxm lane`](#kxm-lane), [`kxm run --lane`](#kxm-run) |
162
162
  | Land the current branch | [`kxm land`](#kxm-land) |
163
+ | Resume the roadmap supervisor | [`kxm supervise`](#kxm-supervise) |
163
164
  | Delegate and accept a developer assignment | [`kxm assign`](#kxm-assign) |
164
165
  | Inspect runs | [`kxm runs list`](#kxm-runs-list), [`kxm runs status`](#kxm-runs-status), [`kxm runs receipt`](#kxm-runs-receipt), [`kxm tenant status`](#kxm-tenant-status), [`kxm workflow list`](#kxm-workflow-list) |
165
166
  | 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) |
@@ -1454,20 +1455,42 @@ Outside a KXM project the command refuses `project_required` (exit 1). Unknown a
1454
1455
 
1455
1456
  | Stage | What it does |
1456
1457
  |---|---|
1457
- | `verify` | Refuses `land_dirty_tree` unless `git status --porcelain` is empty. Skips when `.kxm/logs/land-verify-<tree>.json` for `HEAD^{tree}` is younger than 30 minutes. Otherwise runs `npm run verify` and writes that receipt. |
1458
+ | `verify` | Refuses `land_dirty_tree` unless `git status --porcelain` is empty. Skips when `.kxm/logs/land-verify-<tree>.json` for `HEAD^{tree}` is younger than 30 minutes and records `ok: true`. Otherwise runs `npm run verify` and writes that receipt. A failure writes the full output to `.kxm/logs/land-verify-<tree>.log` and a receipt with `ok: false`, the exit code, the failing step, and a short excerpt. The stage line names that exit code, the failing lines, and the log path. |
1458
1459
  | `docs` | Runs `plans/kxm-roadmap/update-dashboard.mjs` when that file exists. Commits changes under `docs/roadmap/`, `plans/kxm-roadmap/`, or `docs/architecture/` as `docs(roadmap): regenerate after verify`. Any other path, including `state.json`, is `land_docs_failed`. When the generator is absent the stage passes with `docs: skipped (generator absent)`. |
1459
1460
  | `push` | `git push -u origin <branch>`. After a rebase in this run, the push uses `--force-with-lease`. |
1460
1461
  | `pr` | Reuses the branch's open pull request, or creates one with `gh pr create --body-file`. The title is `--title` when that option is set; otherwise it is the subject of the first commit (`git log --reverse --format=%s origin/main..HEAD`). `--dry-run` prints that title. |
1461
- | `rebase` | When `mergeStateStatus` is `BEHIND` or `DIRTY`: fetch, rebase onto `origin/main`, and resolve only three conflicts (take `plugins/kxm/dist` from main and rebuild; union CHANGELOG Unreleased bullets, ours first; union the tracker "Landed in this tree" list, newest first). Runs `docs` again when the tree changed, re-verifies unless `git merge-tree --write-tree origin/main HEAD` was clean, and pushes with the lease. Five rounds, then `land_conflict_manual`. |
1462
+ | `rebase` | Polls `mergeStateStatus` while it is `UNKNOWN`, with bounded backoff (`KXM_LAND_MERGE_STATE_POLL_MS`, default 1 second, doubling up to 30 seconds, for at most `KXM_LAND_MERGE_STATE_WAIT_MS`, default 2 minutes). `UNKNOWN` at that bound is `land_merge_state_unknown` and is never treated as ready. `CLEAN` and `HAS_HOOKS` proceed. `BEHIND` and `DIRTY` fetch and rebase onto `origin/main`, resolving only three conflicts (take `plugins/kxm/dist` from main and rebuild; union CHANGELOG Unreleased bullets, ours first; union the tracker "Landed in this tree" list, newest first). Any other known state, including `BLOCKED`, is `land_merge_not_ready`. Runs `docs` again when the tree changed, re-verifies unless `git merge-tree --write-tree origin/main HEAD` was clean, and pushes with the lease. Five rounds, then `land_conflict_manual`. |
1462
1463
  | `unblock` | Reads `statusCheckRollup` and `reviewDecision`. Reruns one failed check with `gh run rerun --failed`. A second failure or `REVIEW_REQUIRED` is `land_blocked`. |
1463
- | `merge` | Enables auto-merge with `enablePullRequestAutoMerge` and `mergeMethod: SQUASH`. When the response contains "clean status", squash-merges with `PUT /repos/{owner}/{repo}/pulls/{n}/merge` and commit title `<title> (#n)`. Polls `gh pr view --json state` every 30 seconds for up to 20 minutes. |
1464
+ | `merge` | Refuses `land_merge_state_unknown` when `mergeStateStatus` stays `UNKNOWN`, and `land_merge_not_ready` for any known state other than `CLEAN` or `HAS_HOOKS`. Otherwise enables auto-merge with `enablePullRequestAutoMerge` and `mergeMethod: SQUASH`. When the response contains "clean status", squash-merges with `PUT /repos/{owner}/{repo}/pulls/{n}/merge` and commit title `<title> (#n)`. Polls `gh pr view --json state` every 30 seconds for up to 20 minutes. |
1464
1465
  | `release` | Records the newest `v*` tag before the merge, waits for the Auto-Release run whose title contains the pull request title, requires a newer tag from `git ls-remote --tags origin`, then waits for the first `release.yml` run whose `createdAt` is after that Auto-Release run, with no title filter. Both run ids are written to `.kxm/logs/land-release-context.json` and printed in the stage detail (`PUBLISHED <version> auto-release <id> release <id>`). Each wait prints one JSON line every 2 minutes (`waiting auto-release.yml 4m`). The npm poll is `npm view @kontextmind/kxm@<version> version` every 30 seconds for 10 minutes. |
1465
1466
  | `milestone` | Compares `plans/kxm-roadmap/state.json` from before and after `docs`. When a phase goes from an open task to all tasks `done`, or the pull request body contains a `Milestone:` line, prints `deep_review_required: true` and exits 0. The review is the `/reanalyze-roadmap` skill, not this command. An absent state file passes with skipped. |
1466
1467
 
1467
- Refusals (exit 1): `project_required`, `land_dirty_tree`, `land_verify_failed`, `land_docs_failed`, `land_push_rejected`, `land_pr_body_missing`, `land_pr_title_missing`, `land_conflict_manual`, `land_blocked`, `land_merge_failed`, `land_release_failed`, `land_publish_timeout`, `land_milestone_failed`.
1468
+ Refusals (exit 1): `project_required`, `land_dirty_tree`, `land_verify_failed`, `land_docs_failed`, `land_push_rejected`, `land_pr_body_missing`, `land_pr_title_missing`, `land_conflict_manual`, `land_merge_state_unknown`, `land_merge_not_ready`, `land_blocked`, `land_merge_failed`, `land_release_failed`, `land_publish_timeout`, `land_milestone_failed`.
1468
1469
 
1469
1470
  `land_pr_title_missing`: the `pr` stage was not given `--title`, and the first commit subject on `origin/main..HEAD` is empty.
1470
1471
 
1472
+ `land_verify_failed`: `npm run verify` exited non-zero. The stage line and `.kxm/logs/land-verify-<tree>.json` include `exitCode`, the failing step when the output names one, a short excerpt of the failing lines, and `log` (the full output).
1473
+
1474
+ `land_merge_state_unknown`: `mergeStateStatus` stayed `UNKNOWN` until the wait bound. The stage does not pass and does not merge.
1475
+
1476
+ `land_merge_not_ready`: GitHub reported a known merge state that is not `CLEAN` or `HAS_HOOKS`, and the rebase stage is not going to rebase it (`BLOCKED`, and on the merge stage also `BEHIND` or `DIRTY`). The detail names the status.
1477
+
1478
+ ## `kxm supervise`
1479
+
1480
+ Persist the roadmap supervisor's memory in `.kxm/state/supervisor.json` (`kxm.supervisor.v1`, mode `0600`). The file holds in-flight lanes, the last-seen pull request number and merge state, the last-seen CI status and conclusion, and backoff timers. A missing file is empty. A malformed file or any other schema is refused and left unchanged. A later process reads the same file and continues.
1481
+
1482
+ ```text
1483
+ kxm supervise status
1484
+ kxm supervise record --lane <unit> [--pr <n> --merge-state <status>] [--ci-status <status> --ci-conclusion <conclusion>] [--backoff-ms <n>] [--settled]
1485
+ kxm supervise tick
1486
+ ```
1487
+
1488
+ `record` updates one lane and keeps fields that this call does not set. `--settled` marks the lane not in flight. `--backoff-ms 0` clears the timer. `tick` clears backoff timers whose deadline has passed, leaves the lane in flight with its last-seen pull request and CI state, and appends one line to `.kxm/logs/supervisor.log`. A timer still in the future is unchanged. `--dry-run` prints the writes and does not change the file or the log. `KXM_SUPERVISE_NOW` overrides the clock with an ISO-8601 UTC timestamp.
1489
+
1490
+ Outside a KXM project the command refuses `project_required` (exit 1).
1491
+
1492
+ Refusals (exit 1): `project_required`, `supervisor_unreadable`, `supervisor_schema_unsupported`, `supervisor_clock_invalid`, `lane_unit_invalid`, `supervisor_pr_invalid`, `supervisor_ci_invalid`, `supervisor_backoff_invalid`.
1493
+
1471
1494
  ## `kxm assign`
1472
1495
 
1473
1496
  `kxm assign` is the entry to this repository's developer assignment runner. Each verb spawns `node scripts/assignment-run.mjs <verb> ...` from the project root with that argument list, `stdio: "inherit"`, and the process environment unchanged. The command returns the child's exit code. It does not load a `.env` file, resolve paths, or repeat any runner check. Every validation rule, refusal code, and file the runner writes stays in [`scripts/assignment-run.mjs`](../../scripts/assignment-run.mjs). The loop, the records, and those codes are documented in [Assignment runner](../contributing/assignment-runner.md).
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@kontextmind/kxm",
3
- "version": "0.7.169",
3
+ "version": "0.7.171",
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.169",
5
+ "version": "0.7.171",
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",