@kontextmind/kxm 0.7.91 → 0.7.93

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 (92) hide show
  1. package/.claude-plugin/marketplace.json +1 -1
  2. package/.kxm/workflows/default.yaml +1 -1
  3. package/CHANGELOG.md +212 -0
  4. package/README.md +3 -0
  5. package/docs/README.md +3 -0
  6. package/docs/agent-skills.md +123 -60
  7. package/docs/architecture.md +5 -2
  8. package/docs/cli-reference.md +3527 -0
  9. package/docs/config-reference.md +1943 -0
  10. package/docs/configuration.md +30 -4
  11. package/docs/continuous-improvement.md +122 -10
  12. package/docs/contracts/routing.md +95 -11
  13. package/docs/harness-routing.md +616 -0
  14. package/docs/kxm-handbook.md +106 -19
  15. package/docs/templates/README.md +1 -1
  16. package/docs/test-matrix.md +12 -6
  17. package/docs/troubleshooting.md +2 -2
  18. package/examples/project/.kxm/workflows/fix.yaml +1 -1
  19. package/examples/project/.kxm/workflows/improve.yaml +1 -1
  20. package/package.json +1 -1
  21. package/plugins/kxm/.claude-plugin/plugin.json +9 -10
  22. package/plugins/kxm/README.md +238 -56
  23. package/plugins/kxm/dist/claude-hook.js +10083 -0
  24. package/plugins/kxm/dist/cli.js +2487 -1848
  25. package/plugins/kxm/dist/client.js +64 -0
  26. package/plugins/kxm/dist/core.js +102 -9
  27. package/plugins/kxm/dist/extension.js +210 -68
  28. package/plugins/kxm/dist/mcp-server.js +217 -40
  29. package/plugins/kxm/dist/runtime-supervisor.js +1628 -157
  30. package/plugins/kxm/dist/runtime.js +1874 -298
  31. package/plugins/kxm/dist/server.js +416 -82
  32. package/plugins/kxm/package.json +1 -1
  33. package/plugins/kxm/skills/hints.json +1 -1
  34. package/plugins/kxm/skills/kxm/SKILL.md +48 -24
  35. package/plugins/kxm/skills/kxm/references/protocol.md +3 -3
  36. package/plugins/kxm/skills/kxm-context-memory/SKILL.md +67 -21
  37. package/plugins/kxm/skills/kxm-definitions/SKILL.md +9 -0
  38. package/plugins/kxm/skills/kxm-harness-auth/SKILL.md +82 -16
  39. package/plugins/kxm/skills/kxm-harvest/SKILL.md +1 -1
  40. package/plugins/kxm/skills/kxm-hub-ops/SKILL.md +55 -27
  41. package/plugins/kxm/skills/kxm-insights/SKILL.md +1 -1
  42. package/plugins/kxm/skills/kxm-mind/SKILL.md +2 -2
  43. package/plugins/kxm/skills/{kxm-setup → kxm-mind-setup}/SKILL.md +4 -4
  44. package/plugins/kxm/skills/kxm-peer/SKILL.md +68 -93
  45. package/plugins/kxm/skills/kxm-project-setup/SKILL.md +156 -23
  46. package/plugins/kxm/skills/kxm-projects/SKILL.md +1 -1
  47. package/plugins/kxm/skills/kxm-protocol/SKILL.md +1 -1
  48. package/plugins/kxm/skills/kxm-query/SKILL.md +1 -1
  49. package/plugins/kxm/skills/kxm-routing-improve/SKILL.md +74 -15
  50. package/plugins/kxm/skills/kxm-runs/SKILL.md +46 -17
  51. package/plugins/kxm/skills/kxm-session/SKILL.md +64 -36
  52. package/plugins/kxm/skills/kxm-skill-lifecycle/SKILL.md +44 -15
  53. package/plugins/kxm/skills/kxm-tasks/SKILL.md +16 -4
  54. package/plugins/kxm/skills/kxm-triage/SKILL.md +1 -1
  55. package/plugins/kxm/skills/kxm-work/SKILL.md +1 -1
  56. package/plugins/kxm/skills/kxm-workflow/SKILL.md +60 -19
  57. package/plugins/kxm/src/arbiter.ts +67 -22
  58. package/plugins/kxm/src/autocomplete.ts +1 -1
  59. package/plugins/kxm/src/claude-hook.ts +192 -0
  60. package/plugins/kxm/src/cli/project.ts +11 -5
  61. package/plugins/kxm/src/cli/system.ts +85 -13
  62. package/plugins/kxm/src/cli/types.ts +4 -1
  63. package/plugins/kxm/src/cli/workflows.ts +18 -16
  64. package/plugins/kxm/src/cli.ts +23 -13
  65. package/plugins/kxm/src/client.ts +15 -4
  66. package/plugins/kxm/src/commands.ts +19 -9
  67. package/plugins/kxm/src/config.ts +42 -7
  68. package/plugins/kxm/src/context-packet.ts +14 -2
  69. package/plugins/kxm/src/context.ts +16 -5
  70. package/plugins/kxm/src/dispatch-context.ts +286 -0
  71. package/plugins/kxm/src/engine-plan.ts +40 -0
  72. package/plugins/kxm/src/engine.ts +138 -6
  73. package/plugins/kxm/src/hub-env.ts +17 -1
  74. package/plugins/kxm/src/hub.ts +92 -29
  75. package/plugins/kxm/src/improve-sources.ts +228 -0
  76. package/plugins/kxm/src/improve.ts +325 -140
  77. package/plugins/kxm/src/local-snapshot.ts +101 -42
  78. package/plugins/kxm/src/mcp-server.ts +129 -30
  79. package/plugins/kxm/src/memory.ts +43 -20
  80. package/plugins/kxm/src/project-config.ts +25 -0
  81. package/plugins/kxm/src/protocol.ts +11 -0
  82. package/plugins/kxm/src/relevance.ts +138 -0
  83. package/plugins/kxm/src/retrospective.ts +16 -10
  84. package/plugins/kxm/src/runtime-service.ts +8 -1
  85. package/plugins/kxm/src/runtime-supervisor.ts +16 -2
  86. package/plugins/kxm/src/session-token-hint.ts +17 -0
  87. package/plugins/kxm/src/suggest.ts +7 -7
  88. package/plugins/kxm/src/workflow-manager.ts +80 -78
  89. package/plugins/kxm/src/workflow.ts +202 -12
  90. package/scripts/build-runtime.mjs +7 -1
  91. package/scripts/check-generated.mjs +1 -0
  92. package/scripts/emit-codex-artifacts.mjs +1 -1
