@staix/agent-hub 0.12.45 → 0.14.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 (108) hide show
  1. package/CHANGELOG.md +31 -0
  2. package/README.md +85 -116
  3. package/docs/agent-notes/README.md +6 -0
  4. package/docs/agent-notes/adapters.md +10 -7
  5. package/docs/agent-notes/budget.md +7 -1
  6. package/docs/agent-notes/bus.md +6 -4
  7. package/docs/agent-notes/daemon.md +14 -2
  8. package/docs/agent-notes/local-worker.md +2 -1
  9. package/docs/agent-notes/tasks.md +16 -3
  10. package/docs/agent-notes/tests.md +5 -3
  11. package/docs/dogfood.md +95 -0
  12. package/docs/events.md +14 -1
  13. package/docs/operations.md +290 -27
  14. package/docs/profiles.md +2 -2
  15. package/docs/quickstart.md +13 -3
  16. package/docs/sdlc.md +59 -8
  17. package/docs/security.md +26 -13
  18. package/docs/smoke.md +9 -4
  19. package/docs/specs/2026-09-19-agent-hub-design.md +797 -8
  20. package/docs/verification/2026-10-19-reviewer-isolation.md +2 -0
  21. package/docs/verification/2026-10-22-code-facts.md +78 -0
  22. package/docs/verification/2026-10-22-codex-reviewer-isolation.md +61 -0
  23. package/docs/verification/2026-10-22-kimi-qwen-compat.md +79 -0
  24. package/docs/verification/2026-10-23-0.13.0.md +89 -0
  25. package/docs/verification/2026-10-23-0.14.0.md +69 -0
  26. package/docs/verification/dogfood/2026-10-22.md +3109 -0
  27. package/docs/verified.json +22 -17
  28. package/package.json +1 -1
  29. package/plugins/agent-hub/.claude-plugin/plugin.json +1 -1
  30. package/plugins/agent-hub/server.js +702 -347
  31. package/src/adapters/agy/entry.ts +5 -4
  32. package/src/adapters/claude-channel.ts +20 -9
  33. package/src/adapters/codex-appserver.ts +18 -2
  34. package/src/adapters/codex-review-isolation.ts +29 -11
  35. package/src/adapters/compat.ts +140 -0
  36. package/src/adapters/definitions/builtin-metadata.ts +3 -2
  37. package/src/adapters/definitions/native-cli-doctor.ts +12 -5
  38. package/src/adapters/definitions/native-cli-entry-codex.ts +13 -0
  39. package/src/adapters/definitions/native-cli-entry-jcode.ts +9 -6
  40. package/src/adapters/definitions/native-cli-jcode-mcp.ts +9 -2
  41. package/src/adapters/definitions/native-cli-preview-common.ts +2 -0
  42. package/src/adapters/definitions/native-cli-preview-jcode.ts +8 -2
  43. package/src/adapters/definitions/native-cli-preview-qwen.ts +2 -1
  44. package/src/adapters/definitions/qwen-native.ts +7 -3
  45. package/src/adapters/definitions/qwen.ts +2 -1
  46. package/src/adapters/jcode.ts +72 -31
  47. package/src/adapters/kimi-review.ts +6 -3
  48. package/src/adapters/registry-types.ts +3 -1
  49. package/src/adapters/registry.ts +1 -0
  50. package/src/adapters/reviewer-print.ts +34 -7
  51. package/src/cli/agents.ts +40 -0
  52. package/src/cli/console-review.ts +54 -0
  53. package/src/cli/console-state.ts +124 -29
  54. package/src/cli/console-symbols.ts +74 -0
  55. package/src/cli/console-usage.ts +89 -0
  56. package/src/cli/console.ts +26 -1
  57. package/src/cli/help.ts +17 -6
  58. package/src/cli/identity.ts +29 -6
  59. package/src/cli/init.ts +109 -11
  60. package/src/cli/main.ts +127 -11
  61. package/src/cli/output.ts +91 -3
  62. package/src/cli/preview.ts +1 -1
  63. package/src/cli/sdlc-release-guard.ts +82 -0
  64. package/src/cli/status-lines.ts +1 -1
  65. package/src/hub/action-class.ts +362 -0
  66. package/src/hub/action-policy.ts +106 -0
  67. package/src/hub/board.ts +65 -9
  68. package/src/hub/budget.ts +71 -2
  69. package/src/hub/bus.ts +43 -3
  70. package/src/hub/checks.ts +80 -31
  71. package/src/hub/code-facts.ts +223 -0
  72. package/src/hub/config-apply.ts +10 -1
  73. package/src/hub/config-trust.ts +7 -4
  74. package/src/hub/daemon.ts +319 -34
  75. package/src/hub/dogfood-read.ts +199 -0
  76. package/src/hub/dogfood.ts +163 -0
  77. package/src/hub/envelope.ts +1 -1
  78. package/src/hub/events.ts +11 -2
  79. package/src/hub/hub-tools.ts +26 -3
  80. package/src/hub/inbox-polling.ts +25 -0
  81. package/src/hub/instances.ts +87 -5
  82. package/src/hub/manager.ts +2 -2
  83. package/src/hub/profiles.ts +7 -2
  84. package/src/hub/release-worktree.ts +56 -0
  85. package/src/hub/review-brief.ts +36 -0
  86. package/src/hub/sdlc-metrics.ts +260 -0
  87. package/src/hub/sdlc-queue.ts +31 -9
  88. package/src/hub/sdlc-release-authorization.ts +173 -0
  89. package/src/hub/sdlc.ts +54 -14
  90. package/src/hub/settings.ts +5 -2
  91. package/src/hub/snapshots.ts +93 -8
  92. package/src/hub/spec-read.ts +31 -0
  93. package/src/hub/symbol-normalizer.ts +26 -0
  94. package/src/hub/task-code-group.ts +52 -0
  95. package/src/hub/task-code-symbols.ts +90 -0
  96. package/src/hub/task-sweep.ts +3 -2
  97. package/src/hub/tasks.ts +455 -67
  98. package/src/hub/test-integrity.ts +162 -0
  99. package/src/hub/ui.ts +3 -0
  100. package/src/hub/usage-rollup.ts +419 -0
  101. package/src/local/sandbox.ts +5 -3
  102. package/src/ui/index.html +274 -2
  103. package/templates/AGENTS.block.md +1 -1
  104. package/templates/profiles/dev.json +6 -1
  105. package/templates/profiles/general.json +6 -1
  106. package/templates/profiles/research.json +6 -1
  107. package/templates/profiles/writing.json +6 -1
  108. 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,39 @@ 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
