@kontextmind/kxm 0.7.128 → 0.7.131

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.
Files changed (65) hide show
  1. package/.claude-plugin/marketplace.json +1 -1
  2. package/.kxm/README.md +4 -1
  3. package/.kxm/agents/coordinator.yaml +1 -0
  4. package/.kxm/agents/critic-arch.yaml +1 -4
  5. package/.kxm/agents/critic-cli.yaml +1 -4
  6. package/.kxm/agents/implementer.yaml +1 -4
  7. package/.kxm/workflows/implement-only.yaml +1 -1
  8. package/.kxm/workflows/review-arch-only.yaml +1 -1
  9. package/.kxm/workflows/review-cli-only.yaml +1 -1
  10. package/CHANGELOG.md +42 -1
  11. package/docs/architecture/inventory.md +1 -1
  12. package/docs/concepts/architecture.md +1 -1
  13. package/docs/concepts/data-and-storage.md +1 -1
  14. package/docs/contracts/README.md +1 -1
  15. package/docs/contracts/migration.md +12 -8
  16. package/docs/contracts/routing.md +3 -3
  17. package/docs/contributing/assignment-runner.md +14 -15
  18. package/docs/contributing/development.md +22 -6
  19. package/docs/contributing/harness-routing-internals.md +8 -8
  20. package/docs/glossary.md +1 -1
  21. package/docs/guides/agent-skills.md +1 -1
  22. package/docs/operations/backup-and-restore.md +7 -7
  23. package/docs/operations/upgrade.md +2 -2
  24. package/docs/reference/cli-reference.md +37 -14
  25. package/docs/reference/config-reference.md +81 -155
  26. package/docs/reference/harness-routing.md +29 -21
  27. package/docs/reference/workflow-catalog.md +4 -4
  28. package/docs/start/first-workflow.md +1 -1
  29. package/examples/project/.kxm/agents/coordinator.yaml +1 -2
  30. package/examples/project/.kxm/agents/critic-1.yaml +1 -3
  31. package/examples/project/.kxm/agents/critic-2.yaml +1 -2
  32. package/examples/project/.kxm/agents/critic-3.yaml +2 -3
  33. package/examples/project/.kxm/agents/implementer.yaml +1 -2
  34. package/examples/project/.kxm/agents/planner.yaml +1 -2
  35. package/examples/project/.kxm/agents/reproducer.yaml +1 -2
  36. package/examples/project/.kxm/agents/reviewer.yaml +1 -2
  37. package/examples/project/.kxm/workflows/fix.yaml +0 -12
  38. package/package.json +8 -8
  39. package/plugins/kxm/.claude-plugin/plugin.json +1 -1
  40. package/plugins/kxm/dist/cli.js +2565 -465
  41. package/plugins/kxm/dist/mcp-server.js +1 -1
  42. package/plugins/kxm/dist/runtime-supervisor.js +683 -348
  43. package/plugins/kxm/dist/runtime.js +694 -358
  44. package/plugins/kxm/dist/server.js +17 -14
  45. package/plugins/kxm/package.json +1 -1
  46. package/plugins/kxm/skills/kxm/SKILL.md +1 -1
  47. package/plugins/kxm/skills/kxm-definitions/SKILL.md +8 -0
  48. package/plugins/kxm/src/cli/lanes.ts +109 -4
  49. package/plugins/kxm/src/cli/project.ts +6 -6
  50. package/plugins/kxm/src/database.ts +25 -19
  51. package/plugins/kxm/src/engine.ts +168 -163
  52. package/plugins/kxm/src/init-guide-setup.ts +75 -15
  53. package/plugins/kxm/src/mcp-server.ts +1 -1
  54. package/plugins/kxm/src/oneshot-producer.ts +3 -15
  55. package/plugins/kxm/src/project-config.ts +83 -70
  56. package/plugins/kxm/src/routes.ts +26 -18
  57. package/plugins/kxm/src/runtime-service.ts +1 -0
  58. package/plugins/kxm/src/runtime-store.ts +390 -77
  59. package/plugins/kxm/src/runtime-supervisor.ts +87 -21
  60. package/plugins/kxm/src/suggest.ts +2 -2
  61. package/plugins/kxm/src/template.ts +2 -14
  62. package/schemas/agent.schema.json +8 -0
  63. package/scripts/roster-policy.d.mts +2 -1
  64. package/scripts/roster-policy.mjs +119 -22
  65. package/scripts/run-bounded.mjs +53 -0
@@ -177,7 +177,7 @@ kxm init [--name <name>] [--project-id <id>] [--repository <id=absolute-path>]..
177
177
 
178
178
  Creates, validates, repairs, resumes, or joins a KXM project at the Git root that contains the current directory. A new project gets a minimal configuration: one coordinator agent, one implementer agent, a `test` command gate, and a `default` plan-implement-verify workflow. An existing project is validated without rewriting. Conflict-free template updates to non-authority fields are applied; authority changes, overlapping edits, and provenance-free or legacy state stay planning-only. `init` does not start a hub or the Runtime.
179
179
 
180
- The starter `defaultHarness: pi` and `npm test` gate are generic settings, not repository detection. Creation and create-planning output include `guidance`: for Claude, set `defaultHarness: claude` in `.kxm/project.yaml`, update any explicit `harness` overrides in `.kxm/agents/*.yaml`, and configure compatible agent models; for .NET or other non-npm repositories, set `.kxm/gates.yaml` → `gates.test.argv` to the repository's actual test command. Preflight reports an actionable prerequisite for `npm test` without a readable `package.json` test script; `task run` refuses it before creating a run.
180
+ The starter `defaultHarness: pi` and `npm test` gate are generic settings, not repository detection. Initialized agents bind roles. Harness, model, and effort are set in `.kxm/roles/*.yaml` and `.kxm/models/*.yaml`. Creation and create-planning output include `guidance`: for Claude, set `defaultHarness: claude` in `.kxm/project.yaml`; for .NET or other non-npm repositories, set `.kxm/gates.yaml` → `gates.test.argv` to the repository's actual test command. Preflight reports an actionable prerequisite for `npm test` without a readable `package.json` test script; `task run` refuses it before creating a run.
181
181
 
182
182
  | Option | Argument | Default | Description |
183
183
  |---|---|---|---|
@@ -393,7 +393,7 @@ kxm completion install --shell zsh --dry-run
393
393
 
394
394
  Compares the authority-bearing fields of the project configuration against a base Git revision. The base is materialized into a temporary shadow with a sanitized environment; nothing in the project is written. Both subcommands refuse `--workspace` (exit 2) and need no hub.
395
395
 
396
- The comparison covers the loaded bundle only: `project.yaml`, `agents/`, `models/`, `workflows/`, `gates.yaml`, `project/env.yaml`, and each member repository's `repo.yaml` and `env.yaml`. It does not read `routes.yaml`, `roles/`, `roster.yaml`, or `prices.yaml`, so a new route admission, roster entry, developer-roster route, or price change never counts as an expansion. Review those files by hand.
396
+ The comparison covers the loaded bundle only: `project.yaml`, `agents/`, `models/`, `workflows/`, `gates.yaml`, `project/env.yaml`, and each member repository's `repo.yaml` and `env.yaml`. It does not read `routes.yaml`, `roles/`, `the role files`, or `prices.yaml`, so a new route admission, roster entry, developer policy route, or price change never counts as an expansion. Review those files by hand.
397
397
 