@@ -1,53 +1,81 @@
1
1
  ---
2
2
  name: kxm-session
3
- description: Set up a local KXM hub session brief — recent tasks/plans, status-line stats, and harness chrome. Use when starting a new session, running kxm init, kxm hub bind, or configuring a status bar. Local-only Runtime insights and SSH/remote hubs are after MVP.
3
+ description: Read KXM session and hub status with kxm session status and kxm hub view, create a session manifest with kxm session start, open kxm dash screens, and lay out workflows with kxm studio layout. Use at the start of a session, when asked what is in flight, or when opening the dashboard. kxm session brief, kxm session stop, and kxm studio serve are operator steps because they save a 24-hour session token, stop the hub, or open a listener.
4
4
  ---
5
5
 
6
- # KXM session (hub local)
6
+ # KXM session
7
7
 
8
- MVP is a **local hub** on this machine. Work is agents and workflows. Do not configure hub chrome for local-only init.
8
+ Start a session by reading status. The agent steps below write no token and
9
+ start no process.
9
10
 
10
- ## Tracks
11
+ ## Agent steps
11
12
 
12
- | Track | When | What this skill does |
13
+ | Command | Purpose | Options / arguments |
13
14
  |---|---|---|
14
- | **Local-only** | `kxm init` (project-only) | Skip hub start, skip session brief, skip status-line chrome |
15
- | **Hub local (MVP)** | `kxm hub bind <url>` | Bind this host to a running loopback hub; session brief + status line |
16
- | **SSH / HTTPS remote** | After MVP | Fail closed. Not available |
17
-
18
- In-harness Pi can attach to a local hub. Local Runtime workflow insights (`kxm dash` from the KXM event store) are after MVP.
19
-
20
- ## First run (hub local)
21
-
22
- ```text
23
- kxm init
24
- kxm hub start # other terminal
25
- kxm hub bind <url>
26
- kxm session brief
27
- ```
28
-
29
- Keep `.kxm/config` in Git; do not recopy templates onto an existing project. `KXM_SERVER_URL` still overrides a bound URL.
30
-
31
- ## Session brief
32
-
33
- `kxm session brief` reads the **local hub** SQLite (tasks = workflow runs, plans = journal `plan` rows). No message bodies. `--status` prints the status line for harnesses that have one.
15
+ | `kxm session status` | Session claims and recovery envelopes | `--json` |
16
+ | `kxm hub view` | Hub `/health` and `/ready`; exits 1 when the hub is down | `--json` |
17
+ | `kxm session start` | Create an agent/gate or workflow session manifest; launches nothing | `--id`, `--workflow`, `--mix`, `--json` |
18
+ | `kxm dash` | Live screens for agents, tasks, workflows, and plans | `--screen agents\|tasks\|workflows\|plans\|inbox\|procs\|spend` |
19
+ | `kxm studio layout [workflowPath]` | DAG, stepper, and swimlane layout JSON for a workflow | `--json` |
20
+
21
+ At the start of a session, run `kxm session status` and `kxm hub view`. In a
22
+ repository with no session token file both leave the user config directory
23
+ and the Git tree unchanged. `kxm session status` prints
24
+ `0 session claim(s), 0 recovery envelope(s)` on a fresh project, and
25
+ `kxm hub view` prints `hub health=false ready=false · loopback hub` and exits 1
26
+ while the hub is down.
27
+
28
+ ## Hub binding
29
+
30
+ | Track | When | What to do |
31
+ |---|---|---|
32
+ | Local only | `kxm init` without a hub | Work with runs and Git memory; skip hub status chrome |
33
+ | Local hub | The user ran `kxm hub bind http://127.0.0.1:7331` | Read status with `kxm hub view` |
34
+ | Remote hub | The user ran `kxm hub bind <https-url>` | Binds when a credential resolves; otherwise it fails closed with `hub_bind_unauthenticated` |
34
35
 
35
- Interactive **Pi TUI**: on `startup` / `/new` / `/fork`, the kxm extension offers recent tasks/plans and paints the footer + widget (including a `ship` line: dirty vs local commits vs PR after CI). `/kxm` reopens the picker. `/kxm status` refreshes chrome only. `/kxm hub` is hub view. Workers, RPC, and print mode never prompt. `KXM_SESSION_BRIEF=off` disables the picker only.
36
+ `KXM_SERVER_URL` overrides a bound URL. Binding is an operator step owned by
37
+ `kxm-hub-ops`.
36
38
 
37
39
  ## Harness support (fail closed)
