@kontextmind/kxm 0.7.94 → 0.7.96

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 (140) hide show
  1. package/.claude-plugin/marketplace.json +1 -1
  2. package/.kxm/README.md +39 -9
  3. package/CHANGELOG.md +1 -1
  4. package/README.md +147 -257
  5. package/SECURITY.md +21 -12
  6. package/docs/README.md +133 -54
  7. package/docs/adr/ADR-0002-browser-automation-steel-doks.md +24 -18
  8. package/docs/adr/ADR-0003-sqlite-only-store.md +100 -0
  9. package/docs/adr/ADR-0004-edge-identity-authentik.md +99 -0
  10. package/docs/adr/README.md +33 -0
  11. package/docs/concepts/architecture.md +262 -0
  12. package/docs/concepts/data-and-storage.md +194 -0
  13. package/docs/concepts/trust-model.md +152 -0
  14. package/docs/contracts/README.md +22 -14
  15. package/docs/contracts/effects-and-recovery.md +3 -0
  16. package/docs/contracts/migration.md +2 -2
  17. package/docs/contracts/routing.md +6 -5
  18. package/docs/contributing/assignment-runner.md +388 -0
  19. package/docs/contributing/ci-and-release.md +231 -0
  20. package/docs/contributing/development.md +362 -0
  21. package/docs/contributing/harness-routing-internals.md +192 -0
  22. package/docs/{packages.md → contributing/packages.md} +13 -15
  23. package/docs/{skills → contributing}/repo-work-delivery.md +20 -21
  24. package/docs/contributing/test-matrix.md +208 -0
  25. package/docs/{tui-components.md → contributing/tui-components.md} +30 -22
  26. package/docs/contributing/writing-docs.md +340 -0
  27. package/docs/glossary.md +471 -0
  28. package/docs/guides/agent-skills.md +137 -0
  29. package/docs/guides/browser-automation.md +160 -0
  30. package/docs/guides/context-and-memory.md +352 -0
  31. package/docs/guides/continuous-improvement.md +228 -0
  32. package/docs/guides/governed-skills.md +173 -0
  33. package/docs/guides/nous-providers.md +186 -0
  34. package/docs/guides/peer-messaging.md +304 -0
  35. package/docs/guides/pi-workers.md +219 -0
  36. package/docs/guides/provenance-gates.md +313 -0
  37. package/docs/guides/webhook-workflows.md +364 -0
  38. package/docs/kb/how-credentials-retrieved-safely.md +38 -12
  39. package/docs/kb/how-to-capture-and-annotate-section.md +15 -13
  40. package/docs/kb/how-to-connect-playwright-to-steel.md +16 -11
  41. package/docs/kb/how-to-recover-expired-session-or-orphan.md +26 -16
  42. package/docs/kb/how-to-resume-after-mfa.md +19 -11
  43. package/docs/kb/how-to-take-over-session.md +17 -13
  44. package/docs/kb/why-authentication-disappeared.md +22 -14
  45. package/docs/kb/why-automation-opened-different-browser.md +23 -14
  46. package/docs/kb/why-session-viewer-cannot-control.md +13 -12
  47. package/docs/operations/backup-and-restore.md +248 -0
  48. package/docs/operations/deploy.md +307 -0
  49. package/docs/operations/monitoring.md +209 -0
  50. package/docs/operations/runtime-sync.md +192 -0
  51. package/docs/operations/troubleshooting.md +265 -0
  52. package/docs/operations/upgrade.md +124 -0
  53. package/docs/prompts/browser-annotate-feedback.md +7 -7
  54. package/docs/prompts/browser-diagnose-recover.md +11 -10
  55. package/docs/prompts/browser-explore.md +7 -7
  56. package/docs/prompts/browser-repro-fix.md +7 -7
  57. package/docs/prompts/browser-start.md +12 -11
  58. package/docs/prompts/browser-takeover.md +8 -8
  59. package/docs/{cli-reference.md → reference/cli-reference.md} +83 -41
  60. package/docs/{config-reference.md → reference/config-reference.md} +159 -148
  61. package/docs/reference/configuration.md +299 -0
  62. package/docs/reference/harness-routing.md +508 -0
  63. package/docs/reference/http-api.md +203 -0
  64. package/docs/reference/tools.md +370 -0
  65. package/docs/{workflow-guide.md → reference/workflow-catalog.md} +92 -153
  66. package/docs/reference/workflow-definitions.md +286 -0
  67. package/docs/start/first-workflow.md +287 -0
  68. package/docs/start/install.md +146 -0
  69. package/docs/start/quickstart-claude-code.md +405 -0
  70. package/docs/start/quickstart-pi.md +213 -0
  71. package/docs/templates/README.md +78 -73
  72. package/docs/templates/adr.md +13 -13
  73. package/docs/templates/architecture.md +55 -71
  74. package/docs/templates/bug-fix.md +13 -16
  75. package/docs/templates/feature.md +14 -19
  76. package/docs/templates/handoff.md +44 -46
  77. package/docs/templates/postmortem.md +30 -43
  78. package/docs/templates/research.md +15 -20
  79. package/docs/templates/review.md +49 -50
  80. package/docs/templates/runbook.md +38 -30
  81. package/docs/templates/test-plan.md +16 -23
  82. package/docs/templates/test-report.md +14 -17
  83. package/examples/README.md +9 -5
  84. package/examples/provenance-workflow.json +1 -1
  85. package/examples/webhook-workflows/jira-development.json +59 -0
  86. package/examples/webhook-workflows/jira-issue-updated.json +12 -0
  87. package/package.json +2 -2
  88. package/packages/core/tui/README.md +1 -1
  89. package/plugins/kxm/.claude-plugin/plugin.json +1 -1
  90. package/plugins/kxm/README.md +31 -32
  91. package/plugins/kxm/dist/cli.js +5 -5
  92. package/plugins/kxm/dist/mcp-server.js +1 -1
  93. package/plugins/kxm/dist/runtime.js +1 -1
  94. package/plugins/kxm/package.json +1 -1
  95. package/plugins/kxm/skills/kxm/references/protocol.md +3 -1
  96. package/plugins/kxm/skills/kxm-browser-auth/SKILL.md +1 -1
  97. package/plugins/kxm/skills/kxm-browser-diagnostics/SKILL.md +5 -5
  98. package/plugins/kxm/skills/kxm-browser-explore/SKILL.md +2 -2
  99. package/plugins/kxm/skills/kxm-browser-session/SKILL.md +10 -13
  100. package/plugins/kxm/skills/kxm-browser-takeover/SKILL.md +1 -1
  101. package/plugins/kxm/skills/kxm-browser-verify/SKILL.md +1 -1
  102. package/plugins/kxm/skills/kxm-context-memory/SKILL.md +13 -4
  103. package/plugins/kxm/skills/kxm-hub-ops/SKILL.md +3 -1
  104. package/plugins/kxm/skills/kxm-mind-setup/SKILL.md +2 -1
  105. package/plugins/kxm/skills/kxm-project-setup/SKILL.md +31 -54
  106. package/plugins/kxm/skills/kxm-projects/SKILL.md +1 -1
  107. package/plugins/kxm/skills/kxm-protocol/SKILL.md +1 -1
  108. package/plugins/kxm/skills/kxm-routing-improve/SKILL.md +15 -7
  109. package/plugins/kxm/skills/kxm-runs/SKILL.md +11 -5
  110. package/plugins/kxm/skills/kxm-session/SKILL.md +1 -1
  111. package/plugins/kxm/skills/kxm-tasks/SKILL.md +9 -7
  112. package/plugins/kxm/skills/kxm-workflow/SKILL.md +10 -2
  113. package/plugins/kxm/src/cli/system.ts +1 -1
  114. package/plugins/kxm/src/cli.ts +3 -3
  115. package/plugins/kxm/src/init-guide-setup.ts +1 -1
  116. package/plugins/kxm/src/mcp-server.ts +1 -1
  117. package/plugins/kxm/src/modes.ts +1 -1
  118. package/schemas/README.md +1 -1
  119. package/docs/agent-communication-envelopes-and-gates.md +0 -553
  120. package/docs/agent-skills.md +0 -198
  121. package/docs/architecture.md +0 -245
  122. package/docs/assignment-runner.md +0 -264
  123. package/docs/browser-automation.md +0 -139
  124. package/docs/configuration.md +0 -437
  125. package/docs/continuous-improvement.md +0 -226
  126. package/docs/getting-started.md +0 -277
  127. package/docs/harness-routing.md +0 -616
  128. package/docs/kb/qa-authentik-authentication.md +0 -97
  129. package/docs/kb/qa-extension-install-and-hub-bootstrap.md +0 -85
  130. package/docs/kb/qa-hub-on-a-public-host.md +0 -48
  131. package/docs/kb/qa-sqlite-vs-duckdb.md +0 -35
  132. package/docs/kb/qa-what-the-hub-stores.md +0 -64
  133. package/docs/kxm-handbook.md +0 -1181
  134. package/docs/operations.md +0 -510
  135. package/docs/operator-pi-packages.md +0 -67
  136. package/docs/provenance-gates.md +0 -295
  137. package/docs/skills.md +0 -47
  138. package/docs/test-matrix.md +0 -132
  139. package/docs/troubleshooting.md +0 -293
  140. package/docs/webhook-workflows.md +0 -240