+ The dashboard's Usage view (#683 U4) sits in the Agents view, under Usage budgets, and shows the same roll-up: the lifetime, without-cache and cache-read totals with
154
+ the peers whose figure is unknown, the last 14 UTC days as stacked columns per peer (a day with no record is a `no record` column, never zero), a limits table with the
155
+ open pauses and the last 20 pause, resume and reset events, and a task table (state, owner, PR, input, output, cache read, cache write, total). Every figure names its
156
+ source like the console does and `unknown` stays the word. A private task shows `#id [pii]` and never its title. The page asks for the figures only while the Agents view is open
157
+ (`view: "agents"` in the snapshot request), so a hidden view costs no roll-up work on the 1 s poll. When the roll-up is still behind a very large log the snapshot carries
158
+ `usage.behind: true` and the view says the figures are a lower bound until it catches up; a roll-up that cannot be read shows `unavailable`, never the last figures. When there
159
+ are more than 20 limit events the table says how many older ones it leaves out and that `ahub usage limits` lists them. Colours are design tokens only, the view reflows to one column at 600 px, and its
160
+ tables and chart scroll inside themselves.
161
+
162
+ 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,
163
+ `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
164
+ 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
165
+ 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
166
+ 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
167
+ `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
168
+ 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
169
+ prefilled. The console runs `ahub review` as the person; the hub still decides who may review, and nothing about authority changed.
170
+
171
+ 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
172
+ 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
173
+ 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
174
+ 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
175
+ `ahub review N approved|changes_requested [note]`.
176
+
144
177
  The console attention companion (#280) shows waiting, blocked, review, working,
145
178
  idle or asleep with separate action/watch counts. Outside the Tasks table, `n` selects the exact approval,
146
179
  held queue or review row, or prints applicable guidance; it never answers a
@@ -175,10 +208,10 @@ the stream; only the header is tab-flattened before tokenizing. Body lines retai
175
208
  the previous rendering and stay plain, as does command output; colour never changes
176
209
  what the panel's text says.
177
210
 
178
- Peers, Approvals, Tasks, Queue and Settings are tables with a header row. A column is as
211
+ Peers, Approvals, Tasks, Queue, Settings and Usage are tables with a header row. A column is as
179
212
  wide as its widest cell, up to a third of the terminal; the last column takes
180
213
  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.
214
+ meter keeps a fixed column at the right edge. In Usage, LAST RESET is the column that gives way on a narrow terminal.
182
215
 
183
216
  | Panel | Columns |
184
217
  |---|---|
@@ -188,6 +221,7 @@ meter keeps a fixed column at the right edge.
188
221
  | Tasks, backlog view (`b`) | ID PRI RESERVED DEPS CLASS AGE TITLE |
189
222
  | Queue | ID PEER STATE REV AGE |
190
223
  | Settings | GROUP KEY VALUE SOURCE APPLIES |
224
+ | Usage | PEER TODAY 7D LIFETIME PAUSE QUOTA LAST RESET |
191
225
 
192
226
  In Tasks, `b` toggles open work and backlog. The backlog heading counts its rows;
193
227
  rows sort by rank, then id. PRI shows priority/rank, RESERVED the reserved peer,
@@ -320,7 +354,21 @@ owner and to stop working.
320
354
  `ahub task assign <id> <peer> [--role owner|reviewer] [--revision N]` uses a
321
355
  checked console operation when a revision is present. Without it, existing CLI
322
356
  behavior is unchanged. `ahub queue resolve ... [--revision N]` uses the supplied
323
- revision instead of a fresh show. Revision mismatches and superseded post-await
357
+ 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
358
+ the time); `dispatching` and `accepted` deliveries stay refused, and `retry` keeps its rules. A refused resolution names its cause (`active delivery`, `peer busy`,
359
+ `stale revision`, `unknown delivery`, `already resolved`, `queued delivery is already scheduled`, `recipient queue is full`, `delivery journal unavailable`), never one
360
+ generic sentence (#713).
361
+
362
+ `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
363
+ 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
364
+ 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
365
+ 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
366
+ 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.
367
+
368
+ 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
369
+ 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
370
+ 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
371
+ a task that is sent back and done again three times leaves one queued notice, the newest. Revision mismatches and superseded post-await
324
372
  publications fail without a board or delivery side effect; a reviewer-only change
325
373
  keeps a valid owner assignment but refreshes its reviewer content.
326
374
 
@@ -922,6 +970,17 @@ ahub board
922
970
  ahub task show <task-id>
923
971
  ```
924
972
 
973
+ At a terminal width `ahub board` prints one line per task (#724): TITLE is cut
974
+ with `...`, dependencies are an AFTER column (`#113,+2`), and STAGE is the
975
+ progress meter with the stage and a short next-step code (`intent: issue?`,
976
+ `review: CI?`, `ready`), explained by a legend line. A column that is `-` on
977
+ every row is left out. When TITLE would get fewer than 30 columns (or fewer
978
+ than its longest title), REVIEWER, CLASS and AGE give way in that order, then
979
+ STAGE drops the stage name; at a width still too narrow AFTER goes and OWNER is
980
+ cut.
981
+ `ahub board --full`, `ahub task show <id>` and piped output without `COLUMNS`
982
+ keep the whole title and the full SDLC advice.
983
+
925
984
  The owner accepts with `hub_task_accept`, makes the change, and reports with
926
985
  `hub_task_done` including a summary and refs. The reviewer receives a review
927
986
  request and responds with `hub_review`. A console review uses:
@@ -967,6 +1026,59 @@ conductor sees it, and only a stub for a PII task. Other peers are refused. Each
967
1026
  request also carries the hub's review brief for the task's commit: reviewable and
968
1027
  excluded files with reasons, the `REVIEW.md` and `ahub-review-rules` rules read at
969
1028
  the merge base, and a cross-check list; `ahub review brief <id>` prints it.
1029
+ Optional code facts (#682) add per-file observed symbol names, kinds and spans,
1030
+ plus changed/planned, changed/not-planned and planned/not-seen lists. Install
1031
+ ripwire 0.6.7 on the daemon's host PATH to enable them; missing, unsupported,
1032
+ failed, busy or timed-out reads show `unknown` and never stop the review request.
1033
+ Each native call is capped at 5 s, output is capped, and successful values are
1034
+ cached by repo, commit and query. PII tasks never query it; displayed names use
1035
+ the hub's public metadata screen. It always analyzes a disposable raw-blob
1036
+ snapshot, never main or a dirty/untracked owner tree, and removes that worktree
1037
+ after the owned process stops. No heavy slot is acquired. Trees beyond the
1038
+ regular-file/4096-file/32-MiB bounds remain unknown.
1039
+
1040
+ Ripwire 0.6.7 omits deleted definitions and does not classify change kinds, so
1041
+ all symbol rows say `change: unknown`; planned/not-seen is not proof of no
1042
+ change. Graph name-only matches are hidden as ambiguous, and graph count floors
1043
+ remain explicit. The file-based coverage gate is unchanged. A future both-tree
1044
+ inventory or bounded base/head probes can improve this advice after dogfooding.
1045
+
1046
+ At completion, optional symbol advice also becomes a head-bound task observation
1047
+ (`#684` phase 2a). Console task detail and the UI task snapshot expose the
1048
+ screened IDs, paths, names and kinds, without source/spans or internal repository
1049
+ keys. Public reads re-screen current policy; PII stores nothing. Unknown or
1050
+ stale evidence remains unavailable. Completion prepares this optional data even
1051
+ without a reviewer, without adding a review gate. Foreign-repository tasks stay
1052
+ unknown until the brief can independently bind its analysis repository.
1053
+
1054
+ 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
1055
+ `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
1056
+ 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)`;
1057
+ 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
1058
+ 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.
1059
+
1060
+ The dashboard's task sheet has the same block as a "Code" group (#684 phase 2c): the changed symbols as buttons marked `matching` or `unplanned` against the plan, the plan names nothing changed under as `untouched`, and the same advisory line (deleted symbols are not reported, an incomplete observation may hide a changed one). The hub classifies them (`codeGroup` on each task of the UI snapshot, from the same public observation the console reads), so the page never matches names itself. A symbol opens the Memory tab with its canonical node (`symbol:<name>@<path>`) as the graph start; the list is capped at 40 with the count of what is left out, and a summary line counts matching, unplanned and untouched before the cap. A plan name the hub withholds (screened out or malformed) is counted, and the line then says a symbol marked unplanned may be in the plan. Without code facts the group reads `unknown (no code facts)`, and a private task has no group at all, so the sheet shows nothing for it.
1061
+
1062
+ Task detail also shows an `overlaps` block (#379), using the hub's read-only
1063
+ `symbolOverlaps` projection of its existing open-task/other-owner overlap selection.
1064
+ Each row names the other task ID and screened shared paths. A row says "same file,
1065
+ different symbols" only when the same current screened predicate used by completion
1066
+ notices establishes observed disjointness; otherwise it remains an ordinary overlap.
1067
+ The block states "observed names only, deleted definitions may be missing". PII tasks
1068
+ are excluded on both sides by their markers, even if the routing locality limit is
1069
+ disabled. No pair result is stored, and the console never infers
1070
+ one from its own plan or receipt. Missing fields on older hubs show no block; an
1071
+ empty list says `none`. At most 40 pairs and 40 paths per pair are listed, with
1072
+ omitted counts. Related-note counts remain a later slice.
1073
+
1074
+ When both tasks have complete public observations and non-empty disjoint sets
1075
+ for every exact shared file, a completed-change notice says "same file, different
1076
+ symbols" with the observed-name/deleted-definition ceiling. It still tells the
1077
+ recipient to check the completed work. Missing or partial evidence keeps the
1078
+ ordinary notice; plan-time overlap and coverage gates are unchanged. The receipt
1079
+ keeps one current head, with at most 500 entries and 64 KiB total. This is the
1080
+ data slice; the formatted Code panel and memory links follow separately.
1081
+
970
1082
  `hub_review` takes `coverage {reviewed, skipped}`: in enforce an approval that leaves
971
1083
  a reviewable path unaccounted is refused, and advise notes the gap in the verdict
972
1084
  (#520). The hub
@@ -1100,8 +1212,14 @@ ahub report --since 7d # turns, busy time and tokens per peer, messages
1100
1212
  ahub report --since 7d --json # the same numbers as JSON
1101
1213
  ahub report --by task # tokens, turns and wall time per task and class, with the unattributed share
1102
1214
  ahub export --since 24h # the raw events as JSON lines, for your own analysis
1215
+ ahub report --dogfood [--since 7d|<iso>] [--json] # read-only scorecard: flow, quality, cost, delivery, performance, adoption
1103
1216
  ```
1104
1217
 
1218
+ `ahub report --dogfood` (#687) reads the event log, `hub.log`, the board and the delivery journal in `hub.db`, up to 16 recently merged
1219
+ 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
1220
+ document as `--json` with `unknown` in place of null; missing or truncated evidence is unknown, never 0. It exports ids, timestamps and counts
1221
+ only and runs no model. Its bounds and the weekly routine are in [dogfood](dogfood.md#weekly-scorecard).
1222
+
1105
1223
  Per peer and model route, `ahub report` also counts model changes between consecutive
1106
1224
  decisions, how many of them landed inside tool loops and, with `stay_switch` on, sessions
1107
1225
  and the planner's switches (#197). With `stay_switch = "off"` session boundaries are not
@@ -1109,6 +1227,41 @@ recorded; `shadow` routes exactly as `off` does, so it is the baseline to compar
1109
1227
  A route event records the decision, so a load-moved request that its MLX fallback served counts
1110
1228
  as a change to `dgx/fast`; the relay's request journal records which backend served it.
1111
1229
 
1230
+ The event log also keeps the history of limits (#683). Each Codex update that adds tokens writes a
1231
+ `usage` event with source `codex_appserver` next to the existing `tokens` event, split into input, cached
1232
+ input, output and reasoning, so re-sent cached context is visible. A budget pause writes `budget_pause`
1233
+ and its end `budget_resume`, with the window, the reset time and who ended it (`hub`, or `person` for an
1234
+ `ahub budget resume`). A window's reset writes `quota_reset` when its `resetsAt` moves forward or its `used`
1235
+ falls by 0.5 or more inside one window id, once per peer, window and `resetsAt`. These events hold ids and
1236
+ numbers only, add no protocol version and are ignored by a reader that does not know them. `ahub report`
1237
+ 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 shows the same roll-up in the Agents view (#683 U4, see [Operator console and panels](#operator-console-and-panels)).
1238
+
1239
+ `ahub usage` reads the same events from an incremental roll-up in `hub.db` instead of rescanning the log (#683):
1240
+
1241
+ ```bash
1242
+ ahub usage [--since 7d|<iso>] [--by peer|task|issue|pr|release|day|model] [--json]
1243
+ ahub usage limits [--peer <id>] [--limit <n>] [--json] # current quota windows, plus the newest 200 (up to 5000) pauses, resumes and resets
1244
+ ahub usage total [--json] # lifetime development tokens since the first event, per peer and overall
1245
+ ```
1246
+
1247
+ 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
1248
+ 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
1249
+ 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,
1250
+ 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
1251
+ rescans the whole file on each call.
1252
+ `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
1253
+ for the history it already spent. A task with no link, and a record with no task, are `unattributed`.
1254
+
1255
+ Every number names its source: `native` (the provider's own counter), `hub count` (the hub's own `tokens` increments, which cover peers that
1256
+ report no usage record, such as Jcode and Kimi), or `unknown`. Unknown is never shown as 0. `TOTAL` includes cache reads and `WITHOUT CACHE`
1257
+ 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
1258
+ 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
1259
+ `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
1260
+ as 0 (it is `unknown` when no peer is known). The overall `WITHOUT CACHE` is unknown unless every peer's cache split is known.
1261
+ `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
1262
+ 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
1263
+ 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.
1264
+
1112
1265
  `ahub report` counts the same overlap warnings as `scripts/overlaps.ts`, from the
1113
1266
  structured events instead of log lines. `--by task` uses the task each usage and
1114
1267
  token record was attributed to when it was written: the delivery that started the
@@ -1451,6 +1604,14 @@ stays the default until an evaluation says otherwise (`docs/cooperbench.md`).
1451
1604
  turns the stale-notice drop off, for a controlled comparison only (the #106
1452
1605
  ablation in `docs/cooperbench.md`); the hub logs it at start.
1453
1606
 
1607
+ ## Always-ask action policy foundation (#636)
1608
+
1609
+ 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.
1610
+
1611
+ 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).
1612
+
1613
+ 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.
1614
+
1454
1615
  ## Permission modes for running peers
1455
1616
 
1456
1617
  Use a plain terminal to inspect or change a running peer:
@@ -1794,6 +1955,8 @@ with the headless command rather than guessed. The launcher uses the running
1794
1955
  hub's `kimi_cmd`; when that value has no TUI form, the refusal says so, since an
1795
1956
  edited file value waits for `ahub settings reload`.
1796
1957
 
1958
+ 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.
1959
+
1797
1960
  The TUI is pull-only. Its model reads `hub_inbox` at the start of work, between
1798
1961
  steps of longer work and before reporting, and shares results, and mid-work
1799
1962
  blockers or changed contracts, explicitly with `hub_send` addressed with `to`
@@ -1803,7 +1966,9 @@ and important markers do not push or interrupt it. There is no hub busy state,
1803
1966
  watchdog, console approval, hub permission mode, context reading, automatic quota
1804
1967
  pause/handoff, shared final answer or session resume. When important messages are
1805
1968
  waiting, every other hub tool result ends with a line saying how many, so the model
1806
- reads `hub_inbox` before starting long local work (#398). Read quota and approve tools
1969
+ reads `hub_inbox` before starting long local work (#398). One `hub_inbox` read drains the queue oldest first up to
1970
+ 64 KiB of rendered text and, when more waits, ends with `N more waiting: call hub_inbox again before you start work` (#713).
1971
+ Read quota and approve tools
1807
1972
  in Kimi's own TUI; a person can pause the hub queue by hand. End the native terminal
1808
1973
  session there, not with `ahub stop kimi`.
1809
1974
 
@@ -1973,8 +2138,9 @@ but cannot show pushes, so it attaches tools-only (protocol 16, issue #205):
1973
2138
  `messages wait for hub_inbox; for pushes restart Claude with ahub claude`; the
1974
2139
  console footer shows `tools-only: ahub claude` and the dashboard shows the
1975
2140
  status line.
1976
- - The session reads them with `hub_inbox`, at most ten messages at a time plus
1977
- the hub's recall note when one waits (it says how many still wait). Each read is one `completed` delivery (`read through
2141
+ - 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
2142
+ 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
2143
+ `N more waiting: call hub_inbox again before you start work`. Each read is one `completed` delivery (`read through
1978
2144
  hub_inbox` in `ahub queue list`); nothing is ever `accepted`, and the session
1979
2145
  has no `hub_delivery_done`. A read the model never saw (the tool call was
1980
2146
  cancelled, the plugin died) cannot be retried: `ahub queue show <id>` of the
@@ -2184,6 +2350,22 @@ killed.
2184
2350
  Rows without a live process are stale registrations; forget them with
2185
2351
  `ahub projects remove <id>`.
2186
2352
 
2353
+ ## Agent CLI versions
2354
+
2355
+ 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).
2356
+
2357
+ - `ahub agents [--json]` lists each gated CLI with its version, verdict and verified versions.
2358
+ - **verified:** a receipt exists.
2359
+ - **in-line:** untested, inside the compatible line that a contract gate (Jcode, agy) declares; it starts with a note.
2360
+ - **trusted:** you accepted this exact version on this machine.
2361
+ - **refused:** the reason names the installed and verified versions and the command that admits it.
2362
+ - **unknown:** the CLI is not on PATH, or its version could not be read.
2363
+ - `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.
2364
+ - 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.
2365
+ - What trust cannot prove is printed when you trust.
2366
+ - `ahub doctor` shows one line for each installed CLI that is not on a verified version.
2367
+ - 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.
2368
+
2187
2369
  ## Upgrade and crash recovery
2188
2370
 
2189
2371
  On a terminal, one command reviews, applies and follows an upgrade:
@@ -3349,7 +3531,7 @@ that the file is untracked; a dropped value is reported by the settings result.
3349
3531
 
3350
3532
  ## SDLC artifact receipts and gates
3351
3533
 
3352
- 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.
3534
+ 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). `ahub sdlc metrics [--since <YYYY-MM-DD|ISO time>]` (the planner tool `hub_sdlc_metrics`, planner or person, read-only) reports lead times, first-pass approval and check rates, rework rounds, stale-head re-reviews and open stalls from board history, with `unknown` counted and never 0; optional `[bands.<metric>]` in `sdlc.toml` add control bands that the daemon checks every five minutes in advise or enforce, writing a hub notice (`log`), a read-only `triage` backlog task (`diagnose`) or a `plan` backlog card (`propose`), one per breach episode and never a GitHub issue (S7, see [metrics](sdlc.md#sdlc-metrics-and-control-bands-481-s7)). With the lifecycle on, a task's issue identity is linked when it is proposed or accepted, from the one issue it names (an imported GitHub issue, or a `#N`, `owner/repo#N` or issue URL in its title); a title that names a sibling repository (`agent-mem#13`) links only when `refs.repo` names it, and without `refs.repo` the propose result says to set it. `ahub backlog groom` also backfills up to 32 links per call, within 1 s (#723). 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 [test integrity](sdlc.md#test-integrity-481-s4) and S6 [release authorization](#release-approval-and-optional-tag-guard) are in source too. S5 continuous evals, release metrics and bands, 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.
3353
3535
  ## Optional Qwen Code peer
3354
3536
 
3355
3537
  Select Qwen per project with `ahub roster add qwen`; it defaults off and is absent
@@ -3358,11 +3540,12 @@ starts its ACP peer. `--print-command` previews; TUI is refused until #286's
3358
3540
  verification. `ahub stop qwen` stops the owned process group. A conductor may start
3359
3541
  it only through the selected roster and existing capabilities.
3360
3542
 
3361
- The supported native ACP identity is qwen-code 0.25.0. A CLI version label alone
3543
+ 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
3362
3544
  is insufficient: startup verifies its native identity/version and advertised mode
3363
- ids before any prompt. The observed local installation reports CLI 0.25.0 while
3364
- its package/ACP source says 0.24.1; do not call that installation ready. Configure
3365
- `qwen_cmd` only in untracked config.local.json with a verified 0.25.0 executable.
3545
+ ids before any prompt. The earlier local installation reported CLI 0.25.0 while
3546
+ its package/ACP source said 0.24.1; that was not a ready identity. The 0.26.0 receipt
3547
+ used a private prefix and did not upgrade PATH. Configure `qwen_cmd` only in
3548
+ untracked config.local.json with a receipt-verified executable.
3366
3549
  The provider's tunnel/relay, if needed, must already be available. Startup cannot
3367
3550
  prove network readiness from a version command.
3368
3551
 
@@ -3394,11 +3577,11 @@ TUI input may interleave with an ACP turn; there is no single-writer guard. Clos
3394
3577
  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.
3395
3578
 
3396
3579
 
3397
- 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.
3580
+ 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.
3398
3581
 
3399
3582
  ## Optional Antigravity (agy) peer
3400
3583
 
3401
- 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.
3584
+ 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.
3402
3585
 
3403
3586
  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`.
3404
3587
 
@@ -3452,7 +3635,7 @@ A bundled adapter module is authorized by the person with `adapter_modules` (at
3452
3635
 
3453
3636
  - 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`.
3454
3637
  - 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.
3455
- - 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.
3638
+ - 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.
3456
3639
  - 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).
3457
3640
  - Native T0 limits: [ACP watch verification](verification/2026-10-11-acp-watch-t0.md).
3458
3641
 
@@ -3623,6 +3806,14 @@ unapproved dependencies, frozen scope and overriding person P0 order/reservation
3623
3806
  The person uses `ahub backlog pull-ready <id> <revision> <reason>` or Pull within
3624
3807
  WIP on a card. Explicit manual pull/assignment retain their existing authority.
3625
3808
 
3809
+ A pull of a reserved backlog task (#718) assigns it to exactly that reserved peer
3810
+ in the same write and sends it a fresh owner offer, so it can accept at once. A
3811
+ reserved peer that cannot take it (offline, paused, excluded, at its WIP limit)
3812
+ refuses the pull before any change, naming the peer and its reason; it is not
3813
+ passed over to another peer. `ahub route explain` previews the same reserved-only
3814
+ choice. An unreserved pull keeps reserving a slot as above. A reserved offer that
3815
+ was never started can return untouched and keeps its reservation.
3816
+
3626
3817
  Capacity defaults to one per peer. Each declared instance has its own `tasks.wip_limits.<instance>` key. `ahub settings set tasks.wip_limits.codex 2`
3627
3818
  uses the existing machine-local registry path; integers 0..32 apply live (the next
3628
3819
  admission reads the current value), with 0 disabling planner admission for that
@@ -3883,12 +4074,68 @@ restart the hub. For example, a reviewer can use:
3883
4074
 
3884
4075
  Start them with `ahub codex --as codex-review` and
3885
4076
  `ahub kimi --as kimi-review`. Codex keeps its native TUI and its own hub-owned
3886
- app-server proxy; extra Kimi, Qwen, Pi and local instances use headless starts.
3887
- Extra Jcode instances are unavailable. An extra Claude instance is a reviewer or
3888
- planner you start yourself: declare it with `roles` only (for example
3889
- `"claude-review": { "kind": "claude", "roles": ["reviewer"] }`, no `start_mode` or
3890
- `worktree`) and run `ahub claude --as claude-review` in a terminal. It keeps its own
3891
- `claude-*.claude-review.json` state files.
4077
+ app-server proxy; extra Kimi, Qwen, Jcode, Pi and local instances use headless starts.
4078
+ Extra Jcode instances are supported (#685), in `.agenthub/config.local.json` only:
4079
+
4080
+ ```json
4081
+ {
4082
+ "instances": {
4083
+ "jcode-claude": { "kind": "jcode", "roles": ["verifier"], "jcode_home": "/Users/you/.agenthub/jcode/claude",
4084
+ "provider": "claude", "model": "claude-sonnet-5-5" },
4085
+ "jcode-local": { "kind": "jcode", "roles": ["verifier"], "jcode_home": "/Users/you/.agenthub/jcode/local",
4086
+ "provider_profile": "onprem" }
4087
+ }
4088
+ }
4089
+ ```
4090
+
4091
+ `jcode_home` is required, absolute, and must differ from the base `jcode_home`, from
4092
+ every other instance's and from any directory inside or around them, however it is
4093
+ spelled: a symlink, a system alias such as `/tmp` and `/private/tmp`, or a case-only
4094
+ alias is the same home (the hub refuses the configuration at startup otherwise). At most 8 Jcode instances are accepted per
4095
+ project. `provider`, `model` and `provider_profile` are optional and passed to Jcode
4096
+ as `-p`, `--model` and `--provider-profile` exactly as written; any other field is
4097
+ refused, and these fields are never editable from settings.
4098
+
4099
+ You authenticate inside Jcode, never in the hub. Run Jcode's own setup against each
4100
+ home (provider login, API key, an OpenAI-compatible base URL such as the on-prem
4101
+ gateway, a provider profile), and initialize that home's MCP store with Jcode's own
4102
+ client, as for the base peer. The hub never reads, writes, copies or injects a model
4103
+ credential, a key, a token or a provider URL, and never creates the home. Its only
4104
+ start preflight is read-only: the home must exist as a directory you own, otherwise
4105
+ the start is refused and nothing runs. A Jcode sign-in failure appears as the start
4106
+ or turn error, with bounded text. An instance reports tokens only if Jcode itself
4107
+ does; until then they show `unknown`, never 0.
4108
+
4109
+ Each instance owns its private `jcode serve` and `jcode acp` pair, socket, roster row
4110
+ and launch record, and acts as its own peer id (for example `jcode-local`), never as
4111
+ `jcode`. Start it with `ahub jcode --as jcode-local` (headless; an instance has no
4112
+ TUI) and stop it with `ahub stop jcode-local`: the base Jcode and every other instance
4113
+ keep running. An extra implementer needs the same managed worktree as every extra
4114
+ implementer (`worktree: ".worktrees/jcode-local"`). Every instance is a cloud peer
4115
+ (`trust.onPrem` is false), whatever provider you configured: it never receives PII
4116
+ work. `ahub status` and the dashboard show the provider, model and profile as
4117
+ declared, and `ahub doctor` lists the MCP servers each rostered instance's home would
4118
+ load.
4119
+
4120
+ Upgrade note (#368): the project's `.jcode/mcp.json` entry for Jcode no longer names
4121
+ `AGENTHUB_PEER_ID`, because the file is shared by every Jcode in the project and Jcode
4122
+ gives an entry's own env precedence over the process env. Each hub-started Jcode now
4123
+ supplies its own peer id and a launch id, and the hub admits a hub-tools connection
4124
+ for Jcode only with the launch id of that peer's current owned launch. After
4125
+ upgrading, `ahub status` and `ahub doctor` report the Jcode MCP entry as not ready
4126
+ once (`jcodeMcpReady` is false for the old entry): run `ahub jcode` once and it
4127
+ rewrites its own entry, or follow the remedy text the start refusal shows. A Jcode you
4128
+ run yourself in this project outside the hub has no peer id: its hub tools refuse
4129
+ every call with that reason and never default to `jcode`, and a process that only
4130
+ sets `AGENTHUB_PEER_ID` without an owned launch is refused by the hub.
4131
+
4132
+ An extra Claude instance is one you start yourself: declare it with `roles` (for example
4133
+ `"claude-review": { "kind": "claude", "roles": ["reviewer"] }`) and run `ahub claude --as claude-review` in a
4134
+ terminal. It keeps its own `claude-*.claude-review.json` state files. A planner or reviewer instance reads the
4135
+ project checkout and refuses `worktree`. From #525-1 an `implementer` or `verifier` instance is accepted and
4136
+ requires its own `worktree` (for example `"claude-impl": { "kind": "claude", "roles": ["implementer"],
4137
+ "worktree": ".worktrees/claude-impl" }`). The hub does not route code tasks to it until #525-5 proves its execution root (`ahub route explain` shows `owner admission: declared write owner requires a verified isolated execution root` for implement, test and bulk_edit tasks). A headless Claude instance stays a reviewer (`roles`
4138
+ exactly `["reviewer"]`); a headless implementer is refused until #525-4.
3892
4139
 
3893
4140
  A headless Claude reviewer is run by the hub instead (#223): declare
3894
4141
  `"claude-review": { "kind": "claude", "roles": ["reviewer"], "start_mode": "headless" }`
@@ -3920,17 +4167,17 @@ From 0.12.37 reviews can rotate across fresh headless Claude and Codex reviewers
3920
4167
  }
3921
4168
  ```
3922
4169
 
3923
- 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.
4170
+ Start them with `ahub claude --as claude-review`, `ahub codex --as codex-review` and `ahub kimi --as kimi-review`; stop with `ahub stop <id>`. For the Claude and Codex reviewers the command asks the running hub to start the reviewer and returns: nothing runs in that terminal, it takes no launch arguments, and `--print-command` or `--dry-run` prints a hub-start note instead of a launch (#720). 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.
3924
4171
 
3925
- 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.
4172
+ 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.
3926
4173
 
3927
- 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.
4174
+ 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.
3928
4175
 
3929
4176
  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.
3930
4177
 
3931
4178
  agy is not a reviewer (person decision, 2026-10-19): it is an optional implementer peer (see [Optional Antigravity peer](#optional-antigravity-agy-peer)), and its retained reviewer slot is outside the default pool and always unavailable with `agy is an implementer peer (#485)`. A manual pool accepts only registered reviewer kinds (Claude, Codex, Kimi, agy), so local/Pi are refused. An explicit pool without declared candidates blocks for person action; an omitted pool retains legacy routing without declarations and falls back to an eligible planner when fresh candidates are exhausted. The exhausted pool walk remains recorded; without an eligible planner, the person reviews. A manual pool may name agy, or a Kimi reviewer whose preparation fails; the reason appears as an unavailable skip, and they never run or count as approval. Do not substitute plan mode for isolation.
3932
4179
 
3933
- A failed start or any child ending without a saved verdict advances to the remaining eligible candidates; no quota meaning is guessed from text. A same-family fallback is a distinct fresh reviewer session. Exhaustion records `blocked: no reviewer can run`; with no explicit pool the eligible planner fallback is retained, while an explicit pool or absent eligible planner leaves person action. It never auto-approves. A per-review isolation preparation failure excludes that peer from later tasks until a verified restart. Recovery targets must advertise `reviewer_pool` when a pool is explicitly configured or fresh reviewer instances are declared. Verdict history retains `reviewContext` and adds `reviewerFamily` and `reviewerReason` from the host.
4180
+ A failed start or any child ending without a saved verdict advances to the remaining eligible candidates; no quota meaning is guessed from text. A same-family fallback is a distinct fresh reviewer session. Exhaustion records `blocked: no reviewer can run`; with no explicit pool the eligible planner fallback is retained, while an explicit pool or absent eligible planner leaves person action. It never auto-approves. A per-review isolation preparation failure excludes that peer from later tasks until a verified restart. Its reason names one cause from a closed list (`version`, `binary`, `mcp_inventory`, `plugins_hooks_apps`, `sandbox_proof`, `credential_root`, `timeout`, `unknown`) with a fixed hint, on the peer's `reviewer` line in `ahub status` and in a `hub.log` line, and never a path, native CLI text or token (#722). Only the Codex preparation classifies its failures; other reviewers report `unknown`. Recovery targets must advertise `reviewer_pool` when a pool is explicitly configured or fresh reviewer instances are declared. Verdict history retains `reviewContext` and adds `reviewerFamily` and `reviewerReason` from the host.
3934
4181
 
3935
4182
  Fake-CLI tests and CI cover these host contracts. [Offline isolation evidence](verification/2026-10-19-reviewer-isolation.md) and fake-CLI tests do not prove an account/model review or observed network denial. Native acceptance remains a separate [manual smoke leg](smoke.md#reviewer-pool-native-legs-519-2026-10-19); existing #223 Claude behavior has no new live proof here. Kimi's authenticated fresh-verdict receipts, and its still-open interactive-TUI coexistence and usage checks, are tracked in [Kimi smoke legs](smoke.md#kimi-reviewer-and-task-bound-mcp-533).
3936
4183
 
@@ -3965,11 +4212,19 @@ CLI checks the saved workspace against the current declaration again before any
3965
4212
  context change or file restoration. Removing the declaration intentionally
3966
4213
  blocks that undo until the identity and workspace can be reviewed.
3967
4214
 
4215
+ ## Release approval and optional tag guard
4216
+
4217
+ 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.
4218
+
4219
+ `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.
4220
+
4221
+ 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).
4222
+
3968
4223
  ## Planner release scheduling
3969
4224
 
3970
4225
  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.
3971
4226
 
3972
- The per-release "gates run" record is deferred to S6/S7. Person ruling 2026-10-19 supersedes the earlier S11 upgrade-admission ruling: upgrades use pre-S11 recovery readiness (no busy peers, pending approvals resolved, completion checks settled) and retain the existing operation checks. S11 adds no host-admission, quota or all-idle requirement to upgrade. An active/frozen configured train still requires recovery-target `release_trains` compatibility. Release-cut admission remains unchanged: follow its refusal's applicable exit by stopping or resuming the paused peer, adjusting `ahub budget set`, or using `ahub release drop`. Missing/shadowed `dot` requires the resource controller or a PATH correction for the cut; nested admission requires retrying the cut outside the outer lease.
4227
+ The per-release "gates run" record is not built: S7 shipped task metrics and bands without it, and release metrics with the gates-run record are the next slice. Person ruling 2026-10-19 supersedes the earlier S11 upgrade-admission ruling: upgrades use pre-S11 recovery readiness (no busy peers, pending approvals resolved, completion checks settled) and retain the existing operation checks. S11 adds no host-admission, quota or all-idle requirement to upgrade. An active/frozen configured train still requires recovery-target `release_trains` compatibility. Release-cut admission remains unchanged: follow its refusal's applicable exit by stopping or resuming the paused peer, adjusting `ahub budget set`, or using `ahub release drop`. Missing/shadowed `dot` requires the resource controller or a PATH correction for the cut; nested admission requires retrying the cut outside the outer lease.
3973
4228
 
3974
4229
 
3975
4230
  ## Codex cancellation identity notice
@@ -3978,3 +4233,11 @@ If Codex is offline with `statusNotice.code = unidentified-cancellation`, the hu
3978
4233
  could not identify a cancelled native turn. Reattach the native session before
3979
4234
  further hub delivery. A late completion keeps this hold; verified native
3980
4235
  reattachment or TUI detachment clears it. The notice contains fixed guidance, without native ids.
4236
+
4237
+ ## Roles per SDLC stage (#525)
4238
+
4239
+ The design for planner, implementer and reviewer instances per stage is in the spec section "Amendment: roles per SDLC stage (#525)". Slice 525-1 is available: role capability per kind and form, Claude channel instances as implementer or verifier in their own worktree, and `planner` refused on kinds that cannot plan (pi, local, agy and configured ACP agents): an instance declaring it is refused with `this kind cannot plan`, and a `roles` entry keeps its other roles with the notice `roles.<peer>: ignored roles outside the adapter ceiling`. The operator steps for 525-1 are in "Additional headless peer instances" above; each later slice (525-2 to 525-6) adds its own here when it ships, and until then those instance rules are current. The planned changes an operator will see:
4240
+
4241
+ - Code tasks routed to a terminal Claude implementer instance once its execution root is proven (525-5), and a headless Claude implementer with `start_mode: "headless"` (`permission_mode` `ask` or `ask-when-needed`; `never-ask` refused) (525-4).
4242
+ - 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.
4243
+ - 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.
package/docs/profiles.md CHANGED
@@ -49,9 +49,9 @@
49
49
 
50
50
  # Project profile overrides
51
51
 
52
- - `profile_overrides` in shared/local config selects project data over the chosen shipped profile. `classes`, `role_text` and `catalog_roles` maps merge by key across layers; `plan_keys`, `agents` and `example_checks` replace as units. A local `profile` selects the active base for both init and native contracts.
52
+ - `profile_overrides` in shared/local config selects project data over the chosen shipped profile. `classes`, `role_text` and `catalog_roles` maps merge by key across layers; `plan_keys`, `agents`, `example_checks` and `review_checklist` replace as units. A local `profile` selects the active base for both init and native contracts.
53
53
  - Classes may add or remap profile aliases to existing base classes. Existing base names keep their meanings; class maps are bounded to 32 override entries. Task storage and route explanations preserve the alias, including a dev custom alias, while routing/budgets/checks use its base. Other tasks under custom dev data retain the selected profile semantics too, so document sections do not become whole-path overlaps. Unmodified dev rows retain their old shape.
54
54
  - `plan_keys` selects distinct existing plan fields, not new board fields. Task proposals and their schemas use the selection. Accept schemas retain legacy fields for already existing tasks/profile changes.
55
55
  - `role_text` is bounded guidance for existing flow roles (2000 characters each); it grants no authority. `catalog_roles` supplies bounded compatible guidance ids; the existing explicit catalog selections take precedence. Actual flow maps remain in `roles` and retain their existing validation.
56
- - `agents` replaces only the managed AGENTS block (16000 characters maximum); surrounding instructions remain. Init previews contain paths/actions only. `example_checks` is at most 16 display strings of 2000 characters, shown as advisory examples by init and never selected as executable checks. Existing `checks` trust remains authoritative.
56
+ - `agents` replaces only the managed AGENTS block (16000 characters maximum); surrounding instructions remain. Init previews contain paths/actions only. `example_checks` is at most 16 display strings of 2000 characters, shown as advisory examples by init and never selected as executable checks. `review_checklist` is at most 16 non-empty strings of 500 characters, customizing the checklist items in review requests with `{target}` and `{check}` interpolations. Existing `checks` trust remains authoritative.
57
57
  - No override mutates shipped profile data or another project. Unknown fields, base remapping and invalid/oversized data are refused. Headers are quoted. Daemon/MCP restart is required for config changes; no native model consumption or research measurement is claimed.