38
40
 
39
- | Harness | Picker | Status line | Setup |
41
+ | Harness | Picker | Status line | First reply |
40
42
  |---|---|---|---|
41
- | Pi TUI | Yes, extension | Yes, `setStatus` + widget | Load the kxm extension (default from the package) |
42
- | Pi RPC / worker | No | Status only if UI helpers exist; never a picker | Skip |
43
- | Claude Code | No coded picker | Only if the operator points `statusLine` at `kxm session brief --status` | Do not invent chrome |
44
- | Codex, Kimi, Gemini, DeepSeek | Unknown | None unless that CLI documents a status command | Skip chrome; first reply may run `kxm session brief` |
43
+ | Pi TUI | Yes, kxm extension | Yes, `setStatus` plus widget | Load the kxm extension (default from the package) |
44
+ | Pi RPC / worker | No | Only if UI helpers exist; never a picker | Skip chrome |
45
+ | Claude Code | No picker | The plugin's SessionStart hook adds a short KXM brief only in projects with `.kxm`; a statusLine is optional and an operator step | Run `kxm session status` and `kxm hub view` |
46
+ | Codex, Kimi, Gemini, DeepSeek | Unknown | None unless that CLI documents a status command | Run `kxm session status` and `kxm hub view` |
47
+
48
+ Interactive Pi TUI: on `startup`, `/new`, and `/fork`, the kxm extension offers
49
+ recent tasks and plans and paints the footer and widget, including a `ship`
50
+ line (dirty tree, local commits, PR after CI). `/kxm` reopens the picker,
51
+ `/kxm status` refreshes chrome only, and `/kxm hub` is the hub view. Workers,
52
+ RPC, and print mode never prompt. `KXM_SESSION_BRIEF=off` disables the picker
53
+ only.
45
54
 
46
55
  This skill never grants tools or permissions.
47
56
 
48
- ## Operator loop
57
+ ## Operator steps
58
+
59
+ Ask the user to run these in their own terminal.
49
60
 
50
- 1. Confirm hub local: `kxm hub view`.
51
- 2. `kxm session brief` or Pi `/kxm`.
52
- 3. Pick a task/plan or start fresh.
53
- 4. Live peek remains `kxm dash` (tasks / plans tabs).
61
+ Until kxm session brief stops minting tokens, running it saves a 24-hour
62
+ operator session token; when that token expires every kxm_* tool fails with
63
+ tool_policy_denied. Its plain output prints a Session token line, so never
64
+ paste it into a conversation.
65
+
66
+ | Command | Purpose | Options |
67
+ |---|---|---|
68
+ | `kxm session brief` | Recent hub tasks (workflow runs) and plans (journal `plan` rows) from the local hub store; every form saves the session token | `--status` (status line only), `--json` |
69
+ | `kxm session token --status` | Report the active session token | `--json` |
70
+ | `kxm session token --clear` | Delete the session token file | `--json` |
71
+ | `kxm session stop` | Request managed hub and worker shutdown | `--wait-ms <ms>` |
72
+ | `kxm studio serve` | Start the Web Studio listener (default `http://localhost:4242`) | `--port`, `--host`, `--token` |
73
+
74
+ - `kxm session token --status` reports
75
+ `No active session token found in env or disk` for an expired or malformed
76
+ file, even though that file still blocks every kxm_* tool. `--clear` is the
77
+ fix for such a file.
78
+ - Claude Code statusLine: the user may point `statusLine` at
79
+ `kxm session brief --status`, which saves the same 24-hour token.
80
+ - Pi operator loop: `kxm hub view`, then `kxm session brief` or Pi `/kxm`, pick
81
+ a task or plan or start fresh, and keep `kxm dash` open for a live view.
@@ -1,31 +1,60 @@
1
1
  ---
2
2
  name: kxm-skill-lifecycle
3
- description: Govern candidate/evaluate/promote/quarantine/reject/verify lifecycle; distinguish this from bundled skills.
3
+ description: Govern KXM skill candidates. Create one from verified runs, journal entries, receipts, or a kxm improve skill candidate, record static-review, sandbox, functional and safety evaluations, and list or verify pinned hashes, while promotion and rejection stay a non-author decision. Use when turning a repeated practice into a reusable skill. Not for editing the bundled kxm-* plugin skills.
4
4
  ---
5
5
 
6
- # KXM Governed Skills
6
+ # KXM governed skills
7
7
 
8
- This is the governed candidate lifecycle, not the bundled `plugins/kxm/skills`
9
- suite. Bundled skills are authored in Git and mirrored to `.agents/skills`.
10
- Do not invent `candidate`, `quarantine`, `rollback`, `validate`, or `get`.
8
+ This is the governed candidate lifecycle under `.kxm/skills/`, not the bundled
9
+ `plugins/kxm/skills` suite. Bundled skills are authored in Git and mirrored to
10
+ `.agents/skills`. Do not invent `candidate`, `quarantine`, `rollback`,
11
+ `validate`, or `get` verbs.
11
12
 
12
- ## Commands
13
+ ## Agent steps
13
14
 
14
15
  | Command | Purpose | Options / arguments |
15
16
  |---|---|---|
16
- | `kxm skills create` | Submit a candidate from verified episodes | `--file`, `--name`, `--description`, `--created-by`, `--run`, `--journal`, `--receipt`, `--harness`, `--models`, `--supersedes` |
17
+ | `kxm skills create` | Submit a candidate from verified episodes | Required `--file`, `--name`, `--created-by`, `--harness`, `--models`; optional `--description`, `--run`, `--journal`, `--receipt`, `--supersedes` |
17
18
  | `kxm skills evaluate <skillId>` | Record a protected evaluation | `--kind static-review\|sandbox\|functional\|safety\|optimization`, `--evaluator`, `--fail`, `--score`, `--details` |
