@aron98/load-lock 0.1.2 → 1.1.0

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 (53) hide show
  1. package/README.md +40 -12
  2. package/demo/demo.sh +1 -1
  3. package/dist/adapters/opencode/plugin.js +39 -18
  4. package/dist/adapters/opencode/tui.d.ts +61 -0
  5. package/dist/adapters/opencode/tui.js +358 -0
  6. package/dist/cli/hook.js +35 -1
  7. package/dist/cli/install.js +708 -24
  8. package/dist/cli/main.js +130 -61
  9. package/dist/cli/top.d.ts +7 -0
  10. package/dist/cli/top.js +99 -0
  11. package/dist/core/daemon.d.ts +25 -4
  12. package/dist/core/daemon.js +67 -30
  13. package/dist/core/persist.js +5 -12
  14. package/dist/core/state.js +7 -0
  15. package/dist/dashboard/feed.d.ts +74 -0
  16. package/dist/dashboard/feed.js +256 -0
  17. package/dist/dashboard/frame.d.ts +13 -0
  18. package/dist/dashboard/frame.js +95 -0
  19. package/dist/dashboard/view.d.ts +149 -0
  20. package/dist/dashboard/view.js +214 -0
  21. package/dist/mcp/server.d.ts +2 -1
  22. package/dist/mcp/server.js +54 -19
  23. package/dist/policy/text.d.ts +1 -1
  24. package/dist/policy/text.js +3 -0
  25. package/dist/shared/client.d.ts +10 -0
  26. package/dist/shared/client.js +41 -14
  27. package/dist/shared/protocol.d.ts +29 -0
  28. package/dist/shared/transport.d.ts +40 -0
  29. package/dist/shared/transport.js +136 -0
  30. package/dist/shared/wait-turn.d.ts +29 -0
  31. package/dist/shared/wait-turn.js +315 -0
  32. package/dist/test/cli.test.d.ts +1 -0
  33. package/dist/test/cli.test.js +99 -0
  34. package/dist/test/dashboard.test.d.ts +1 -0
  35. package/dist/test/dashboard.test.js +406 -0
  36. package/dist/test/hook.test.js +53 -3
  37. package/dist/test/install.test.d.ts +1 -0
  38. package/dist/test/install.test.js +658 -0
  39. package/dist/test/mcp.test.js +36 -1
  40. package/dist/test/platform.test.d.ts +1 -0
  41. package/dist/test/platform.test.js +193 -0
  42. package/dist/test/testutil.js +4 -1
  43. package/dist/test/tui.test.d.ts +1 -0
  44. package/dist/test/tui.test.js +89 -0
  45. package/dist/test/wait-turn.test.d.ts +1 -0
  46. package/dist/test/wait-turn.test.js +376 -0
  47. package/docs/ADAPTERS.md +5 -2
  48. package/docs/AGENT_GUIDE.md +34 -1
  49. package/docs/ARCHITECTURE.md +25 -5
  50. package/docs/COMPATIBILITY.md +217 -26
  51. package/docs/OPERATIONS.md +335 -8
  52. package/package.json +25 -1
  53. package/skills/load-lock/SKILL.md +4 -0
@@ -11,6 +11,10 @@ Status labels mean exactly this:
11
11
  against the harness's documented hook/tool JSON contracts
12
12
  (`src/test/hook.test.ts`, `src/test/mcp.test.ts`), but the harness itself
13
13
  was not installed during verification.
14
+ - **Implemented + config-tested** — installer shipped; install/uninstall
15
+ round-trip tested against a temp HOME and validated against the harness's
16
+ documented config schemas (`src/test/install.test.ts`), but the harness
17
+ itself was not installed during verification.
14
18
  - **Proposed / fallback** — research-verified harness capabilities only; no
15
19
  adapter shipped beyond the generic MCP path.
16
20
 
@@ -31,14 +35,14 @@ Status labels mean exactly this:
31
35
  | Harness | MCP tools | Policy injection | Compaction restore | Idle wakeup | Async push into context | Cleanup fidelity | Overall |
32
36
  | --- | --- | --- | --- | --- | --- | --- | --- |
33
37
  | **OpenCode** (v2) | Native plugin, `load_lock_*` (better than MCP) | Context hook every model call | Yes (hook re-fires per model call) | Yes — `session.synthetic` at execution boundary (live-verified; never interrupts in-flight shell) | Yes (live-verified) | Full: `run_with_permit` tracked jobs + session heartbeats | **Implemented + live-verified** |