398
398
  ### `kxm trust diff`
399
399
 
@@ -1069,14 +1069,32 @@ Lists admitted and disabled routes.
1069
1069
 
1070
1070
  No command-specific options.
1071
1071
 
1072
- - Reads only. JSON keys: `policy` (`schema`, `updatedAt`, `admitted`, `disabled`, `roles`).
1072
+ - Reads only. JSON keys: `policy` (`schema`, `updatedAt`, `admitted`, `disabled`) and `membership` (strings `<role> <route-id>` from `.kxm/roles/*.yaml`). `policy` has no `roles` field. A model file named by no roster, such as `opus-claude`, is absent from `membership`.
1073
1073
 
1074
1074
  ```bash
1075
1075
  kxm routes list
1076
1076
  ```
1077
1077
 
1078
1078
  ```text
1079
- no route decisions
1079
+ admitted anthropic/fable
1080
+ admitted google/gemini-3.8-flash-high
1081
+ admitted google/gemini-3.8-flash-medium
1082
+ admitted openai/gpt-5.6-sol
1083
+ admitted openrouter/qwen/qwen3-coder-plus
1084
+ admitted openrouter/qwen/qwen3.8-flash
1085
+ admitted openrouter/z-ai/glm-5.3-flash
1086
+ admitted qwen-token-plan/deepseek-v4.1-flash
1087
+ admitted qwen-token-plan/qwen3.8-flash
1088
+ admitted qwen-token-plan/qwen3.8-max
1089
+ admitted xai/grok-4.7
1090
+ admitted zai-coding-cn/glm-5.3
1091
+ admitted zai-coding-cn/glm-5.3-flash
1092
+ planner fable-claude
1093
+ reviewer-arch fable-claude
1094
+ reviewer-cli sol-codex
1095
+ writer grok-native
1096
+ writer qwen-openrouter-pi
1097
+ writer gemini-agy
1080
1098
  ```
1081
1099
 
1082
1100
  ```bash
@@ -1084,7 +1102,7 @@ kxm routes list --json
1084
1102
  ```
1085
1103
 
1086
1104
  ```text
1087
- {"schema":"kxm.cli-result.v1","ok":true,"command":"routes list","policy":{"schema":"kxm.routes.v2","updatedAt":"2026-09-23T13:50:08.663Z","admitted":[],"disabled":[],"roles":{}}}
1105
+ {"schema":"kxm.cli-result.v1","ok":true,"command":"routes list","policy":{"schema":"kxm.routes.v2","updatedAt":"2026-09-24T00:00:00.000Z","admitted":["anthropic/fable","google/gemini-3.8-flash-high","google/gemini-3.8-flash-medium","openai/gpt-5.6-sol","openrouter/qwen/qwen3-coder-plus","openrouter/qwen/qwen3.8-flash","openrouter/z-ai/glm-5.3-flash","qwen-token-plan/deepseek-v4.1-flash","qwen-token-plan/qwen3.8-flash","qwen-token-plan/qwen3.8-max","xai/grok-4.7","zai-coding-cn/glm-5.3","zai-coding-cn/glm-5.3-flash"],"disabled":[]},"membership":["planner fable-claude","reviewer-arch fable-claude","reviewer-cli sol-codex","writer grok-native","writer qwen-openrouter-pi","writer gemini-agy"]}
1088
1106
  ```
1089
1107
 
1090
1108
  ### `kxm routes count`
@@ -1217,7 +1235,7 @@ Adds a role definition. Without a role ID, or with `--pick`, local scope offers
1217
1235
 
1218
1236
  - Writes `.kxm/roles/<id>.yaml` in local scope, or `<KXM_USER_CONFIG_DIR>/roles/<id>.yaml` in global scope. `--dry-run` plans the write and writes nothing.
1219
1237
  - Each `--route` is checked the same way as `kxm role modify --add-route`. If `.kxm/models/<route-id>.yaml` is missing, the command exits 1 and writes `kxm: route '<route-id>' is not a file under .kxm/models/` to stderr. It does not write the role file, including under `--dry-run`. `--file` checks every `roster[].route` the same way before writing, in local scope and in global scope.