18
- | `kxm skills promote <skillId>` | Promote a candidate that passed required evaluations | `--decided-by`, `--evidence`, `--reason` |
19
- | `kxm skills reject <skillId>` | Reject a candidate; history is retained | `--decided-by`, `--reason` |
20
- | `kxm skills list` | List skills by state | `--state candidate\|promoted\|quarantined\|rejected` |
21
- | `kxm skills verify <skillId>` | Verify pinned content hash | `--state candidate\|promoted\|quarantined\|rejected` |
19
+ | `kxm skills list` | List skills by state (default `promoted`) | `--state candidate\|promoted\|quarantined\|rejected` |
20
+ | `kxm skills verify <skillId>` | Verify the pinned content hash | `--state candidate\|promoted\|quarantined\|rejected` |
22
21
 
23
22
  ```bash
23
+ kxm skills create --file /tmp/retry-backoff/SKILL.md --name retry-backoff --created-by agent_writer --harness claude-code --models claude/fable --run run_1 --receipt receipt:run_1/verify --dry-run --json
24
24
  kxm skills list --state candidate --json
25
25
  kxm skills evaluate skill_123 --kind static-review --evaluator eval-1.0.0 --json
26
- kxm skills promote skill_123 --decided-by agent_reviewer --evidence receipt:run_1/verify --json
27
- kxm skills verify skill_123 --state promoted --json
26
+ kxm skills verify skill_123 --state candidate --json
28
27
  ```
29
28
 
30
- Promotion requires durable evidence and a non-author decision. A candidate
31
- cannot grant tools, skip review, or auto-promote bundled skills.
29
+ A failed `functional` or `safety` evaluation quarantines the candidate
30
+ automatically. Promotion needs passing `static-review`, `sandbox`,
31
+ `functional`, and `safety` evaluations.
32
+
33
+ ## From an improvement candidate
34
+
35
+ `kxm improve report` proposes a `skill` candidate as a diff that adds a
36
+ `SKILL.md` draft (`kxm-routing-improve`).
37
+
38
+ 1. Review the proposed `SKILL.md` draft with the user and rewrite it into
39
+ real instructions.
40
+ 2. Save it outside `.kxm/skills`, for example under `/tmp/<name>/SKILL.md`.
41
+ 3. Run
42
+ `kxm skills create --file <draft> --name <name> --created-by <id> --harness claude-code --models <models> --run <ids> --receipt <refs>`.
43
+ 4. Record the evaluations above.
44
+
45
+ Never apply the candidate diff into `.kxm/skills`. `kxm skills list --state candidate`
46
+ ignores a hand-written `.kxm/skills/candidates/<slug>/SKILL.md`; only
47
+ `kxm skills create` registers a candidate with its pinned hash and history.
48
+
49
+ ## Operator steps
50
+
51
+ Promotion and rejection belong to a non-author: the user or another
52
+ authorized reviewer, never the agent that created the candidate.
53
+
54
+ | Command | Purpose | Options / arguments |
55
+ |---|---|---|
56
+ | `kxm skills promote <skillId>` | Promote a candidate that passed the required evaluations; writes a patch for review | `--decided-by` (must differ from the author), `--evidence`, `--reason` |
57
+ | `kxm skills reject <skillId>` | Reject a candidate; history is retained | `--decided-by`, `--reason` |
58
+
59
+ Promotion requires durable evidence. A candidate cannot grant tools, skip
60
+ review, or auto-promote bundled skills.
@@ -1,9 +1,9 @@
1
1
  ---
2
2
  name: kxm-tasks
3
- description: Recommend workflows and manage goals/tasks with explicit SCM/tracker boundaries.
3
+ description: Recommend workflows and manage goals and tasks with explicit SCM and tracker boundaries. Use when asked what workflow fits, to plan work as goals and tasks, or to sync with GitHub or Jira. Run a kxm suggest workflow ID only after kxm workflow definitions lists it.
4
4
  ---
5
5
 
6
- # KXM Suggest, Goals, and Tasks
6
+ # KXM suggest, goals, and tasks
7
7
 
8
8
  Bind SCM and issue trackers from this repo's conventions. Implemented today:
9
9
  GitHub and Jira. An unimplemented tracker fails closed. Do not invent
@@ -16,7 +16,7 @@ GitHub and Jira. An unimplemented tracker fails closed. Do not invent
16
16
  | `kxm suggest <prompt...>` | Recommend workflow, area, roles, and skills | `--json` |
17
17
  | `kxm goal create <title>` | Create a project goal | `--area`, `--metric`, `--target-date` |
18
18
  | `kxm goal list` | List project goals | `--json` |
19
- | `kxm task create <title>` | Create a task | `--goal`, `--objective`, `--workflow`, `--tracker github\|jira`, `--issue` |
19
+ | `kxm task create <title>` | Create a task | `--goal`, `--objective`, `--workflow`, `--tracker github\|jira`, `--issue <number-or-key>` |
20
20
  | `kxm task list` | List project tasks | `--goal`, `--status todo\|in_progress\|blocked\|in_review\|done` |
21
21
  | `kxm task get <taskId>` | Task details and linked workflow status | `--json` |
22
22
  | `kxm task run <taskId>` | Launch a workflow run driven by this task | `--json` |
