@staix/agent-hub 0.12.44 → 0.13.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 (197) hide show
  1. package/CHANGELOG.md +40 -0
  2. package/README.md +83 -116
  3. package/THIRD_PARTY_NOTICES.md +29 -0
  4. package/docs/adding-an-ai-tool.md +102 -14
  5. package/docs/agent-notes/README.md +15 -0
  6. package/docs/agent-notes/adapters.md +63 -46
  7. package/docs/agent-notes/budget.md +7 -2
  8. package/docs/agent-notes/bus.md +6 -4
  9. package/docs/agent-notes/daemon.md +15 -8
  10. package/docs/agent-notes/local-worker.md +6 -4
  11. package/docs/agent-notes/tasks.md +18 -5
  12. package/docs/agent-notes/tests.md +11 -9
  13. package/docs/dogfood.md +92 -0
  14. package/docs/events.md +14 -1
  15. package/docs/operations.md +349 -26
  16. package/docs/profiles.md +2 -2
  17. package/docs/quickstart.md +12 -2
  18. package/docs/sdlc.md +61 -13
  19. package/docs/security.md +24 -12
  20. package/docs/smoke.md +9 -4
  21. package/docs/specs/2026-09-19-agent-hub-design.md +873 -13
  22. package/docs/verification/2026-10-19-reviewer-isolation.md +2 -0
  23. package/docs/verification/2026-10-21-469-r1-dashboard-tokens.md +43 -0
  24. package/docs/verification/2026-10-21-469-r2-dashboard-shell.md +54 -0
  25. package/docs/verification/2026-10-21-469-r3-dashboard-board.md +51 -0
  26. package/docs/verification/2026-10-21-469-r4-dashboard-views.md +77 -0
  27. package/docs/verification/2026-10-21-469-r6-dashboard-motion-states.md +82 -0
  28. package/docs/verification/2026-10-22-0.12.45.md +60 -0
  29. package/docs/verification/2026-10-22-code-facts.md +78 -0
  30. package/docs/verification/2026-10-22-codex-reviewer-isolation.md +47 -0
  31. package/docs/verification/2026-10-22-kimi-qwen-compat.md +79 -0
  32. package/docs/verification/2026-10-23-0.13.0.md +89 -0
  33. package/docs/verification/assets/469-r1-board-1440-dark.png +0 -0
  34. package/docs/verification/assets/469-r1-board-1440-light.png +0 -0
  35. package/docs/verification/assets/469-r1-board-390-dark.png +0 -0
  36. package/docs/verification/assets/469-r1-board-390-light.png +0 -0
  37. package/docs/verification/assets/469-r2-agents-1440-dark.png +0 -0
  38. package/docs/verification/assets/469-r2-agents-768-dark.png +0 -0
  39. package/docs/verification/assets/469-r2-board-1024-light.png +0 -0
  40. package/docs/verification/assets/469-r2-board-1440-dark.png +0 -0
  41. package/docs/verification/assets/469-r2-board-1440-light.png +0 -0
  42. package/docs/verification/assets/469-r2-board-390-dark.png +0 -0
  43. package/docs/verification/assets/469-r2-board-390-light.png +0 -0
  44. package/docs/verification/assets/469-r2-board-768-light.png +0 -0
  45. package/docs/verification/assets/469-r3-board-1440-dark.png +0 -0
  46. package/docs/verification/assets/469-r3-board-1440-light.png +0 -0
  47. package/docs/verification/assets/469-r3-board-390-light.png +0 -0
  48. package/docs/verification/assets/469-r3-detail-1440-dark.png +0 -0
  49. package/docs/verification/assets/469-r3-detail-1440-light.png +0 -0
  50. package/docs/verification/assets/469-r3-detail-390-dark.png +0 -0
  51. package/docs/verification/assets/469-r3-plan-1440-light.png +0 -0
  52. package/docs/verification/assets/469-r3-plan-390-light.png +0 -0
  53. package/docs/verification/assets/469-r4-agents-1440-dark.png +0 -0
  54. package/docs/verification/assets/469-r4-agents-1440-light.png +0 -0
  55. package/docs/verification/assets/469-r4-agents-390-dark.png +0 -0
  56. package/docs/verification/assets/469-r4-memory-1440-dark.png +0 -0
  57. package/docs/verification/assets/469-r4-memory-1440-light.png +0 -0
  58. package/docs/verification/assets/469-r4-memory-390-dark.png +0 -0
  59. package/docs/verification/assets/469-r4-message-1440-light.png +0 -0
  60. package/docs/verification/assets/469-r4-office-1440-dark.png +0 -0
  61. package/docs/verification/assets/469-r4-office-1440-light.png +0 -0
  62. package/docs/verification/assets/469-r4-settings-1440-dark.png +0 -0
  63. package/docs/verification/assets/469-r4-settings-1440-light.png +0 -0
  64. package/docs/verification/assets/469-r4-stream-1440-light.png +0 -0
  65. package/docs/verification/assets/469-r4-stream-390-light.png +0 -0
  66. package/docs/verification/assets/469-r6-agents-empty-390-dark.png +0 -0
  67. package/docs/verification/assets/469-r6-agents-empty-390-light.png +0 -0
  68. package/docs/verification/assets/469-r6-board-1440-light.png +0 -0
  69. package/docs/verification/assets/469-r6-board-390-dark.png +0 -0
  70. package/docs/verification/assets/469-r6-board-empty-1440-dark.png +0 -0
  71. package/docs/verification/assets/469-r6-board-empty-1440-light.png +0 -0
  72. package/docs/verification/assets/469-r6-disconnected-1440-dark.png +0 -0
  73. package/docs/verification/assets/469-r6-disconnected-1440-light.png +0 -0
  74. package/docs/verification/assets/469-r6-disconnected-390-dark.png +0 -0
  75. package/docs/verification/assets/469-r6-skeleton-1440-light.png +0 -0
  76. package/docs/verification/assets/469-r6-skeleton-390-dark.png +0 -0
  77. package/docs/verification/assets/469-r6-toasts-1440-dark.png +0 -0
  78. package/docs/verification/assets/469-r6-toasts-1440-light.png +0 -0
  79. package/docs/verification/assets/469-r6-toasts-390-light.png +0 -0
  80. package/docs/verification/dogfood/2026-10-22.md +3109 -0
  81. package/docs/verified.json +19 -16
  82. package/package.json +5 -1
  83. package/plugins/agent-hub/.claude-plugin/plugin.json +1 -1
  84. package/plugins/agent-hub/server.js +10120 -9623
  85. package/src/adapters/adapter-api.ts +2 -0
  86. package/src/adapters/agy/definition.ts +3 -1
  87. package/src/adapters/agy/entry.ts +5 -4
  88. package/src/adapters/claude-channel.ts +32 -12
  89. package/src/adapters/codex-appserver.ts +19 -2
  90. package/src/adapters/codex-review-isolation.ts +12 -5
  91. package/src/adapters/compat.ts +140 -0
  92. package/src/adapters/conformance/acp.ts +114 -0
  93. package/src/adapters/conformance/codex.ts +191 -0
  94. package/src/adapters/conformance/enrollment.ts +43 -0
  95. package/src/adapters/conformance/factories.ts +17 -0
  96. package/src/adapters/conformance/fake-paths.ts +12 -0
  97. package/src/adapters/conformance/fakes/acp-server.ts +267 -0
  98. package/src/adapters/conformance/fakes/acp-watchdog-agent.ts +10 -0
  99. package/src/adapters/conformance/fakes/app-server.ts +119 -0
  100. package/src/adapters/conformance/fakes/codex-bin.ts +15 -0
  101. package/src/adapters/conformance/fakes/jcode-agent.ts +91 -0
  102. package/src/adapters/conformance/fakes/model-server.ts +39 -0
  103. package/src/adapters/conformance/fakes/pi-rpc.ts +32 -0
  104. package/src/adapters/conformance/index.ts +11 -0
  105. package/src/adapters/conformance/kit.ts +50 -0
  106. package/src/adapters/conformance/local-pi.ts +100 -0
  107. package/src/adapters/conformance/owned-acp.ts +44 -0
  108. package/src/adapters/definitions/builtin-metadata.ts +25 -7
  109. package/src/adapters/definitions/native-cli-doctor.ts +12 -5
  110. package/src/adapters/definitions/native-cli-entry-jcode.ts +9 -6
  111. package/src/adapters/definitions/native-cli-entry-kimi.ts +7 -1
  112. package/src/adapters/definitions/native-cli-entry.ts +7 -19
  113. package/src/adapters/definitions/native-cli-jcode-mcp.ts +9 -2
  114. package/src/adapters/definitions/native-cli-preview-common.ts +2 -0
  115. package/src/adapters/definitions/native-cli-preview-jcode.ts +8 -2
  116. package/src/adapters/definitions/native-cli-preview-qwen.ts +2 -1
  117. package/src/adapters/definitions/native-cli-preview.ts +2 -11
  118. package/src/adapters/definitions/qwen-native.ts +7 -3
  119. package/src/adapters/definitions/qwen.ts +5 -1
  120. package/src/adapters/external-metadata.ts +48 -0
  121. package/src/adapters/external-module-scan.ts +126 -0
  122. package/src/adapters/external-modules.ts +240 -0
  123. package/src/adapters/jcode.ts +72 -31
  124. package/src/adapters/kimi-review.ts +6 -3
  125. package/src/adapters/pi.ts +2 -0
  126. package/src/adapters/public.ts +7 -0
  127. package/src/adapters/registry-types.ts +13 -1
  128. package/src/adapters/registry.ts +52 -15
  129. package/src/cli/agents.ts +40 -0
  130. package/src/cli/console-review.ts +54 -0
  131. package/src/cli/console-state.ts +124 -29
  132. package/src/cli/console-symbols.ts +74 -0
  133. package/src/cli/console-usage.ts +89 -0
  134. package/src/cli/console.ts +26 -1
  135. package/src/cli/help.ts +36 -21
  136. package/src/cli/identity.ts +30 -7
  137. package/src/cli/init.ts +109 -11
  138. package/src/cli/main.ts +145 -16
  139. package/src/cli/output.ts +44 -1
  140. package/src/cli/preview.ts +1 -1
  141. package/src/cli/sdlc-release-guard.ts +82 -0
  142. package/src/cli/upgrade-labels.ts +1 -1
  143. package/src/cli/upgrade-presentation.ts +1 -1
  144. package/src/cli/upgrade-restore-state.ts +3 -2
  145. package/src/cli/upgrade-runtime.ts +4 -1
  146. package/src/hub/action-class.ts +362 -0
  147. package/src/hub/action-policy.ts +106 -0
  148. package/src/hub/board.ts +72 -11
  149. package/src/hub/budget.ts +71 -2
  150. package/src/hub/bus.ts +43 -3
  151. package/src/hub/checks.ts +80 -31
  152. package/src/hub/code-facts.ts +223 -0
  153. package/src/hub/config-apply.ts +28 -11
  154. package/src/hub/config-reload.ts +8 -5
  155. package/src/hub/config-trust.ts +7 -4
  156. package/src/hub/control-client.ts +4 -1
  157. package/src/hub/daemon.ts +332 -61
  158. package/src/hub/dogfood-read.ts +199 -0
  159. package/src/hub/dogfood.ts +163 -0
  160. package/src/hub/envelope.ts +1 -1
  161. package/src/hub/events.ts +11 -2
  162. package/src/hub/facts.ts +76 -13
  163. package/src/hub/hub-tools.ts +45 -7
  164. package/src/hub/inbox-polling.ts +25 -0
  165. package/src/hub/instance-config.ts +17 -5
  166. package/src/hub/instance-execution.ts +20 -8
  167. package/src/hub/instances.ts +75 -2
  168. package/src/hub/peers.ts +9 -0
  169. package/src/hub/plan-conformance.ts +83 -0
  170. package/src/hub/profiles.ts +7 -2
  171. package/src/hub/release-worktree.ts +56 -0
  172. package/src/hub/review-brief.ts +36 -0
  173. package/src/hub/review-findings.ts +104 -0
  174. package/src/hub/roster.ts +31 -5
  175. package/src/hub/sdlc-queue.ts +48 -11
  176. package/src/hub/sdlc-release-authorization.ts +173 -0
  177. package/src/hub/sdlc-release.ts +16 -1
  178. package/src/hub/sdlc.ts +71 -18
  179. package/src/hub/settings.ts +10 -3
  180. package/src/hub/snapshots.ts +101 -14
  181. package/src/hub/symbol-normalizer.ts +26 -0
  182. package/src/hub/task-code-symbols.ts +90 -0
  183. package/src/hub/task-sweep.ts +3 -2
  184. package/src/hub/tasks.ts +556 -88
  185. package/src/hub/test-integrity.ts +162 -0
  186. package/src/hub/ui.ts +2 -2
  187. package/src/hub/usage-rollup.ts +361 -0
  188. package/src/local/sandbox.ts +5 -3
  189. package/src/ui/index.html +560 -78
  190. package/src/ui/office-model.ts +3 -1
  191. package/src/ui/peer-palette.ts +44 -0
  192. package/templates/AGENTS.block.md +1 -1
  193. package/templates/profiles/dev.json +6 -1
  194. package/templates/profiles/general.json +6 -1
  195. package/templates/profiles/research.json +6 -1
  196. package/templates/profiles/writing.json +6 -1
  197. package/templates/release-guard.block.md +6 -0