34
- | **Claude Code** | MCP `mcp__load-lock__*` (user scope) | `SessionStart` hook plain-stdout context | Yes (SessionStart matcher `compact`) | Yes — `Stop` hook decision-block + async `await-offer` (`asyncRewake`) | Yes, via asyncRewake (contract-tested) | Full via MCP `run_with_permit` + server heartbeats | **Implemented + contract-tested** |
35
- | **Codex CLI** | MCP `mcp__load-lock__*` (config.toml) | `SessionStart` + `SubagentStart` additionalContext; `~/.codex/AGENTS.md` | Yes (SessionStart matcher `compact`) | Yes — `Stop` hook `decision:"block"` continuation | Partial: async background hooks do NOT start a new turn (documented limitation); stop-hook covers turn boundaries | Full via MCP `run_with_permit` + server heartbeats | **Implemented + contract-tested** (hooks need `/hooks` trust review) |
36
- | **Gemini CLI** | Yes (`settings.json` mcpServers) | `GEMINI.md` global (config `context.fileName`) | Via global GEMINI.md (always loaded) | No hook mechanism exists | No | Via MCP `run_with_permit` only | Proposed (MCP fallback) |
37
- | **GitHub Copilot CLI** | Yes (`copilot mcp add`) | AGENTS.md/GEMINI.md discovery + custom instructions | Via global instructions file | Hooks reported in ecosystem content but **not verified in official docs** — treat as unavailable | Unverified | Via MCP `run_with_permit` only | Proposed (MCP fallback) |
38
- | **Cursor** | Yes (`.cursor/mcp.json` + global) | `.cursor/rules/*.md` Always rules | Rules re-apply (Always) | `stop` hook exists but **beta** with a less-documented JSON contract — not shipped | Beta/unverified | Via MCP `run_with_permit` only | Proposed (MCP fallback; hook tier possible later) |
39
- | **Cline** | Yes (`cline_mcp_settings.json`) | `.clinerules/` + `~/.cline/rules` | Rules always loaded | No hooks | No | Via MCP `run_with_permit` only | Proposed (MCP fallback) |
40
- | **Roo Code** | Yes (Roo MCP settings) | `.roo/` rule dirs | Rules always loaded | No hooks | No | Via MCP `run_with_permit` only | Proposed (MCP fallback) |
41
- | **Goose** | Yes (`~/.config/goose/config.yaml`) | `.goosehints` (global + project) | Hints always loaded | No hooks | No | Via MCP `run_with_permit` only | Proposed (MCP fallback) |
38
+ | **Claude Code** | MCP `mcp__load-lock__*` (user scope) — live-verified round-trip | `SessionStart` hook plain-stdout context (live-verified: model quoted the brief verbatim) | Yes (SessionStart matcher `compact`) — live-verified after `/compact` | Yes — `Stop` hook decision-block + async `await-offer` (`asyncRewake`), both live-verified | Yes, via asyncRewake (live-verified; the wake text itself is not rendered by the TTY, but the wake drives the full claim lifecycle) | Full via MCP `run_with_permit` + server heartbeats (live-verified incl. a job outliving the claude process) | **Implemented + live-verified** |
39
+ | **Codex CLI** | MCP `mcp__load-lock__*` (config.toml) | `SessionStart` + `SubagentStart` additionalContext; `~/.codex/AGENTS.md` | Yes (SessionStart matcher `compact`) | Yes — `Stop` hook `decision:"block"` continuation | Partial: async background hooks do NOT start a new turn (documented limitation); stop-hook covers turn boundaries | Full via MCP `run_with_permit` + server heartbeats | **Implemented + live-verified** (interactive TUI in Docker; exec-mode MCP blocked upstream — see Tier 2 notes) |
40
+ | **Gemini CLI** | Yes (`settings.json` mcpServers, first-class installer) | `GEMINI.md` global (config `context.fileName`) | Via global GEMINI.md (always loaded) | No hooks — `wait_for_turn` bounded sleep is the primary idle mechanism | No | Via MCP `run_with_permit` only | **Implemented (fallback tier, config-tested)** |
41
+ | **GitHub Copilot CLI** | Yes (`~/.copilot/mcp-config.json`, merge-only installer; verify with `copilot mcp list`) | `~/.copilot/copilot-instructions.md` (marked section) | Via global instructions file | Hooks reported in ecosystem content but **not verified in official docs** — none installed; `wait_for_turn` bounded sleep is the primary idle mechanism | Unverified | Via MCP `run_with_permit` only | **Implemented (fallback tier, config-tested)** |
42
+ | **Cursor** | Yes (project `.cursor/mcp.json`, merge-only installer) | `.cursor/rules/load-lock.mdc` (dedicated Always-apply rule file) | Rules re-apply (Always) | Yes — `stop` hook `{"followup_message":...}` continuation (hooks v1, officially documented) | No async watcher shipped; stop hook covers turn boundaries | Via MCP `run_with_permit` only | **Implemented + contract-tested** (hooks tier; not live-verified) |
43
+ | **Cline** | Yes (`cline_mcp_settings.json`, merge-only installer) | `~/.cline/rules/load-lock.md` (dedicated rule file) | Rules always loaded | No hooks — `wait_for_turn` bounded sleep is the primary idle mechanism | No | Via MCP `run_with_permit` only | **Implemented (fallback tier, config-tested)** |
44
+ | **Roo Code** | Yes (project `.roo/mcp.json` + existing global `mcp_settings.json`, merge-only installer) | `~/.roo/rules/load-lock.md` (dedicated rule file) | Rules always loaded | No hooks — `wait_for_turn` bounded sleep is the primary idle mechanism | No | Via MCP `run_with_permit` only | **Implemented (fallback tier, config-tested)** |
45
+ | **Goose** | Yes (`~/.config/goose/config.yaml` `extensions` block, merge-only installer) | `.goosehints` (global, marked section) | Hints always loaded | No hooks — `wait_for_turn` bounded sleep is the primary idle mechanism | No | Via MCP `run_with_permit` only | **Implemented (fallback tier, config-tested)** |
42
46
  | **Aider** | **No MCP support** | CONVENTIONS.md / `.aider.conf.yml` | Read each run | N/A | N/A | CLI-only: same-shell `load-lock acquire ... && cmd; load-lock release ...` (manual) | Proposed (manual CLI tier) |