1220
- - Local scope belongs to a KXM project: the file lands in the project root's `.kxm/roles/` from any subdirectory, and outside a project the command refuses with `project_not_found` and creates nothing. Before writing, the project loader checks the project with the new role in place of any file of that ID. The loader reads only `writer.yaml`, whose roster must name a route whose model is the `implementer` agent's model (see [Roles](config-reference.md#kxmrolesroleyaml-kxmrolev2)). If the project would not load, the command refuses with `role_invalid`, lists each issue and writes nothing, also under `--dry-run`, and `--overwrite` replaces a `writer.yaml` the loader refuses. Global scope is not checked, because no loader reads it.
1238
+ - Local scope belongs to a KXM project: the file lands in the project root's `.kxm/roles/` from any subdirectory, and outside a project the command refuses with `project_not_found` and creates nothing. Before writing, the project loader checks the project with the new role in place of any file of that ID. Validation is the role file schema, and every roster route must exist under `.kxm/models/`. If the project would not load, the command refuses with `role_invalid`, lists each issue and writes nothing, also under `--dry-run`, and `--overwrite` replaces a role file the loader refuses. Global scope is not checked, because no loader reads it.
1221
1239
  - Refusals exit 2 and honor `--json`: `project_not_found` and `role_invalid` (with `issues`, each `{phase, code, file, message}`). A missing `--route` file, and an existing role without `--overwrite`, exit 1 with a plain stderr line, also under `--dry-run` (`kxm: route '<route-id>' is not a file under .kxm/models/`, or `role add failed: role_already_exists: ...`).
1222
1240
  - JSON keys: `roleId`, `id`, `filePath`, `scope`.
1223
1241
 
@@ -1345,7 +1363,7 @@ dry run: resume workflow run wf_dry_run (stage: review)
1345
1363
 
1346
1364
  ## `kxm lane`
1347
1365
 
1348
- One lane is one git worktree, one branch named the unit, and one recorded base sha. The record lives in the control checkout's state directory, `.kxm/state/lanes.json` (`kxm.lanes.v1`, mode 0600). The worktree path is `../<control-dir-basename>-<unit>`, next to the control checkout. Creating a lane resolves the base ref to a sha and does not fetch. There is no push, merge, pull request, or branch delete. Selecting a lane sets the discovery cwd to that lane's path and rebuilds workspace directories from it; `KXM_WORKDIR` still selects the workspace root when it is set, so the lane path is the workspace root only when `KXM_WORKDIR` is unset.
1366
+ One lane is one git worktree, one branch named the unit, and one recorded base sha. The record lives in the control checkout's state directory, `.kxm/state/lanes.json` (`kxm.lanes.v1`, mode 0600). The worktree path is `../<control-dir-basename>-<unit>`, next to the control checkout. Creating a lane resolves the base ref to a sha and does not fetch. There is no push, merge, pull request, or branch delete. Selecting a lane sets the discovery cwd to that lane's path and rebuilds workspace directories from it; `KXM_WORKDIR` still selects the workspace root when it is set, so the lane path is the workspace root only when `KXM_WORKDIR` is unset. A lane worktree of an already registered repository registers in the Runtime registry under that project: the same project id, its own control root and event store, and the same home runtime. A foreign clone with the same id is refused with `project_home_conflict`.
1349
1367
 
1350
1368
  ```text
1351
1369
  kxm lane create <unit> [--base <ref>]
@@ -1375,15 +1393,19 @@ Prints one line per lane: unit, branch, base sha, `dirty` (porcelain line count)
1375
1393
 
1376
1394
  ### `kxm lane status`
1377
1395
 
1378
- The list row for one unit, plus `status` of `lastRunId` when the Runtime supervisor is already running and answers. Otherwise `status=unknown`. `kxm lane status` never starts the Runtime supervisor.
1396
+ The list row for one unit, plus `status` of `lastRunId` when the Runtime supervisor is already running and answers. Otherwise `status=unknown`. The text ends with `root=<path>`, the lane root that status read. JSON includes the same path as `root`. `kxm lane status` never starts the Runtime supervisor.
1379
1397
 
1380
1398
  Refusals (exit 1): `lane_missing`, `lane_unit_invalid`, `lanes_unreadable`.
1381
1399
 
1382
1400
  ### `kxm lane drop`
1383
1401
 
1384
- Removes the worktree and the record. Refuses `lane_dirty` when porcelain is nonempty, and `lane_run_open` when `lastRunId` is set and that run is not `completed`, `failed`, or `cancelled`. `--force` overrides both. `git worktree remove --force` is used only with `--force`. The branch is not deleted; the text says so (`branchDeleted: false`). Unless `--force` is set, drop may start the Runtime supervisor to check whether the lane's last run is still open; `--dry-run` only attaches to a supervisor that is already running. `kxm lane status` never starts the supervisor.
1402
+ Removes the worktree and the record, and unregisters that lane root from the Runtime registry. When the supervisor is running, drop calls `POST /v1/projects/unregister`. A stopped supervisor is not an error: drop takes the registry write lock, deletes the row when that transaction still sees no live supervisor, and continues when the registry is absent. If the lock finds a live supervisor, drop uses the unregister route instead of editing the file. The route answers 409 `runtime_project_busy` when that root has an unsettled run, and 409 `runtime_project_has_lanes` when the root is the home row and other lanes are still registered, unless the body sets `force` to true. Refuses `lane_dirty` when porcelain is nonempty. Refuses `lane_run_open` when any run in the lane event store is not `completed`, `failed`, or `cancelled`. The text names each unsettled run id, and JSON includes `runIds`. That list is the same read as `kxm runs list` when the supervisor is running, and a direct read of the lane event store when the supervisor is stopped. A lane with no event store keeps the earlier check: `lane_run_open` when `lastRunId` is set and that run is not settled. `--force` overrides both refusals and sends `force: true` on the unregister request. `git worktree remove --force` is used only with `--force`. The branch is not deleted; the text says so (`branchDeleted: false`). Unless `--force` is set, the `lastRunId` check may start the Runtime supervisor. The event-store list only attaches to a supervisor that is already running. `--dry-run` only attaches and does not unregister. `kxm lane status` never starts the supervisor.
1385
1403
 
1386
- Refusals (exit 1): `lane_missing`, `lane_dirty`, `lane_run_open`, `lane_git_failed`.
1404
+ `lane_run_open` also uses the detail `lane <unit> runs could not be read: <detail>` when the lane's project or store could not be read, so the drop fails closed and does not remove the worktree. `<detail>` is the error message, cut to 200 characters, or `run list failed` when the thrown value is not an `Error`. The unsettled-run details stay `lane <unit> run <ids> is unsettled` (with `runIds`) and `lane <unit> run <lastRunId> is <status>`.
1405
+
1406
+ `lane_unregister_failed` uses the detail `lane <unit> could not be unregistered: <detail>`. `<detail>` is cut to 300 characters. It appears when a live supervisor refused the unregister or the request failed: the error message, `lane root could not be unregistered` when the failure is not an `Error`, or `runtime supervisor is live but could not be reached` when the registry lock finds a live supervisor that drop cannot attach to.
1407
+
1408
+ Refusals (exit 1): `lane_missing`, `lane_dirty`, `lane_run_open`, `lane_git_failed`, `lane_unregister_failed`.
1387
1409
 
1388
1410
  ### `kxm lane run`
1389
1411
 
@@ -1638,8 +1660,8 @@ kxm runs status <runId> [--lane <unit>]
1638
1660
  Show the projected status of a run, including durable drive receipt state (open / receipt verified / unsettled / orphaned).
1639
1661
 
1640
1662
  - Arguments: `<runId>`, Run id. `--lane <unit>` discovers the project from that lane's worktree (`lane_missing` when the record or path is absent, `lane_unit_invalid` when the unit is not a KXM identifier, `lanes_unreadable` when the record cannot be read). Reads run state, but starts the supervisor if needed (not under `--dry-run`).
1641
- - Text: `run <id>: <status> (workflow <id>, updated <time>)`, plus a drive line such as `drive <id>: open`, `completed (receipt verified)`, `unsettled <reason>`, `handoff`, `cancelled (<reason>)`, or `no receipt (orphaned)`.
1642
- - JSON keys: `run` (`runId`, `status`, `workflowId`, `configRevision`, `updatedAt`, ...), `drive` (`driveId`, `mode`, `openedAt`, `receipt`, `verified`, `divergence`).
1663
+ - Text: `run <id>: <status> (workflow <id>, updated <time>)`, then `root <path>` (the checkout the status read, which is the lane worktree when `--lane` is set), plus a drive line such as `drive <id>: open`, `completed (receipt verified)`, `unsettled <reason>`, `handoff`, `cancelled (<reason>)`, or `no receipt (orphaned)`.
1664
+ - JSON keys: `projectRoot` (that same checkout), `run` (`runId`, `status`, `workflowId`, `configRevision`, `updatedAt`, ...), `drive` (`driveId`, `mode`, `openedAt`, `receipt`, `verified`, `divergence`).
1643
1665
  - Errors: `project_required`, `run_status_failed`, `run_status_io_failed` (exit 1).
1644
1666
 
1645
1667
  ```bash
@@ -1648,6 +1670,7 @@ kxm runs status run_a80e84c98f514299b82f0157f4537ea3
1648
1670
 
1649
1671
  ```text
1650
1672
  run run_a80e84c98f514299b82f0157f4537ea3: preparing (workflow default, updated 2026-09-23T17:47:29.682Z)
1673
+ root /work/proj
1651
1674
  ```
1652
1675
 
1653
1676
  With no supervisor running:
@@ -1666,7 +1689,7 @@ kxm runs status --dry-run refused: the Runtime supervisor is not running and --d
1666
1689
  kxm runs drive <runId> [--simulated] [--wait] [--timeout-ms <n>] [--lane <unit>]
1667
1690
  ```
1668
1691
 
1669
- 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`).
1692
+ 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 its route must be on the writer roster in `.kxm/roles/writer.yaml`. 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`).
1670
1693
 
1671
1694
  | Option | Argument | Default | Description |
1672
1695
  |---|---|---|---|
@@ -2838,7 +2861,7 @@ Recommends a flat workflow ID backed by a shipped template, with category metada
2838
2861
 
2839
2862
  - Explicit `Claude only`, `Claude-only`, or `only Claude Code` constraints exclude other harnesses. A missing, unauthenticated, or unsupported required harness produces `harness_unavailable`; KXM never silently substitutes Grok or Codex.
2840
2863
  - A write workflow requires an audited writer profile for the selected harness. Only Pi and Grok currently have one; Claude-only bug fixes produce `live_write_unsupported` with direct-Claude implementation guidance, never a Grok substitution. No misleading create/drive command is emitted for an unsupported profile.
2841
- - For supported work, every suggested agent binding uses the selected detected, authenticated, dispatch-ready harness. Configure its compatible admitted model, install the exact template, validate project configuration, then use the separate create/live-drive/status/receipt commands. Writers also require single-assignment/single-run admission, any configured developer-roster writer approval, and the repository's actual verification gate. An already-present recommended workflow ID produces `workflow_already_exists`; KXM will not assume its agents or permissions match the template.
2864
+ - For supported work, every suggested agent binding uses the selected detected, authenticated, dispatch-ready harness. Configure its compatible admitted model, install the exact template, validate project configuration, then use the separate create/live-drive/status/receipt commands. Writers also require single-assignment/single-run admission, any configured developer policy writer approval, and the repository's actual verification gate. An already-present recommended workflow ID produces `workflow_already_exists`; KXM will not assume its agents or permissions match the template.
2842
2865
  - Arguments: `<prompt...>`; no command-specific options. No hub needed. `--dry-run` skips native authentication probes to avoid their side effects and does not claim verified availability.
2843
2866
  - JSON keys: `prompt`, `workflowId`, `template`, `area`, `confidence`, `reasons`, `suggestedSkills`, `roles` (an array of `{agent, harness, role}`), `suggestedCommand` (installation only), and `execution`. Supported execution includes `prerequisites`, `shell`, `createCommand`, `driveCommand`, `statusCommand`, and `receiptCommand`; refusal includes `error`, `reason`, and `nextSteps`, sets `ok: false`, and exits 1.
2844
2867
 
@@ -25,13 +25,12 @@ Related pages:
25
25
  | `.kxm/project.yaml` | `kxm.project.v1` | Project identity, repositories, defaults, run limits | You; `kxm init` creates it | Yes |
26
26
  | `.kxm/repo/repo.yaml` (in each repository) | `kxm.repository.v1` | Per-repository definition | You; `kxm init` creates the control one | Yes, in the repository it describes |
27
27
  | `.kxm/project/env.yaml`, `.kxm/repo/env.yaml` | `kxm.environment.v1` | Portable, non-secret environment | You | Yes |
28
- | `.kxm/agents/<id>.yaml` | `kxm.agent.v1` | Agent harness, model, and permission ceilings | You; `kxm init` creates two | Yes |
28
+ | `.kxm/agents/<id>.yaml` | `kxm.agent.v1` | Agent role and permission ceilings | You; `kxm init` creates two | Yes |
29
29
  | `.kxm/models/<id>.yaml` | `kxm.model.v2` | One model route (harness, vendor, status, permissions) | You; `kxm init` | Yes |
30
30
  | `.kxm/workflows/<id>.yaml` | `kxm.workflow.v1` | Ordered steps and typed transitions | You; `kxm init` creates `default` | Yes |
31
31
  | `.kxm/gates.yaml` | `kxm.gate-registry.v1` | The executable gate registry | You; `kxm init` creates it | Yes |
32
32
  | `.kxm/roles/<role>.yaml` | `kxm.role.v2` | Model rosters per role | You, `kxm role`, `kxm models` | Yes |
33
33
  | `.kxm/routes.yaml` | `kxm.routes.v2` | Admitted and disabled model routes | You, `kxm routes`, `kxm models` | Yes |
34
- | `.kxm/roster.yaml` | `kxm.developer-roster.v1` | Developer assignment roster for the KXM source repository | Maintainers | Yes, and it must be committed |
35
34
  | `.kxm/prices.yaml` | `kxm.prices.v1` | Dated, hash-pinned list prices | You | Yes |
36
35
  | `.kxm/models/inventory.yaml` | `kxm.model-inventory.v1` | Discovered model catalog | `kxm models inventory-refresh` only | Your choice (generated) |
37
36
  | `.kxm/config.yaml`, `~/.config/kxm/config.yaml` | `kxm.config.v1` | Personalization and hub auto-start | `kxm config set` | Project file: yes, unless ignored |
@@ -44,6 +43,8 @@ Related pages:
44
43
  | Claude Code plugin `userConfig` | Claude plugin manifest | Hub URL, token, agent identity for Claude Code | Claude Code, per user | No |
45
44
  | `<state root>/update.yaml` | `kxm.update.v1` | Updater settings | You | No (host-local) |
46
45
 
46
+ The developer assignment runner reads `.kxm/roles/*.yaml` and `.kxm/models/*.yaml` at `refs/remotes/origin/main`.
47
+
47
48
  `kxm init` does not write a `.gitignore`. See
48
49
  [Workspace layout](#workspace-layout-tracked-ignored-and-state) for the entries
49
50
  to add.
@@ -57,17 +58,16 @@ to add.
57
58
  .kxm/workflows/<id>.yaml ── coordinator ──> .kxm/agents/coordinator.yaml
58
59
  │ steps[]
59
60
  ├─ kind agent | moa | approval | wait ──> .kxm/agents/<id>.yaml
60
- │ ├─ harness ──> pi | claude | codex | grok | agy | kimi | deepseek
61
- │ ├─ model ────> {provider, model} | {profile} | {tag}
62
- │ │ └─> .kxm/models/<id>.yaml
61
+ │ ├─ role ──> .kxm/roles/<role>.yaml
62
+ │ │ └─ roster[].route ──> .kxm/models/<route>.yaml
63
+ │ │ (harness, model, vendor, permissions)
63
64
  │ └─ tools, repositories, network: permission ceilings
64
65
  └─ kind gate ──> .kxm/gates.yaml: command | artifacts-exist | reserved
65
66
 
66
67
  Live dispatch admission (checked by the Runtime for every attempt):
67
- agent model "provider/model" ──> .kxm/routes.yaml: admitted and not disabled
68
- └─> .kxm/roles/<role>.yaml roster, if that file exists
69
- (role = agent id; "writer" for agent "implementer")
70
- developer assignments (scripts/assignment-run.mjs) ──> .kxm/roster.yaml routes + lineup
68
+ agent.role ──> .kxm/roles/<role>.yaml roster ──> .kxm/models/<route>.yaml
69
+ .kxm/routes.yaml holds only the admitted and disabled lists
70
+ developer assignments (scripts/assignment-run.mjs) ──> .kxm/roles/*.yaml and .kxm/models/*.yaml
71
71
 
72
72
  Cost accounting:
73
73
  producer token usage ──> .kxm/prices.yaml (dated today, hash verified) ──> list estimate
@@ -79,13 +79,14 @@ Cost accounting:
79
79
  | Check | Files it covers | Where it runs |
80
80
  |---|---|---|
81
81
  | Project bundle load: restricted YAML, JSON Schema, cross-file semantics | `project.yaml`, every `repo.yaml` and `env.yaml`, `agents/`, `models/`, `workflows/`, `gates.yaml`, `template-provenance.yaml`, plus the `roles/writer.yaml` cross-check | `kxm init` (validate mode), `kxm run` and `kxm run --dry-run`, `kxm trust`, every Runtime request |
82
+ | Retired agent routing fields | `harness` or `model` on `.kxm/agents/<id>.yaml` | Loader refuses the file with `retired_agent_routing_fields` (`routing resolves from role; remove model and harness`) |
82
83
  | Workflow compile | `workflows/` | `kxm run` when it creates a run |
83
- | Runtime acceptance | Workflow steps, gate definitions, agent models, `routes.yaml`, `roles/` | `kxm runs drive` and live drives, per step |
84
+ | Runtime acceptance | Workflow steps, gate definitions, the route selected from `agent.role`, `routes.yaml` (`admitted` and `disabled` only), `roles/` | `kxm runs drive` and live drives, per step |
84
85
  | Permission diff | The bundle only | `kxm trust diff`, `kxm trust check` |
85
86
  | Revisions pinned on every run | `configRevision` (the bundle), memory revision (`.kxm/memory` without `candidates/`, plus `.kxm/skills/promoted`), executor policy, tool policy (agent and step `tools` plus the gate registry) | `kxm run` |
86
87
 
87
88
  Nothing validates `routes.yaml`, `roles/` (other than `writer.yaml`),
88
- `roles`, `roster.yaml`, `prices.yaml`, `inventory.yaml`,
89
+ `roles`, `the role files`, `prices.yaml`, `inventory.yaml`,
89
90
  `config.yaml`, `modes.yaml`, memory, tasks, goals, or webhook JSON during
90
91
  `kxm init`. Their own readers report problems when they run. None of them are
91
92
  part of `configRevision`, and `kxm trust check` does not see them: a change to
@@ -147,7 +148,7 @@ makes a directory a KXM project. Parser: `loadKxmProject` in
147
148
  | `description` | String, at most 2,000 characters | Optional | Display only |
148
149
  | `defaultWorkflow` | Identifier | Optional, `default` | Loader only: the named workflow must exist (`default_workflow_unknown`). `kxm run` always takes an explicit workflow. |
149
150
  | `defaultExecutor` | `local`, `ssh`, or `exe-dev` | Optional | Loader (`executor_unknown`); recorded in the run's executor-policy revision |
150
- | `defaultHarness` | `pi`, `claude`, `codex`, `grok`, `agy`, `kimi`, or `deepseek` | Optional, `pi` | Loader (`harness_unknown`); harness for agents without `harness`; fallback harness for live dispatch |
151
+ | `defaultHarness` | `pi`, `claude`, `codex`, `grok`, `agy`, `kimi`, or `deepseek` | Optional, `pi` | Loader (`harness_unknown`); project default harness. An agent file does not set `harness`; that field is `retired_agent_routing_fields`. |
151
152
  | `repositories` | Array of 1 to 64 entries | Required | Loader, Runtime |
152
153
  | `repositories[].id` | Identifier | Required | Must be unique after case folding (`repository_id_collision`) |
153
154
  | `repositories[].role` | `control` or `member` | Required | Exactly one `control` (`control_repository_count`) |
@@ -166,8 +167,11 @@ makes a directory a KXM project. Parser: `loadKxmProject` in
166
167
  | `limits.maxAgentTimeMs` | Integer, 0 to 31,536,000,000 | Optional | Runtime refuses to drive any run while it is set (`limit_unsupported`); leave it out |
167
168
  | `limits.agentStepTimeoutMs` | Integer, 60,000 to 31,536,000,000 | Optional, 3,600,000 | Wall clock for one live agent or moa step when the step omits `timeoutMs`. The supervisor passes it to the one-shot producer. A step `timeoutMs` narrower than this wins; a wider step value is refused (`step_unsupported`, field `timeoutMs`) |
168
169
 
169
- The Runtime binds one project `id` to one control root per state root, so a
170
- second checkout with the same ID is refused with `project_home_conflict`.
170
+ The Runtime binds a project `id` to one repository. A git worktree of that
171
+ repository registers as a lane: the same id, its own control root, and the
172
+ same home runtime. A foreign clone with the same id is refused with
173
+ `project_home_conflict`. A registered root whose directory is gone is replaced
174
+ when a new root for that id registers.
171
175
 
172
176
  Example (validated with `kxm init --json`, including a nested member checkout
173
177
  at `repositories/api`):
@@ -319,15 +323,21 @@ One file per agent; the filename is the agent ID that workflow steps
319
323
  reference. Schema: `schemas/agent.schema.json`; semantic checks in
320
324
  `validateBundle` in `plugins/kxm/src/project-config.ts`.
321
325
 
326
+ An agent does not name a harness or a model. Routing starts at the agent's
327
+ `role`, continues through `.kxm/roles/<role>.yaml`, and uses the selected
328
+ `.kxm/models/<route>.yaml`. `harness` and `model` on an agent file fail load
329
+ with `retired_agent_routing_fields`.
330
+
322
331
  | Field | Type and allowed values | Required, default | What reads it |
323
332
  |---|---|---|---|
324
333
  | `schema` | `kxm.agent.v1` | Required | Loader |
325
334
  | `purpose` | String, 1 to 2,000 characters | Required | Display; neutral in `kxm trust` |
335
+ | `role` | Identifier | Optional | Loader: `.kxm/roles/<role>.yaml`, then the selected `.kxm/models/<route>.yaml` |
336
+ | `effort` | `off`, `minimal`, `low`, `medium`, `high`, `xhigh`, or `max` | Optional | Recorded on the agent. Dispatch effort comes from the attempt, not this field. |
337
+ | `skills` | Unique identifiers, at most 64 | Optional | Skill names granted to the agent |
326
338
  | `instructions` | String, at most 16,000 characters | Optional | Not read by any code path yet. Step `instructions` are what reach the prompt. |
327
- | `harness` | `pi`, `claude`, `codex`, `grok`, `agy`, `kimi`, or `deepseek` | Optional, the project's `defaultHarness` | Loader (`harness_unknown`, harness and model pairing); live dispatch launches this harness |
328
- | `model` | One selector: `{provider, model}`, `{profile}`, or `{tag, capabilities}` | Optional | See [Model selectors](#model-selectors) |
329
339
  | `executor` | `local`, `ssh`, or `exe-dev` | Optional | Loader (`executor_unknown`); recorded in the executor-policy revision; no dispatch path selects an executor from it yet |
330
- | `tools.preset` | `coordinator`, `read-only`, `workspace-writer`, or `tests-writer` | Optional | Loader (`tool_preset_unknown`); pinned in the tool-policy revision |
340
+ | `tools.preset` | `coordinator`, `read-only`, `workspace-writer`, or `tests-writer` | Optional | Loader (`tool_preset_unknown`): the preset must be registered. An agent `tools.preset` may only narrow the role preset. Enforcement of that rule is pending (P3). |
331
341
  | `tools.allow`, `tools.deny` | Unique identifiers, at most 128 each | Optional | A tool in both lists is `tool_policy_contradiction`; steps may only narrow the ceiling |
332
342
  | `defaultRepositoryAccess` | `none`, `read`, or `write` | Optional; the ceiling is `none` when absent | Loader: access ceiling for repositories not listed in `repositories` |
333
343
  | `repositories` | Map of repository ID to `none`, `read`, or `write`; at most 64 | Optional | Loader: per-repository access ceiling; IDs must be declared (`repository_unknown`) |
@@ -340,80 +350,24 @@ reference. Schema: `schemas/agent.schema.json`; semantic checks in
340
350
  | `session.maxIdleMs` | Integer, 0 to 31,536,000,000 | Optional | Not read by any code path yet |
341
351
 
342
352
  Tool presets are names checked against a registered list. The live one-shot
343
- producer launches each harness with a fixed argument set and does not
344
- translate `tools` into harness flags. A read-only step uses the read-only set
345
- (`READ_ONLY_ONESHOT_ARGS` in `plugins/kxm/src/harness.ts`). A step with `write`
346
- access uses the audited writer set (`WRITER_ONESHOT_ARGS`), which exists only
347
- for `pi` (`-a` with extensions, skills, prompt templates and sessions off) and
348
- `grok` (`--always-approve` with subagents and web search off). Both approve
349
- every tool call, shell commands included, and neither confines the process to
350
- the checkout. A live write step on any other harness is handed off; see
353
+ producer launches the harness named by the selected model file, with a fixed
354
+ argument set, and does not translate `tools` into harness flags. A read-only
355
+ step uses the read-only set (`READ_ONLY_ONESHOT_ARGS` in
356
+ `plugins/kxm/src/harness.ts`). A step with `write` access uses the audited
357
+ writer set (`WRITER_ONESHOT_ARGS`), which exists only for `pi` (`-a` with
358
+ extensions, skills, prompt templates and sessions off) and `grok`
359
+ (`--always-approve` with subagents and web search off). Both approve every
360
+ tool call, shell commands included, and neither confines the process to the
361
+ checkout. A live write step on any other harness is handed off; see
351
362
  [Steps the Runtime does not execute yet](#steps-the-runtime-does-not-execute-yet).
352
363
 
353
- ### Model selectors
354
-
355
- A selector has exactly one of three shapes (`common.schema.json#/$defs/modelSelector`):
356
-
357
- | Shape | Fields | Resolves to |
358
- |---|---|---|
359
- | Direct | `provider` (identifier), `model` (1 to 200 characters) | That provider and model. The route string is `provider/model`, for example `openrouter/qwen/qwen3-coder-plus`. |
360
- | Profile | `profile` (identifier) | `.kxm/models/<profile>.yaml` (`model_profile_unknown` if missing) |
361
- | Tag | `tag` (identifier), optional `capabilities` (identifiers) | Every profile carrying the tag and all listed capabilities (`model_tag_unresolved` if none) |
362
-
363
- For live dispatch, only the direct shape works. The Runtime reads the agent's
364
- `model.provider` and `model.model` and joins them into the route string; a
365
- profile or tag selector validates at load time but live dispatch refuses the
366
- step with `producer_route_unsupported: invalid model declaration`. An agent
367
- with no model is refused too, except that an agent named `implementer`
368
- without a model falls back to `xai/grok-4.6`.
369
-
370
- ### Harness and model pairing
371
-
372
- The loader checks that the agent's harness can host its model, using
373
- `validateHarnessModelPair` in `plugins/kxm/src/harness.ts`. It applies this
374
- check only to models reached through a profile or tag selector. A direct
375
- `{provider, model}` selector is not checked at load time; the live producer
376
- checks it when it probes the harness before dispatch.
377
-
378
- | Harness | Accepts |
379
- |---|---|
380
- | `claude` | Provider `anthropic`; rejects model IDs starting with `gpt-`, `o1-`, `o3-`, `grok-`, `gemini-`, `kimi-`, `moonshot-`, `deepseek-`, or `qwen-` |
381
- | `codex` | Provider `openai`; rejects `claude-`, `fable-`, `grok-`, `gemini-`, `kimi-`, `moonshot-`, `deepseek-`, and `qwen-` models |
382
- | `grok` | Provider `xai` and `grok-` models |
383
- | `agy` | Provider `google` and `gemini-` models |
384
- | `kimi` | Provider `moonshot` and `kimi`, `moonshot`, or `kimi-for-coding` models |
385
- | `deepseek` | Provider `deepseek` and `deepseek-` models |
386
- | `pi` | Refuses a native vendor's model named directly, through the vendor's Pi provider or behind an aggregator, except `antigravity/gemini-…` (`pi_native_impersonation_blocked`); see [the brake](harness-routing.md#what-the-brake-refuses) |
387
-
388
- A mismatch is reported as `harness_unhosted_model`. Which route to choose for a
389
- model that more than one harness can reach is covered in
390
- [Harness routing](harness-routing.md).
391
-
392
- ### Live dispatch requirements
393
-
394
- Before a live attempt, the Runtime (`resolveProducerRoute` in
395
- `plugins/kxm/src/engine.ts`) requires all of the following. A failure hands the
396
- run off with `step_unsupported` and a `producer_route_unsupported` detail.
397
-
398
- 1. The agent declares a direct `{provider, model}` selector (see above).
399
- 2. `provider/model` is listed in `.kxm/routes.yaml` `admitted` and not in
400
- `disabled`.
401
- 3. If `.kxm/roles/<role>.yaml` exists, its roster contains exactly
402
- `provider/model`. The role is the agent ID, except that agent
403
- `implementer` maps to role `writer`.
404
-
405
- Example (validated with `kxm init --json`):
364
+ Example (the shape `kxm init` writes for `implementer`):
406
365
 
407
366
  ```yaml
408
- # .kxm/agents/implementer.yaml — the filename is the agent id.
367
+ # .kxm/agents/implementer.yaml. The filename is the agent id.
409
368
  schema: kxm.agent.v1
410
369
  purpose: Implement the approved change within the declared repository scope.
411
- instructions: Keep changes inside the files named by the approved plan.
412
- harness: grok # pi | claude | codex | grok | agy | kimi | deepseek
413
- model: # direct selector: provider + model
414
- provider: xai
415
- model: grok-4.6
416
- executor: local # local | ssh | exe-dev
370
+ role: writer # .kxm/roles/writer.yaml -> .kxm/models/<route>.yaml
417
371
  tools:
418
372
  preset: workspace-writer # coordinator | read-only | workspace-writer | tests-writer
419
373
  allow: [read, edit, write, bash]
@@ -421,47 +375,26 @@ tools:
421
375
  defaultRepositoryAccess: none # ceiling for repositories not listed below
422
376
  repositories:
423
377
  control: write
424
- api: write
425
378
  secrets:
426
379
  - ref: npm-token # secret reference name
427
380
  as: NPM_TOKEN # environment variable name inside the attempt
428
381
  required: false
429
382
  network: provider-only # none | provider-only | restricted | host
430
383
  resultSchema: kxm.assignment-result.v1
431
- session:
432
- reuse: compatible-run-scope
433
- maxIdleMs: 1800000
434
384
  ```
435
385
 
436
- A critic that uses a profile selector:
437
-
438
- ```yaml
439
- schema: kxm.agent.v1
440
- purpose: Architecture critic for the approved change.
441
- harness: claude
442
- model:
443
- profile: critic-claude # profile selector: .kxm/models/critic-claude.yaml
444
- tools:
445
- preset: read-only
446
- defaultRepositoryAccess: read
447
- network: provider-only
448
- resultSchema: kxm.assignment-result.v1
449
- ```
386
+ `kxm init` creates `coordinator` (`role: planner`) and `implementer`
387
+ (`role: writer`) without `harness` or `model`, and admits `anthropic/fable`
388
+ and `xai/grok-4.6` in `.kxm/routes.yaml`. An interactive `kxm init` can add
389
+ workflow-guide agents for reviewed pairs whose harness is authenticated, and
390
+ admits their selectors too, but skips Google guide candidates because the
391
+ Runtime cannot reach the `antigravity` Pi provider yet. `kxm run` and the
392
+ Runtime read the agents. `kxm trust` diffs them.
450
393
 
451
- Error codes: `executor_unknown`, `harness_unknown`, `tool_preset_unknown`,
452
- `tool_policy_contradiction`, `repository_unknown`, `model_profile_unknown`,
453
- `model_tag_unresolved`, `harness_unhosted_model`,
454
- `pi_native_impersonation_blocked`, the path codes under
455
- [Rules shared by the project bundle](#rules-shared-by-the-project-bundle), and
456
- `role_roster_conflicts_with_agent` (see [Roles](#kxmrolesroleyaml-kxmrolev2)).
457
-
458
- Commands: `kxm init` creates `coordinator` (`claude`, `anthropic/fable`) and
459
- `implementer` (`grok`, `xai/grok-4.6`) and admits both selectors in
460
- `.kxm/routes.yaml`; an interactive `kxm init` can add workflow-guide agents for
461
- reviewed harness/model pairs whose harness is authenticated, and admits their
462
- selectors too, but skips Google guide candidates because the Runtime cannot
463
- reach the `antigravity` Pi provider yet; `kxm run` and the Runtime read them;
464
- `kxm trust` diffs them.
394
+ Error codes: `retired_agent_routing_fields`, `executor_unknown`,
395
+ `tool_preset_unknown`, `tool_policy_contradiction`, `repository_unknown`, and
396
+ the path codes under
397
+ [Rules shared by the project bundle](#rules-shared-by-the-project-bundle).
465
398
 
466
399
  ## `.kxm/models/<id>.yaml` (`kxm.model.v2`)
467
400
 
@@ -500,11 +433,11 @@ status: admitted
500
433
  permissions:
501
434
  - edit
502
435
  origin:
503
- source: .kxm/roster.yaml
504
- sha256: b2bd628604e8fec5afdff2a1f2c104ed14ff785686bb1f38272bc972a310c806
436
+ source: docs/reference/harness-routing.md
437
+ sha256: 321c821ed6dbce7b2e98309fdc29387671577049619b6979c685a7ba60c63337
505
438
  ```
506
439
 
507
- Commands: `kxm init`, `kxm run`, and `kxm trust` load these files. `kxm role modify --add-route` refuses a route id that has no file here (exit 1). Dispatch membership for a role reads `harness`, `model`, and `vendor`. The developer assignment runner still reads `.kxm/roster.yaml` until P2.
440
+ Commands: `kxm init`, `kxm run`, and `kxm trust` load these files. `kxm role modify --add-route` refuses a route id that has no file here (exit 1). Dispatch membership for a role reads `harness`, `model`, and `vendor`. The developer assignment runner reads `.kxm/roles/*.yaml` and `.kxm/models/*.yaml` at `refs/remotes/origin/main`.
508
441
 
509
442
  ## `.kxm/workflows/<id>.yaml` (`kxm.workflow.v1`)
510
443
 
@@ -692,9 +625,8 @@ these, the run is handed off (`step_unsupported`, `gate_unsupported`, or
692
625
  - A live (non-simulated) step with `write` access to any repository when its
693
626
  agent's harness has no audited writer profile (only `pi` and `grok` have
694
627
  one), when `assignments.maximum` is above 1, when the project's
695
- `limits.maxConcurrentRuns` is above 1, or when `.kxm/roster.yaml` exists and
696
- is not a `kxm.developer-roster.v1` whose writer lineup admits that harness
697
- and model with `edit` permission. The checkout witness can attribute a
628
+ `limits.maxConcurrentRuns` is above 1, or when `.kxm/roles/writer.yaml` does not
629
+ admit that route with `edit` permission. The checkout witness can attribute a
698
630
  change only to one writer at a time.
699
631
  - Gate steps with `assignments.allowedAgents`, any assignment count or
700
632
  `maxAttemptsPerAssignment` other than 1, `distinctBy`,
@@ -962,12 +894,10 @@ not this registry.
962
894
  One role. The filename is the role id. `kxm config` validation checks the file
963
895
  against `schemas/role.schema.json`.
964
896
 
965
- `listRoleBindings` reads `roster[].route`. Dispatch treats `implementer` as
966
- `writer` and requires the agent selector to be one of the selectors named by
967
- those route files (`model`, `vendor/model`, or `harness/model`). The writer
968
- cross-check (`role_roster_conflicts_with_agent`) does the same comparison
969
- against the `implementer` agent's model, or the `writer` agent when there is
970
- no implementer. `kxm role` reads and writes these files. A file without
897
+ `listRoleBindings` reads `roster[].route`. An agent binds `role`, and dispatch
898
+ resolves harness, model, and effort from that role's roster and the matching
899
+ `.kxm/models/<route-id>.yaml` (`model`, `vendor/model`, or `harness/model`).
900
+ `kxm role` reads and writes these files. A file without
971
901
  `schema: kxm.role.v2` is skipped by `kxm role`. A local file overrides a
972
902
  global one with the same id.
973
903
 
@@ -989,9 +919,10 @@ global one with the same id.
989
919
  | `policy.vendorIndependenceRequired`, `policy.maxTransitions`, `policy.requiresGateVerification` | Boolean, or a positive integer for `maxTransitions` | Optional | Recorded on the role |
990
920
 
991
921
  Roster order is preference. `kxm role list` shows the first route as the
992
- primary. The Runtime checks membership. The model that runs still comes from
993
- the agent file. The developer assignment runner still reads `.kxm/roster.yaml`
994
- until P2.
922
+ primary. The Runtime checks membership. Harness, model, and effort resolve
923
+ from the agent's `role` roster and that route's `.kxm/models/<route-id>.yaml`,
924
+ never from the agent file. The developer assignment runner reads
925
+ `.kxm/roles/*.yaml` and `.kxm/models/*.yaml` at `refs/remotes/origin/main`.
995
926
 
996
927
  ```yaml
997
928
  schema: kxm.role.v2
@@ -1026,11 +957,10 @@ unknown keys are ignored).
1026
957
  | `schema` | `kxm.routes.v2` | Required | Anything else fails with `invalid .kxm/routes.yaml` |
1027
958
  | `admitted` | Array of route strings | Required | Runtime route check; `kxm routes list`, `kxm routes count`; `kxm models` |
1028
959
  | `disabled` | Array of route strings | Optional, `[]` | Runtime: a disabled route is refused even if admitted |
1029
- | `roles` | Map of name to route strings | Optional, `{}` | Not read by any code path yet; shown only by `kxm routes list --json`, and preserved on rewrite. Role rosters live in `.kxm/roles/` |
1030
960
  | `updatedAt` | ISO timestamp string | Optional | Rewritten by every CLI change |
1031
961
 
1032
- A route string is exactly the agent's `model.provider`, a slash, and
1033
- `model.model`: `xai/grok-4.6`, `anthropic/fable`,
962
+ A route string is derived from the selected model file's `vendor` and
963
+ `model`, joined by a slash: `xai/grok-4.6`, `anthropic/fable`,
1034
964
  `openrouter/qwen/qwen3-coder-plus`. When the file is missing, nothing is
1035
965
  admitted and every live attempt is refused (`producer_route_unsupported`, or
1036
966
  `producer_route_not_admitted` from the live producer). A leftover
@@ -1054,31 +984,29 @@ admitted:
1054
984
  - xai/grok-4.6
1055
985
  - openrouter/qwen/qwen3-coder-plus
1056
986
  disabled: []
1057
- roles:
1058
- implementer:
1059
- - xai/grok-4.6
1060
987
  ```
1061
988
 
1062
- Validated with `kxm routes list --json` and `kxm routes count --json`.
989
+ Validated with `kxm routes list --json` and `kxm routes count --json`. `policy` has `admitted` and `disabled`. Membership comes from `.kxm/roles/*.yaml`, not from this file.
1063
990
 
1064
991
  Commands: `kxm routes list|count|admit|disable` (`--dry-run` supported for
1065
992
  changes), `kxm models` (interactive), the Runtime, and the live producer.
1066
993
  Route changes are not part of `configRevision` and `kxm trust check` does not
1067
994
  report them; review them in the pull request diff.
1068
995
 
1069
- ## `.kxm/roster.yaml` (`kxm.developer-roster.v1`)
996
+ ## Developer assignment policy
1070
997
 
1071
- The developer roster for `scripts/assignment-run.mjs` (the `just` assignment
1072
- recipes; see [Assignment runner](../contributing/assignment-runner.md)). It applies to the KXM
1073
- source repository itself: the loader in `scripts/roster-policy.mjs` reads the
1074
- copy committed at `HEAD` of the repository that contains the script, and
1075
- refuses unless the worktree is clean, `HEAD` is an ancestor of
1076
- `origin/main`, and the working file is byte-identical to the committed one.
1077
- It has no dispatch authority in the project Runtime.
998
+ The developer roster for `scripts/assignment-run.mjs` (`kxm assign`; see
999
+ [Assignment runner](../contributing/assignment-runner.md)). It applies to the KXM
1000
+ source repository itself: the loader in `scripts/roster-policy.mjs` reads
1001
+ `.kxm/roles/*.yaml` and `.kxm/models/*.yaml` committed at `HEAD` of the
1002
+ repository that contains the script, and refuses unless the worktree is clean,
1003
+ `HEAD` is an ancestor of `origin/main`, and each working file is byte-identical
1004
+ to the committed one. It has no dispatch authority in the project Runtime.
1005
+ The assembled object has `routes`, `lineup`, `required_critics`, and `model_origins`.
1078
1006
 
1079
1007
  | Field | Type and allowed values | Notes |
1080
1008
  |---|---|---|
1081
- | `schema` | `kxm.developer-roster.v1` | The five top-level keys are all required and no others are allowed |
1009
+ | source files | `.kxm/roles/*.yaml` (`kxm.role.v2`), `.kxm/models/*.yaml` (`kxm.model.v2`) | The loader assembles the object below from these files |
1082
1010
  | `routes.<id>` | Route ID matching `^[a-z0-9]+(?:-[a-z0-9]+)*$` | At least one route |
1083
1011
  | `routes.<id>.harness` | `grok`, `agy`, `claude`, `codex`, or `pi` | Other harnesses are refused (`unsupported harness`) |
1084
1012
  | `routes.<id>.model` | Token without whitespace | Native harnesses: a bare model ID. Pi: `openrouter/<vendor>/<model>`, `nous-portal/<vendor>/<model>`, or `antigravity/gemini-<id>` |
@@ -1098,8 +1026,7 @@ only`, `Pi critic/planner cannot edit`, `writer and critics must have
1098
1026
  independent vendors`, and `retired .kxm/roster.json present`.
1099
1027
 
1100
1028
  ```yaml
1101
- # .kxm/roster.yaml — developer roster policy for scripts/assignment-run.mjs.
1102
- schema: kxm.developer-roster.v1
1029
+ # Assembled by scripts/roster-policy.mjs from .kxm/roles/*.yaml and .kxm/models/*.yaml.
1103
1030
  routes:
1104
1031
  grok-native: # route id: lowercase words joined by "-"
1105
1032
  harness: grok
@@ -1130,7 +1057,7 @@ routes:
1130
1057
  permissions: [read-only]
1131
1058
  status: admitted
1132
1059
  lineup: # routes admitted for each role
1133
- writer: [grok-native, qwen-openrouter-pi]
1060
+ writer: [grok-native, qwen-openrouter-pi, gemini-agy]
1134
1061
  planner: [fable-claude]
1135
1062
  reviewer-arch: [fable-claude]
1136
1063
  reviewer-cli: [sol-codex]
@@ -1688,7 +1615,7 @@ Everything under `.kxm/` at the project root falls into one of three groups.
1688
1615
  | Path | Group | Written by |
1689
1616
  |---|---|---|
1690
1617
  | `project.yaml`, `repo/`, `project/env.yaml`, `agents/`, `models/*.yaml` (except `inventory.yaml`), `workflows/`, `gates.yaml`, `template-provenance.yaml` | Tracked configuration (the bundle) | You and `kxm init` |
1691
- | `roles/`, `routes.yaml`, `prices.yaml`, `modes.yaml`, `roster.yaml` | Tracked configuration outside the bundle | You and their commands |
1618
+ | `roles/`, `routes.yaml`, `prices.yaml`, `modes.yaml`, `the role files` | Tracked configuration outside the bundle | You and their commands |
1692
1619
  | `config.yaml` | Tracked if the project wants shared preferences; otherwise ignore it | `kxm config set` |
1693
1620
  | `memory/`, `skills/`, `candidates/`, `goals/` | Tracked durable records | Their commands |
1694
1621
  | `models/inventory.yaml` | Generated; track it if you want a reviewed snapshot | `kxm models inventory-refresh` |
@@ -1883,7 +1810,6 @@ updatedAt: '2026-09-23T00:00:00.000Z'
1883
1810
  admitted:
1884
1811
  - xai/grok-4.6
1885
1812
  disabled: []
1886
- roles: {}
1887
1813
  ```
1888
1814
 
1889
1815
  Why each piece is there:
@@ -1899,8 +1825,8 @@ Why each piece is there:
1899
1825
  without a writable repository.
1900
1826
  - The workflow omits `limits.maxAgentTimeMs`; with it, the Runtime refuses to
1901
1827
  drive the run.
1902
- - There is no `.kxm/roles/writer.yaml`. If you add one, it must list
1903
- `xai/grok-4.6`, or the loader reports `role_roster_conflicts_with_agent`.
1828
+ - `kxm init` writes `.kxm/roles/writer.yaml`. The role file is the writer
1829
+ roster. An agent model is not checked against it.
1904
1830
 
1905
1831
  Validate and run it:
1906
1832