@@ -24,10 +24,22 @@ GitHub and Jira. An unimplemented tracker fails closed. Do not invent
24
24
 
25
25
  ```bash
26
26
  kxm suggest "implement trusted roster policy brakes" --json
27
+ kxm workflow definitions
27
28
  kxm goal create "Land the skills suite" --area software-engineering --json
28
29
  kxm task create "Repair trust loader" --goal goal_1 --tracker github --issue 127 --json
29
30
  kxm task list --status in_progress --json
30
31
  kxm task sync task_1 --json
31
32
  ```
32
33
 
33
- Do not silently use GitHub when the operator picked an unimplemented tracker.
34
+ ## Suggested workflows
35
+
36
+ `kxm suggest` picks its workflow ID from a built-in catalog, and that ID may
37
+ not exist in this project. Before running the suggested `kxm run` command,
38
+ confirm that `kxm workflow definitions` lists the ID; otherwise `kxm run`
39
+ fails with `run_workflow_unknown`. Pick a listed workflow instead, or write
40
+ one with the user (`kxm-project-setup`).
41
+
42
+ `--issue` on `kxm task create` is a tracker issue number or Jira key, not a
43
+ credential. In this build `kxm task sync` marks only the local task record
44
+ synced; it does not contact GitHub or Jira. Do not silently use GitHub when
45
+ the operator picked an unimplemented tracker.
@@ -1,6 +1,6 @@
1
1
  ---
2
2
  name: kxm-triage
3
- description: Work the KontextMind review queue. Use when asked to triage the mind, review drafts, promote or skip learnings, resolve drift/contradiction/gap/loop items, or call km_review.
3
+ description: KontextMind knowledge plane only, the separate kontext CLI and km_ tools, not KXM or this plugin's kxm_* tools. Works the KontextMind review queue with km_review. Use only when the user names KontextMind, the kontext CLI, or a km_ tool.
4
4
  license: Apache-2.0
5
5
  compatibility: Any agent that can run a shell or MCP client. No vendor-only tools.
6
6
  metadata:
@@ -1,6 +1,6 @@
1
1
  ---
2
2
  name: kxm-work
3
- description: KontextMind work context — tracker read-through, checkpoints, and claimable handoffs. Use when asked what is in flight, what to pick up, checkpoints, handoffs, km_work_current, km_work_update, km_handoff_save, or km_handoff_load.
3
+ description: KontextMind knowledge plane only, the separate kontext CLI and km_ tools, not KXM or this plugin's kxm_* tools. Reads and updates KontextMind tracker work state with the km_work and km_handoff tools. Use only when the user names KontextMind, the kontext CLI, or a km_ tool.
4
4
  license: Apache-2.0
5
5
  compatibility: Any agent that can run a shell or MCP client. No vendor-only tools.
6
6
  metadata:
@@ -1,45 +1,86 @@
1
1
  ---
2
2
  name: kxm-workflow
3
- description: Operate webhook workflows, waits/signals, evidence checkpoints, provenance, and deterministic gates.
3
+ description: Work inside a durable KXM workflow run. Read it, record plan, decision, contradiction, error and lesson journal entries, pass stage checkpoints with keyed evidence and peer evidence refs, wait for signed CI or review callbacks, start webhook workflows, and export retrospectives (kxm_workflow_get, kxm_workflow_record, kxm_workflow_checkpoint, kxm_workflow_wait). Use when a workflow run ID is involved or the user asks to checkpoint, gate, or wait on CI.
4
4
  ---
5
5
 
6
- # KXM Workflow and Gates
6
+ # KXM workflow and gates
7
7
 
8
- Use the current CLI. Peer-reply requirements need durable replied message IDs
9
- in `--evidence-refs`. Caller-authored text never satisfies peer quorum.
8
+ Durable workflow runs live on the hub and start from signed webhooks. Read the
9
+ run first (`kxm_workflow_get` or `kxm workflow get <runId>`), record material
10
+ plans, decisions, contradictions, errors, and lessons as you work, and pass
11
+ every stage checkpoint before replying.
12
+
13
+ Peer-reply requirements need durable replied message IDs in
14
+ `--evidence-refs` (MCP `evidenceRefs`). Caller-authored text never satisfies
15
+ peer quorum.
16
+
17
+ `kxm workflow list` and `kxm workflow get` show hub webhook runs. Runs created
18
+ with `kxm run` are Runtime runs; inspect them with `kxm runs list`
19
+ (`kxm-runs`).
10
20
 
11
21
  ## Workflow
12
22
 
13
23
  | Command | Purpose | Options / arguments |
14
24
  |---|---|---|