@@ -0,0 +1,364 @@
1
+ # Run webhook workflows
2
+
3
+ A webhook workflow turns a signed Jira, GitHub, or generic webhook into a durable, staged run on the KXM [hub](../glossary.md#hub). A long-lived [coordinator](../glossary.md#coordinator) agent works through the stages, proves each one with keyed evidence, and pauses for CI without holding a model turn open. This guide sets up the included Jira example end to end and explains definitions, checkpoints, waits, signals, and retrospectives.
4
+
5
+ > [!IMPORTANT]
6
+ > Hub webhook workflows are separate from Runtime runs. `kxm run` executes `kxm.workflow.v1` YAML files in `.kxm/workflows/` on the local Runtime; see [Run your first workflow](../start/first-workflow.md). A webhook workflow is a JSON definition that the hub loads from `KXM_WEBHOOK_WORKFLOWS_FILE`. Pointing that variable at a YAML workflow stops the hub from starting.
7
+
8
+ | | Hub webhook workflow | Runtime run |
9
+ |---|---|---|
10
+ | Defined in | A JSON array of definitions with ordered `stages` | `.kxm/workflows/<id>.yaml` with `steps` |
11
+ | Started by | A signed `POST /v1/webhooks/<id>`, or `kxm workflow start` | `kxm run <workflow>` |
12
+ | Executed by | A coordinator agent connected to the hub | The Runtime supervisor on your machine |
13
+ | Inspected with | `kxm workflow list`, `kxm workflow get`, `kxm_workflow_get` | `kxm runs status`, `kxm runs list` |
14
+
15
+ [Architecture](../concepts/architecture.md) explains how the two planes relate.
16
+
17
+ ## Before you begin
18
+
19
+ - The `kxm` CLI, and Pi with the KXM package for the coordinator: see [Install KXM](../start/install.md).
20
+ - An admin token for the hub and a project token for the `product` project. See [Trust model](../concepts/trust-model.md) for which credential goes where.
21
+ - Two separate random secrets of at least 16 characters: one that starts workflows and one that signs callbacks.
22
+ - For a real Jira connection, the hub behind a TLS proxy that Jira can reach, with ingress restricted to Jira. See [Deploy KXM](../operations/deploy.md). A loopback hub is enough for the local test below.
23
+ - For the CI stage, a GitHub token that can read checks.
24
+
25
+ ## How a webhook workflow runs
26
+
27
+ The sequence below shows one run of the Jira example, from the signed delivery to the final checkpoint.
28
+
29
+ ```mermaid
30
+ sequenceDiagram
31
+ participant Jira
32
+ participant Hub as KXM hub
33
+ participant Coord as Coordinator (Pi worker)
34
+ participant Watch as kxm gate github watch
35
+ participant GitHub
36
+ Jira->>Hub: POST /v1/webhooks/jira-development (HMAC, delivery ID)
37
+ Hub->>Hub: store the run and the coordinator prompt
38
+ Hub->>Coord: workflow prompt
39
+ Coord->>Hub: kxm_workflow_checkpoint for reproduce, plan, implement
40
+ Coord->>Hub: kxm_workflow_wait on stage checks
41
+ Coord-->>Hub: settle the turn while the run waits
42
+ Watch->>GitHub: poll the required checks
43
+ Watch->>Hub: signed signal with status and evidence
44
+ Hub->>Hub: checkpoint the waiting stage
45
+ Hub->>Coord: resume prompt
46
+ Coord->>Hub: kxm_workflow_checkpoint for report
47
+ Hub-->>Coord: completed = true
48
+ ```
49
+
50
+ The hub verifies the signature over the raw body, deduplicates retries by delivery ID, and records the run before it answers. It keeps a SHA-256 hash of the payload and the rendered prompt, not the raw body, so keep prompt templates narrow.
51
+
52
+ ## Copy the example definition
53
+
54
+ KXM ships a small, validated Jira definition at [`examples/webhook-workflows/jira-development.json`](../../examples/webhook-workflows/jira-development.json) and a matching test payload, `jira-issue-updated.json`. Copy both into your repository outside `.kxm`:
55
+
56
+ ```bash
57
+ mkdir -p ops/kxm
58
+ # From a KXM source checkout, or from the npm package:
59
+ EXAMPLES="$(npm root -g)/@kontextmind/kxm/examples/webhook-workflows"
60
+ cp "$EXAMPLES/jira-development.json" ops/kxm/webhook-workflows.json
61
+ cp "$EXAMPLES/jira-issue-updated.json" ops/kxm/jira-issue-updated.json
62
+ ```
63
+
64
+ > [!WARNING]
65
+ > Never put a definition in `.kxm/config/workflows/`. KXM treats JSON there as legacy state and refuses to load the whole project (`legacy_state_unsupported`).
66
+
67
+ The definition has five stages: `reproduce`, `plan`, `implement`, `checks`, and `report`. This excerpt shows its top level and the stage that waits for CI.
68
+
69
+ `ops/kxm/webhook-workflows.json` (excerpt):
70
+
71
+ ```json
72
+ [
73
+ {
74
+ "id": "jira-development",
75
+ "source": "jira",
76
+ "project": "product",
77
+ "target": "coordinator",
78
+ "secretEnv": "JIRA_WEBHOOK_SECRET",
79
+ "signalSecretEnv": "WORKFLOW_SIGNAL_SECRET",
80
+ "event": "jira:issue_updated",
81
+ "filter": { "path": "issue.fields.status.name", "equals": "In Progress" },
82
+ "delivery": "followUp",
83
+ "promptTemplate": "Jira issue {{issue.key}} moved to In Progress: {{issue.fields.summary}}. ...",
84
+ "stages": [
85
+ {
86
+ "id": "checks",
87
+ "label": "Pull request checks",
88
+ "instructions": "Push the branch and open or update the pull request. Then call kxm_workflow_wait ...",
89
+ "requiredEvidence": ["github.check:ci"],
90
+ "maxAttempts": 3,
91
+ "area": "gates"
92
+ }
93
+ ]
94
+ }
95
+ ]
96
+ ```
97
+
98
+ ## Write your own definition
99
+
100
+ A file holds a JSON array of definitions. The hub checks every limit below at start.
101
+
102
+ | Field | Meaning |
103
+ |---|---|
104
+ | `id` | Up to 64 characters; the last segment of the webhook URL. |
105
+ | `source` | `jira`, `github`, or `generic` (the default). A label on the run. |
106
+ | `project`, `target` | Hub project, and the coordinator's agent name or ID. |
107
+ | `secretEnv` | Variable holding the start secret. `secret` takes a literal instead; prefer the variable. |
108
+ | `signalSecretEnv` | Variable holding the callback secret. Without it, callbacks use the start secret. |
109
+ | `event` | Optional. Must equal the `X-GitHub-Event` header, or the payload's `webhookEvent` or `event` field. |
110
+ | `filter` | Optional. `path` is a dotted payload path whose string value must equal `equals`. |
111
+ | `delivery` | `followUp` (the default) or `steer` for the coordinator prompt. |
112
+ | `ttlMs` | Coordinator prompt lifetime. Defaults to the hub message TTL (24 hours). |
113
+ | `promptTemplate` | Up to 20,000 characters. `{{dotted.path}}` inserts payload values; a missing value becomes empty. |
114
+ | `stages` | One to 32 ordered stages. |
115
+
116
+ Each stage has an `id`, a `label`, `instructions` (up to 4,000 characters), `requiredEvidence` (up to 32 keys), `maxAttempts` (1 to 20, default 3), and an optional `area` that files its automatic journal entries under `harness`, `gates`, `implementation`, `workflow`, `documentation`, `security`, or `other`. A stage may also declare `evidencePolicies`, which require verified replies from named peers; see [Peer provenance and quorum gates](provenance-gates.md). Typed transitions (`on`, `maxTransitions`), `autoResumeLimit`, and the `reproOracle` and `planHash` locks are described in [Workflow definition reference](../reference/workflow-definitions.md).
117
+
118
+ The hub appends the stages, their evidence keys, and the coordinator procedure to the rendered prompt, so the template only needs the task.
119
+
120
+ ## Validate the definition
121
+
122
+ Set both secrets, then validate the file. Validation parses it exactly as the hub will and never prints a secret.
123
+
124
+ ```bash
125
+ export JIRA_WEBHOOK_SECRET="replace-with-a-high-entropy-secret"
126
+ export WORKFLOW_SIGNAL_SECRET="replace-with-a-separate-callback-secret"
127
+ kxm gate validate --file ops/kxm/webhook-workflows.json
128
+ ```
129
+
130
+ Expected output:
131
+
132
+ ```text
133
+ validated 1 workflow(s) from file
134
+ ```
135
+
136
+ ## Start the hub with the definition
137
+
138
+ Start the hub in the same environment. It loads the definitions once, at start; restart it after every change.
139
+
140
+ ```bash
141
+ export KXM_AUTH_TOKEN="replace-with-the-admin-token"
142
+ export KXM_PROJECT_TOKENS='{"product":"replace-with-the-project-token"}'
143
+ export KXM_WEBHOOK_WORKFLOWS_FILE=ops/kxm/webhook-workflows.json
144
+ kxm hub start
145
+ ```
146
+
147
+ <details><summary>PowerShell</summary>
148
+
149
+ ```powershell
150
+ $env:JIRA_WEBHOOK_SECRET = "replace-with-a-high-entropy-secret"
151
+ $env:WORKFLOW_SIGNAL_SECRET = "replace-with-a-separate-callback-secret"
152
+ $env:KXM_AUTH_TOKEN = "replace-with-the-admin-token"
153
+ $env:KXM_PROJECT_TOKENS = '{"product":"replace-with-the-project-token"}'
154
+ $env:KXM_WEBHOOK_WORKFLOWS_FILE = "ops/kxm/webhook-workflows.json"
155
+ kxm hub start
156
+ ```
157
+
158
+ </details>
159
+
160
+ > [!WARNING]
161
+ > `KXM_PROJECT_TOKENS` replaces the hub's saved project-token map; it does not merge. This example starts a hub that knows only this project. On a hub that already serves other projects, build the full map with the merge command in [Set up a new project](../start/quickstart-claude-code.md#3-start-the-hub).
162
+
163
+ `KXM_WEBHOOK_WORKFLOWS` takes the JSON array inline instead. Setting both variables stops the hub from starting.
164
+
165
+ ## Start the coordinator
166
+
167
+ The coordinator must have registered with the hub at least once before a webhook targets it. A delivery for a coordinator that never registered is refused with `409`, which makes Jira retry. A registered but offline coordinator is fine: the prompt waits for it in the queue.
168
+
169
+ In another terminal, start a [supervised Pi worker](pi-workers.md) as the coordinator, with the project token and the hub tools its stages need:
170
+
171
+ ```bash
172
+ export KXM_SERVER_URL=http://127.0.0.1:7331
173
+ export KXM_AUTH_TOKEN="replace-with-the-project-token"
174
+ export KXM_WORKDIR=~/work/product
175
+ kxm agent worker --name coordinator --project product --model <pi-model> \
176
+ --session-isolation workflow
177
+ ```
178
+
179
+ `--session-isolation workflow` gives each run its own Pi session. Any connected harness can coordinate instead, such as Claude Code with the agent name `coordinator`.
180
+
181
+ ## Send a test delivery
182
+
183
+ `kxm workflow start` signs a payload and posts it to the hub at `KXM_SERVER_URL`, as a provider would. It signs with `KXM_WORKFLOW_SECRET`, or with the definition's own start secret when `KXM_WEBHOOK_WORKFLOWS_FILE` is set in the same shell.
184
+
185
+ ```bash
186
+ export KXM_SERVER_URL=http://127.0.0.1:7331
187
+ export KXM_WORKFLOW_SECRET="replace-with-a-high-entropy-secret"
188
+ kxm workflow start jira-development \
189
+ --payload @ops/kxm/jira-issue-updated.json --delivery-id local-test-0001
190
+ ```
191
+
192
+ Expected output:
193
+
194
+ ```text
195
+ started workflow run_7c187d2a0fde408ea408f0d8c53201c8
196
+ ```
197
+
198
+ Repeating the command with the same delivery ID returns the same run. Inspect runs from the hub's workspace on the hub host; these commands read its local SQLite store:
199
+
200
+ ```bash
201
+ kxm workflow list
202
+ kxm workflow get run_7c187d2a0fde408ea408f0d8c53201c8 --json
203
+ ```
204
+
205
+ ## Connect Jira
206
+
207
+ In Jira, create a webhook for the `jira:issue_updated` event that posts to `https://<kxm-host>/v1/webhooks/jira-development`, and set the same start secret. The hub accepts these headers:
208
+
209
+ | Header | Sent by | Purpose |
210
+ |---|---|---|
211
+ | `X-Hub-Signature-256` or `X-Hub-Signature` | GitHub, Jira, `kxm` | `sha256=<hex>` HMAC of the exact body. Other algorithms are refused. |
212
+ | `X-Atlassian-Webhook-Identifier` | Jira | Delivery ID. Checked first. |
213
+ | `X-GitHub-Delivery` | GitHub | Delivery ID. Checked second. |
214
+ | `X-KXM-Delivery-ID` | `kxm` and your own senders | Delivery ID. Checked last. |
215
+
216
+ | Response | Meaning |
217
+ |---|---|
218
+ | `202` | Run created and coordinator prompt queued. |
219
+ | `200` with `"duplicate": true` | The delivery ID was seen before; the existing run is returned. The body is not compared. |
220
+ | `204` | The event or filter did not match. Nothing was stored. |
221
+ | `401` | Missing, unsupported, or wrong signature. |
222
+ | `404` | No definition with that ID. |
223
+ | `409` | The coordinator has never registered. |
224
+
225
+ Webhook authentication authorizes only workflow creation. The `report` stage updates Jira through the coordinator's own authorized Jira tool; never put Jira credentials in a definition or prompt.
226
+
227
+ ## Follow the coordinator procedure
228
+
229
+ For every stage, the coordinator:
230
+
231
+ 1. Calls `kxm_workflow_get` and works only on `currentStage`.
232
+ 2. Records plans, decisions, contradictions, errors, and lessons with `kxm_workflow_record`, passing the `stageId`.
233
+ 3. Gathers evidence for every `requiredEvidence` key.
234
+ 4. For a stage with a peer policy, sends requests with `workflowContext` ([Peer provenance and quorum gates](provenance-gates.md)).
235
+ 5. Calls `kxm_workflow_checkpoint`, or `kxm_workflow_wait` when an external system must finish the stage.
236
+ 6. After a `warning` or `failed` result, corrects the problem and tries again until the stage passes or `maxAttempts` runs out.
237
+ 7. Replies to the prompt only after a checkpoint reports `completed: true`, or right after entering a wait.
238
+
239
+ Replying while the run is `running` and stages remain fails the run and records a workflow error. Replying after a successful wait is expected: it releases the model turn, and the signed callback creates a fresh prompt later. If the prompt's TTL passes first, the run fails.
240
+
241
+ ## Checkpoint a stage
242
+
243
+ A passing checkpoint needs a non-empty value for every required evidence key. Keys are matched after trimming, collapsing whitespace, and ignoring case; an extra key never stands in for a missing one.
244
+
245
+ Tool call (`kxm_workflow_checkpoint`):
246
+
247
+ ```json
248
+ {
249
+ "runId": "run_7c187d2a0fde408ea408f0d8c53201c8",
250
+ "stageId": "reproduce",
251
+ "status": "passed",
252
+ "summary": "Reproduced with a failing test.",
253
+ "evidence": { "reproduction": "test/checkout.test.ts fails: npm test -- checkout" }
254
+ }
255
+ ```
256
+
257
+ The CLI twin is `kxm workflow checkpoint`, run under the coordinator's agent name.
258
+
259
+ Only the assigned coordinator can read, journal, checkpoint, or wait a run, and checkpoints and waits apply only to the active stage. A `warning` or `failed` checkpoint uses up an attempt and records an error; its evidence stays in the journal but does not count toward a later pass. Reaching `maxAttempts` fails the run. The hub enforces stage order and evidence keys; the agents stay responsible for the truth of what they submit.
260
+
261
+ ## Wait for CI and other external work
262
+
263
+ A coordinator should not hold a model turn open while CI runs. On the active stage it calls `kxm_workflow_wait` with a stable signal key, a summary of the expected result, any evidence it already has, and an optional timeout from 1 second to 30 days (24 hours by default). The run and stage become `waiting`, and the coordinator settles its turn. If the deadline passes, the run fails and the coordinator receives a notice.
264
+
265
+ Tool call (`kxm_workflow_wait`):
266
+
267
+ ```json
268
+ {
269
+ "runId": "run_7c187d2a0fde408ea408f0d8c53201c8",
270
+ "stageId": "checks",
271
+ "signalKey": "github-pr-42-checks",
272
+ "summary": "Waiting for the required GitHub checks on pull request 42",
273
+ "timeoutMs": 3600000
274
+ }
275
+ ```
276
+
277
+ Store the run ID and signal key where the external system can find them, such as pull-request metadata; they are not secrets.
278
+
279
+ The external system reports back with a signed signal to `POST /v1/webhooks/<definition-id>/runs/<run-id>/signals/<signal-key>`. The body is `{"status": "passed" | "warning" | "failed", "summary": "...", "evidence": {...}}`, signed with the callback secret in `X-Hub-Signature-256`, with a stable `X-KXM-Delivery-ID`.
280
+
281
+ - `passed` applies the evidence rule to the saved and new evidence together, then advances or completes the run.
282
+ - `warning` or `failed` uses up an attempt, records an error, and sends the coordinator a correction prompt while attempts remain.
283
+ - The same delivery ID and body returns the original receipt; the same delivery ID with a different signal or body is refused with `409`.
284
+ - Optional `workflow.run`, `workflow.stage`, and `workflow.signal` evidence values must match the route, or the hub refuses the signal with `409`.
285
+
286
+ The run, receipt, journal entry, and resume prompt commit in one transaction. The response carries only status flags, never the run or its evidence.
287
+
288
+ ## Send a signal from the CLI
289
+
290
+ `kxm gate signal` signs and posts a signal. It needs `KXM_WORKFLOW_ID` and the callback secret, either through the active definition file or `KXM_WORKFLOW_SIGNAL_SECRET`. Evidence is `key=value` pairs.
291
+
292
+ ```bash
293
+ export KXM_WORKFLOW_ID=jira-development
294
+ export KXM_WORKFLOW_SIGNAL_SECRET="replace-with-a-separate-callback-secret"
295
+ kxm gate signal run_7c187d2a0fde408ea408f0d8c53201c8 github-pr-42-checks passed \
296
+ "All required checks passed" "github.check:ci=https://github.example/org/repo/actions/runs/123" \
297
+ --delivery-id github-check-run-123-attempt-1
298
+ ```
299
+
300
+ Expected output:
301
+
302
+ ```text
303
+ posted signed signal
304
+ ```
305
+
306
+ > [!WARNING]
307
+ > Run `kxm gate signal` and `kxm workflow wait` outside any KXM project directory. Inside a Git repository with `.kxm/project.yaml`, they treat a `run_<32-hex>` ID as a Runtime run and send it to the Runtime supervisor, not the hub. Check with `--dry-run`: the hub path prints `would post signed signal`, the Runtime path `would post signal to KXM run`.
308
+
309
+ For your own adapters, [`examples/workflow-signal.ts`](../../examples/workflow-signal.ts) shows the same signed request in about 60 lines.
310
+
311
+ ## Watch GitHub checks
312
+
313
+ `kxm gate github watch` polls a pull request's check runs and posts the signal for you. It reads the token from `GITHUB_TOKEN` or `GH_TOKEN`, and the workflow ID and callback secret as `kxm gate signal` does.
314
+
315
+ ```bash
316
+ export GITHUB_TOKEN="replace-with-a-checks-read-token"
317
+ kxm gate github watch --run-id run_7c187d2a0fde408ea408f0d8c53201c8 --stage-id checks \
318
+ --signal-key github-pr-42-checks --repo org/repo --pr 42 --required ci
319
+ ```
320
+
321
+ - It reports each required check as evidence `github.check:<name>`, so `--required ci` satisfies the `github.check:ci` requirement. Without `--required`, every check counts.
322
+ - `failure`, `cancelled`, `timed_out`, `action_required`, `stale`, and `startup_failure` fail the stage. Only `success` passes; a `neutral` or `skipped` check keeps the watcher waiting.
323
+ - It polls every 15 seconds for up to 30 minutes (`--interval-ms`, `--timeout-ms`). On timeout it posts a signed `failed` signal with summary `github_watch_timeout` and exits `4`. It never invents a pass.
324
+ - Each invocation uses a new delivery ID that includes the head commit, and its own retries reuse it. After a failure, start a new wait and a new watcher. Pass `--delivery-id` only when an outside supervisor must retry the same callback. Without a token it exits with `github_auth_unavailable`.
325
+
326
+ ## Keep a journal and export retrospectives
327
+
328
+ The coordinator records knowledge with `kxm_workflow_record` in ten categories: `plan`, `decision`, `contradiction`, `error`, `lesson`, `observation`, `hypothesis`, `experiment`, `state-change`, and `skill-candidate`. Lessons and skill candidates require evidence. Passing `stageId` binds the entry to that stage and its current attempt. The hub adds its own error entries for failed checkpoints and signals, timeouts, and early settlement.
329
+
330
+ When a run completes or fails, the hub writes a proposed retrospective to `.kxm/assets/retrospectives/<run-id>.json` and `.md`. Regenerate it with `kxm workflow export <run-id>`, and summarize learning across runs with `kxm_improvement_report`. [Continuous improvement](continuous-improvement.md) describes the review loop.
331
+
332
+ > [!CAUTION]
333
+ > The hub deletes a finished run and its journal 7 days after it ends. Export anything you want to keep before then. Signal deduplication for that run ends at the same time.
334
+
335
+ ## Degrade a peer quorum
336
+
337
+ If a stage's peer policy declares a lower `degradation.minProducers`, an operator holding the admin token can approve that lower minimum for the current attempt only with `kxm gate degrade`. Coordinators, peers, and callback secrets cannot. See [Peer provenance and quorum gates](provenance-gates.md#degrade-only-through-an-explicit-admin-decision).
338
+
339
+ ## Security notes
340
+
341
+ - The start secret authorizes creating runs; the callback secret authorizes only checkpointing a waiting run with a matching signal key. Keep them separate with `signalSecretEnv`.
342
+ - The signature covers the body, not the delivery-ID header, and there is no timestamp window. Anyone who captures a signed request can replay it under a new delivery ID, so terminate TLS and restrict ingress.
343
+ - Repository rules, human approvals, and harness permissions stay in charge of pushing, merging, and changing Jira.
344
+
345
+ ## Troubleshooting
346
+
347
+ | Symptom | Cause | Fix |
348
+ |---|---|---|
349
+ | The hub does not start: `Unexpected token` or `must be a JSON array` | `KXM_WEBHOOK_WORKFLOWS_FILE` points at YAML or a single object. | Point it at a JSON array such as the example. |
350
+ | `workflow.secret must be a string` | The variable named by `secretEnv` or `signalSecretEnv` is not set. | Export it in the hub's environment. |
351
+ | Jira retries and the hub returns `409` | The coordinator never registered. | Start the coordinator once, then redeliver. |
352
+ | `kxm workflow start` prints `started workflow accepted` and no run appears | The event or filter did not match (`204`). | Check the payload's event and filter path. |
353
+ | `stage <id> is missing required evidence: <key>` | A required key is missing or empty. | Supply every key from `kxm_workflow_get`. |
354
+ | `stage <id> is not currently active` or `workflow is waiting` | Wrong stage, or the stage is paused for a signal. | Use `currentStage`; send the signal instead of a checkpoint. |
355
+ | The run fails right after the coordinator replies | It settled before the last checkpoint. | Checkpoint every stage, or wait, before replying. |
356
+ | `kxm gate signal` prints `signed signal failed` | Wrong signal key, run not waiting, or a delivery-ID conflict. | Compare the run's `waiting.signalKey`; use a new delivery ID for a new result. |
357
+
358
+ ## Next steps
359
+
360
+ - Make the coordinator long-lived and unattended: [Run supervised Pi workers](pi-workers.md)
361
+ - Require verified replies from named reviewers before a stage passes: [Peer provenance and quorum gates](provenance-gates.md)
362
+ - Turn journals into improvements: [Continuous improvement](continuous-improvement.md)
363
+ - Every definition field: [Workflow definition reference](../reference/workflow-definitions.md); every route: [Hub HTTP API reference](../reference/http-api.md)
364
+ - The Runtime alternative: [Run your first workflow](../start/first-workflow.md)
@@ -7,25 +7,51 @@ project: "kxm"
7
7
  status: "accepted"
8
8
  owner: "@operator"
9
9
  created: "2026-09-14"
10
- updated: "2026-09-14"
10
+ updated: "2026-09-23"
11
11
  authority: "instruction"
12
12
  confidence: "verified"
13
- summary: "Explains safe pass-cli credential delivery and environment piping patterns that avoid LLM context leakage."
13
+ summary: "How the Steel client resolves its API key and how browser work keeps secrets out of model context."
14
14
  tags: ["browser", "credentials", "pass-cli", "security"]
15
- related: ["docs/browser-automation.md", "docs/agent-skills.md"]
15
+ related: ["docs/guides/browser-automation.md", "docs/guides/agent-skills.md"]
16
16
  ---
17
17
 
18
18
  # How are credentials retrieved without exposing them to the model?
19
19
 
20
- To protect passwords, MFA secrets, and API tokens from leaking into model reasoning traces, KXM enforces strict credential-reference boundaries:
20
+ Browser automation keeps passwords, MFA secrets and API tokens out of model
21
+ prompts and reasoning traces. The model sees references to credentials, never
22
+ their values.
23
+
24
+ ## How the Steel client finds its API key
25
+
26
+ `resolveSteelConfig()` resolves each setting in this order, and never writes a
27
+ secret to disk or to a log:
28
+
29
+ | Setting | Resolved from |
30
+ |---|---|
31
+ | API URL | An explicit override, then `STEEL_API_URL`, then a built-in default |
32
+ | API key | An explicit override, then `STEEL_API_KEY`, then a `pass-cli` lookup |
33
+ | Session viewer URL | An explicit override, then `STEEL_UI_URL`, then `<api-url>/ui` |
34
+
35
+ The built-in default URL and the `pass-cli` lookup point at the maintainers'
36
+ own Steel deployment and vault. Set `STEEL_API_URL` and `STEEL_API_KEY` for
37
+ yours, and set `USE_PASS_CLI=false` to turn the lookup off.
21
38
 
22
39
  ## Mechanisms
23
40
 
24
- 1. **Authoritative Store**: All secrets reside in `pass-cli` (Proton Pass).
25
- 2. **In-Process Environment Piping**:
26
- - Automated test scripts use `pass-cli run -- npm test` or retrieve credentials directly into child process memory via standard environment variables.
27
- - The LLM prompt only receives credential references (e.g., `vault: "AI Provider Keys", item: "Steel Browser (KontextMind DOKS)"`), never raw secret values.
28
- 3. **Log Sanitization**:
29
- - The KXM browser client strips API keys and token parameters (`apiKey=[REDACTED]`, `steel_[REDACTED]`) before logging or emitting outputs.
30
- 4. **Human Handoff for High-Privilege Auth**:
31
- - For sensitive production accounts or MFA, the agent never touches the credential at all; it invokes `kxm-browser-takeover` and lets the human authenticate directly in the UI.
41
+ 1. **One secret store.** Keep secrets in a secret manager, for example Proton
42
+ Pass through `pass-cli`, and load them into the environment of the process
43
+ that needs them:
44
+
45
+ ```bash
46
+ # Runs the tests with secrets injected for this process only.
47
+ pass-cli run -- npm test
48
+ ```
49
+
50
+ 2. **References, not values.** A prompt receives a credential reference such
51
+ as `vault: "<vault>", item: "<item>"`, never the secret itself.
52
+ 3. **Log sanitization.** The KXM browser client redacts `apiKey=` query values,
53
+ `steel_…` keys, and any field named like a key, secret, token, auth or
54
+ password before it logs or returns output.
55
+ 4. **Human handoff for high-privilege sign-in.** For production accounts or
56
+ MFA, the agent does not touch the credential at all. It follows the
57
+ `kxm-browser-takeover` skill and lets you sign in directly.
@@ -7,29 +7,30 @@ project: "kxm"
7
7
  status: "accepted"
8
8
  owner: "@operator"
9
9
  created: "2026-09-14"
10
- updated: "2026-09-14"
10
+ updated: "2026-09-23"
11
11
  authority: "instruction"
12
12
  confidence: "verified"
13
- summary: "Guide to capturing specific DOM elements, attaching visual annotations, and submitting structured change feedback to the agent."
13
+ summary: "Capture one DOM element, attach annotations, and send structured change feedback to an agent."
14
14
  tags: ["browser", "annotation", "screenshot", "feedback", "ui"]
15
- related: ["docs/browser-automation.md", "docs/prompts/browser-annotate-feedback.md"]
15
+ related: ["docs/guides/browser-automation.md", "docs/prompts/browser-annotate-feedback.md"]
16
16
  ---
17
17
 
18
18
  # How do I capture a UI section and annotate changes for an agent?
19
19
 
20
- When reviewing a web interface in a Steel session, you can isolate a specific component, attach annotations, and deliver structured change requests directly back to an agent.
20
+ When you review a web interface in a Steel session, you can isolate one
21
+ component, annotate it, and send a structured change request back to an agent.
21
22
 
22
23
  ## Workflow
23
24
 
24
- 1. **Capture the Component**:
25
- Use Playwright element screenshotting to crop only the affected container:
25
+ 1. **Capture the component.** Use a Playwright element screenshot to crop only
26
+ the affected container:
26
27
 
27
28
  ```typescript
28
- await page.locator('.billing-card').screenshot({ path: '.kxm/artifacts/browser/billing-card.png' });
29
+ await page.locator(".billing-card").screenshot({ path: ".kxm/artifacts/browser/billing-card.png" });
29
30
  ```
30
31
 
31
- 2. **Draft the Annotation Feedback**:
32
- Record the target selector, observed issues, and required fixes:
32
+ 2. **Write the annotation feedback.** Record the target selector, the observed
33
+ issues and the required fixes:
33
34
 
34
35
  ```typescript
35
36
  import { createAnnotationFeedback, formatAnnotationFeedbackPrompt } from "@kontextmind/kxm/runtime";
@@ -41,9 +42,9 @@ When reviewing a web interface in a Steel session, you can isolate a specific co
41
42
  overallSummary: "Billing tier layout breaks on mobile viewport",
42
43
  annotations: [
43
44
  {
44
- label: "Tier Name Overflow",
45
+ label: "Tier name overflow",
45
46
  selector: ".tier-title",
46
- note: "Truncate or wrap long tier titles with ellipsis",
47
+ note: "Truncate or wrap long tier titles with an ellipsis",
47
48
  severity: "fix"
48
49
  }
49
50
  ],
@@ -56,5 +57,6 @@ When reviewing a web interface in a Steel session, you can isolate a specific co
56
57
  const prompt = formatAnnotationFeedbackPrompt(feedback);
57
58
  ```
58
59
 
59
- 3. **Send to the Agent**:
60
- Feed the rendered prompt into the agent session or KXM workflow run. The agent reads the screenshot, navigates to the source code, applies the changes, and verifies the result with Playwright.
60
+ 3. **Send it to the agent.** Put the rendered prompt into the agent session or
61
+ the workflow run. The agent reads the screenshot, finds the source, applies
62
+ the changes, and verifies the result with Playwright.
@@ -7,21 +7,23 @@ project: "kxm"
7
7
  status: "accepted"
8
8
  owner: "@operator"
9
9
  created: "2026-09-14"
10
- updated: "2026-09-14"
10
+ updated: "2026-09-23"
11
11
  authority: "instruction"
12
12
  confidence: "verified"
13
- summary: "Guide to attaching Playwright tests to an active remote Steel browser session using chromium.connectOverCDP()."
13
+ summary: "Attach Playwright to an active remote Steel browser session with chromium.connectOverCDP()."
14
14
  tags: ["browser", "playwright", "cdp", "steel", "testing"]
15
- related: ["docs/browser-automation.md", "docs/kb/why-automation-opened-different-browser.md"]
15
+ related: ["docs/guides/browser-automation.md", "docs/kb/why-automation-opened-different-browser.md"]
16
16
  ---
17
17
 
18
18
  # How do I connect Playwright to the existing Steel session?
19
19
 
20
- To run Playwright tests against self-hosted Steel on DOKS instead of a local browser:
20
+ Run Playwright against a remote Steel session instead of a local browser by
21
+ connecting over the Chrome DevTools Protocol (CDP).
21
22
 
22
- ## 1. Retrieve the CDP Endpoint
23
+ ## 1. Build the CDP endpoint
23
24
 
24
- Format the WebSocket CDP URL using the active session ID and API key:
25
+ Build the WebSocket CDP URL from the active session ID. The configuration comes
26
+ from `STEEL_API_URL` and `STEEL_API_KEY`:
25
27
 
26
28
  ```typescript
27
29
  import { formatCDPEndpoint, resolveSteelConfig } from "@kontextmind/kxm/runtime";
@@ -30,8 +32,11 @@ const config = resolveSteelConfig();
30
32
  const cdpUrl = formatCDPEndpoint({ id: sessionId, websocketUrl: "" }, config);
31
33
  ```
32
34
 
33
- The resulting URL will look like:
34
- `wss://steel.kontextmind.com/v1/devtools?sessionId=<SESSION_ID>&apiKey=<STEEL_API_KEY>`
35
+ The result has this shape. It carries the API key, so never log it:
36
+
37
+ ```text
38
+ wss://<steel-host>/v1/devtools?sessionId=<session-id>&apiKey=<steel-api-key>
39
+ ```
35
40
 
36
41
  ## 2. Connect in Playwright
37
42
 
@@ -40,15 +45,15 @@ import { test, expect, chromium } from "@playwright/test";
40
45
 
41
46
  test("execute test on remote steel session", async () => {
42
47
  const browser = await chromium.connectOverCDP(process.env.STEEL_CDP_URL!);
43
-
44
- // Use existing context or create one
48
+
49
+ // Use the existing context and page, or create them.
45
50
  const context = browser.contexts()[0] || await browser.newContext();
46
51
  const page = context.pages()[0] || await context.newPage();
47
52
 
48
53
  await page.goto("https://app.example.com");
49
54
  await expect(page.getByRole("heading", { level: 1 })).toBeVisible();
50
55
 
51
- // Disconnecting closes the Playwright CDP socket without terminating the remote container
56
+ // Closing drops the CDP socket; it does not end the remote session.
52
57
  await browser.close();
53
58
  });
54
59
  ```
@@ -7,48 +7,58 @@ project: "kxm"
7
7
  status: "accepted"
8
8
  owner: "@operator"
9
9
  created: "2026-09-14"
10
- updated: "2026-09-14"
10
+ updated: "2026-09-23"
11
11
  authority: "instruction"
12
12
  confidence: "verified"
13
- summary: "Procedures for detecting and releasing stale or orphaned browser sessions on Steel."
13
+ summary: "Find and release stale or orphaned browser sessions on Steel."
14
14
  tags: ["browser", "cleanup", "orphans", "troubleshooting"]
15
- related: ["docs/browser-automation.md", "docs/kb/why-authentication-disappeared.md"]
15
+ related: ["docs/guides/browser-automation.md", "docs/kb/why-authentication-disappeared.md"]
16
16
  ---
17
17
 
18
18
  # How do I recover an expired session or remove an orphaned browser?
19
19
 
20
- If an automation run crashed or disconnected without calling `/release`, a browser container may remain idling on DOKS.
20
+ If an automation run crashed or disconnected without releasing its session, the
21
+ browser can keep running on your Steel deployment until its timeout.
21
22
 
22
- ## 1. List Active Remote Sessions
23
+ ## 1. List active sessions
24
+
25
+ Load `STEEL_API_URL` and `STEEL_API_KEY` from your secret manager first. With
26
+ `pass-cli`, for example:
23
27
 
24
28
  ```bash
25
- STEEL_KEY=$(pass-cli item view --vault-name "AI Provider Keys" --item-title "Steel Browser (KontextMind DOKS)" --field STEEL_API_KEY)
29
+ export STEEL_API_URL="https://<steel-host>"
30
+ STEEL_API_KEY=$(pass-cli item view --vault-name "<vault>" --item-title "<item>" --field STEEL_API_KEY)
31
+ export STEEL_API_KEY
26
32
 
27
- curl -s https://steel.kontextmind.com/v1/sessions \
28
- -H "x-steel-api-key: $STEEL_KEY" | jq .
33
+ curl -s "$STEEL_API_URL/v1/sessions" \
34
+ -H "x-steel-api-key: $STEEL_API_KEY" | jq .
29
35
  ```
30
36
 
31
- ## 2. Release Orphaned Sessions
32
-
33
- To terminate a specific stale session:
37
+ ## 2. Release an orphaned session
34
38
 
35
39
  ```bash
36
- curl -s -X POST https://steel.kontextmind.com/v1/sessions/<SESSION_ID>/release \
37
- -H "x-steel-api-key: $STEEL_KEY"
40
+ curl -s -X POST "$STEEL_API_URL/v1/sessions/<session-id>/release" \
41
+ -H "x-steel-api-key: $STEEL_API_KEY"
38
42
  ```
39
43
 
40
- ## 3. Automatic Orphan Sweeping via KXM Client
44
+ ## 3. Sweep orphans with the KXM client
41
45
 
42
- The KXM client provides `checkOrphanedSessions(maxIdleMs)` to automate this:
46
+ `checkOrphanedSessions(maxIdleMs)` returns two kinds of session: live or idle
47
+ sessions this client does not track that have run longer than the limit, and
48
+ tracked sessions idle longer than the limit, unless a person has taken over.
49
+ `releaseSession()` releases one:
43
50
 
44
51
  ```typescript
45
52
  import { SteelClient } from "@kontextmind/kxm/runtime";
46
53
 
47
54
  const client = new SteelClient();
48
- const orphans = await client.checkOrphanedSessions(600000); // > 10 min idle
55
+ const orphans = await client.checkOrphanedSessions(600000); // idle over 10 minutes
49
56
 
50
57
  for (const sessionId of orphans) {
51
58
  console.log(`Releasing orphaned session: ${sessionId}`);
52
59
  await client.releaseSession(sessionId);
53
60
  }
54
61
  ```
62
+
63
+ It returns an empty list when the Steel API request fails, so an empty result
64
+ does not prove there are no orphans. Check with the `curl` call above.