@staix/agent-hub 0.12.43 → 0.12.45

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 (180) hide show
  1. package/CHANGELOG.md +38 -3
  2. package/README.md +3 -3
  3. package/THIRD_PARTY_NOTICES.md +29 -0
  4. package/docs/adding-an-ai-tool.md +228 -0
  5. package/docs/agent-notes/README.md +14 -0
  6. package/docs/agent-notes/adapters.md +61 -42
  7. package/docs/agent-notes/budget.md +2 -2
  8. package/docs/agent-notes/daemon.md +16 -10
  9. package/docs/agent-notes/local-worker.md +4 -3
  10. package/docs/agent-notes/tasks.md +13 -6
  11. package/docs/agent-notes/tests.md +9 -7
  12. package/docs/operations.md +163 -12
  13. package/docs/quickstart.md +10 -2
  14. package/docs/sdlc.md +26 -8
  15. package/docs/security.md +11 -6
  16. package/docs/specs/2026-09-19-agent-hub-design.md +273 -8
  17. package/docs/verification/2026-10-21-0.12.44.md +54 -0
  18. package/docs/verification/2026-10-21-469-r1-dashboard-tokens.md +43 -0
  19. package/docs/verification/2026-10-21-469-r2-dashboard-shell.md +54 -0
  20. package/docs/verification/2026-10-21-469-r3-dashboard-board.md +51 -0
  21. package/docs/verification/2026-10-21-469-r4-dashboard-views.md +77 -0
  22. package/docs/verification/2026-10-21-469-r6-dashboard-motion-states.md +82 -0
  23. package/docs/verification/2026-10-21-grok-config-acp-t0.md +30 -0
  24. package/docs/verification/2026-10-22-0.12.45.md +60 -0
  25. package/docs/verification/assets/469-r1-board-1440-dark.png +0 -0
  26. package/docs/verification/assets/469-r1-board-1440-light.png +0 -0
  27. package/docs/verification/assets/469-r1-board-390-dark.png +0 -0
  28. package/docs/verification/assets/469-r1-board-390-light.png +0 -0
  29. package/docs/verification/assets/469-r2-agents-1440-dark.png +0 -0
  30. package/docs/verification/assets/469-r2-agents-768-dark.png +0 -0
  31. package/docs/verification/assets/469-r2-board-1024-light.png +0 -0
  32. package/docs/verification/assets/469-r2-board-1440-dark.png +0 -0
  33. package/docs/verification/assets/469-r2-board-1440-light.png +0 -0
  34. package/docs/verification/assets/469-r2-board-390-dark.png +0 -0
  35. package/docs/verification/assets/469-r2-board-390-light.png +0 -0
  36. package/docs/verification/assets/469-r2-board-768-light.png +0 -0
  37. package/docs/verification/assets/469-r3-board-1440-dark.png +0 -0
  38. package/docs/verification/assets/469-r3-board-1440-light.png +0 -0
  39. package/docs/verification/assets/469-r3-board-390-light.png +0 -0
  40. package/docs/verification/assets/469-r3-detail-1440-dark.png +0 -0
  41. package/docs/verification/assets/469-r3-detail-1440-light.png +0 -0
  42. package/docs/verification/assets/469-r3-detail-390-dark.png +0 -0
  43. package/docs/verification/assets/469-r3-plan-1440-light.png +0 -0
  44. package/docs/verification/assets/469-r3-plan-390-light.png +0 -0
  45. package/docs/verification/assets/469-r4-agents-1440-dark.png +0 -0
  46. package/docs/verification/assets/469-r4-agents-1440-light.png +0 -0
  47. package/docs/verification/assets/469-r4-agents-390-dark.png +0 -0
  48. package/docs/verification/assets/469-r4-memory-1440-dark.png +0 -0
  49. package/docs/verification/assets/469-r4-memory-1440-light.png +0 -0
  50. package/docs/verification/assets/469-r4-memory-390-dark.png +0 -0
  51. package/docs/verification/assets/469-r4-message-1440-light.png +0 -0
  52. package/docs/verification/assets/469-r4-office-1440-dark.png +0 -0
  53. package/docs/verification/assets/469-r4-office-1440-light.png +0 -0
  54. package/docs/verification/assets/469-r4-settings-1440-dark.png +0 -0
  55. package/docs/verification/assets/469-r4-settings-1440-light.png +0 -0
  56. package/docs/verification/assets/469-r4-stream-1440-light.png +0 -0
  57. package/docs/verification/assets/469-r4-stream-390-light.png +0 -0
  58. package/docs/verification/assets/469-r6-agents-empty-390-dark.png +0 -0
  59. package/docs/verification/assets/469-r6-agents-empty-390-light.png +0 -0
  60. package/docs/verification/assets/469-r6-board-1440-light.png +0 -0
  61. package/docs/verification/assets/469-r6-board-390-dark.png +0 -0
  62. package/docs/verification/assets/469-r6-board-empty-1440-dark.png +0 -0
  63. package/docs/verification/assets/469-r6-board-empty-1440-light.png +0 -0
  64. package/docs/verification/assets/469-r6-disconnected-1440-dark.png +0 -0
  65. package/docs/verification/assets/469-r6-disconnected-1440-light.png +0 -0
  66. package/docs/verification/assets/469-r6-disconnected-390-dark.png +0 -0
  67. package/docs/verification/assets/469-r6-skeleton-1440-light.png +0 -0
  68. package/docs/verification/assets/469-r6-skeleton-390-dark.png +0 -0
  69. package/docs/verification/assets/469-r6-toasts-1440-dark.png +0 -0
  70. package/docs/verification/assets/469-r6-toasts-1440-light.png +0 -0
  71. package/docs/verification/assets/469-r6-toasts-390-light.png +0 -0
  72. package/docs/verified.json +38 -20
  73. package/package.json +5 -1
  74. package/plugins/agent-hub/.claude-plugin/plugin.json +1 -1
  75. package/plugins/agent-hub/server.js +10287 -9560
  76. package/src/adapters/acp-owned-group.ts +124 -0
  77. package/src/adapters/acp.ts +92 -25
  78. package/src/adapters/adapter-api.ts +2 -0
  79. package/src/adapters/agy/definition.ts +4 -4
  80. package/src/adapters/agy/entry.ts +4 -1
  81. package/src/adapters/channel-peer.ts +143 -0
  82. package/src/adapters/claude-channel.ts +21 -12
  83. package/src/adapters/codex-appserver.ts +17 -2
  84. package/src/adapters/configured-acp.ts +124 -0
  85. package/src/adapters/conformance/acp.ts +114 -0
  86. package/src/adapters/conformance/codex.ts +191 -0
  87. package/src/adapters/conformance/enrollment.ts +43 -0
  88. package/src/adapters/conformance/factories.ts +17 -0
  89. package/src/adapters/conformance/fake-paths.ts +12 -0
  90. package/src/adapters/conformance/fakes/acp-server.ts +267 -0
  91. package/src/adapters/conformance/fakes/acp-watchdog-agent.ts +10 -0
  92. package/src/adapters/conformance/fakes/app-server.ts +119 -0
  93. package/src/adapters/conformance/fakes/codex-bin.ts +15 -0
  94. package/src/adapters/conformance/fakes/jcode-agent.ts +91 -0
  95. package/src/adapters/conformance/fakes/model-server.ts +39 -0
  96. package/src/adapters/conformance/fakes/pi-rpc.ts +32 -0
  97. package/src/adapters/conformance/index.ts +11 -0
  98. package/src/adapters/conformance/kit.ts +50 -0
  99. package/src/adapters/conformance/local-pi.ts +100 -0
  100. package/src/adapters/conformance/owned-acp.ts +44 -0
  101. package/src/adapters/definitions/builtin-metadata.ts +60 -39
  102. package/src/adapters/definitions/native-channel-state.ts +41 -0
  103. package/src/adapters/definitions/native-cli-entry-kimi.ts +9 -1
  104. package/src/adapters/definitions/native-cli-entry.ts +7 -19
  105. package/src/adapters/definitions/native-cli-preview-qwen.ts +1 -1
  106. package/src/adapters/definitions/native-cli-preview.ts +2 -11
  107. package/src/adapters/definitions/native-identity-defaults.ts +14 -0
  108. package/src/adapters/definitions/qwen.ts +7 -9
  109. package/src/adapters/definitions/reviewer-variants.ts +3 -7
  110. package/src/adapters/external-metadata.ts +48 -0
  111. package/src/adapters/external-module-scan.ts +126 -0
  112. package/src/adapters/external-modules.ts +240 -0
  113. package/src/adapters/jcode.ts +4 -0
  114. package/src/adapters/local-worker.ts +4 -0
  115. package/src/adapters/pi.ts +6 -0
  116. package/src/adapters/public.ts +7 -0
  117. package/src/adapters/registry-types.ts +28 -16
  118. package/src/adapters/registry.ts +115 -17
  119. package/src/adapters/reviewer-print.ts +4 -0
  120. package/src/cli/agy-mcp.ts +3 -2
  121. package/src/cli/configured-acp-entry.ts +58 -0
  122. package/src/cli/console-state.ts +9 -4
  123. package/src/cli/facts-hook.ts +2 -1
  124. package/src/cli/help.ts +27 -16
  125. package/src/cli/identity.ts +8 -7
  126. package/src/cli/launch.ts +5 -4
  127. package/src/cli/main.ts +101 -32
  128. package/src/cli/memory.ts +13 -10
  129. package/src/cli/output.ts +1 -0
  130. package/src/cli/preview.ts +9 -0
  131. package/src/cli/recovery-guidance.ts +1 -1
  132. package/src/cli/terminal-recovery.ts +16 -13
  133. package/src/cli/upgrade-labels.ts +1 -1
  134. package/src/cli/upgrade-presentation.ts +1 -1
  135. package/src/cli/upgrade-restore-state.ts +6 -4
  136. package/src/cli/upgrade-runtime.ts +25 -7
  137. package/src/cli/upgrade.ts +3 -2
  138. package/src/hub/board.ts +37 -8
  139. package/src/hub/budget.ts +9 -2
  140. package/src/hub/bus.ts +6 -2
  141. package/src/hub/claude-state.ts +2 -37
  142. package/src/hub/conductor.ts +8 -6
  143. package/src/hub/config-apply.ts +37 -17
  144. package/src/hub/config-reload.ts +42 -11
  145. package/src/hub/config-trust.ts +1 -1
  146. package/src/hub/context-window.ts +6 -0
  147. package/src/hub/control-client.ts +4 -1
  148. package/src/hub/crash.ts +8 -6
  149. package/src/hub/daemon.ts +412 -328
  150. package/src/hub/facts.ts +76 -13
  151. package/src/hub/hub-tools.ts +57 -5
  152. package/src/hub/instance-config.ts +33 -12
  153. package/src/hub/instance-execution.ts +23 -11
  154. package/src/hub/instances.ts +47 -22
  155. package/src/hub/lifecycle-schema.ts +5 -3
  156. package/src/hub/memory-profile.ts +16 -3
  157. package/src/hub/no-writes.ts +50 -0
  158. package/src/hub/peers.ts +9 -0
  159. package/src/hub/permission-mode.ts +9 -7
  160. package/src/hub/plan-conformance.ts +83 -0
  161. package/src/hub/review-findings.ts +104 -0
  162. package/src/hub/roster.ts +35 -6
  163. package/src/hub/routing.ts +21 -20
  164. package/src/hub/sdlc-queue.ts +19 -4
  165. package/src/hub/sdlc-release.ts +16 -1
  166. package/src/hub/sdlc.ts +27 -12
  167. package/src/hub/settings.ts +54 -16
  168. package/src/hub/snapshots.ts +32 -6
  169. package/src/hub/start-mode.ts +10 -8
  170. package/src/hub/task-sweep.ts +8 -2
  171. package/src/hub/tasks.ts +358 -60
  172. package/src/hub/ui.ts +2 -2
  173. package/src/memory/capture.ts +2 -1
  174. package/src/ui/index.html +563 -77
  175. package/src/ui/office-model.ts +3 -1
  176. package/src/ui/peer-palette.ts +44 -0
  177. package/templates/AGENTS.block.md +1 -1
  178. package/templates/profiles/general.json +1 -1
  179. package/templates/profiles/research.json +1 -1
  180. package/templates/profiles/writing.json +1 -1