15
- | `kxm workflow list` | List local workflow runs | `--json` |
16
- | `kxm workflow get <runId>` | Show one run | `--json` |
17
- | `kxm workflow checkpoint [runId] [stageId] [status] [summary]` | Record a stage result | `--run-id`, `--stage-id`, `--status passed\|warning\|failed`, `--summary`, `--evidence`, `--evidence-refs` |
18
- | `kxm workflow record [runId] [category] [area] [summary]` | Journal plan/decision/contradiction/error/lesson | `--category`, `--area`, `--severity`, `--details`, `--evidence` |
19
- | `kxm workflow wait [runId] [stageId] [signalKey] [summary]` | Wait for a signed callback | `--signal-key`, `--timeout-ms`, `--evidence`, `--evidence-refs` |
20
- | `kxm workflow signal <runId> <signalKey> <status> <summary>` | Resume a wait or KXM run | `[evidence...]`, `--delivery-id` |
21
- | `kxm workflow start [definitionId]` | POST a signed workflow-start webhook | `--payload`, `--delivery-id`, `--event` |
25
+ | `kxm workflow list` | List hub workflow runs in the local store | `--json` |
26
+ | `kxm workflow get <runId>` | Show one run with its stages and journal | `--json` |
27
+ | `kxm workflow checkpoint [runId] [stageId] [status] [summary]` | Record a stage result | `--run-id`, `--stage-id`, `--status passed\|warning\|failed`, `--summary`, `--evidence <json>`, `--evidence-refs <json>` |
28
+ | `kxm workflow record [runId] [category] [area] [summary]` | Journal one of ten categories | `--category`, `--area`, `--stage-id`, `--severity info\|warning\|error`, `--summary`, `--details`, `--evidence <items...>`, `--related-entry-ids <ids...>` |
29
+ | `kxm workflow wait [runId] [stageId] [signalKey] [summary]` | Wait for a signed callback | `--signal-key`, `--summary`, `--timeout-ms`, `--evidence <json>`, `--evidence-refs <json>` |
30
+ | `kxm workflow signal <runId> <signalKey> <status> <summary>` | Resume a wait or unblock a KXM run | `[evidence...]` as `required-key=evidence`, `--delivery-id` |
31
+ | `kxm workflow start [definitionId]` | POST a signed workflow-start webhook | `--payload <json\|@file>`, `--delivery-id`, `--event` |
22
32
  | `kxm workflow export <runId>` | Export a proposed retrospective | `--input`, `--out-dir` |
23
- | `kxm workflow definitions` | List definitions | `--scope all\|global\|local` |
33
+ | `kxm workflow definitions` | List project and global workflow definitions | `--scope all\|global\|local` |
24
34
  | `kxm workflow add [workflowId]` | Add a definition | `--file`, `--description`, `--scope`, `--overwrite`, `--pick` |
25
35
  | `kxm workflow remove [workflowId]` | Remove a definition | `--scope`, `--pick` |
26
36
  | `kxm workflow modify [workflowId]` | Modify a definition | `--description`, `--scope`, `--pick` |
27
37
 
38
+ ```bash
39
+ kxm workflow get run_12345 --json
40
+ kxm workflow checkpoint run_12345 implement passed "Implementation complete" --evidence '{"tests":"npm test passed"}' --json
41
+ kxm workflow wait run_12345 verify github-pr-42-checks "CI running on PR 42" --json
42
+ ```
43
+
44
+ ## Journal
45
+
46
+ Categories are `plan`, `decision`, `contradiction`, `error`, `lesson`,
47
+ `observation`, `hypothesis`, `experiment`, `state-change`, and
48
+ `skill-candidate`. A `lesson` or `skill-candidate` requires `--evidence`; the
49
+ hub refuses one without it. Pass `--stage-id` to bind an entry to its stage:
50
+ the hub derives the attempt, and area defaults to the stage's declared area,
51
+ so `kxm workflow record <runId> <category> <summary> --stage-id <id>` works
52
+ without an area. Never supply an attempt. Link a decision or lesson that
53
+ resolves a contradiction with `--related-entry-ids`. The journal covers hub
54
+ webhook runs; a `kxm run` ID is `workflow_not_found`.
55
+
56
+ ```bash
57
+ kxm workflow record run_12345 lesson "Flaky test hid a race" --stage-id verify --severity warning --evidence https://ci.example.com/run/42 --json
58
+ kxm workflow record run_12345 decision "Serialize the fixture setup" --stage-id verify --related-entry-ids je_123 --json
59
+ ```
60
+
28
61
  ## Gates
29
62
 
30
63
  | Command | Purpose | Options / arguments |
31
64
  |---|---|---|
32
- | `kxm gate validate` | Parse workflow definitions without printing secrets | `--file` |
33
- | `kxm gate artifacts-exist` | Verify a non-empty workspace artifact | `--path` (required) |
65
+ | `kxm gate validate` | Parse JSON webhook workflow definitions without printing secrets | `--file <path>` |
66
+ | `kxm gate artifacts-exist` | Verify a non-empty file under workspace assets | `--path` (required) |
34
67
  | `kxm gate degrade <runId> <stageId>` | Approve a configured lower peer quorum | `--requirement`, `--reason` |
35
- | `kxm gate signal <runId> <signalKey> <status> <summary>` | Post a signed callback | `[evidence...]`, `--delivery-id` |
68
+ | `kxm gate signal <runId> <signalKey> <status> <summary>` | Post a signed callback | `[evidence...]`, `--delivery-id`, `--recovery-action retry\|fail\|cancel\|unblock` |
36
69
  | `kxm gate github watch` | Poll required GitHub checks and signal | `--run-id`, `--stage-id`, `--signal-key`, `--repo`, `--pr`, `--required`, `--timeout-ms`, `--interval-ms`, `--delivery-id` |
37
70
 
71
+ `kxm gate validate` reads the hub's webhook definitions (`--file`, else
72
+ `KXM_WEBHOOK_WORKFLOWS_FILE` or `KXM_WEBHOOK_WORKFLOWS`), which are JSON. It
73
+ does not read project `.kxm/workflows/*.yaml`; validate those with
74
+ `kxm init --dry-run --json`.
75
+
76
+ `kxm gate signal` on a KXM run ID (`run_` plus 32 hex digits) goes to the
77
+ Runtime and passes `--recovery-action` through; on a hub webhook run it posts a
78
+ signed callback that needs `KXM_WORKFLOW_ID` and the definition's signal
79
+ secret. Reuse `--delivery-id` to retry one unchanged callback.
80
+
38
81
  ```bash
39
- kxm workflow list --json
40
- kxm workflow get run_12345 --json
41
- kxm workflow checkpoint run_12345 stage_abc passed "Implementation complete" --evidence '{"code_changes":"added feature"}' --json
42
- kxm gate validate --file workflows/default.yaml --json
82
+ kxm gate validate --file workflows.json --json
83
+ kxm gate signal run_0123456789abcdef0123456789abcdef verify passed "operator verified" --recovery-action unblock --dry-run
43
84
  ```