43
47
 
44
48
  ## How each integration tier works
@@ -52,14 +56,23 @@ Global plugin (`load-lock install --harness opencode`) registers:
52
56
  - a `load-lock` skill with the full agent guide,
53
57
  - daemon event watcher → `ctx.session.synthetic` offer messages with the
54
58
  real claim token, delivered at execution boundaries,
55
- - per-session heartbeats.
59
+ - per-session heartbeats,
60
+ - the package's `./tui` export: a **live lock-status dashboard panel**
61
+ (issue #10) inside the opencode TUI — `/load-lock` opens the
62
+ `load-lock.dashboard` session panel in-window (issue #24: side panel,
63
+ not full-screen) with pools, holder cards, offer countdowns, queue,
64
+ paused, event feed, `inspect_machine`; a clickable sidebar widget
65
+ (`load-lock 0/3`, health-colored; `gl` as the mouse-free fallback) and
66
+ a home-footer `lock 1/1·Q2` indicator; and an `x` force-release flow
67
+ gated behind an explicit select + confirm (the panel itself is read-only
68
+ and never acquires a permit).
56
69
 
57
70
  Verified live on opencode v2.0.29: plugin load, policy visible to the model,
58
71
  tool round-trip (acquire → queued → wakeup → claim → release), execution-
59
72
  boundary delivery without interrupting a running `sleep`, skill visible,
60
73
  and graceful handling of a deliberately missed offer (paused → cancel).
61
74
 
62
- ### Tier 2 — Claude Code / Codex CLI (hooks + MCP, contract-tested)
75
+ ### Tier 2 — Claude Code / Codex CLI (hooks + MCP, live-verified)
63
76
 
64
77
  `load-lock install --harness claude-code|codex` configures the MCP server
65
78
  (user scope) plus hooks:
@@ -76,20 +89,152 @@ and graceful handling of a deliberately missed offer (paused → cancel).
76
89
  shown); the agent claims via `pending`/`claim`.
77
90
 
78
91
  Contract tests cover all hook stdin/stdout shapes against the documented