package/CHANGELOG.md CHANGED
@@ -2,9 +2,44 @@
2
2
 
3
3
  Issue and pull request numbers in the entries for 0.7.7 and earlier refer to the previous repository, archived on 2026-09-30 when this repository's history was rewritten; the one exception is the open smoke-check issue, formerly #12, which moved here as #1. Numbers in newer entries refer to this repository.
4
4
 
5
- ## Unreleased
6
-
7
- - Incoming HEAD moves during open turns no longer produce false concurrent-edit warnings or attributed undo paths; bounded per-worktree reflog evidence distinguishes them from peer commits and sequencer replay changes, which stay attributed unless ambiguous. Rebase/pull --rebase start checkout is incoming; uncommitted edits carried through autostash or checkout -m are never excluded. Real concurrent change sets publish at most one notice per affected task; snapshot Git work shares a 250 ms burst budget and records unknown boundaries on exhaustion. Status reuses a checkout-branch probe only within the current synchronous burst. Pi terminal reconciliation uses the bounded existing owner monitor instead of spawning `ps` on every read; effect admission and teardown still verify fresh process identity (#576).
5
+ ## 0.12.45
6
+
7
+ The dashboard is redesigned (#469), a machine-local, content-pinned adapter module can add an ACP agent (#451 A4), the peer lists and the CLI surface come from the adapter definitions (#625), and reviews gain structured findings with an Important gate (#481 S3) and plan conformance with a person-only departure acceptance (#481 S2).
8
+
9
+ - Load external adapter modules: `adapter_modules: [{ path, sha256 }]` in an untracked `.agenthub/config.local.json` names a self-contained ESM bundle that the daemon imports at startup from its hash-checked bytes, with `@staix/agent-hub/adapter` bound to the running hub. A module is refused on its own (bad pin, unsupported import, an import that does not settle within 2 s) while the builtins start; the CLI only sees bounded metadata. `@staix/agent-hub/adapter` and `@staix/agent-hub/conformance` are published for module authors. A pin authorizes code to run with daemon privileges; it is not a sandbox (#451 A4, #648, #653).
10
+ - The peer lists (`KNOWN_PEERS`, `OPTIONAL_PEERS`, `START_MODE_PEERS`, role, help and planner order) and each builtin's `ahub <kind>` entry, preview and help usage come from the adapter definitions; the orders shown are unchanged (#625, #656). The daemon reads optional peer capability members for session generation and permission mode instead of checking peer classes (#625, #667).
11
+ - `hub_review` and `ahub review --findings` take structured findings (id, pass, severity, disposition). In enforce, an open Important finding blocks approval and carries across later heads until a reviewer marks it fixed or false_positive or the person accepts it with a rationale; a supplied head must be the task's current head, and a finding id seen on a second task proposes one backlog card for an AGENTS.md or area-note amendment. `sdlc.max_nits` sets the nit cap (default 5) (#481 S3, #652, #657).
12
+ - Plan conformance: a task plan may carry `proof` and `risks`, and review, status and the merge queue compare the PR's changed files with the plan's literal file or directory entries (renames and copies read as unknown). In enforce, a departure needs an amended plan or the person's `ahub sdlc accept-departure <task> <head> <reason...>`, which binds the repository, PR, head, file inventory, plan and repository scope and expires on restart (#481 S2, #665).
13
+ - Review readiness keeps a lost reuse proof apart from quiet review drift, a delayed check that is refused tells its owner, and review-thread authors other than the person count as pending bot comments (#230, #641, #643, #644).
14
+ - A large file rewritten with the same size within one filesystem tick on Linux is now seen as changed: facts compare a recently written file by its bytes, within a bounded budget (#428, #646).
15
+ - When `ahub kimi --mode tui` fails because the running hub's retained `kimi_cmd` has no TUI form, the error says the command is the running hub's and that the file's value waits for `ahub settings reload` (#654, #658).
16
+ - The daemon's snapshot Git budget reads an injectable clock, a host-only `snapshotClock` option, so a test can hold it fixed and a slow CI disk cannot spend the 250 ms before a turn's end evidence is read (an exhausted budget records an unknown tree, and an unknown tree restarts the empty-turn stall count: the intermittent #661 failure of the #498 stall test). Nothing in the shipped daemon sets it, no configuration key or tool reaches it, and the share stays 250 ms.
17
+ - The dashboard board gets simpler cards (title, owner avatar, one state and priority badge, release), a task detail that opens as a right-hand sheet with its lines grouped into Status, People, Dates and Links, and a Plan sheet that holds the five board forms (plan, split, releases, delivery evidence, propose) with the fields and actions they had (#469 R3).
18
+ - The dashboard gets an app shell: from 1024px a sticky top bar and a left sidebar, below it a row of tabs that scrolls inside itself so the page never scrolls sideways; the connection status shows a coloured dot; keys `1`-`7` switch views, `?` lists the keys and `Esc` closes a sheet, ignored while typing; navigation icons come from an inline sprite of nine Tabler icons (MIT notice in `THIRD_PARTY_NOTICES.md`). View names, order, the cookie and ids are unchanged (#469 R2).
19
+ - The dashboard's style block is rewritten around one token block (colour, a 4px spacing scale, a type scale, a 6/10/full radius rule and two shadows), the dead rules are gone, the indigo accent replaces green for primary actions, focus and selection, and every peer colour has a light and a dark variant. The layout, views and behaviour are unchanged, and the contrast test now also covers the accent, dots, disabled controls and the peer palette in both themes (#469 R1).
20
+ - The dashboard's Agents rows become a summary line plus one folded details block, the Stream gets aligned event rows with its filters above the list, Memory rows and detail use the colour tokens, Settings groups are sections with a "restart pending" badge and Message uses the shared form styles. The star, arrow, checking and Office condition glyphs become Tabler icons and drawn shapes next to their words (three more icons, twelve in all), the Office scene's colours are checked through the same tokens in the contrast test, and a test now fails if a listed glyph comes back (#469 R4, R5).
21
+ - The dashboard keeps task cards, peer rows and memory rows per id across a poll, so hover, an open fold and a focused card survive an update that changed something else. It gets quiet motion on `transform` and `opacity` only (press, view fade, card arrival, toast, sheets) that is off under reduced motion, first-load skeletons for the board and the peers, empty views that name their command, a disconnected state with the time of the last snapshot, and notices as an `aria-live` toast stack where errors stay until dismissed and a success fades after 4 s (#469 R6).
22
+ - A board card's one badge now also says when the task is overdue or its check is running ("in progress P2, overdue, check running"), in its text and in the card's accessible name, instead of a second and third badge. An overdue card turns the badge warn unless the state is already an error, and the task detail keeps its own Overdue and Check running lines (#293).
23
+ - The dashboard keeps its first-load skeletons for the board and the peers after a project switch until that project's first snapshot renders (a slow or failed first snapshot no longer reads as "No project selected", which is now only for no running project), and a toast that is dismissed, expires or is pushed out no longer takes keyboard focus from another toast's Dismiss button (#469 R6 follow-ups).
24
+ - Four #450 I2 follow-ups: the interactive recovery screen and `--verbose` receipt now show a restored extra's recorded outcome after its declaration was revoked instead of throwing "invalid restore receipt"; a roster key left by an undeclared instance (`codex-2`) loads as inert and `ahub roster remove codex-2` deletes it, while any other unknown key is still an unsupported peer (only a kind that can declare an instance counts: `roster.jcode-worker` stays a typo). A key that lives only in the shared `.agenthub/config.json` is refused with "edit .agenthub/config.json" instead of reporting a removal that changes nothing, and the removal says "stale roster key removed" instead of a roster state; the `peer_instances` refusal text and the spec say it applies only while a declared instance may be live, and ask to end the extra sessions before removing a declaration; and the git calls behind an extra-instance start share one 5 s allowance instead of 5 s each (#225).
25
+ - A Claude channel tool result no longer tells a session to call `hub_inbox` while a delivery hold refuses its pulls (the counts and held rows stay), and a `hub_inbox` result states the queued count once (#221). Documented limit: mid-turn steer of a push-capable Claude session was proven only on Claude Code 2.1.302.
26
+
27
+ ## 0.12.44
28
+
29
+ A standard ACP agent can join a project from config alone (#451 A3), adapter definitions become the single dispatch point (#451 A2b), agy stops reading Claude's state files (#624), and owner turns that write nothing are flagged (#498).
30
+
31
+ - Add project-local config-only ACP adapters: optional headless kinds stay off roster by default, with pinned native identity, strict resume identity, owned-group admission and optional mapped or read-only native modes. Grok live verification remains pending; the generic path does not by itself verify Grok support (#451, #486). Adapter role defaults cannot appoint a conductor; explicit project role policy remains available. Unknown fields in `peers.<id>` now refuse daemon startup as well as strict reload.
32
+ - `ahub permission` and `ahub settings` reach the running hub even when `.agenthub/config.local.json` does not parse, and `ahub settings undo` asks for the peer-id confirmation of a configured kind's roster undo (#638 follow-up).
33
+ - agy no longer shows Claude's context reading, session or transcript usage as its own: Claude state files are read only for Claude peers, and the shared file-name helper refuses any other id (#624). An upgrade no longer refuses a planned fresh start of a `codex-<name>` instance, and native observation uses the sending peer's own roles.
34
+ - Owner turns that write nothing are flagged: two completed `implement` or `bulk_edit` owner turns in a row with no recorded writes or task-tool effects raise a planner notice, and a done report with no writes since acceptance says so in its review request (`task_sweep.no_writes_turns`, default 2, 0 disables). Evidence that cannot be proven, such as private turns, work under `.worktrees` or a recorded branch, PR or head, stays unknown and never counts as empty (#498).
35
+ - In the agy TUI the hub tools can be missing on the first turn: the launcher says so and the `AGENTS.md` block tells agents to use them from the next turn instead of reporting the hub unavailable; run `ahub init` to refresh the block (#607).
36
+ - An invalid `kimi_cmd` releases the Kimi TUI admission instead of holding it (#549).
37
+ - Codex shows a fixed `unidentified-cancellation` status notice in `ahub status`, the console and the dashboard while its offline latch holds; verified reattachment or TUI detach clears it (#451).
38
+ - The recovery lease logs its release retry once per failure streak instead of once a second (#479).
39
+ - Add "Adding an AI Tool" guide (`docs/adding-an-ai-tool.md`) documenting Adapter API 1 definitions, transport selection, and the conformance test kit (#451 A5).
40
+ - Adapter declarations now refuse ordinary agy extra instances at config load; they previously loaded as inert pull-only extras. Fresh-reviewer metadata remains unavailable (#451, #625).
41
+ - Qwen context occupancy is now unknown until a native source is verified; it no longer inherits Kimi's usage parser (#451, #625).
42
+ - When the hub itself starts headless Kimi or Qwen it also opens `ahub watch <peer>` through the terminal provider, a fixed command opened at most once per 30 s; with no provider or a failing one the start still succeeds and the watch command is reported (#286 AC10).
8
43
 
9
44
  ## 0.12.43
10
45
 
package/README.md CHANGED
@@ -5,7 +5,7 @@ Codex, Kimi Code, Pi and a hub-owned local worker (Qwen Code, Jcode and Antigrav
5
5
  optional) share a message bus and a task board per project and one memory on the
6
6
  machine, each attached through its own native interface.
7
7
 
8
- Status: 0.12.43, control protocol 16. Bun + TypeScript, one loopback daemon per
8
+ Status: 0.12.45, control protocol 16. Bun + TypeScript, one loopback daemon per
9
9
  project. Site: [agent-hub.staix.net](https://agent-hub.staix.net).
10
10
 
11
11
  ## What it does today
@@ -88,14 +88,14 @@ keeps headless startup. On 0.12.39 the TUI launcher stopped after the first hub
88
88
  0.12.40 opens it again (#564). On 0.12.40 `ahub codex` in a recorded Orca terminal
89
89
  failed with `invalid remote address`; 0.12.41 starts it again (#586). Since 0.12.42
90
90
  `ahub agy` accepts the Antigravity CLI 1.4.x line (1.4.2 verified; other patches warn),
91
- because agy updates itself and 0.12.41 refused 1.4.3 (#589). Since 0.12.43 agy's native reads are recorded as peer facts on 1.4.2 and 1.4.3 (#555); 1.4.3 stays unverified, and in its TUI the hub tools appear from the second turn (#267).
91
+ because agy updates itself and 0.12.41 refused 1.4.3 (#589). Since 0.12.43 agy's native reads are recorded as peer facts on 1.4.2 and 1.4.3 (#555); 1.4.3 stays unverified, and in its TUI the hub tools appear from the second turn (#267, #607). Since 0.12.44 a headless Kimi or Qwen start made by the hub also opens `ahub watch <peer>` through the terminal provider (#286), an owner turn streak with no recorded writes raises a planner notice (`task_sweep.no_writes_turns`, default 2, 0 disables; #498), a declared agy extra instance is refused at config load (#625), and a standard ACP agent can join a project as a config-only adapter declared in untracked `.agenthub/config.local.json`: headless, off roster by default and pinned to its native identity (#451). Since 0.12.45 the dashboard has a sidebar or tabs, view keys `1`-`7`, a task side sheet, a Plan sheet for the board forms and loading, empty and disconnected states (#469); a person can add a bundled ACP adapter module, pinned by `sha256` in untracked `.agenthub/config.local.json` and imported only by the daemon at startup, and module CI gets the `@staix/agent-hub/conformance` kit (#451); reviews take structured findings, and in SDLC enforce an open Important finding or a changed file outside the plan refuses approval until fixed, amended or accepted by the person with `ahub sdlc accept-departure` (#481); and `ahub roster remove` deletes the roster key of an instance no longer declared (#225).
92
92
  Cancellation needs a daemon advertising `task_cancel`; controlled downgrades
93
93
  refuse while cancelled rows exist. Live-test delegated CLI commands need a daemon
94
94
  advertising `cli_audit_delegated` and refuse with an upgrade remedy otherwise
95
95
  (#410). For local/Pi implementation, opt in to an available
96
96
  `checks.runner = "container"`; issue-backed owners also need authenticated
97
97
  spec access. PII work and designated reviewers retain their separate exemptions.
98
- See [release verification](docs/verification/2026-10-21-0.12.43.md) for the live legs
98
+ See [release verification](docs/verification/2026-10-22-0.12.45.md) for the live legs
99
99
  still open, including #418 AC8, #438 AC7, the native console settings leg (#439 AC5),
100
100
  real upgrades with reattach (#463) and with a managed terminal and cancelled tasks
101
101
  (#478), a live headless Claude review (#223), a Claude session over 32 MiB (#517), a
@@ -17,3 +17,32 @@ Unless required by applicable law or agreed to in writing, software distributed
17
17
  under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
18
18
  CONDITIONS OF ANY KIND, either express or implied. See the License for the
19
19
  specific language governing permissions and limitations under the License.
20
+
21
+ ## Tabler Icons
22
+
23
+ The inline icon sprite in `src/ui/index.html` (the dashboard's navigation, help, close, star, arrow and checking icons) contains the SVG
24
+ path data of twelve icons from Tabler Icons (outline set, 24x24, stroke 2), copied unchanged from https://github.com/tabler/tabler-icons:
25
+ `layout-kanban`, `building-store`, `activity`, `users`, `message`, `brain`, `settings`, `help`, `x`, `star`, `arrow-right` and
26
+ `refresh`. No Tabler package is installed or bundled.
27
+
28
+ MIT License
29
+
30
+ Copyright (c) 2020-2026 Paweł Kuna
31
+
32
+ Permission is hereby granted, free of charge, to any person obtaining a copy
33
+ of this software and associated documentation files (the "Software"), to deal
34
+ in the Software without restriction, including without limitation the rights
35
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
36
+ copies of the Software, and to permit persons to whom the Software is
37
+ furnished to do so, subject to the following conditions:
38
+
39
+ The above copyright notice and this permission notice shall be included in all
40
+ copies or substantial portions of the Software.
41
+
42
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
43
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
44
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
45
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
46
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
47
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
48
+ SOFTWARE.
@@ -0,0 +1,228 @@
1
+ # Adding an AI Tool (Adapter Guide)
2
+
3
+ This guide documents how to add an AI coding agent or tool to **agent-hub** as an adapter. agent-hub treats every agent as a peer in a shared project directory, coordinating through messages, task board assignments, shared memory, and lifecycle gates.
4
+
5
+ Under **Adapter API 1** (issue #451), every tool is integrated as an `AdapterDefinition`. The core daemon, task board, and CLI interact only through this typed contract, without hard-coded vendor name comparisons.
6
+
7
+ ---
8
+
9
+ ## 1. Architecture & Principles
10
+
11
+ 1. **One Definition per Kind**: Each tool is identified by a unique `kind` (e.g., `codex`, `kimi`, `agy`, `claude`, `jcode`, `pi`, `local`, `qwen`). Its capabilities, transport, launch argv, permission mappings, and recovery contracts are encapsulated in an `AdapterDefinition`.
12
+ 2. **Core Independence**: Core daemon logic (`src/hub/daemon.ts`), task management (`src/hub/tasks.ts`), and routing (`src/hub/routing.ts`) inspect definition fields (such as `transport`, `identity`, `attach`, `budget`), never raw vendor names.
13
+ 3. **Strict Conformance**: Every builtin adapter must run the conformance test kit (`test/conformance/`). Unsupported legs must provide explicit, documented reasons rather than silent test skips. (A4 exports the same callable contracts for external package CI.)
14
+ 4. **Trust & Security Boundaries**:
15
+ - **On-prem vs. Cloud**: `trust.onPrem` controls whether private/PII tasks may be routed to the agent. A cloud agent (`trust.onPrem: false`) never receives PII task text or memory entries.
16
+ - **Path Protection**: Any workspace configuration directory owned by the agent (e.g., `.agents/`, `.codex/`) must be listed in `AGENT_CONFIG_SEGMENTS` (`src/hub/permission-mode.ts`) so agents cannot self-modify their own hooks or plugins.
17
+ - **Process Isolation**: Spawns must run in their own process groups and are stopped as a whole group by the hub.
18
+
19
+ ---
20
+
21
+ ## 2. Choosing a Transport
22
+
23
+ agent-hub supports five transport architectures (`transport` in `AdapterDefinition`):
24
+
25
+ | Transport | Description | Best Suited For | Examples |
26
+ | :--- | :--- | :--- | :--- |
27
+ | **`acp`** | Agent Client Protocol over stdio JSON-RPC | Agents implementing the standard ACP specification | Kimi, Qwen, Jcode |
28
+ | **`codex-appserver`** | Bidirectional JSON-RPC with proxy intercept and thread tracking | Tools with structured thread resume and proxy-injectable RPC | Codex |
29
+ | **`claude-channel`** | MCP plugin running inside the host agent with WebSocket control link | Agents with native plugin systems that push notifications | Claude Code |
30
+ | **`ws-pull`** | Workspace MCP plugin connecting to daemon WebSocket; pull-only message inbox | Agents with local plugin discovery and interactive TUIs | Google Antigravity (`agy`) |
31
+ | **`hub-native`** | Direct in-process or local sandbox execution | Custom on-prem models, sandboxed local worker, or Pi RPC bridge | `local`, `pi` |
32
+
33
+ ### Generic ACP vs. Dedicated Adapter
34
+
35
+ * **Generic ACP (Config-only)**: A3 supports project-local standard ACP (stdio JSON-RPC) definitions without extending the builtin registry. Native support still requires a verified foreground contract and isolated T0 receipt.
36
+ * **Dedicated Adapter**: Current builtin mechanism where each tool exports an `AdapterDefinition` under `src/adapters/` with its transport, hooks, and launch plans.
37
+
38
+ ---
39
+
40
+ ## 3. AdapterDefinition Fields Reference
41
+
42
+ An adapter exports an object conforming to `AdapterDefinition` in `src/adapters/registry-types.ts`. Read that type as the source of truth: most blocks are declared inline there, so there are no separate `Adapter*Definition` types to import, and some members are required. Its top-level fields are:
43
+
44
+ - Required: `api` (always `1`), `kind`, `displayName`, `family`, `transport`, `roles`, `trust` (`onPrem`, `hubSandboxed`), `start`, `identity` (`mcp`), `permission`, `session`, `turnEnd`, `cli`, `display`.
45
+ - Optional: `optional`, `defaultOnRoster`, `enabledSwitchStartOnly`, `instances`, `attach`, `reporting`, `routing`, `budget`, `configuration`, `capabilities`, `hooks`, `reviewerVariant`, `reviewer`.
46
+ - Registry load runs `assertDefinitionCoherence` (`src/adapters/registry.ts`) on every definition and reviewer variant, so an incoherent combination fails at load, not at run time.
47
+
48
+ ### Key Sub-Objects
49
+
50
+ #### `start`
51
+ Defines how the agent process is started and commanded:
52
+ * `forms`: `["tui"]`, `["headless"]`, or `["tui", "headless"]`.
53
+ * `defaultForm`: The default startup mode.
54
+ * `launch(ctx)`: Generates the `LaunchPlan` (command, argv, environment variables, launch metadata).
55
+ * `create(ctx)`: Factory constructing the `PeerAdapter` instance for driven daemon peers.
56
+ * `conductorStartable`: Boolean indicating if `ahub up` or conductor can spawn this peer automatically.
57
+ * `ownedFlags`: CLI flags owned by this tool's launcher.
58
+
59
+ #### `identity`
60
+ Defines how the hub MCP server identity reaches the tool:
61
+ * `"acp-session"`: Sent in `session/new` `mcpServers` parameter.
62
+ * `"project-file"`: Written into workspace plugin or config files (e.g. `.agents/plugins/agent-hub/mcp_config.json`).
63
+ * `"channel-plugin"`: Provided by an installed MCP bundle.
64
+ * `"env"`: Passed via environment variables.
65
+
66
+ #### `permission`
67
+ Maps hub permission modes (`ask`, `ask-when-needed`, `never-ask`) to native flags or ACP mode IDs:
68
+ * `grants`: the text that says what each hub mode does for this kind (`ahub permission` and the settings preview show it). It is a description, not the native flag: the native mode id or policy is applied by the adapter (an ACP mode id, Codex's `approvalPolicy`).
69
+ * `genericAcp`: for a configured ACP kind, the validated `modes` (hub mode to ACP mode id) and `readOnly` id that the runtime and the instance ceiling both read.
70
+ * `unmanaged`, `unmanagedNote`, `unmanagedStatusNote`: why the hub cannot manage native permissions, when it cannot.
71
+ * `unmanagedForms`: start forms where permissions remain unmanaged (e.g. `["tui"]`).
72
+ * `trackUnattended`: the daemon records whether a start was `--unattended` and refuses a second start with a different setting while one is in progress (Codex).
73
+
74
+ #### `session`
75
+ Controls session restoration, thread continuity, and crash recovery:
76
+ * `idField`: `"sessionId"` or `"threadId"`.
77
+ * `currentId(peer)`: Extracts the active session or thread ID from a running adapter.
78
+ * `resumeArgv(binding, fresh)`: Builds CLI arguments to resume a persisted session.
79
+ * `transcriptPath(binding)`: Resolves local transcript file path for audit/usage tracking.
80
+
81
+ #### `projection`
82
+ Where the kind sits in the host's derived lists: `roleRank`, `startRank`, `helpRank` and `plannerEligible`. `KNOWN_PEERS`, `OPTIONAL_PEERS`, `START_MODE_PEERS`, `ROLE_ORDER`, `HELP_ORDER` and `PLANNER_PEERS` are derived from `ADAPTERS` and these ranks (`deriveAdapterLists`). The only hand-kept lists are the cross-release copies in `src/hub/peer-catalog.ts` (`KNOWN_PEERS`, `OPTIONAL_PEERS`) and `src/hub/start-mode.ts` (`START_MODE_PEERS`, for a dual-form kind): a new native kind is added there too, and `test/registry-guard.test.ts` fails until those copies equal the derived lists. A configured or external definition cannot set `projection`.
83
+
84
+ #### `cli`
85
+ The `ahub <kind>` surface: `command`, `startWords(form)`, and for a fixed builtin `entry` (the launcher and whether it needs lifecycle admission), `preview`, `usage` (help lines) and `launchConfigKeys`. `builtinCliEntries(definitions)` projects the entries; no configuration or module can register one.
86
+
87
+ #### `instances` and `attach`
88
+ `instances.mode` is `headless` (needs a headless `start.create` factory) or `external` (a channel instance started from the person's terminal). A kind with no `instances` policy (Jcode) takes no extra instance, and `validateInstances` refuses a declaration of one. `attach` holds the pull-only forms, external-hello admission and the refusal texts for a TUI that attaches without a driven peer.
89
+
90
+ #### Peer capability members
91
+ The runtime peer (`PeerAdapter`, `src/hub/peers.ts`) exposes optional members that the daemon reads instead of checking its class (#625 phase 6): `sessionGeneration` (native session identity: absent keeps the owner-token fallback, present but undefined is unknown and refuses bounded checks), `checkpointUsesSessionGeneration` (opt out of generation-fenced checkpoints, as Pi does), `permissionModeState` (the effective native approval state, or `unknown` / `unmanaged`) and `setPermissionMode(mode)` (resolves only after the native side acknowledges). Native safety proofs (Codex proxy and restoration, Claude launch generation) stay in the daemon.
92
+
93
+ #### `budget`
94
+ Defines quota and context window consumption readers:
95
+ * `account`: Account identifier for quota grouping.
96
+ * `reader`: `QuotaReader` extracting usage windows from native status outputs.
97
+ * `context`: `ContextReader` measuring token occupancy against window limits.
98
+
99
+ ---
100
+
101
+ ## 4. Config-Only Generic ACP Adapters (#451 A3)
102
+
103
+ Declare `adapters.<kind>` only in a Git-confirmed untracked `.agenthub/config.local.json`. The declaration is wholly machine-local authority and cannot override builtin kinds or select executable modules. Its `transport` is `acp`; it names a command, optional argv, display name and default roles, with optional native identity, permission-mode mappings and read-only mode. Adapter defaults cannot appoint the conductor. A project captures its catalog at startup, so declaration changes require restart.
104
+
105
+ Configured kinds are headless and off roster by default. Before a start can succeed, the native initialize name/version must match the configured pin and the spawned process group must be positively owned. Resume also requires an exact session/load echo. Missing native permission mappings mean unmanaged policy; missing usage/context sources remain exempt/unknown. Titles do not auto-approve tools, and read-only mode requires both native acknowledgement and host mutation fences.
106
+
107
+ Grok declarations require explicit `--no-leader`, since native defaults can inherit ambient `[cli] use_leader`. Grok 1.0.62 remains unsupported: its isolated probe lacked initialize identity and refused session creation. The [T0 receipt](verification/2026-10-21-grok-config-acp-t0.md) records the remaining evidence. The full A3 schema and proof boundary are in the [spec amendment](specs/2026-09-19-agent-hub-design.md#amendment-config-only-acp-adapters-a3-451-486).
108
+
109
+ ---
110
+
111
+ ## 5. External Adapter Modules (#451 A4)
112
+
113
+ A person can authorize a bundled adapter in an untracked
114
+ `.agenthub/config.local.json`:
115
+
116
+ ```json
117
+ {
118
+ "adapter_modules": [
119
+ { "path": ".agenthub/adapters/my-tool.mjs", "sha256": "<64 hex digits from the exact bundle>" }
120
+ ]
121
+ }
122
+ ```
123
+
124
+ The daemon imports the module at startup. Settings, reload, previews and ordinary
125
+ CLI reads never import it. A changed declaration requires restart; it cannot be
126
+ written through a hub tool or Settings. The CLI sees only the running daemon's
127
+ bounded kind and display name. Tracked/shared/cloned configuration cannot enable
128
+ code. A wrong hash, API mismatch, builtin collision, symlink component, path
129
+ outside the project, nonregular/world-writable file, oversized file or unsupported
130
+ module construct refuses loading without exposing source text in the error.
131
+
132
+ Publish a single self-contained ESM JavaScript bundle. Bundle third-party
133
+ implementation into that file; do not add runtime dependencies to the hub.
134
+ Runtime imports may use Node/Bun builtins except `bun:ffi` and `bun:test`, plus
135
+ `@staix/agent-hub/adapter`, bound to the running daemon's own SDK rather than
136
+ project packages. `/conformance` is available to module CI, not runtime modules.
137
+ Relative or other package imports, dynamic imports, require (including
138
+ `import.meta.require`), eval and Function-based loading
139
+ are refused. The scanner also refuses any `require`, `eval` or `Function` word token, including non-loading uses such as `o.require` or `instanceof Function`. String literal contents are not word tokens. The scan is conservative, not a sandbox. Authorized module code runs
140
+ with daemon privileges; inspect the full bundle before recording its hash. Each import must settle within two seconds or that module is refused while startup continues. This deadline stops waiting; it does not cancel module code or undo its side effects.
141
+ Dependencies must be ESM-only. The loader verifies the captured original bytes,
142
+ rewrites only scanned SDK imports to host-owned file URLs, and imports that result
143
+ rather than reopening a mutable source path after the hash check. A refused
144
+ module appears in bounded status diagnostics while other adapters keep working.
145
+ Its roster row stays inert; CLI config reads also retain inert unknown rows when
146
+ trusted module declarations exist but daemon metadata is unavailable.
147
+
148
+ The module exports `definition` with `api: ADAPTER_API`. Import the version,
149
+ `AcpPeer`, `BasePeer` and definition/context types from
150
+ `@staix/agent-hub/adapter`. The initial executable subset uses a headless ACP
151
+ factory, its own kind/family, `optional: true`, `defaultOnRoster: false` and
152
+ `trust: { onPrem: false, hubSandboxed: false }`. Preserve the host's factory
153
+ options and instance id when constructing the transport. Supply a launch plan,
154
+ start/identity/session/CLI/display metadata and only supported native mode
155
+ mappings; no mapping means unmanaged. By default the host disables native
156
+ hub-tool title auto-approval for external kinds. A cooperating module must preserve
157
+ that factory option; approved module code runs with daemon privileges, so this is
158
+ a host default rather than isolation from a module that overrides its options. The loader refuses fixed-builtin capabilities and unsupported
159
+ fields rather than silently ignoring them. See `validateExternalDefinition` for
160
+ the exact accepted subset.
161
+
162
+ External kind instances are explicitly unsupported in A4. Follow-up board task
163
+ #287 owns execution roots, CLI identity and recovery for them. Broadening the
164
+ module transport beyond the existing headless ACP host port also needs a reviewed
165
+ host contract (#625 phase 6); selecting a different native engine is not a generic extension.
166
+
167
+ For example, bundle an ESM definition with its dependencies and record its digest:
168
+
169
+ ```bash
170
+ bun build ./adapter.ts --target bun --format esm --external @staix/agent-hub/adapter --outfile .agenthub/adapters/my-tool.mjs
171
+ shasum -a 256 .agenthub/adapters/my-tool.mjs
172
+ ```
173
+
174
+ Enable its roster row only after loading it and exercising the applicable
175
+ conformance contracts. Source and target recovery runtimes must advertise
176
+ `external_adapter_modules`; old runtimes cannot preserve the verified catalog.
177
+
178
+ ---
179
+
180
+ ## 6. The Conformance Kit
181
+
182
+ The exported Bun kit (`@staix/agent-hub/conformance`) supplies callable transport
183
+ registrations, `createFromDefinition`, `registerConformance`, explicit unsupported
184
+ leg validation and immutable `fakePaths`. Importing it starts no fake transport;
185
+ call a registration in your own Bun test file. The hub's `test/conformance/`
186
+ wrappers run the same implementation against every applicable builtin.
187
+
188
+ An external adapter test imports its definition and calls
189
+ `registerConformance` with its real factory and a fake transport fixture. ACP
190
+ adapters also call `registerAcpConformance({ definition, ... })`; unsupported native
191
+ approval, mode or quota legs require concrete reasons, never silent skips. Other
192
+ exports cover Codex, owned ACP, Pi, local and external-channel fixture contracts.
193
+ Those exports do not grant an external runtime access to a builtin-only engine.
194
+ The shipped fake assets live under `src/adapters/conformance/fakes`, so package CI needs
195
+ no development-tree files. Native account/TUI probes remain separate from these
196
+ synthetic protocol contracts.
197
+
198
+ The kit validates that an adapter satisfies hub invariants:
199
+
200
+ ```bash
201
+ # Run the complete conformance suite
202
+ bun test test/conformance
203
+
204
+ # Run conformance for a specific kind
205
+ bun test test/conformance/acp.test.ts
206
+ bun test test/conformance/agy.test.ts
207
+ ```
208
+
209
+ ### Invariants Verified by the Kit:
210
+ 1. **Enrollment & Coherence (`enrollment.test.ts`)**: Every definition in `ADAPTERS` and reviewer variant must be enrolled. A kind without kit coverage needs a reason in `UNSUPPORTED` (`test/conformance/enrollment.ts`), and an ACP kind that skips one of the shared `ACP_LEGS` needs a reason per leg in `UNSUPPORTED_LEGS`.
211
+ 2. **Delivery Occupancy**: Sending messages updates driven peer state to `busy` synchronously, delivering digests reliably. A channel peer whose native hooks own occupancy (Claude) stays idle on insertion by design.
212
+ 3. **Recovery Metadata Hygiene**: No prompt text or credentials leaked into `recoveryMetadata`.
213
+ 4. **Watchdog Cancellation & Generation Fencing**: Expiry of the turn watchdog cancels prompts cleanly and generation fences prevent stale late answers from publishing.
214
+ 5. **Teardown & Cleanup**: When an adapter is stopped (`peer.stop()`), all owned child processes in the process group stop with it (the peer reaches offline).
215
+
216
+ ---
217
+
218
+ ## 7. Review & Verification Checklist
219
+
220
+ When adding or updating an adapter, verify each item before opening a PR:
221
+
222
+ - [ ] **T0 Receipt Recorded**: Document native CLI version, observed hook payloads, and protocol outputs in `docs/verification/YYYY-MM-DD-<kind>-t0.md`.
223
+ - [ ] **Registered in ADAPTERS**: the definition is declared under `src/adapters/definitions/` (or its own directory) with explicit `displayName`, `family`, `transport`, `trust` and `projection`, and listed in `ADAPTERS` (`src/adapters/registry.ts`). The peer lists are derived from it. The hand-kept cross-release copies in `src/hub/peer-catalog.ts` and `src/hub/start-mode.ts` and the pinned tests listed in `docs/agent-notes/adapters.md` change with it, and the PR names them as expected test changes.
224
+ - [ ] **Config Path Protection**: Agent configuration directories added to `AGENT_CONFIG_SEGMENTS` in `src/hub/permission-mode.ts`.
225
+ - [ ] **PII Safety Verified**: Cloud models have `trust.onPrem: false`; verify PII markers refuse routing to this peer.
226
+ - [ ] **Process Group Teardown**: Daemon-spawned processes use process groups and exit cleanly on stop.
227
+ - [ ] **Conformance Kit Passes**: `bun test test/conformance` passes with zero regressions and no silent skips.
228
+ - [ ] **Documentation Freshness**: Run `node scripts/check-docs.mjs` and update verified stamps in `docs/verified.json`.
@@ -15,3 +15,17 @@ Background behind some of the rules, moved from the top-level README on 2026-10-
15
15
  `docs/verification/2026-10-03-0.12.6.md`); minimal reproductions did not hang, so
16
16
  the full trigger is not pinned down. On Bun 1.4.2 the next test still starts there
17
17
  (#121, checked directly); the spin was not reproduced on demand on either version.
18
+
19
+ The #451 predicate and identity-lookup moves exposed unbound names in a VM fixture
20
+ that extracts daemon closures. Reconnecting imported helpers and deterministic
21
+ identity ports preserved the fixture's existing assertions; changing the asserted
22
+ behavior would have hidden the missing dependency.
23
+
24
+ Background moved out of the area notes for 0.12.45:
25
+
26
+ - Instance execution git budget (#225, daemon.md): `resolveInstanceExecutionCwd` runs synchronously on the daemon's event loop (an extra-instance start, `turn_revert`) with up to four git calls, measured at about 15 ms on a warm local repository. One shared 5 s allowance holds the loop at most 5 s, not 4 x 5 s. It stays synchronous because the CLI and recovery callers are; an async twin for the two daemon call sites is the upgrade if a typical resolution ever costs more than a few ms.
27
+ - Claude steer version gate (#493, #221, daemon.md): mid-turn consumption of a steered important message was proven only on Claude Code 2.1.302 (`docs/verification/2026-10-18-493-claude-busy-channel.md`); on an older client a dropped mid-turn notification stays `accepted` until a person resolves it. The hello does not carry the client version, and the oldest working version is unknown, so a gate would be a guess that could disable a working path.
28
+ - Planner reservation and done gates (tasks.md): in #378 a same-value planner write laundered a person's reservation; in #382 advise recorded overlapping dones, and an owner could neither relink a PR nor be stopped from carrying a head to a new PR.
29
+ - Process-tree stops (adapters.md): three review rounds of #113 found snapshot gaps while the tree ran (groups of its own, an unreadable first read, a child started during the grace period).
30
+ - Local worker (local-worker.md): a bare `private/` deny entry once denied all of `/private/var`; `existsSync` made a dangling link look like a new file, so a write landed at its target.
31
+ - Suite watchdog (tests.md): macOS CI hit `AHUB_CHECK_HANG_S` while tests still passed (#378, #593).