44
85
 
45
86
  Do not invent `gate list`, `gate run`, or `gate status`.
@@ -1,6 +1,7 @@
1
1
  import {
2
2
  DEFAULT_CONTEXT_BUDGET_TOKENS,
3
3
  MAX_CONTEXT_ITEMS,
4
+ contextItemCharacters,
4
5
  estimateContextTokens,
5
6
  parseContextItem,
6
7
  parseContextRequest,
@@ -15,6 +16,13 @@ import {
15
16
  type ContextSourceType,
16
17
  } from "./context.ts";
17
18
  import { ProtocolError } from "./protocol.ts";
19
+ import {
20
+ compareCodeUnitIds,
21
+ contextItemRelevanceText,
22
+ relevanceTokens,
23
+ roundRelevance,
24
+ scoreRelevance,
25
+ } from "./relevance.ts";
18
26
  import type { SkillLifecycle } from "./skills.ts";
19
27
  import type { JournalCategory, WorkflowJournalEntry } from "./workflow.ts";
20
28
  import type { MemoryRecord } from "./memory.ts";
@@ -44,7 +52,7 @@ export const ROLE_POLICIES: readonly RoleContextPolicy[] = [
44
52
  {
45
53
  role: "repro",
46
54
  label: "Reproduction specialist",
47
- kinds: ["episode", "knowledge"],
55
+ kinds: ["episode", "knowledge", "evidence"],
48
56
  journalCategories: ["error", "lesson", "observation", "contradiction"],
49
57
  budgetTokens: 8_000,
50
58
  },
@@ -65,7 +73,7 @@ export const ROLE_POLICIES: readonly RoleContextPolicy[] = [
65
73
  {
66
74
  role: "implementer",
67
75
  label: "Implementer",
68
- kinds: ["knowledge", "state", "skill", "episode"],
76
+ kinds: ["knowledge", "state", "skill", "episode", "evidence"],
69
77
  journalCategories: ["plan", "decision", "lesson", "state-change"],
70
78
  budgetTokens: 16_000,
71
79
  },
@@ -97,8 +105,9 @@ export interface ArbiterOptions {
97
105
  /** Pool item IDs that represent open contradictions; they are routed to the
98
106
  * packet's contradictions section instead of their kind's section. */
99
107
  contradictionIds?: string[];
100
- /** Governed skill lifecycle to read and verify promoted skills by hash. */
101
- skillLifecycle?: SkillLifecycle;
108
+ /** Governed skill lifecycle (or a pre-verified snapshot of one) to read and
109
+ * verify promoted skills by hash. */
110
+ skillLifecycle?: Pick<SkillLifecycle, "list" | "verify">;
102
111
  }
103
112
 
104
113
  export interface ArbiterOutcome {
@@ -114,12 +123,19 @@ export interface ArbiterOutcome {
114
123
  candidateCount: number;
115
124
  excludedSuperseded: number;
116
125
  unresolvedGaps: string[];
126
+ /** Numbers only: distinct task tokens, eligible candidates sharing a task
127
+ * token, and each selected item's rounded BM25 score (index-aligned with
128
+ * selectedIds). */
129
+ relevance: { taskTokens: number; matchedCandidates: number; selected: number[] };
117
130
  };
118
131
  }
119
132
 
120
133
  /** Deterministically assemble a role-aware context packet. Superseded and
121
134
  * rejected records are excluded by default; cross-project content fails
122
- * closed; the token budget is enforced on the serialized selection. */
135
+ * closed; eligible candidates are ranked by contradiction, project-first,
136
+ * task match, role kind priority, BM25 task relevance, confidence,
137
+ * authority, recency (newest first), then id by code unit; the token budget
138
+ * is filled first-fit on the serialized selection. */
123
139
  export function arbitrate(
124
140
  requestInput: unknown,
125
141
  pool: ContextItem[],
@@ -180,38 +196,62 @@ export function arbitrate(
180
196
  return rank === undefined ? requestedKinds.length : rank;
181
197
  };
182
198
 
183
- const ordered = [...candidates].sort((left, right) =>
199
+ // Eligibility: open contradictions always compete; everything else must be
200
+ // a requested kind and not an inert proposal (a non-current state or a
201
+ // proposed skill), so proposals never consume budget.
202
+ const allowedKinds = new Set<ContextItemKind>(requestedKinds);
203
+ const inertProposal = (item: ContextItem): boolean =>
204
+ (item.kind === "state" && item.status !== undefined && item.status !== "current")
205
+ || (item.kind === "skill" && item.status === "proposed");
206
+ const eligible = candidates.filter((item) =>
207
+ contradictions.has(item.id) || (allowedKinds.has(item.kind) && !inertProposal(item)));
208
+
209
+ const scoreList = scoreRelevance(request.task, eligible.map(contextItemRelevanceText));
210
+ const scores = new Map<ContextItem, number>();
211
+ eligible.forEach((item, index) => scores.set(item, scoreList[index] ?? 0));
212
+ const scoreOf = (item: ContextItem): number => scores.get(item) ?? 0;
213
+ // Recency from the item's own timestamps; never reads the clock.
214
+ const when = (item: ContextItem): number => {
215
+ const parsed = Date.parse(item.observedAt ?? item.validFrom ?? "");
216
+ return Number.isFinite(parsed) ? parsed : 0;
217
+ };
218
+
219
+ const ordered = [...eligible].sort((left, right) =>
184
220
  (contradictions.has(right.id) ? 1 : 0) - (contradictions.has(left.id) ? 1 : 0)
185
221
  || (left.project === request.project ? 0 : 1) - (right.project === request.project ? 0 : 1)
222
+ || (scoreOf(right) > 0 ? 1 : 0) - (scoreOf(left) > 0 ? 1 : 0)
186
223
  || kindPreference(left) - kindPreference(right)
224
+ || scoreOf(right) - scoreOf(left)
187
225
  || CONFIDENCE_RANK[right.confidence] - CONFIDENCE_RANK[left.confidence]
188
226
  || AUTHORITY_WEIGHT[right.authority] - AUTHORITY_WEIGHT[left.authority]
189
- || left.id.localeCompare(right.id),
227
+ || when(right) - when(left)
228
+ || compareCodeUnitIds(left.id, right.id),
190
229
  );
191
230
 
192
- const kindAllowed = (item: ContextItem): boolean =>
193
- (request.includeKinds ?? policy.kinds).includes(item.kind)
194
- || contradictions.has(item.id);
195
-
231
+ // First-fit: an item that does not fit is deferred and smaller items keep
232
+ // filling the budget.
196
233
  const selected: ContextItem[] = [];
197
234
  const unresolvedGaps: string[] = [];
235
+ let characters = 0;
236
+ let deferredForBudget = 0;
198
237
  for (const item of ordered) {
199
238
  if (selected.length >= MAX_CONTEXT_ITEMS) {
200
239
  unresolvedGaps.push("context item limit reached; refine the task or kinds");
201
240
  break;
202
241
  }
203
- if (!kindAllowed(item)) continue;
204
- const nextTokens = estimateContextTokens([...selected, item]);
205
- if (nextTokens > budget) {
206
- if (selected.length === 0) {
207
- unresolvedGaps.push(`budget of ${budget} tokens cannot fit any selected context`);
208
- break;
209
- }
210
- unresolvedGaps.push(`budget of ${budget} tokens reached; ${ordered.length - selected.length} candidates deferred`);
211
- break;
242
+ const itemCharacters = contextItemCharacters(item);
243
+ if (Math.ceil((characters + itemCharacters) / 4) > budget) {
244
+ deferredForBudget += 1;
245
+ continue;
212
246
  }
247
+ characters += itemCharacters;
213
248
  selected.push(item);
214
249
  }
250
+ if (deferredForBudget > 0) {
251
+ unresolvedGaps.push(selected.length === 0
252
+ ? `budget of ${budget} tokens cannot fit any selected context`
253
+ : `budget of ${budget} tokens reached; ${deferredForBudget} candidates deferred`);
254
+ }
215
255
  if (candidates.length === 0) {
216
256
  unresolvedGaps.push("no context records exist for this project yet");
217
257
  }
@@ -223,6 +263,7 @@ export function arbitrate(
223
263
  workingState: options.workingState ?? {},
224
264
  currentState: bySection("state").filter((item) => item.status === "current" || item.status === undefined),
225
265
  knowledge: bySection("knowledge"),
266
+ evidence: bySection("evidence"),
226
267
  episodes: bySection("episode"),
227
268
  skills: bySection("skill").filter((item) => item.status !== "proposed"),
228
269
  contradictions: selected.filter((item) => contradictions.has(item.id)),
@@ -249,6 +290,11 @@ export function arbitrate(
249
290
  candidateCount: candidates.length,
250
291
  excludedSuperseded,
251
292
  unresolvedGaps,
293
+ relevance: {
294
+ taskTokens: new Set(relevanceTokens(request.task)).size,
295
+ matchedCandidates: scoreList.filter((score) => score > 0).length,
296
+ selected: selected.map((item) => roundRelevance(scoreOf(item))),
297
+ },
252
298
  },
253
299
  };
254
300
  }
@@ -273,12 +319,11 @@ export function journalEntryToContextItem(entry: WorkflowJournalEntry, project:
273
319
  },
274
320
  authority: entry.category === "decision" || entry.category === "plan" ? "evidence" : "evidence",
275
321
  confidence: entry.severity === "error" ? "probable" : "probable",
276
- ...(entry.stageId !== undefined ? { observedAt: entry.createdAt } : {}),
322
+ observedAt: entry.createdAt,
277
323
  evidenceRefs: entry.evidence
278
324
  .filter((ref) => ref.length > 0 && ref.length <= 200)
279
325
  .slice(0, 16),
280
326
  };
281
- if (entry.stageId !== undefined) item.observedAt = entry.createdAt;
282
327
  if (kind === "skill") item.status = "proposed";
283
328
  return parseContextItem(item);
284
329
  }
@@ -145,7 +145,7 @@ _kxm() {
145
145
  'peer:Peer agent messaging and coordination'
146
146
  'workflow:Start and inspect workflow runs'
147
147
  'gate:Validate definitions and operate evidence gates'
148
- 'improve:Propose CLI or project improvements'
148
+ 'improve:Propose coded-repeat candidates from routing records'
149
149
  'context:KXM context operating-system queries'
150
150
  'skills:Governed skill candidate lifecycle'
151
151
  'memory:Harness-agnostic Git memory operations'