79
- harness contracts. Live verification pending a machine with the harness
80
- installed. Codex requires the user to trust the hooks once via `/hooks`
81
- (the installer prints this).
92
+ harness contracts. Claude Code is additionally **live-verified** (in Docker,
93
+ via `docker/run-claude-verify.sh` against the real `claude` CLI): SessionStart
94
+ policy brief in context at startup; the same brief re-injected after
95
+ `/compact` (the model quoted the fresh "load-lock now:" line, not a stale
96
+ copy); the full MCP round-trip acquire → queued → offer → claim →
97
+ `run_with_permit` → auto-release; the `Stop` hook decision-block live (async
98
+ watcher disabled so the stop path was exercised directly); the async
99
+ `await-offer` (`asyncRewake`) watcher waking an idle interactive session,
100
+ which then claimed and released; a `Task`-tool subagent using the MCP tools
101
+ and seeing the policy; and a `run_with_permit` background job that
102
+ outlived the `claude` process and was auto-released. One nuance: the TUI does
103
+ not render the async watcher's stderr wake text — the wake still works (the
104
+ session resumes and the claim round-trip succeeds), only the message is not
105
+ shown.
82
106
 
83
- ### Tier 3 — MCP-only fallback (Gemini CLI, Copilot CLI, Cursor, Cline, Roo Code, Goose)
107
+ Codex CLI (0.163.0) is live-verified end-to-end in Docker
108
+ (`docker/verify-codex.sh`, driven through a real pty TUI session via
109
+ `docker/codex-pty.py`): SessionStart policy injection visible to the model,
110
+ MCP `status`/`acquire`/`claim`/`release` round-trip, FIFO queueing behind a
111
+ competing CLI holder, and Stop-hook `decision:"block"` wakeup that continues
112
+ the session with the claim instruction. The verification drives the real
113
+ interactive hook-trust review (the "Hooks need review" dialog) once —
114
+ `--dangerously-bypass-hook-trust` is deliberately NOT used because it forces
115
+ codex embedded mode, which never injects MCP tools (real users trust the
116
+ hooks once via `/hooks`; the installer prints this). Note: async background
117
+ hooks do not wake an idle Codex session — the Stop hook covers turn
118
+ boundaries only (verified as such).
119
+
120
+ Two upstream/provider constraints shaped the harness:
121
+
122
+ - Headless `codex exec` cannot inject MCP tools at all
123
+ (openai/codex#38689), so live verification drives the interactive TUI
124
+ through a pty.
125
+ - codex 0.163.0 declares MCP tools to the Responses API using the
126
+ `namespace` tool type; the trustedtokens/GLM endpoint silently drops
127
+ namespace tools, which made MCP tools invisible to the model even in the
128
+ TUI. The verification container therefore fronts the API with
129
+ `docker/codex-api-shim.js`, which rewrites namespaces to flat
130
+ `mcp__<server>__<tool>` function tools on the wire (request, SSE
131
+ response, and conversation history). This is a verification-harness
132
+ workaround for the provider, not a change to load-lock itself.
133
+
134
+ ### Tier 2 (contract-tested) — Cursor (hooks + MCP)
135
+
136
+ Cursor 1.7 (Sept 2025) introduced hooks as a beta with an under-documented
137
+ JSON contract — the original reason Cursor sat on the MCP-fallback tier
138
+ (issue #6: "do not ship against an unstable contract"). As of 2026-10-19 the
139
+ official docs at https://cursor.com/docs/hooks fully document the hooks
140
+ system with no beta caveats (per-event reference, common input schema, exit
141
+ codes, config-source priority, cloud-agent support, third-party hook
142
+ compatibility), so the contract is now treated as stable and the adapter
143
+ below shipped (issue #6).
144
+
145
+ `load-lock install --harness cursor --project <dir>` wires (all
146
+ project-level, merge-only, reversible by uninstall):
147
+
148
+ - **MCP server** — `mcpServers.load-lock` (stdio, absolute node path) in
149
+ `<project>/.cursor/mcp.json` (documented project MCP config).
150
+ - **Stop hook** — flat hooks-v1 entry
151
+ `{"type":"command","command":"<node> <cli> hook cursor stop"}` in
152
+ `<project>/.cursor/hooks.json` (`{"version":1,"hooks":{"stop":[...]}}`;
153
+ `version` is added only if absent, user entries preserved).
154
+ - **Rule** — dedicated `<project>/.cursor/rules/load-lock.mdc` with YAML
155
+ frontmatter `alwaysApply: true` (note: plain `.md` files in
156
+ `.cursor/rules` are ignored by Cursor — the rule must be `.mdc`).
157
+
158
+ The handler's documented stop contract: stdin carries `workspace_roots`
159
+ (no `cwd`) and `loop_count` (number of prior stop-hook followups for the
160
+ conversation); stdout JSON `{"followup_message":"..."}` is auto-submitted as
161
+ the next user message — Cursor's loop-style idle wakeup, analogous to
162
+ Claude/Codex `decision:"block"`. The handler is silent unless the daemon has
163
+ an offer matching `workspace_roots[0]`, and silent when `loop_count > 0`
164
+ (Cursor's equivalent of `stop_hook_active` — prevents wakeup loops;
165
+ Cursor's own `loop_limit` default of 5 per script is a second backstop).
166
+
167
+ **Verification status: contract-tested, not live-verified.** The stdin
168
+ parsing, followup payload, loop guard and silence paths are tested against
169
+ the documented contract in `src/test/hook.test.ts`; the install/uninstall
170
+ round-trip (user config preserved, idempotent re-install, clean bare-project
171
+ uninstall) is tested in `src/test/install.test.ts`. No Cursor install
172
+ exists on the verification machine, so end-to-end behavior inside the real
173
+ editor is unverified — the installer output says so explicitly. Sources:
174
+ official docs (https://cursor.com/docs/hooks, checked 2026-10-19), 1.7
175
+ release announcement (forum.cursor.com, Sept 2025), GitButler deep-dive
176
+ (Sept 2025), InfoQ coverage (Oct 2025).
177
+
178
+ ### Tier 3 — MCP-only fallback (Gemini CLI, Copilot CLI, Cline, Roo Code, Goose)
84
179
 
85
180
  The MCP server itself carries the policy in its `instructions`, every tool
86
181
  result surfaces pending offers (tool-boundary delivery), and
87
- `inspect_machine`/`get_config`/`configure` work unchanged. Where the harness
88
- supports global instruction files (GEMINI.md / AGENTS.md / rules / hints),
89
- `load-lock install --harness generic` prints a snippet to add. Missing:
90
- idle wakeup and automatic context re-injection — queued agents discover
91
- offers at their next tool call or via a bounded `wait`. This is the honest
92
- fallback; we do not pretend these harnesses got push notifications.
182
+ `inspect_machine`/`get_config`/`configure` work unchanged.
183
+
184
+ **Every tier-3 MCP harness ships a first-class merge-only installer**
185
+ (Gemini CLI landed with issue #1; Copilot CLI, Cline, Roo Code
186
+ and Goose with issue #2). Cursor moved up to the hooks tier with its own
187
+ installer in issue #6 (see the Cursor section above). Each installer merges
188
+ into the harness's real config file
189
+ and never deletes anything the user wrote; `load-lock uninstall` removes
190
+ exactly the entries the installer added:
191
+
192
+ - **Gemini CLI** — `mcpServers.load-lock` (stdio) into
193
+ `~/.gemini/settings.json` + a marked policy section in
194
+ `~/.gemini/GEMINI.md`.
195
+ - **Copilot CLI** — `mcpServers.load-lock` (`type: local`) into
196
+ `~/.copilot/mcp-config.json` (verify with `copilot mcp list`) + a marked
197
+ section in `~/.copilot/copilot-instructions.md`. Hooks are reported in
198
+ ecosystem content but unverified in official docs, so the installer ships
199
+ none — no idle wakeup is claimed.
200
+ - **Cline** — `mcpServers.load-lock` into the MCP settings file
201
+ (`~/.cline/data/settings/cline_mcp_settings.json`, or the path in
202
+ `CLINE_MCP_SETTINGS_PATH`) + a dedicated `~/.cline/rules/load-lock.md`
203
+ (our own file, so uninstall is a simple delete).
204
+ - **Roo Code** — `mcpServers.load-lock` into `<project>/.roo/mcp.json`
205
+ (pass `--project`) plus every **existing** global `mcp_settings.json`
206
+ (VS Code globalStorage for Roo/Zoo; existing files only — the installer
207
+ never guesses storage paths) + a dedicated `~/.roo/rules/load-lock.md`.
208
+ - **Goose** — an `extensions.load-lock` stdio block (marker-tagged
209
+ block-mapping YAML) into `~/.config/goose/config.yaml`
210
+ (XDG_CONFIG_HOME respected) + a marked section in
211
+ `~/.config/goose/.goosehints`.
212
+
213
+ Install/uninstall round-trips for all five are tested against a temp HOME
214
+ and validated against the harnesses' documented config schemas
215
+ (`src/test/install.test.ts`), but not live-verified against real
216
+ harnesses — hence *config-tested*.
217
+
218
+ `load-lock install --harness generic` still prints snippets for harnesses
219
+ without a dedicated installer.
220
+ Idle policy: these harnesses get no push notifications, so the PRIMARY
221
+ idle mechanism is the MCP `wait_for_turn` tool — a bounded, honest sleep
222
+ until it is the caller's turn. Queued agents call
223
+ `mcp__load-lock__wait_for_turn` (optionally with `timeout_ms`, default
224
+ 600000, capped by the machine's `maxTurnWaitMs` config) and get a
225
+ structured result on every path: `granted` (the offer was auto-claimed —
226
+ `permit_token` included), `still_queued` (timeout, with queue position
227
+ and holders), `paused`/`cancelled`, or `not_found` (e.g. the daemon
228
+ restarted). The wait itself holds no permit and consumes no capacity;
229
+ harness tool cancellation aborts it cleanly. (Codex CLI note: async
230
+ background hooks never wake an idle session there either — the Stop hook
231
+ covers turn boundaries only — so the same `wait_for_turn` sleep is the
232
+ honest pattern for Codex subagents without hook wiring.)
233
+
234
+ Still missing on this tier: automatic context re-injection — there is no
235
+ push channel, so a woken agent relies on tool results (which carry
236
+ pending offers) rather than harness messages. We do not pretend these
237
+ harnesses got push notifications.
93
238
 
94
239
  ### Tier 4 — Aider (manual CLI)
95
240
 
@@ -97,6 +242,28 @@ No MCP/hooks; nothing to install. Agents (or humans) use the CLI directly in
97
242
  the shell: `load-lock acquire ... && load-lock run -- <cmd>` handles
98
243
  acquire→run→release in one command line.
99
244
 
245
+ ## Live dashboard surfaces (issue #10)
246
+
247
+ `load-lock dashboard` is portable and harness-agnostic: any integration that
248
+ spawns the daemon gets the same live dashboard in any terminal. The table
249
+ below is deliberately honest about what each harness gets **inside its own
250
+ UI** — where no UI surface exists, we say so rather than claim parity.
251
+
252
+ | Harness | In-harness dashboard | Also available |
253
+ | --- | --- | --- |
254
+ | **OpenCode** | Native TUI panel: `/load-lock` opens `load-lock.dashboard` **in-window** as a side panel (also opened by the `gl` binding or a click on the sidebar widget) with pools, holder cards, offer claim-window countdowns, queue, paused, event feed, `inspect_machine`; home-footer `lock 1/1` indicator on the home screen plus a clickable, health-colored `load-lock 0/3` widget in the session sidebar; `x` force-release behind select + confirm. Implemented as the package's `./tui` companion plugin (main plugin unchanged). Live-verified: opencode 2.0.29 (panel render, palette/slash command, confirm-gated force-release) and opencode 2.0.32 in a real pty (sidebar widget render, click-to-open, `gl`, in-window presentation). | `load-lock dashboard` (alias `top`) |
255
+ | **Claude Code** | None in-app — Claude Code has no plugin UI surface. The model can report status via the `load_lock_status` MCP tool at tool boundaries, and each `SessionStart` hook injects a one-line machine summary. | `load-lock dashboard` (any terminal) |
256
+ | **Codex CLI** | None in-app — same reason. `load_lock_status` MCP tool; `SessionStart`/`SubagentStart` machine summary. | `load-lock dashboard` (alias `top`) |
257
+ | **Gemini CLI** | None in-app (no hooks, no UI plugin surface). `load_lock_status` MCP tool at tool boundaries. | `load-lock dashboard` (alias `top`) |
258
+ | **Copilot CLI, Cursor, Cline, Roo Code, Goose** | None in-app. MCP `status` tool where the MCP server is configured. | `load-lock dashboard` (alias `top`) |
259
+ | **Aider** | None (no MCP either). | `load-lock dashboard` (alias `top`) |
260
+
261
+ All dashboard surfaces are **read-only viewers that never acquire a
262
+ permit**; the single admin action (force-release) is gated behind an
263
+ explicit confirm. Data plumbing (event-cursor resume, daemon-restart
264
+ reconnect, view-model shaping, frame rendering) is covered by
265
+ `src/test/dashboard.test.ts`.
266
+
100
267
  ## Session identity per harness
101
268
 
102
269
  | Harness | Identity | Notes |
@@ -104,13 +271,37 @@ acquire→run→release in one command line.
104
271
  | OpenCode | plugin context `sessionID` (also MCP `_meta` when used) | automatic |
105
272
  | Claude Code | hooks get `session_id`; MCP tools take explicit `agent_id` | policy instructs agents to pass it |
106
273
  | Codex | hooks get `session_id`; MCP tools take explicit `agent_id` | same |
274
+ | Cursor | hooks get `conversation_id` (no `cwd` — `workspace_roots[0]` identifies the request); MCP tools take explicit `agent_id` | same |
107
275
  | Tier 3 | explicit `agent_id` argument | falls back to per-server-process id |
108
276
 
277
+ ## Platform matrix
278
+
279
+ | Platform | Status | Notes |
280
+ | --- | --- | --- |
281
+ | **Linux** | Implemented + live-verified | Primary target; all daemon/protocol/CLI tests and the 3-worktree demo run here. `/proc`-based liveness with start-time PID-reuse defense. |
282
+ | **macOS** | Implemented, limitations documented | Works; liveness falls back from `/proc` start times to `kill(pid, 0)` (weaker PID-reuse defense), no cgroup limits. See OPERATIONS.md. |
283
+ | **Windows** | Implemented, unit-tested on Linux — **not Windows-verified** | Named-pipe transport (`\\.\pipe\load-lock`), `%LOCALAPPDATA%` state dir, `kill(pid, 0)` liveness, `taskkill /T` job-tree kills. Covered by `src/test/platform.test.ts` via platform injection; no Windows machine was available for end-to-end verification. See the Windows procedure in OPERATIONS.md. |
284
+
109
285
  ## Verified vs proposed — summary counts
110
286
 
111
- - Live-verified: OpenCode (full adapter, including wakeup).
112
- - Contract-tested: Claude Code, Codex CLI hook/MCP contracts.
113
- - Core daemon/protocol/CLI: 32 automated tests + scripted 3-worktree demo
114
- (three independent agent processes, real git worktrees, one pool).
115
- - Proposed (researched capabilities, generic adapter only): Gemini CLI,
116
- Copilot CLI, Cursor, Cline, Roo Code, Goose, Aider.
287
+ - Live-verified: OpenCode (full adapter, including wakeup); Claude Code
288
+ (full adapter in Docker: policy injection, compact restore, Stop-hook and
289
+ asyncRewake wakeup paths, subagents, background-job cleanup); Codex CLI
290
+ (Docker, interactive TUI: policy injection, MCP round-trip, FIFO queueing
291
+ behind a CLI holder, Stop-hook decision-block wakeup; exec-mode MCP
292
+ injection blocked upstream — see Tier 2 notes).
293
+ - Contract-tested: Cursor (hooks-v1 stop-hook adapter + installer, tested
294
+ against the officially documented hooks JSON contract; no Cursor install
295
+ on the verification machine — see the Cursor section under Tier 2).
296
+ - Config-tested: Gemini CLI, Copilot CLI, Cline, Roo Code, Goose (installer
297
+ round-trips, temp HOME; real harnesses not installed on the verification
298
+ machine).
299
+ - Core daemon/protocol/CLI: 68 automated tests + scripted 3-worktree demo
300
+ (three independent agent processes, real git worktrees, one pool), including
301
+ 9 dashboard tests (view-model shaping, event-cursor resume across a daemon
302
+ restart, reconnect, and a `load-lock dashboard --once` render smoke test) and
303
+ 9 bounded-wait tests (multi-process `wait_for_turn`: FIFO wake with
304
+ auto-claim, timeout/pause/cancel/restart/abort paths, `maxTurnWaitMs` cap,
305
+ MCP snake_case round trip).
306
+ - Proposed (researched capabilities, generic adapter only):
307
+ Aider.