@@ -1,6 +1,6 @@
1
1
  # Operations guide
2
2
 
3
- This guide describes ahub 0.12.43 and control protocol 16. Live verification
3
+ This guide describes ahub 0.13.0 and control protocol 16. Live verification
4
4
  results and remaining prerequisites are recorded separately in [the smoke ledger](smoke.md).
5
5
 
6
6
  ## Agent-operated live verification
@@ -61,7 +61,7 @@ smoke ledger; the presence of this option proves no outstanding #273 live leg.
61
61
 
62
62
  ## Command output
63
63
 
64
- `ahub status`, `board`, `budget`, `doctor`, `projects`, `queue list`, `turns` and `report` print shared console tables and labelled details.
64
+ `ahub status`, `board`, `budget`, `doctor`, `projects`, `queue list`, `turns`, `report` and `usage` print shared console tables and labelled details.
65
65
  `status --all` uses the projects table. `queue show`, `models status` and `budget execution status` show labelled fields; add `--json` for the raw fetched document.
66
66
  `report --by task` keeps the attribution and coverage sentences with section headings and aligned counters.
67
67
  Use `--json` for scripts; it returns the fetched data without color, and `status --json` keeps its existing document.
@@ -141,6 +141,30 @@ its footer. The active tab is bracketed, as in `[2 Approvals]`, so it reads
141
141
  without color. The Approvals tab counts pending requests and the Queue tab
142
142
  counts `needs_review` deliveries, in the attention and failure tones.
143
143
 
144
+ Tab `7 Usage` (#683 U3, key `7`; Left and Right wrap as Settings does, so the `1`..`5` cycle is unchanged) lists each peer with the tokens of today (the UTC day),
145
+ the last seven UTC days and its lifetime, the current budget pause, its quota windows with their reset time, and its last reset. Every number names its
146
+ source (`native`: the provider's own total, `hub`: what the hub counted for a peer that reports none, `at least`: a partial sum); a counter nobody reported
147
+ is `unknown` and a period with no records is `no records`, never 0. Enter opens a peer: today, 7 days and lifetime with input, output, cache read, cache
148
+ write and the hub count each on its own, and the last three resets. The task selected in Tasks when you press `7` is shown below the table with its tokens
149
+ per peer (`#id [pii]` for a private task, never a title). The panel asks the hub's `usage` op only while it is open, and a refusal shows as
150
+ `usage unavailable`. The numbers come from the roll-up of `ahub usage`, so the console and the CLI agree. A request folds the event log first, within 250 ms;
151
+ when a very large log is not folded yet the answer carries `behind: true`, the panel says `still catching up` and `ahub usage` prints a warning on stderr, so a partial number is never shown as the whole.
152
+
153
+ Reviewing from the console (#711): on a task in review in Tasks (a selected row, or the open task detail), `r` asks which verdict first on the prompt line,
154
+ `review #372 "<title>": a approve c request changes Esc cancel` (the title is quoted and cut, a private task shows `[pii]`). `a` puts `review 372 approved ` on
155
+ the input line with a guide line above it (`Enter approves #372. You can type a note first. Esc cancels.`); `c` puts `review 372 changes_requested ` there and
156
+ asks for what to fix (`Say what to fix, then Enter. Esc cancels.`): Enter with no note is refused in the console with `say what to fix before requesting
157
+ changes`, and nothing reaches the hub. The footer says `r approve or request changes`, and `your review: r approve or request changes` when the reviewer is
158
+ `user`, also while the task detail is open. When the command finishes the console shows `#372 approved` or `#372 changes requested`, or the CLI's refusal verbatim
159
+ as a failure notice, in panels mode too. A task that left review while the choice was open says `#372 is no longer in review (<state>)` and nothing is
160
+ prefilled. The console runs `ahub review` as the person; the hub still decides who may review, and nothing about authority changed.
161
+
162
+ An agent shell (a `!` command inside a Claude or Codex session, or any shell with `AGENTHUB_PEER_ID`, `CLAUDECODE` or `CODEX_THREAD_ID` set) runs as that agent, not as
163
+ you. When the hub refuses it something only the person or the task's own reviewer may do, stderr adds one line: `This shell runs as agent "claude" (AGENTHUB_PEER_ID
164
+ and CLAUDECODE are set), not as you. Run the command in your own terminal, or use the ahub console: Tasks, select #372, r.` The refusal and the authority are the
165
+ same as before, and the console identity never gets the line. The hub's notice that a task waits for your review now names the console path as well as
166
+ `ahub review N approved|changes_requested [note]`.
167
+
144
168
  The console attention companion (#280) shows waiting, blocked, review, working,
145
169
  idle or asleep with separate action/watch counts. Outside the Tasks table, `n` selects the exact approval,
146
170
  held queue or review row, or prints applicable guidance; it never answers a
@@ -175,10 +199,10 @@ the stream; only the header is tab-flattened before tokenizing. Body lines retai
175
199
  the previous rendering and stay plain, as does command output; colour never changes
176
200
  what the panel's text says.
177
201
 
178
- Peers, Approvals, Tasks, Queue and Settings are tables with a header row. A column is as
202
+ Peers, Approvals, Tasks, Queue, Settings and Usage are tables with a header row. A column is as
179
203
  wide as its widest cell, up to a third of the terminal; the last column takes
180
204
  the rest and is cut with `...`. In Tasks, TITLE takes the rest and the STAGE
181
- meter keeps a fixed column at the right edge.
205
+ meter keeps a fixed column at the right edge. In Usage, LAST RESET is the column that gives way on a narrow terminal.
182
206
 
183
207
  | Panel | Columns |
184
208
  |---|---|
@@ -188,6 +212,7 @@ meter keeps a fixed column at the right edge.
188
212
  | Tasks, backlog view (`b`) | ID PRI RESERVED DEPS CLASS AGE TITLE |
189
213
  | Queue | ID PEER STATE REV AGE |
190
214
  | Settings | GROUP KEY VALUE SOURCE APPLIES |
215
+ | Usage | PEER TODAY 7D LIFETIME PAUSE QUOTA LAST RESET |
191
216
 
192
217
  In Tasks, `b` toggles open work and backlog. The backlog heading counts its rows;
193
218
  rows sort by rank, then id. PRI shows priority/rank, RESERVED the reserved peer,
@@ -320,7 +345,21 @@ owner and to stop working.
320
345
  `ahub task assign <id> <peer> [--role owner|reviewer] [--revision N]` uses a
321
346
  checked console operation when a revision is present. Without it, existing CLI
322
347
  behavior is unchanged. `ahub queue resolve ... [--revision N]` uses the supplied
323
- revision instead of a fresh show. Revision mismatches and superseded post-await
348
+ revision instead of a fresh show. A `queued` delivery was never handed to its peer, so it can be discarded while the peer is busy (a pull-only TUI is busy most of
349
+ the time); `dispatching` and `accepted` deliveries stay refused, and `retry` keeps its rules. A refused resolution names its cause (`active delivery`, `peer busy`,
350
+ `stale revision`, `unknown delivery`, `already resolved`, `queued delivery is already scheduled`, `recipient queue is full`, `delivery journal unavailable`), never one
351
+ generic sentence (#713).
352
+
353
+ `ahub queue discard --peer <id> --reason <text> [--state queued|needs_review] [--yes] [--json]` clears one peer's backlog and is for the person only (an agent identity is
354
+ refused). Without `--yes` it only says how many deliveries it would discard; with `--yes` it discards each queued (default) or `needs_review` delivery of that peer through the
355
+ same resolution as `queue resolve --action discard`, each recorded with the reason, and prints `discarded N of M` with the causes of any it could not discard. Only the
356
+ named peer's queue is read, so other peers are untouched; it is refused while recovery or shutdown holds queue mutations. In the console Queue panel, `D` on a row opens
357
+ the same command for that row's peer, asks for a reason, then a `y` that names the count. No task is cancelled and no native turn is stopped.
358
+
359
+ Completed-change notices (`Task #N (owner X) is done and touches your open ...`, #713) go one per recipient per done, naming all of that recipient's overlapping tasks, and
360
+ only for tasks the recipient has accepted (in progress, in review or changes requested): an offered or proposed task gets none, since its offer or its accept already shows the
361
+ current board. A newer notice for the same done task replaces the queued, undelivered one for that recipient (a `superseded completed-change notice` discard in the journal), so
362
+ a task that is sent back and done again three times leaves one queued notice, the newest. Revision mismatches and superseded post-await
324
363
  publications fail without a board or delivery side effect; a reviewer-only change
325
364
  keeps a valid owner assignment but refreshes its reviewer content.
326
365
 
@@ -635,6 +674,79 @@ Private envelopes and PII tasks stay redacted. Local-worker and Pi tool details
635
674
  allow decisions stay in `ahub tail` / `ahub permit`; the page can deny them.
636
675
  No dashboard port is opened until requested. See [the session security model](security.md#local-dashboard-sessions-issue-6).
637
676
 
677
+ ### Dashboard design tokens (#469 R1)
678
+
679
+ The page's style block starts with one token block that owns every colour, the 4px spacing scale (4, 8, 12, 16, 24, 32), the
680
+ type scale (12, 13, 14, 16, 20) and the radius rule: 6px for controls, 10px for surfaces (panels, cards, the dialog), full
681
+ radius for chips and avatars. Neutrals are one cool family with three surface levels (page, panel, raised) and one accent for
682
+ primary actions, focus and selection; the state colours keep their meaning. The dark values are written twice, once for
683
+ `data-theme="dark"` and once for the system preference, and a test keeps the two identical. Two shadows exist for raised
684
+ elements only (`--shadow-2` is the dialog's; `--shadow-1` waits for the board cards in R3). Each peer colour has a light and a
685
+ dark variant of one hue, generated from the registry's colour by `peerPaletteThemeCss` (`src/ui/peer-palette.ts`) so it clears
686
+ 3:1 on every surface. System fonts only. The layout, the views, the keys and the behaviour are unchanged by R1; the contrast
687
+ test (`test/office-view.test.ts`) covers the text pairs, the accent, the dots, disabled controls and every peer colour in both
688
+ themes. The Office scene keeps its own rules until R5.
689
+
690
+ ### Dashboard app shell and keys (#469 R2)
691
+
692
+ From 1024px up, a sticky top bar holds the project context, the connection status (a coloured dot and its text, green when live
693
+ and red when disconnected or closed), the attention badge, the theme switch and a shortcuts button; the seven views sit in a
694
+ left sidebar with an icon and a label, and the approval count still rides on Agents. Below 1024px the same nav is one row of
695
+ tabs that scrolls sideways inside itself, so the page never scrolls sideways. The view names, their order, the
696
+ `agent-hub-view` cookie and every id are unchanged.
697
+
698
+ Keys: `1` to `7` switch to Board, Office, Stream, Agents, Message, Memory and Settings, `?` opens the key list, and `Esc` closes
699
+ the key list or the task detail (both are native dialogs). View keys are ignored while you type in an input, a textarea or a
700
+ select, with Ctrl, Alt or Cmd held, and while a dialog is open. Icons come from one inline SVG sprite of nine Tabler icons;
701
+ the MIT notice is in `THIRD_PARTY_NOTICES.md`. Nothing is fetched and no package is added.
702
+
703
+ ### Dashboard board, side sheets and the Plan sheet (#469 R3)
704
+
705
+ A board card shows the title (two lines at most), the owner as a round avatar in the peer colour with its initial, one badge that
706
+ holds the state and the priority ("in review P1") and the release when there is one. Everything else moved to the task detail,
707
+ which keeps the same dialog and opens as a sheet pinned to the right edge (full screen below 600px). The detail groups its
708
+ lines into Status, People, Dates and Links, and leaves out a group with nothing in it. Pull, Assign, the Ready and Waiting
709
+ subheads, "Show N older" and drag and drop are unchanged.
710
+
711
+ The five board forms (plan a task, split a backlog task, releases, delivery evidence, propose a task) are in a Plan sheet, opened by
712
+ the Plan button in the board toolbar. It is a native dialog, so `Esc` closes it, and view keys do nothing while it is open. The
713
+ fields, ids and actions are the ones the forms had. The sheets slide in on `transform` and `opacity` and not at all when the
714
+ system asks for reduced motion.
715
+
716
+ ### Dashboard Agents, Stream, Memory, Settings, Message and the Office colours (#469 R4, R5)
717
+
718
+ Agents: a peer is one row with its name and state, a summary line (queued messages and "Working on #n" or "No active task") and a
719
+ single "Details" fold that holds the diagnostic lines it used to list. A status notice stays visible outside the fold. Approvals
720
+ stay first. Stream: one aligned row per event (time, kind, sender, body in monospace); the filter chips and Follow latest sit in
721
+ the section head, above the scrolling list. Memory: result rows show title, kind and age, the selected row is marked with an accent
722
+ edge, and the graph and the detail pane use the colour tokens. Settings: each group is a section with a short header, a key that waits
723
+ for a restart carries a "restart pending" badge, and "Pending restart" shows a count; the text lines, Preview, Save and Undo are as
724
+ they were. Message uses the shared form styles.
725
+
726
+ Icons replace the symbol glyphs: the star mark, the stream arrow and the Office "checking" mark are sprite icons (twelve Tabler icons
727
+ in all), and the Office draws each condition (idle, busy, offline, paused, waiting on approval) as a shape (ring, disc, cross, bars, raised hand)
728
+ next to its words. Condition names are unchanged. The Office scene names its colours only through the same tokens as the rest of the page, and the
729
+ contrast test checks every token an Office rule uses, in both themes.
730
+
731
+ ### Dashboard keyed rendering, motion and states (#469 R6)
732
+
733
+ The page still polls `/snapshot` once a second and still skips a redraw when a container's JSON did not change. What changed is how
734
+ a changed container is redrawn: task cards, peer rows and memory rows are kept per id. A card, row or memory entry whose own data
735
+ did not change keeps its DOM node, so hover, an open "Details" fold and a focused card survive a poll that changed something
736
+ else. One that changed is replaced by a fresh node, and if it held focus the focus moves to the replacement. A card that moves to
737
+ another column fades in; one that disappears is simply removed.
738
+
739
+ Motion moves `transform` and `opacity` only, 120 to 200 ms, ease-out: button press, a short fade when you switch views, a card
740
+ arriving in a column, the toast, and the side sheets. The Office keeps its own motion. Nothing new loops. With the system setting
741
+ "reduce motion" every transition and animation in the page is off.
742
+
743
+ States: until the first snapshot arrives, after a project switch too, the board and the peer list show grey blocks shaped like them. Each empty view says what
744
+ fills it and the command that does it (`ahub task propose`, `ahub claude` or `ahub codex`, `ahub say`, `ahub permit`, `ahub
745
+ remember`, `ahub goal new`). When a poll fails the connection status turns red, the content dims to 60 percent but stays readable,
746
+ and a line at the top gives the time of the last snapshot and says polling continues. Notices are a stack of up to four toasts in
747
+ the bottom corner (`aria-live="polite"`): a success fades after 4 seconds (and stays under reduce motion), an error stays until
748
+ you press Dismiss.
749
+
638
750
  ## Multiple projects and worktrees
639
751
 
640
752
  Version 0.4.0 adds one independent hub per repository or Git worktree.
@@ -894,6 +1006,57 @@ conductor sees it, and only a stub for a PII task. Other peers are refused. Each
894
1006
  request also carries the hub's review brief for the task's commit: reviewable and
895
1007
  excluded files with reasons, the `REVIEW.md` and `ahub-review-rules` rules read at
896
1008
  the merge base, and a cross-check list; `ahub review brief <id>` prints it.
1009
+ Optional code facts (#682) add per-file observed symbol names, kinds and spans,
1010
+ plus changed/planned, changed/not-planned and planned/not-seen lists. Install
1011
+ ripwire 0.6.7 on the daemon's host PATH to enable them; missing, unsupported,
1012
+ failed, busy or timed-out reads show `unknown` and never stop the review request.
1013
+ Each native call is capped at 5 s, output is capped, and successful values are
1014
+ cached by repo, commit and query. PII tasks never query it; displayed names use
1015
+ the hub's public metadata screen. It always analyzes a disposable raw-blob
1016
+ snapshot, never main or a dirty/untracked owner tree, and removes that worktree
1017
+ after the owned process stops. No heavy slot is acquired. Trees beyond the
1018
+ regular-file/4096-file/32-MiB bounds remain unknown.
1019
+
1020
+ Ripwire 0.6.7 omits deleted definitions and does not classify change kinds, so
1021
+ all symbol rows say `change: unknown`; planned/not-seen is not proof of no
1022
+ change. Graph name-only matches are hidden as ambiguous, and graph count floors
1023
+ remain explicit. The file-based coverage gate is unchanged. A future both-tree
1024
+ inventory or bounded base/head probes can improve this advice after dogfooding.
1025
+
1026
+ At completion, optional symbol advice also becomes a head-bound task observation
1027
+ (`#684` phase 2a). Console task detail and the UI task snapshot expose the
1028
+ screened IDs, paths, names and kinds, without source/spans or internal repository
1029
+ keys. Public reads re-screen current policy; PII stores nothing. Unknown or
1030
+ stale evidence remains unavailable. Completion prepares this optional data even
1031
+ without a reviewer, without adding a review gate. Foreign-repository tasks stay
1032
+ unknown until the brief can independently bind its analysis repository.
1033
+
1034
+ In the console, Enter on a task in Tasks shows a `symbols` block after its plan (#684 phase 2b): each observed changed symbol is `matching` the plan or
1035
+ `unplanned`, and each planned symbol that nothing changed under is `untouched`, with the counts and the head they were observed at. The match rule is the review
1036
+ brief's own (a pair of qualified names must be equal, otherwise the last segments are). Without code facts it reads `changed symbols: unknown (no code facts)`;
1037
+ an incomplete observation says an untouched symbol may have changed; a private task shows nothing. Names are quoted like every agent-written string, at most 40 are
1038
+ listed and the rest are counted. The block is advice: deleted definitions are not reported, so it never proves a plan was or was not followed.
1039
+
1040
+ Task detail also shows an `overlaps` block (#379), using the hub's read-only
1041
+ `symbolOverlaps` projection of its existing open-task/other-owner overlap selection.
1042
+ Each row names the other task ID and screened shared paths. A row says "same file,
1043
+ different symbols" only when the same current screened predicate used by completion
1044
+ notices establishes observed disjointness; otherwise it remains an ordinary overlap.
1045
+ The block states "observed names only, deleted definitions may be missing". PII tasks
1046
+ are excluded on both sides by their markers, even if the routing locality limit is
1047
+ disabled. No pair result is stored, and the console never infers
1048
+ one from its own plan or receipt. Missing fields on older hubs show no block; an
1049
+ empty list says `none`. At most 40 pairs and 40 paths per pair are listed, with
1050
+ omitted counts. Related-note counts remain a later slice.
1051
+
1052
+ When both tasks have complete public observations and non-empty disjoint sets
1053
+ for every exact shared file, a completed-change notice says "same file, different
1054
+ symbols" with the observed-name/deleted-definition ceiling. It still tells the
1055
+ recipient to check the completed work. Missing or partial evidence keeps the
1056
+ ordinary notice; plan-time overlap and coverage gates are unchanged. The receipt
1057
+ keeps one current head, with at most 500 entries and 64 KiB total. This is the
1058
+ data slice; the formatted Code panel and memory links follow separately.
1059
+
897
1060
  `hub_review` takes `coverage {reviewed, skipped}`: in enforce an approval that leaves
898
1061
  a reviewable path unaccounted is refused, and advise notes the gap in the verdict
899
1062
  (#520). The hub
@@ -1027,8 +1190,14 @@ ahub report --since 7d # turns, busy time and tokens per peer, messages
1027
1190
  ahub report --since 7d --json # the same numbers as JSON
1028
1191
  ahub report --by task # tokens, turns and wall time per task and class, with the unattributed share
1029
1192
  ahub export --since 24h # the raw events as JSON lines, for your own analysis
1193
+ ahub report --dogfood [--since 7d|<iso>] [--json] # read-only scorecard: flow, quality, cost, delivery, performance, adoption
1030
1194
  ```
1031
1195
 
1196
+ `ahub report --dogfood` (#687) reads the event log, `hub.log`, the board and the delivery journal in `hub.db`, up to 16 recently merged
1197
+ PRs through `gh`, and the feature table in `docs/dogfood.md`. It covers the last seven days by default, takes no `--by`, and prints the same
1198
+ document as `--json` with `unknown` in place of null; missing or truncated evidence is unknown, never 0. It exports ids, timestamps and counts
1199
+ only and runs no model. Its bounds and the weekly routine are in [dogfood](dogfood.md#weekly-scorecard).
1200
+
1032
1201
  Per peer and model route, `ahub report` also counts model changes between consecutive
1033
1202
  decisions, how many of them landed inside tool loops and, with `stay_switch` on, sessions
1034
1203
  and the planner's switches (#197). With `stay_switch = "off"` session boundaries are not
@@ -1036,6 +1205,41 @@ recorded; `shadow` routes exactly as `off` does, so it is the baseline to compar
1036
1205
  A route event records the decision, so a load-moved request that its MLX fallback served counts
1037
1206
  as a change to `dgx/fast`; the relay's request journal records which backend served it.
1038
1207
 
1208
+ The event log also keeps the history of limits (#683). Each Codex update that adds tokens writes a
1209
+ `usage` event with source `codex_appserver` next to the existing `tokens` event, split into input, cached
1210
+ input, output and reasoning, so re-sent cached context is visible. A budget pause writes `budget_pause`
1211
+ and its end `budget_resume`, with the window, the reset time and who ended it (`hub`, or `person` for an
1212
+ `ahub budget resume`). A window's reset writes `quota_reset` when its `resetsAt` moves forward or its `used`
1213
+ falls by 0.5 or more inside one window id, once per peer, window and `resetsAt`. These events hold ids and
1214
+ numbers only, add no protocol version and are ignored by a reader that does not know them. `ahub report`
1215
+ does not show them: `ahub usage` and `ahub usage limits` read them (below), and the console's Usage tab shows each peer's last resets (#683 U3). A dashboard usage view is pending (U4).
1216
+
1217
+ `ahub usage` reads the same events from an incremental roll-up in `hub.db` instead of rescanning the log (#683):
1218
+
1219
+ ```bash
1220
+ ahub usage [--since 7d|<iso>] [--by peer|task|issue|pr|release|day|model] [--json]
1221
+ ahub usage limits [--peer <id>] [--limit <n>] [--json] # current quota windows, plus the newest 200 (up to 5000) pauses, resumes and resets
1222
+ ahub usage total [--json] # lifetime development tokens since the first event, per peer and overall
1223
+ ```
1224
+
1225
+ The daemon folds `events.jsonl` into `usage_rollup`, `token_rollup` and `limit_events` from a stored byte offset, at most 8 MiB per pass and
1226
+ only whole lines, and each pass commits its rows and the new offset together, so a crash repeats a pass and never half-applies one. A rebuild
1227
+ from byte 0 gives the same rows (tested). A log that was replaced by a shorter one is folded again from the start. On this project's 47 MB,
1228
+ 238k-line log the first fold took about 170 ms and a `usage` command about 95 ms, against about 150 ms for `ahub report --by task`, which
1229
+ rescans the whole file on each call.
1230
+ `issue`, `pr` and `release` are the board's current links of each task, read when you ask, so a task linked later is counted under its issue
1231
+ for the history it already spent. A task with no link, and a record with no task, are `unattributed`.
1232
+
1233
+ Every number names its source: `native` (the provider's own counter), `hub count` (the hub's own `tokens` increments, which cover peers that
1234
+ report no usage record, such as Jcode and Kimi), or `unknown`. Unknown is never shown as 0. `TOTAL` includes cache reads and `WITHOUT CACHE`
1235
+ removes them; Claude's total is the sum of its four reported counters because its transcript carries none (`ahub report` still shows it as unknown), and a source whose input already
1236
+ contains the cached part (Codex, the gateway) gets no derived total. A lifetime total that mixes a native and a hub-counted peer is labelled
1237
+ `hub count`. When a peer is unknown the overall figure is `partial, at least`: a lower bound that leaves the named peers out, never a sum that counts them
1238
+ as 0 (it is `unknown` when no peer is known). The overall `WITHOUT CACHE` is unknown unless every peer's cache split is known.
1239
+ `usage limits` shows the newest 200 events and says `N older events are not shown` when it cuts (`--limit <n>`, up to 5000, shows more). A pause that the events
1240
+ never ended and the hub no longer holds (cleared by `ahub reset`, which keeps the log as it was) shows as `pause ended, end unknown`, not as open. A private task shows its id and `[pii]`, never
1241
+ a title. The person and a configured planner may read usage, in the console, the dashboard control link or the CLI; every other agent is refused.
1242
+
1039
1243
  `ahub report` counts the same overlap warnings as `scripts/overlaps.ts`, from the
1040
1244
  structured events instead of log lines. `--by task` uses the task each usage and
1041
1245
  token record was attributed to when it was written: the delivery that started the
@@ -1301,7 +1505,10 @@ stays the default until an evaluation says otherwise (`docs/cooperbench.md`).
1301
1505
  file that falls out of the 64 a peer touched is named until the peer reads
1302
1506
  that back. Only regular files
1303
1507
  of 256 KB or less inside the project are read, re-resolved at every read and
1304
- opened without following links; larger ones are named without a diff. Facts never go through the bus or the delivery journal;
1508
+ opened without following links; larger ones are named without a diff, compared
1509
+ by size and modification time, or by their bytes while written in the last 2 s
1510
+ (4 MiB or less each, 16 MiB per store per event-loop turn), since a same-size
1511
+ rewrite within one filesystem timestamp tick keeps the stat. Facts never go through the bus or the delivery journal;
1305
1512
  `events.jsonl` records `fact`, `fact_ack` and `capability` events with bytes
1306
1513
  and latencies.
1307
1514
  - Integration. A member's `hub_task_done` is a completion intent. The member
@@ -1375,6 +1582,14 @@ stays the default until an evaluation says otherwise (`docs/cooperbench.md`).
1375
1582
  turns the stale-notice drop off, for a controlled comparison only (the #106
1376
1583
  ablation in `docs/cooperbench.md`); the hub logs it at start.
1377
1584
 
1585
+ ## Always-ask action policy foundation (#636)
1586
+
1587
+ The classifier, local configuration and session budget counter are available in source. Adapter enforcement is pending: each peer's `ahub status` row says `always-ask not enforceable: adapter integration pending`. A default-on policy does not add a pre-tool check to a native CLI by itself.
1588
+
1589
+ Only ignored, untracked `.agenthub/config.local.json` can hold `action_policy`. Its optional keys are `enabled` (default true), `always_ask` (a distinct subset of publish, external-write, destroy, install; default all), and `budgets` (per-class integers 0..1000; defaults 1, 5, 3, 2 respectively). Shared `config.json` cannot set this policy and gets no confirmation offer. A tracked or unknown-trust local file is ignored. Use `ahub settings set action_policy.enabled <true|false>` or `ahub settings set action_policy.budgets.<class> <n>` for registered settings; change `always_ask` in the local file and use the existing confirmed terminal reload. Policy publication invalidates cached grants and pending approval challenges. Startup currently reads the untracked local policy without a retained confirmation; this ceiling must be addressed before the adapter gates ship (#312/#313).
1590
+
1591
+ The ledger asks first and after the approved class budget is spent. Adapter integrations must request that class allowance explicitly; an ordinary one-time answer does not seed the cache, and a cached budget never suppresses a prompt required by the native mode. One approval includes its own invocation; budget zero asks for every invocation. A fresh native session resets counters, and status polls do not bind sessions. Unknown command syntax retains native permission handling. This foundation never changes a sandbox or bypasses a denied permission; per-adapter enforcement tests land with their integrations.
1592
+
1378
1593
  ## Permission modes for running peers
1379
1594
 
1380
1595
  Use a plain terminal to inspect or change a running peer:
@@ -1714,7 +1929,11 @@ entries and fields. It contains machine paths and the peer identity, not a token
1714
1929
  with a warning. `--print-command` shows the change and command without writing.
1715
1930
  The configured `kimi_cmd` must be a single executable or end in exactly `acp`;
1716
1931
  the TUI uses the executable or drops that final word. Other wrappers are refused
1717
- with the headless command rather than guessed.
1932
+ with the headless command rather than guessed. The launcher uses the running
1933
+ hub's `kimi_cmd`; when that value has no TUI form, the refusal says so, since an
1934
+ edited file value waits for `ahub settings reload`.
1935
+
1936
+ After three empty inbox reads within ten minutes by a pull-only peer with no assigned task or review, the hub adds polling advice and one console notice per streak (peer id and counts only); a delivery or assignment resets it, and restart clears the in-memory counters.
1718
1937
 
1719
1938
  The TUI is pull-only. Its model reads `hub_inbox` at the start of work, between
1720
1939
  steps of longer work and before reporting, and shares results, and mid-work
@@ -1725,7 +1944,9 @@ and important markers do not push or interrupt it. There is no hub busy state,
1725
1944
  watchdog, console approval, hub permission mode, context reading, automatic quota
1726
1945
  pause/handoff, shared final answer or session resume. When important messages are
1727
1946
  waiting, every other hub tool result ends with a line saying how many, so the model
1728
- reads `hub_inbox` before starting long local work (#398). Read quota and approve tools
1947
+ reads `hub_inbox` before starting long local work (#398). One `hub_inbox` read drains the queue oldest first up to
1948
+ 64 KiB of rendered text and, when more waits, ends with `N more waiting: call hub_inbox again before you start work` (#713).
1949
+ Read quota and approve tools
1729
1950
  in Kimi's own TUI; a person can pause the hub queue by hand. End the native terminal
1730
1951
  session there, not with `ahub stop kimi`.
1731
1952
 
@@ -1895,8 +2116,9 @@ but cannot show pushes, so it attaches tools-only (protocol 16, issue #205):
1895
2116
  `messages wait for hub_inbox; for pushes restart Claude with ahub claude`; the
1896
2117
  console footer shows `tools-only: ahub claude` and the dashboard shows the
1897
2118
  status line.
1898
- - The session reads them with `hub_inbox`, at most ten messages at a time plus
1899
- the hub's recall note when one waits (it says how many still wait). Each read is one `completed` delivery (`read through
2119
+ - The session reads them with `hub_inbox`, which drains the queue oldest first (important messages marked, as ever) up to 64 KiB of rendered text plus
2120
+ the hub's recall note when one waits (#713; a push peer's digest is still ten items). When more is waiting the result ends
2121
+ `N more waiting: call hub_inbox again before you start work`. Each read is one `completed` delivery (`read through
1900
2122
  hub_inbox` in `ahub queue list`); nothing is ever `accepted`, and the session
1901
2123
  has no `hub_delivery_done`. A read the model never saw (the tool call was
1902
2124
  cancelled, the plugin died) cannot be retried: `ahub queue show <id>` of the
@@ -1934,7 +2156,7 @@ bridge currently receives no positive per-server activation signal.
1934
2156
 
1935
2157
  A Claude session started with `ahub claude` receives pushes when it is idle. While it is busy:
1936
2158
 
1937
- - Every hub tool result carries fresh counts, `(N queued hub message(s), M important hub message(s) waiting: call hub_inbox before starting long local work)`, when important work waits. A zero count clears an earlier hint.
2159
+ - Every hub tool result carries fresh counts, `(N queued hub message(s), M important hub message(s) waiting: call hub_inbox before starting long local work)`, when important work waits. A zero count clears an earlier hint. Under a delivery hold that refuses pulls, the counts and held rows stay but the `hub_inbox` advice is dropped.
1938
2160
  - `hub_inbox` reads the daemon queue (and any local failed-push buffer) in a channel session too. A read is one `completed` delivery, like a tools-only read; holds refuse it the same way.
1939
2161
  - An important message is pushed into the running turn (steered) instead of waiting for the turn to end. Mid-turn reading was verified on Claude Code 2.1.302; an older client that drops it leaves the delivery `accepted` until a person resolves it. Ordinary messages still wait for the idle push or `hub_inbox`.
1940
2162
  - Do not use `[FYI]` for PR ready, done, submitted, ruling-needed, blocker or changed-contract messages: they need action. `hub_task_done` and `hub_review` already notify their recipients.
@@ -2106,6 +2328,22 @@ killed.
2106
2328
  Rows without a live process are stale registrations; forget them with
2107
2329
  `ahub projects remove <id>`.
2108
2330
 
2331
+ ## Agent CLI versions
2332
+
2333
+ Agent CLIs update themselves often, and a hub release cannot follow every one. One policy decides each gated CLI: the Codex and Kimi fresh reviewers, Jcode, agy and Qwen (#691).
2334
+
2335
+ - `ahub agents [--json]` lists each gated CLI with its version, verdict and verified versions.
2336
+ - **verified:** a receipt exists.
2337
+ - **in-line:** untested, inside the compatible line that a contract gate (Jcode, agy) declares; it starts with a note.
2338
+ - **trusted:** you accepted this exact version on this machine.
2339
+ - **refused:** the reason names the installed and verified versions and the command that admits it.
2340
+ - **unknown:** the CLI is not on PATH, or its version could not be read.
2341
+ - `ahub agents trust <id> <version>` accepts one untested version on this machine (`~/.agenthub/agent-compat.json`); `--revoke` undoes it. Only the person can run it; agent shells are refused.
2342
+ - For the reviewers, trust lifts only the version pin. The MCP inventory, Apps/plugins/hooks readback, sandbox denial fixtures and binary fence still run before every child, and a failed leg still refuses.
2343
+ - What trust cannot prove is printed when you trust.
2344
+ - `ahub doctor` shows one line for each installed CLI that is not on a verified version.
2345
+ - A weekly workflow (`.github/workflows/agent-compat.yml`) compares the latest npm versions of Codex, Kimi Code and Qwen Code with the verified sets, keeps one `agent-compat` issue up to date while one is ahead and closes it when none is. The next step for each listed version is a model-free receipt in `docs/verification/`, then adding it to `verified` in the same PR. Jcode and agy are not watched.
2346
+
2109
2347
  ## Upgrade and crash recovery
2110
2348
 
2111
2349
  On a terminal, one command reviews, applies and follows an upgrade:
@@ -3271,7 +3509,7 @@ that the file is untracked; a dropped value is reported by the settings result.
3271
3509
 
3272
3510
  ## SDLC artifact receipts and gates
3273
3511
 
3274
- The playbook lifecycle (#257, #481) is off without `.agenthub/sdlc.toml`. When enabled its stages are intent, spec, plan, build, test, review, merge, release and maintain, grouped as Plan, Design, Build, Test, Deploy and Maintain. Advise shows blockers; enforce checks implemented head-bound review and approval gates. Stored implement/verify receipts read as build/test; new enabled receipts use canonical ids. See [the SDLC contract](sdlc.md) for gates, required check names, local receipts, console-only readbacks and the S0-S8 delivery table. A person accepts a spec issue with `ahub sdlc accept <task> --issue <n>` (S1). The planner records plan reviews with review and CI scope (`hub_plan_review`, `ahub sdlc plan-review`) and grooms with `ahub backlog groom`; `ahub backlog return <task> <revision> <reason>` returns accepted, untouched work (S9). `ahub sdlc costs [owner/repo]` reports review rounds and CI runs of merged PRs without writing the board (S10). S11 release trains ship in 0.12.37 (see [release scheduling](#planner-release-scheduling)). S2-S7 and a full live advise/release cycle remain pending. Owners/reviewers record bounded identities through `hub_sdlc_record`; these grant no additional authority. Off output, receipt and schema shapes stay unchanged.
3512
+ The playbook lifecycle (#257, #481) is off without `.agenthub/sdlc.toml`. When enabled its stages are intent, spec, plan, build, test, review, merge, release and maintain, grouped as Plan, Design, Build, Test, Deploy and Maintain. Advise shows blockers; enforce checks implemented head-bound review and approval gates. Stored implement/verify receipts read as build/test; new enabled receipts use canonical ids. See [the SDLC contract](sdlc.md) for gates, required check names, local receipts, console-only readbacks and the S0-S8 delivery table. A person accepts a spec issue with `ahub sdlc accept <task> --issue <n>` (S1). The planner records plan reviews with review and CI scope (`hub_plan_review`, `ahub sdlc plan-review`) and grooms with `ahub backlog groom`; `ahub backlog return <task> <revision> <reason>` returns accepted, untouched work (S9). `ahub sdlc costs [owner/repo]` reports review rounds and CI runs of merged PRs without writing the board (S10). S11 release trains ship in 0.12.37 (see [release scheduling](#planner-release-scheduling)). S3 structured review findings (an open Important finding blocks approval in enforce; `max_nits` in `sdlc.toml` caps nits) and S2 literal-path plan conformance with the person-only `ahub sdlc accept-departure <task> <head> <reason...>` are in source; see [findings](sdlc.md#structured-review-findings-481-s3) and [plan conformance](sdlc.md#plan-conformance-481-s2). S4-S7 and a full live advise/release cycle remain pending. Owners/reviewers record bounded identities through `hub_sdlc_record`; these grant no additional authority. Off output, receipt and schema shapes stay unchanged.
3275
3513
  ## Optional Qwen Code peer
3276
3514
 
3277
3515
  Select Qwen per project with `ahub roster add qwen`; it defaults off and is absent
@@ -3280,11 +3518,12 @@ starts its ACP peer. `--print-command` previews; TUI is refused until #286's
3280
3518
  verification. `ahub stop qwen` stops the owned process group. A conductor may start
3281
3519
  it only through the selected roster and existing capabilities.
3282
3520
 
3283
- The supported native ACP identity is qwen-code 0.25.0. A CLI version label alone
3521
+ Receipt-verified native ACP identities are qwen-code 0.25.0 and 0.26.0 ([dated receipt](verification/2026-10-22-kimi-qwen-compat.md)). A CLI version label alone
3284
3522
  is insufficient: startup verifies its native identity/version and advertised mode
3285
- ids before any prompt. The observed local installation reports CLI 0.25.0 while
3286
- its package/ACP source says 0.24.1; do not call that installation ready. Configure
3287
- `qwen_cmd` only in untracked config.local.json with a verified 0.25.0 executable.
3523
+ ids before any prompt. The earlier local installation reported CLI 0.25.0 while
3524
+ its package/ACP source said 0.24.1; that was not a ready identity. The 0.26.0 receipt
3525
+ used a private prefix and did not upgrade PATH. Configure `qwen_cmd` only in
3526
+ untracked config.local.json with a receipt-verified executable.
3288
3527
  The provider's tunnel/relay, if needed, must already be available. Startup cannot
3289
3528
  prove network readiness from a version command.
3290
3529
 
@@ -3316,11 +3555,11 @@ TUI input may interleave with an ACP turn; there is no single-writer guard. Clos
3316
3555
  After upgrading to the recipient-retry release, a Jcode project MCP entry written before `AGENTHUB_DELIVERY_ONLY` was added is intentionally stale. Once per project, the person runs `ahub jcode --mode headless` before using conductor/dashboard starts. The launcher rewrites only the owned entry with the delivery-only marker, preserving foreign entries and refusing collisions; recovery relaunches already use this CLI path. Readiness checks never rewrite the file, and an explicit false marker or any other drift remains refused. Until that rewrite, daemon starts keep their actionable stale-entry refusal rather than advertising a retry tool Jcode cannot use.
3317
3556
 
3318
3557
 
3319
- Native policy is unmanaged and has NO hub sandbox; no permission_modes.jcode or runtime mode mapping is invented. Quota and consumption remain unknown. Supported versions run from 0.98.0 up to, not including, 1.0.0 (#529). Every owned jcode process (version probe, daemon, ACP and TUI) runs with `JCODE_NO_TELEMETRY=1`. Native session/load (verified on 0.94.0 and 0.98.1) cannot prove identity, so attached Jcode blocks controlled recovery before effects and crash recovery asks for deliberate native reopening. A requested load without an echoed identity refuses; no fresh session is presented as restored. Provider/tool/native teardown receipts remain unverified in the pinned contract report.
3558
+ Native policy is unmanaged and has NO hub sandbox; no permission_modes.jcode or runtime mode mapping is invented. Quota and consumption remain unknown. Supported versions run from 0.98.0 up to, not including, 1.0.0 (#529); another version is refused before the owned daemon starts, with the version named and the trust command (#691). Every owned jcode process (version probe, daemon, ACP and TUI) runs with `JCODE_NO_TELEMETRY=1`. Native session/load (verified on 0.94.0 and 0.98.1) cannot prove identity, so attached Jcode blocks controlled recovery before effects and crash recovery asks for deliberate native reopening. A requested load without an echoed identity refuses; no fresh session is presented as restored. Provider/tool/native teardown receipts remain unverified in the pinned contract report.
3320
3559
 
3321
3560
  ## Optional Antigravity (agy) peer
3322
3561
 
3323
- Antigravity is optional and off roster by default. Use `ahub roster add agy`, then run `ahub agy` in a terminal: it runs the Antigravity CLI as a fresh pull-only TUI in that terminal. It accepts the 1.4.x line: 1.4.2 is the verified version, any other 1.4.x launches with a stderr warning (agy updates itself), and other versions are refused (#589). The running hub must advertise `native_hook_launch`; an older hub refuses before any write with an upgrade-or-restart remedy. Only the person's launcher starts it: conductor starts are refused, there is no controlled restore or resume, and a declared agy extra instance is refused when the config loads (#625). Headless/print, resume (`--conversation`, `--continue`, `--new`), `--mode`, `--dangerously-skip-permissions` and `--unattended` are refused until their native mapping is verified; `--print-command` previews without writing.
3562
+ Antigravity is optional and off roster by default. Use `ahub roster add agy`, then run `ahub agy` in a terminal: it runs the Antigravity CLI as a fresh pull-only TUI in that terminal. It accepts the 1.4.x line: 1.4.2 is the verified version, any other 1.4.x launches with a stderr warning (agy updates itself), and other versions are refused (#589) unless the person trusts that exact version with `ahub agents trust agy <version>` (#691). The running hub must advertise `native_hook_launch`; an older hub refuses before any write with an upgrade-or-restart remedy. Only the person's launcher starts it: conductor starts are refused, there is no controlled restore or resume, and a declared agy extra instance is refused when the config loads (#625). Headless/print, resume (`--conversation`, `--continue`, `--new`), `--mode`, `--dangerously-skip-permissions` and `--unattended` are refused until their native mapping is verified; `--print-command` previews without writing.
3324
3563
 
3325
3564
  The launcher writes exactly `plugin.json`, `mcp_config.json` and `hooks.json` in `.agents/plugins/agent-hub/`, refusing linked or foreign entries before any write, and removes only its own matching files when the TUI ends. It never writes project-level `.agents/hooks.json` or the person's `~/.gemini` settings. Run `ahub init` afterwards to ignore the untracked plugin directory; a tracked plugin is written with a warning. The native model sees the hub tools under the server name `agent-hub_agent-hub`.
3326
3565
 
@@ -3366,11 +3605,15 @@ Extra instances use the existing `instances` block. For example, `"myagent-revie
3366
3605
 
3367
3606
  Configured kinds are cloud peers and never receive PII work. Quota and context are unknown, and a kind with no budget source is exempt from quota accounting, so pause it by hand if its provider refuses. After a crash the hub resumes a recorded session through `session/load` only when the agent echoes the same id, and it never reuses a recorded model. Controlled recovery (`ahub upgrade`, `restart`, `recovery`) refuses a running configured kind: stop it, then start it again with its session id. While the project declares adapters, the upgrade or restart target must advertise `configured_acp`.
3368
3607
 
3608
+ ## External adapter modules (#451 A4)
3609
+
3610
+ A bundled adapter module is authorized by the person with `adapter_modules` (at most 16 rows of `path` and `sha256`) in an untracked `.agenthub/config.local.json`; in `config.json` or a tracked file it is ignored with a notice, and settings never edit it. Only daemon startup imports a module: settings, reload, previews and CLI reads never do, so a change needs a restart. The module must be a single self-contained ESM file inside the project (no symlinked, world-writable or oversized file) whose bytes match the pin, and it may import only Node/Bun builtins and `@staix/agent-hub/adapter`; the scanner also refuses any `require`, `eval` or `Function` word token. Each import must settle within 2 s. A module that fails any check is refused on its own, with a bounded reason in the status reply, while startup and the other adapters continue; its roster row stays inert. A loaded kind is headless, cloud (never PII), off roster until `ahub roster add <kind>`, and has no controlled recovery: stop it and start it again with its recorded session id. While modules are declared, the upgrade or restart source and target must advertise `external_adapter_modules`. Bundling, the accepted definition subset and the conformance kit (`@staix/agent-hub/conformance`, for module CI only) are in [Adding an AI tool](adding-an-ai-tool.md#5-external-adapter-modules-451-a4). A pin authorizes code that runs with the daemon's rights; it is not a sandbox ([security](security.md)).
3611
+
3369
3612
  # Headless ACP watch (#286)
3370
3613
 
3371
3614
  - A person opens `ahub watch kimi` or `ahub watch qwen` in a second terminal. `--no-thoughts` hides thought items; `--color=auto|always|never` controls colour. `q` or Ctrl+C leaves the view, keeping the peer running. Answer approvals in `ahub console` or with `ahub permit`.
3372
3615
  - Watch replays bounded memory history from this daemon, then displays live updates. It is the hub's read-only view, with no prompt input. It does not read native session files.
3373
- - Kimi's default is its own pull-only TUI (#316). Watch requires `ahub kimi --mode headless`; its own TUI remains visible in its native terminal. Qwen requires roster participation and verified ACP 0.25.0. `ahub qwen --mode tui` remains refused.
3616
+ - Kimi's default is its own pull-only TUI (#316). Watch requires `ahub kimi --mode headless`; its own TUI remains visible in its native terminal. Qwen requires roster participation and a receipt-verified ACP identity. `ahub qwen --mode tui` remains refused.
3374
3617
  - Protocol stays 16; a same-protocol older daemon answers the new request as unknown, which the CLI reports. Since 0.12.44 a headless start that the hub itself makes for Kimi or Qwen opens this view automatically through the terminal provider (see Start modes; #286).
3375
3618
  - Native T0 limits: [ACP watch verification](verification/2026-10-11-acp-watch-t0.md).
3376
3619
 
@@ -3801,8 +4044,62 @@ restart the hub. For example, a reviewer can use:
3801
4044
 
3802
4045
  Start them with `ahub codex --as codex-review` and
3803
4046
  `ahub kimi --as kimi-review`. Codex keeps its native TUI and its own hub-owned
3804
- app-server proxy; extra Kimi, Qwen, Pi and local instances use headless starts.
3805
- Extra Jcode instances are unavailable. An extra Claude instance is a reviewer or
4047
+ app-server proxy; extra Kimi, Qwen, Jcode, Pi and local instances use headless starts.
4048
+ Extra Jcode instances are supported (#685), in `.agenthub/config.local.json` only:
4049
+
4050
+ ```json
4051
+ {
4052
+ "instances": {
4053
+ "jcode-claude": { "kind": "jcode", "roles": ["verifier"], "jcode_home": "/Users/you/.agenthub/jcode/claude",
4054
+ "provider": "claude", "model": "claude-sonnet-5-5" },
4055
+ "jcode-local": { "kind": "jcode", "roles": ["verifier"], "jcode_home": "/Users/you/.agenthub/jcode/local",
4056
+ "provider_profile": "onprem" }
4057
+ }
4058
+ }
4059
+ ```
4060
+
4061
+ `jcode_home` is required, absolute, and must differ from the base `jcode_home`, from
4062
+ every other instance's and from any directory inside or around them, however it is
4063
+ spelled: a symlink, a system alias such as `/tmp` and `/private/tmp`, or a case-only
4064
+ alias is the same home (the hub refuses the configuration at startup otherwise). At most 8 Jcode instances are accepted per
4065
+ project. `provider`, `model` and `provider_profile` are optional and passed to Jcode
4066
+ as `-p`, `--model` and `--provider-profile` exactly as written; any other field is
4067
+ refused, and these fields are never editable from settings.
4068
+
4069
+ You authenticate inside Jcode, never in the hub. Run Jcode's own setup against each
4070
+ home (provider login, API key, an OpenAI-compatible base URL such as the on-prem
4071
+ gateway, a provider profile), and initialize that home's MCP store with Jcode's own
4072
+ client, as for the base peer. The hub never reads, writes, copies or injects a model
4073
+ credential, a key, a token or a provider URL, and never creates the home. Its only
4074
+ start preflight is read-only: the home must exist as a directory you own, otherwise
4075
+ the start is refused and nothing runs. A Jcode sign-in failure appears as the start
4076
+ or turn error, with bounded text. An instance reports tokens only if Jcode itself
4077
+ does; until then they show `unknown`, never 0.
4078
+
4079
+ Each instance owns its private `jcode serve` and `jcode acp` pair, socket, roster row
4080
+ and launch record, and acts as its own peer id (for example `jcode-local`), never as
4081
+ `jcode`. Start it with `ahub jcode --as jcode-local` (headless; an instance has no
4082
+ TUI) and stop it with `ahub stop jcode-local`: the base Jcode and every other instance
4083
+ keep running. An extra implementer needs the same managed worktree as every extra
4084
+ implementer (`worktree: ".worktrees/jcode-local"`). Every instance is a cloud peer
4085
+ (`trust.onPrem` is false), whatever provider you configured: it never receives PII
4086
+ work. `ahub status` and the dashboard show the provider, model and profile as
4087
+ declared, and `ahub doctor` lists the MCP servers each rostered instance's home would
4088
+ load.
4089
+
4090
+ Upgrade note (#368): the project's `.jcode/mcp.json` entry for Jcode no longer names
4091
+ `AGENTHUB_PEER_ID`, because the file is shared by every Jcode in the project and Jcode
4092
+ gives an entry's own env precedence over the process env. Each hub-started Jcode now
4093
+ supplies its own peer id and a launch id, and the hub admits a hub-tools connection
4094
+ for Jcode only with the launch id of that peer's current owned launch. After
4095
+ upgrading, `ahub status` and `ahub doctor` report the Jcode MCP entry as not ready
4096
+ once (`jcodeMcpReady` is false for the old entry): run `ahub jcode` once and it
4097
+ rewrites its own entry, or follow the remedy text the start refusal shows. A Jcode you
4098
+ run yourself in this project outside the hub has no peer id: its hub tools refuse
4099
+ every call with that reason and never default to `jcode`, and a process that only
4100
+ sets `AGENTHUB_PEER_ID` without an owned launch is refused by the hub.
4101
+
4102
+ An extra Claude instance is a reviewer or
3806
4103
  planner you start yourself: declare it with `roles` only (for example
3807
4104
  `"claude-review": { "kind": "claude", "roles": ["reviewer"] }`, no `start_mode` or
3808
4105
  `worktree`) and run `ahub claude --as claude-review` in a terminal. It keeps its own
@@ -3819,7 +4116,9 @@ the builder's summary or agent chat, and the board records its verdicts as
3819
4116
  the planner reviews and the task's assignment note says `reviewer=planner (fallback)`.
3820
4117
  A missing
3821
4118
  `peer_instances` feature on an old daemon or upgrade target requires a supporting
3822
- version before launching or restoring extras.
4119
+ version before launching or restoring extras. An older recovery source without it
4120
+ is refused only while a declared instance is live or unreadable; a source whose
4121
+ status shows every declared id offline proceeds.
3823
4122
 
3824
4123
  ### Fresh reviewer pool (#519)
3825
4124
 
@@ -3838,9 +4137,9 @@ From 0.12.37 reviews can rotate across fresh headless Claude and Codex reviewers
3838
4137
 
3839
4138
  Start them with `ahub claude --as claude-review`, `ahub codex --as codex-review` and `ahub kimi --as kimi-review`; stop with `ahub stop <id>`. Fresh Claude and Codex variants share the project checkout; the Kimi variant runs in a clean temporary working directory and reviews only the screened brief and host facts. All fresh variants refuse a custom worktree or permission mode. Codex and Kimi require `review_mode: "fresh"`; without it, an existing headless instance with the reviewer role keeps its native app-server/ACP behavior, worktree and permission options and does not join this pool. Claude retains its #223 automatic headless-reviewer declaration. The agy reviewer slot remains automatically unavailable (`agy is an implementer peer (#485)`). A failed or unsupported fresh start never downgrades to a native continuing session. Ordinary implementer instances retain their own adapters. The default logical pool order is Claude, Codex, Kimi. Only configured kinds with at least one declared reviewer count as slots, including declared ones that are offline, paused, quota-limited or failing, so availability never renumbers them; configured kinds with no declaration are listed after the walk as `no declared reviewer`. `review.rerotate` defaults to false. Pool selection starts at PR number modulo the number of declared kind slots, or task id when there is no PR, then skips the implementer's family and unavailable candidates. Delta review prefers the preceding eligible independent family in a fresh child. A preceding same-family fallback does not outrank a newly available independent family. `ahub route explain` shows the same pure walk.
3840
4139
 
3841
- Codex requires an installed 0.146.0 executable and a passing bounded runtime receipt before its model child starts. Use the existing `codex_bin` setting to select a supported host executable when PATH resolves another version. PATH 0.163.0 has no receipt in this slice and remains unavailable. The producer checks native binary/config stability, configured MCP inventory with only the two hub tools, effective Apps/plugins/hooks disablement and a named filesystem profile. Apps needs separate feature readback because configured inventory omits runtime connectors. Web search is requested disabled; its effective turn mode is not read back and managed requirements can select Cached (see the verification ceiling). Each launch proves public-read, exact/glob/symlink private-denial, standard host credential denial and write-denial in temporary fixtures. Standard credential and configured tool-home/XDG subtrees are denied independently of project `local.deny`. Its sandboxed shell is filesystem read-only with private paths denied and network configured disabled; it is not Claude's Read/Grep/Glob-only tool whitelist. No credential home is copied.
4140
+ Codex requires a verified `codex-cli` (0.146.0 or 0.163.0) or one the person trusts on this machine, and a passing bounded runtime receipt before its model child starts. Both verified versions passed the [2026-10-22 model-free production admission](verification/2026-10-22-codex-reviewer-isolation.md). A refusal names the installed and verified versions. Either set `codex_bin` to a verified executable, or run `ahub agents trust codex-review <version>`: trust lifts only the version pin, and every proof below still runs before each child (#691). The producer checks native binary/config stability, configured MCP inventory with only the two hub tools, effective Apps/plugins/hooks disablement, an explicit empty legacy `notify` command and a named filesystem profile. Apps needs separate feature readback because configured inventory omits runtime connectors. Web search is requested disabled; its effective turn mode is not read back and managed requirements can select Cached (see the verification ceiling). Each launch proves public-read, exact/glob/symlink private-denial, standard host credential denial and write-denial in temporary fixtures. Standard credential and configured tool-home/XDG subtrees are denied independently of project `local.deny`. Its sandboxed shell is filesystem read-only with private paths denied and network configured disabled; it is not Claude's Read/Grep/Glob-only tool whitelist. No credential home is copied.
3842
4141
 
3843
- Kimi requires Kimi Code 2.2.0 on the hub's PATH, signed in through its managed OAuth provider, and a passing runtime preparation before each child; any other version or a failed preparation is unavailable with its reason. Each review runs a new `kimi -p` child without Seatbelt, in a private `KIMI_CODE_HOME` with managed-provider-only config, a clean temporary working directory and an empty skills directory. Its agent may call only `hub_review` and `hub_task_show`, through a task-bound loopback MCP session that never carries the hub's control token, and has no native read, file or shell tool: it reviews the screened brief and host facts only. Its `credentials/`, `oauth/` and `device_id` link to the person's native Kimi files, so it shares the native login, refresh lock and the Kimi account's budget; no token is copied. A stop revokes its verdict authority first, then waits briefly while an OAuth refresh holds the lock; a forced stop mid-refresh may require a native re-login. Kimi's print output carries no token counts, so its usage stays unknown. This is a native tool-authority boundary, not filesystem or network isolation.
4142
+ Kimi requires a compat-admitted Kimi Code on the hub's PATH (receipt-verified 2.2.0 and 2.3.0), signed in through its managed OAuth provider, and a passing runtime preparation before each child; an unverified/untrusted version or a failed preparation is unavailable with its reason. The [2.3.0 receipt](verification/2026-10-22-kimi-qwen-compat.md) is model-free and does not verify real login/provider readiness. Each review runs a new `kimi -p` child without Seatbelt, in a private `KIMI_CODE_HOME` with managed-provider-only config, a clean temporary working directory and an empty skills directory. Its agent may call only `hub_review` and `hub_task_show`, through a task-bound loopback MCP session that never carries the hub's control token, and has no native read, file or shell tool: it reviews the screened brief and host facts only. Its `credentials/`, `oauth/` and `device_id` link to the person's native Kimi files, so it shares the native login, refresh lock and the Kimi account's budget; no token is copied. A stop revokes its verdict authority first, then waits briefly while an OAuth refresh holds the lock; a forced stop mid-refresh may require a native re-login. The Kimi backend records no measured token counts here, so its usage stays unknown; the 2.3.0 receipt makes no usage claim. This is a native tool-authority boundary, not filesystem or network isolation.
3844
4143
 
3845
4144
  A dangling or unreadable standard/configured credential root makes the Codex reviewer unavailable with only its path class in the reason (for example `~/.docker` or `$DOCKER_CONFIG`, never an expanded path or contents); repair the root and start the reviewer again.
3846
4145
 
@@ -3854,11 +4153,19 @@ An additional implementer must have an existing registered Git worktree and
3854
4153
  attached branch at `.worktrees/<instance-id>`, named explicitly by `worktree` in
3855
4154
  its declaration. The hub validates it and does not create it in this phase.
3856
4155
  Reviewer instances may share the checkout, or name their own validated worktree.
3857
- The execution directory is separate from the canonical hub project. An Orca
4156
+ The execution directory is separate from the canonical hub project. Resolving it
4157
+ (at an extra-instance start and for a turn undo) makes up to four git calls that
4158
+ share one 5 s allowance; when it runs out the start refuses with `Git workspace
4159
+ evidence unavailable`. An Orca
3858
4160
  terminal may belong to either that canonical project or the instance's execution
3859
4161
  worktree; recovery preserves its exact recorded grouping and live launcher.
3860
4162
  Changing or removing the declaration invalidates its saved extra-instance
3861
- binding. Do not infer ownership from a native-kind label alone.
4163
+ binding; the recovery screen and `--verbose` receipt still show what was recorded
4164
+ for it. A roster key left by an undeclared instance (`codex-2`) loads as inert and
4165
+ `ahub roster remove codex-2` deletes it from `.agenthub/config.local.json`; a key
4166
+ held only in the shared `config.json` is refused with a request to edit that file.
4167
+ Any other unknown roster key is still an unsupported peer. Do not infer ownership
4168
+ from a native-kind label alone.
3862
4169
 
3863
4170
  A routing preference for a kind chooses among its instances using current load
3864
4171
  and WIP. A manual instance id remains exact. Reviewers from the same kind share
@@ -3873,6 +4180,14 @@ CLI checks the saved workspace against the current declaration again before any
3873
4180
  context change or file restoration. Removing the declaration intentionally
3874
4181
  blocks that undo until the identity and workspace can be reviewed.
3875
4182
 
4183
+ ## Release approval and optional tag guard
4184
+
4185
+ Run `ahub sdlc authorize-release <version>` from the running daemon's release-candidate checkout in a person's terminal after committed documentation is fresh. The receipt binds commit, package version, remote identity and the docs verdict. Agents and delegated live-test calls cannot authorize. The daemon keeps approval only in memory and logs metadata for audit; restart requires a new approval. The read-only `ahub sdlc release <owner/repo>` report shows its receipt, retaining native-verification blockers.
4186
+
4187
+ `ahub init --release-guard` installs a managed Git pre-push guard and release instructions (`--dry-run --json` previews them). It preserves an existing default hook as `pre-push.agent-hub-original` and passes original argv/stdin to it only after approval matches. Unknown or mismatched `v*` tag pushes fail closed, including when the daemon is down. Non-version branch pushes remain independent of the daemon. Custom `core.hooksPath` and linked/hardlinked hook files require manual integration. Re-run init after changing the Bun/package location. The person's `git push --no-verify` remains an explicit bypass; agents are instructed never to use it.
4188
+
4189
+ For `gh release create` and `npm publish`, use the exact literal command after `ahub sdlc release-guard --`. This preflight reads evidence and never executes the command. New GitHub tags require an explicit full commit target; alternate-repository, workspace/archive and bulk target forms refuse. GitHub/npm adapter enforcement is deferred to #312/#313. This consent record does not certify a tarball, native acceptance or live publication. Detailed limits: [S6 authorization](sdlc.md#person-release-authorization-481-s6).
4190
+
3876
4191
  ## Planner release scheduling
3877
4192
 
3878
4193
  Use `ahub sdlc train report <release-id>` for scope readiness and recorded release metrics. The planner creates future release rows, activates one train, and assigns tasks through the release-only hub tools. A due date requests a cut review; it never clears unfinished work. Move missed tasks to the next row with a reason, then use `ahub sdlc train cut <release-id> <revision>` from the person's terminal or `hub_release_cut` as the planner. The cut retains a short host admission lease through freeze and defers on held gate slots or unknown/quota-limited peer windows with a real quota source. Accounts without a quota reader (Kimi included when `kimi_tokens_5h` is zero) report `no quota source`; manual and budget-record pauses still defer. It does not run release preparation or publish anything. Schedule bundle/check/docs work under separate heavy admission and retain the person's native verification, authorization and upgrade window. A hotfix records its enumerated cause and keeps every gate.
@@ -3886,3 +4201,11 @@ If Codex is offline with `statusNotice.code = unidentified-cancellation`, the hu
3886
4201
  could not identify a cancelled native turn. Reattach the native session before
3887
4202
  further hub delivery. A late completion keeps this hold; verified native
3888
4203
  reattachment or TUI detachment clears it. The notice contains fixed guidance, without native ids.
4204
+
4205
+ ## Roles per SDLC stage (#525, planned)
4206
+
4207
+ The design for planner, implementer and reviewer instances per stage is in the spec section "Amendment: roles per SDLC stage (#525)". Nothing in it is available yet. Each implementation slice (525-1 to 525-6) adds its operator steps here when it ships. Until then, the instance rules in "Additional headless peer instances" above are current. The planned changes an operator will see:
4208
+
4209
+ - A Claude instance with `["implementer"]` and a `worktree`, as a terminal instance (started with `ahub claude --as <id>`) or as `start_mode: "headless"` (`permission_mode` `ask` or `ask-when-needed`; `never-ask` refused).
4210
+ - The `planner` setting accepts a list of peers or instance ids; each task records its own planner, and only that planner or the person runs planner tools on it.
4211
+ - A reviewer is never a peer that implemented the task, and a plan's author does not record its own plan review while another planner is available.