@staix/agent-hub 0.27.0 → 0.29.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 (106) hide show
  1. package/CHANGELOG.md +33 -0
  2. package/README.md +11 -3
  3. package/docs/agent-notes/adapters.md +4 -2
  4. package/docs/agent-notes/bus.md +4 -4
  5. package/docs/agent-notes/daemon.md +9 -3
  6. package/docs/agent-notes/local-worker.md +3 -1
  7. package/docs/agent-notes/tasks.md +7 -0
  8. package/docs/agent-notes/tests.md +7 -5
  9. package/docs/design/adapters.md +76 -0
  10. package/docs/design/agent-hub-architecture.md +67 -0
  11. package/docs/design/agent-hub.md +52 -0
  12. package/docs/design/bus.md +48 -0
  13. package/docs/design/task-board.md +86 -0
  14. package/docs/dogfood.md +21 -3
  15. package/docs/operations.md +370 -12
  16. package/docs/profiles.md +1 -0
  17. package/docs/quickstart.md +19 -3
  18. package/docs/sdlc.md +2 -2
  19. package/docs/security.md +24 -4
  20. package/docs/smoke.md +2 -0
  21. package/docs/specs/2026-09-19-agent-hub-design.md +2 -0
  22. package/docs/verification/2026-10-31-codex-0166-reviewer-isolation.md +2 -0
  23. package/docs/verification/2026-11-02-0.28.0.md +54 -0
  24. package/docs/verification/2026-11-02-autonomy-t4.md +39 -0
  25. package/docs/verification/2026-11-02-codex-0166-reviewer-tool-call.md +53 -0
  26. package/docs/verification/2026-11-02-control-write-after.json +139 -0
  27. package/docs/verification/2026-11-02-control-write-baseline.json +127 -0
  28. package/docs/verification/2026-11-02-control-write-guarded.json +127 -0
  29. package/docs/verification/2026-11-02-control-write-outcomes.md +65 -0
  30. package/docs/verification/2026-11-02-orca-1.4.245-cli-contract.md +51 -0
  31. package/docs/verification/2026-11-03-0.29.0.md +59 -0
  32. package/docs/verification/2026-11-03-control-write-stall-windows.json +241 -0
  33. package/docs/verification/2026-11-03-control-write-stall-windows.md +20 -0
  34. package/docs/verification/2026-11-03-live-board-latency.json +556 -0
  35. package/docs/verification/2026-11-03-live-board-latency.md +64 -0
  36. package/docs/verified.json +14 -14
  37. package/package.json +2 -2
  38. package/plugins/agent-hub/.claude-plugin/plugin.json +1 -1
  39. package/plugins/agent-hub/server.js +857 -125
  40. package/src/adapters/claude-channel.ts +12 -2
  41. package/src/adapters/compat.ts +1 -1
  42. package/src/adapters/definitions/native-cli-claude-setup.ts +31 -9
  43. package/src/adapters/definitions/native-cli-entry-jcode.ts +33 -0
  44. package/src/adapters/definitions/native-cli-entry-qwen.ts +2 -2
  45. package/src/adapters/jcode.ts +37 -4
  46. package/src/adapters/qwen-dual.ts +3 -2
  47. package/src/cli/agents.ts +4 -2
  48. package/src/cli/bench.ts +4 -2
  49. package/src/cli/board-read.ts +25 -0
  50. package/src/cli/claude-homes.ts +2 -2
  51. package/src/cli/console-state.ts +8 -3
  52. package/src/cli/help.ts +6 -5
  53. package/src/cli/identity.ts +9 -1
  54. package/src/cli/init-prompt.ts +179 -0
  55. package/src/cli/init-settings.ts +229 -0
  56. package/src/cli/init.ts +102 -9
  57. package/src/cli/launch.ts +3 -1
  58. package/src/cli/main.ts +106 -35
  59. package/src/cli/output.ts +19 -0
  60. package/src/cli/recovery-package.ts +9 -0
  61. package/src/cli/setup-homes.ts +18 -3
  62. package/src/cli/specs.ts +150 -0
  63. package/src/cli/terminal-recovery.ts +35 -11
  64. package/src/hub/board.ts +59 -5
  65. package/src/hub/bus.ts +54 -7
  66. package/src/hub/child-process.ts +6 -4
  67. package/src/hub/config-apply.ts +4 -1
  68. package/src/hub/config-edit.ts +1 -1
  69. package/src/hub/config-tier.ts +1 -0
  70. package/src/hub/control-client.ts +43 -4
  71. package/src/hub/control-write-trace.ts +409 -0
  72. package/src/hub/cost-governance.ts +22 -6
  73. package/src/hub/crash.ts +20 -0
  74. package/src/hub/daemon.ts +470 -98
  75. package/src/hub/delivery-hints.ts +6 -3
  76. package/src/hub/delivery-journal.ts +14 -3
  77. package/src/hub/hub-tools.ts +10 -6
  78. package/src/hub/peers.ts +1 -1
  79. package/src/hub/review-brief.ts +18 -0
  80. package/src/hub/routing.ts +14 -3
  81. package/src/hub/runtime-capture-prune.ts +4 -1
  82. package/src/hub/sdlc-release-authorization.ts +13 -4
  83. package/src/hub/sdlc-release.ts +23 -10
  84. package/src/hub/sdlc.ts +15 -2
  85. package/src/hub/settings.ts +2 -0
  86. package/src/hub/specs-config.ts +43 -0
  87. package/src/hub/specs-task.ts +108 -0
  88. package/src/hub/specs.ts +1008 -0
  89. package/src/hub/task-list-page.ts +75 -0
  90. package/src/hub/task-sweep.ts +10 -1
  91. package/src/hub/task-worktree.ts +331 -0
  92. package/src/hub/tasks.ts +606 -53
  93. package/src/hub/terminal-open.ts +10 -5
  94. package/src/hub/tool-request.ts +131 -0
  95. package/src/local/deny.ts +49 -0
  96. package/src/local/tools.ts +4 -2
  97. package/src/memory/brief.ts +5 -0
  98. package/src/memory/client.ts +5 -2
  99. package/src/ui/office-model.ts +17 -1
  100. package/src/ui/office-space.ts +1 -1
  101. package/templates/config.json +1 -0
  102. package/templates/profiles/dev.json +1 -0
  103. package/templates/profiles/general.json +1 -0
  104. package/templates/profiles/research.json +1 -0
  105. package/templates/profiles/writing.json +1 -0
  106. package/templates/specs.block.md +5 -0
@@ -1,6 +1,6 @@
1
1
  # Operations guide
2
2
 
3
- This guide describes ahub 0.27.0 and control protocols 16 through 17. Live verification
3
+ This guide describes ahub 0.29.0 and control protocols 16 through 17. 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
@@ -355,6 +355,24 @@ lineages require a person; unrelated new messages retain the one-retry option.
355
355
 
356
356
  The local worker and headless ACP peers, including Jcode, receive tools only inside a delivered turn. A hold prevents that turn, so these delivery-only transports do not offer recipient retry; their held hints ask the person to resolve the row. Ordinary messages stay queued. This restriction follows the current transport, not the vendor name: Kimi TUI remains pull-only and can request its one retry.
357
357
 
358
+ For an active session, `hub_delivery_resolve { delivery_id, revision, action, reason }`
359
+ settles only its own `needs_review` row, including while busy (#1079). Get the
360
+ observed revision from the bounded held-row metadata returned with `hub_inbox`.
361
+ Use `completed` after handling the delivery, or `discard` when it should not be
362
+ acted on, with a nonempty reason. The journal records the recipient actor and
363
+ reason. Another peer's row, a stale revision, remote-room disposition, active
364
+ `dispatching`/`accepted` rows, a paused or held peer, recovery, shutdown or storage
365
+ holds, and a reason that is private or sensitive all refuse. A row that carries an
366
+ invocation stays person-only (`ahub queue show <id>`, then `ahub queue resolve
367
+ <id>`), because the invocation may still be running. This never retries a message, changes a task
368
+ role or cancels the native turn; recipient retry keeps its existing one-use rules.
369
+
370
+ An explicit `hub_inbox` read can return newly queued messages behind a held row
371
+ and name the retained hold. It does not return the held originals, settle them
372
+ or release automatic delivery. Recovery, pause, lifecycle and storage fences
373
+ still apply. A delivery-only peer with no active turn still needs the person to
374
+ resolve its hold.
375
+
358
376
  Every console resolution needs a reason. Queue `discard` removes queued originals or
359
377
  settles a needs_review receipt; it never cancels a task or native turn.
360
378
 
@@ -384,11 +402,18 @@ behavior is unchanged. `ahub queue resolve ... [--revision N]` uses the supplied
384
402
  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
385
403
  the time); `dispatching` and `accepted` deliveries stay refused, and `retry` keeps its rules. A refused resolution names its cause (`active delivery`, `peer busy`,
386
404
  `stale revision`, `unknown delivery`, `already resolved`, `queued delivery is already scheduled`, `recipient queue is full`, `delivery journal unavailable`), never one
387
- generic sentence (#713).
388
-
389
- `ahub queue discard --peer <id> --reason <text> [--state queued|needs_review] [--yes] [--json]` clears one peer's backlog and is for the person or the configured conductor (any other agent identity is
390
- refused, and a remote `peer@label` queue is the person's only). 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
391
- 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
405
+ generic sentence (#713). A failed row that a resolution already settled (for
406
+ example the parent of a retry, whose redelivery is a separate row) is refused as
407
+ `already resolved`, never `stale revision`. `ahub queue show` on a failed row says
408
+ that nothing is left to do, or, if the row is unsettled, prints the `ahub queue
409
+ resolve <id> --action discard|completed` command (#1098).
410
+
411
+ `ahub queue discard --peer <id> --reason <text> [--state queued|needs_review|failed] [--yes] [--json]` clears one peer's backlog and is for the person or the configured conductor (any other agent identity is
412
+ refused, and a remote `peer@label` queue is the person's only). Without `--yes` it only says how many deliveries it would discard; with `--yes` it discards each delivery of that peer through the
413
+ 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. With no `--state` it takes the peer's queued rows and its
414
+ failed rows that no resolution has settled yet (#1096); `needs_review` rows need an explicit `--state needs_review`, and `--state` names exactly one kind. A failed row ran nothing, so it can be discarded while its peer is busy.
415
+ The new request form needs the `queue_discard_states` hub feature: against an older hub the CLI sends nothing for `failed` or for the default and says to upgrade, because that hub would discard queued rows instead.
416
+ `ahub queue list` hides rows a resolution already settled (for example the parent of a retry), prints how many on stderr, and `--all` shows them. Only the
392
417
  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
393
418
  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.
394
419
 
@@ -973,7 +998,48 @@ ahub up
973
998
  ```
974
999
 
975
1000
  `ahub init` writes the project configuration and managed instruction blocks.
976
- Run it after an upgrade when those blocks need refreshing. `ahub setup`
1001
+ Run it after an upgrade when those blocks need refreshing.
1002
+
1003
+ Settings flags (#1100) set the main project settings without a file edit.
1004
+ `--agents <a,b,...|none>` (`start.agents`) and `--autonomy inherit|off` (whether a
1005
+ new project inherits the machine autonomy preset) go to the untracked
1006
+ `config.local.json`. `--sdlc off|advise|enforce` sets `mode` in `sdlc.toml` (`off`
1007
+ writes no file on a new project). `--coordination advisory|turn-free`,
1008
+ `--memory`/`--no-memory`, `--snapshots`/`--no-snapshots` and `--pi`/`--no-pi` set
1009
+ their keys in `config.json`. On an existing project only a flag you give changes
1010
+ anything: `--dry-run` shows each change as `old -> new` (`--dry-run --json` adds a
1011
+ `settings` array), and a value equal to the current one is a no-op. An invalid
1012
+ value, or a flag given twice with different values, is refused with the allowed
1013
+ values, and nothing is written. Executables, secrets, models, providers, instances
1014
+ and permissions have no flags; they stay file edits.
1015
+
1016
+ A person at a terminal who runs `ahub init` on a new project with no flag that
1017
+ sets anything and no `--yes` is asked each setting in turn (#1065 T2): profile,
1018
+ the agents `ahub up` starts, design specs and their review mode, SDLC mode,
1019
+ coordination, shared memory, snapshots, the Pi peer, the machine autonomy
1020
+ preset, the release guard, and shadow history (asked in a folder with no Git
1021
+ work tree, and kept for an existing shadow repository, never in a Git
1022
+ repository). Each question is one line, the label, the choices and the default in
1023
+ brackets. Enter keeps the default and sets nothing; an answer is an explicit
1024
+ choice, exactly as the equivalent flag is, so the files written are the files
1025
+ the same flags write. A wrong answer asks again and is never guessed, and three
1026
+ wrong answers refuse the prompt. The plan follows, in the words `--dry-run`
1027
+ prints (including the machine autonomy preset a new project inherits, unless
1028
+ the answer was `off`), and `Write this? [y/N]` decides: N, an empty answer, Ctrl-C and Ctrl-D
1029
+ write nothing. Only the console role at a terminal (stdin and stdout) is asked:
1030
+ an agent identity is refused before `init` runs, a `--live-test` delegation, a
1031
+ pipe and `--dry-run` are never asked, and `--yes` takes the defaults. A flag of
1032
+ any kind (`--agents`, `--coordination`, `--profile`, `--shadow` and the rest)
1033
+ means the person has chosen, so nothing is asked and the flags apply alone. An
1034
+ existing project is never asked. The questions are built from the init settings
1035
+ table (`INIT_SETTINGS`), so a setting added there is asked without another
1036
+ change, and `test/init-prompt.test.ts` fails when the two disagree. The default
1037
+ in each bracket is read from where the writer gets it (`templates/config.json`,
1038
+ the loader's own defaults), and that test reads every one back through the
1039
+ loaders after a flagless `ahub init`, so a changed default cannot leave the
1040
+ prompt showing one value while Enter writes another. `--dry-run` and the
1041
+ prompt's plan take the machine-autonomy row from the same helper
1042
+ (`autonomyInheritanceChange`). `ahub setup`
977
1043
  installs or updates the Claude plugin and a project-neutral PreToolUse hook in each
978
1044
  Claude configuration home in use (see "Machine autonomy and Claude homes"), and
979
1045
  registers the acon console plugin (see "acon console plugin"). Keep `ahub console` open when a local worker
@@ -1021,6 +1087,34 @@ the sandbox either. `git add`, `commit`, `stash` and `checkout` work, and
1021
1087
  signs with a key under `~/.ssh` or `~/.gnupg` fails, because the sandbox cannot
1022
1088
  read those directories. Run a refused command yourself, in a terminal.
1023
1089
 
1090
+ A task can declare what it needs (#1081): `hub_task_propose {needs_network: true}` or `{needs_worktree: true}`, and
1091
+ `ahub task propose --needs-network` or `--needs-worktree`. Only an explicit `true` sets either, and the hub never
1092
+ infers a need from the title. A declared need keeps the task away from a peer that cannot meet it: automatic
1093
+ routing, a reservation, `ahub task assign` and `hub_task_accept` all refuse it before any write, and
1094
+ `ahub route explain <id>` prints the cause for that peer. It is offered to a peer that can meet the need.
1095
+
1096
+ - `needs_network` is for work that fetches from a package registry, runs `git fetch` or `git push`, or calls an API.
1097
+ A hub-sandboxed peer (Pi, the local worker) has none while `local.bash_network` is off: `no network: local.bash_network
1098
+ is off ...`.
1099
+ - `needs_worktree` is for work that must run in its own git worktree on a task branch (the repository rule). A
1100
+ hub-sandboxed peer cannot run `git worktree add`, because it writes under the git common directory and runs outside
1101
+ the sandbox by design (#270), so since #1115 the hub makes the worktree for it at `.worktrees/<peer>-<task>` on the
1102
+ branch `task/<task>-<peer>`, before the accept write. The peer edits files there and does not run git; at
1103
+ `hub_task_done` the hub stages, commits and pushes the branch. The capability is true only where the hub can make
1104
+ the worktree. Otherwise it is false and the refusal says why and what to do: `no worktree: the hub cannot make a task
1105
+ worktree for it: the project is not a git repository; run git init there, ...`. A refused accept creates nothing,
1106
+ so no empty branch ref is left behind (the Pi accepts of #621 and #565 left two). Declining or cancelling removes
1107
+ the worktree and its empty branch; one with uncommitted changes is kept and reported. The sandbox profile is not
1108
+ changed (docs/security.md, "Hub-made task worktree").
1109
+
1110
+ A peer whose capability the hub cannot tell is never refused on this ground. Work that declares no need is routed as
1111
+ before, with one exception (#1129): write-class work (`implement`, `bulk_edit`) is treated as needing a worktree
1112
+ whether or not it is flagged, because a hub-sandboxed owner cannot commit (#1123) and the hub commits in its worktree.
1113
+ Such work is never offered to a hub-sandboxed peer on a project where the hub cannot make a worktree, and
1114
+ `ahub route explain` says `no worktree` and the cause; `test`, `summarize`, `triage`, `review` and `plan` get none,
1115
+ and neither does a PII task. The other flags are a person's or a planner's statement about the work, so an unflagged
1116
+ task can still reach a peer that cannot meet them.
1117
+
1024
1118
  Network is off unless `local.bash_network` says otherwise. With `true` it goes
1025
1119
  only through the hub's egress proxy on a loopback port (0.11): commands get
1026
1120
  `HTTPS_PROXY` and the other proxy variables, the proxy opens HTTPS (`CONNECT`,
@@ -1098,7 +1192,7 @@ assignment skips it until a delivery completes; inspect `ahub route explain` for
1098
1192
  reason. A task owned by a peer whose task delivery exhausts retries is escalated with
1099
1193
  that delivery's error, and the console is notified.
1100
1194
 
1101
- A delivery in `needs_review` holds later deliveries to that peer. The console announces
1195
+ A delivery in `needs_review` holds automatic later deliveries to that peer; an explicit `hub_inbox` read may read new queued work behind it without releasing or replaying the hold (#1079). The console announces
1102
1196
  the hold once, and status, task assignment and route explanations name its delivery id.
1103
1197
  Inspect it with `ahub queue show <id>` and explicitly choose `completed`, `retry` or
1104
1198
  `discard` through `ahub queue resolve`. Reassignment or approval of its task never
@@ -1124,6 +1218,95 @@ Pi 0.86 assistant usage is forwarded at `message_end`, including tool-loop messa
1124
1218
  and recorded before settlement. If a model supplies no usable count, `ahub report`
1125
1219
  says `tokens not reported`; the hub does not estimate usage from text length.
1126
1220
 
1221
+ ## Control write outcomes and safe retries (#1078)
1222
+
1223
+ A daemon advertising `tool_write_idempotency` accepts a top-level control
1224
+ `requestId` for `hub_task_propose` and `hub_review`. This key is independent of
1225
+ transport `rid` and MCP JSON-RPC IDs. The proposal and review MCP tools expose optional `request_id`:
1226
+ omit it for a new call; after an unknown outcome, reuse the echoed key with the
1227
+ same arguments. The channel removes this transport argument before task
1228
+ validation. A new JSON-RPC ID does not change an explicitly supplied retry key. In-process tool calls (the local worker and Pi) refuse `request_id`: replay keys need the control-protocol transport.
1229
+
1230
+ Read `hub_task_list` for an uncertain proposal, `hub_task_show` for an uncertain
1231
+ verdict, or `ahub queue show <delivery-id>` (person/conductor) for a delivery.
1232
+ Call again only if the task, verdict or completion is absent. For a proposal or
1233
+ review retry, copy the returned `request_id`; do not invent a fresh key. An
1234
+ in-flight retry joins the original execution. A retained result is the receipt
1235
+ of that call, rather than a fresh statement of the task's current state.
1236
+
1237
+ `outcome: unknown` means a sent call may already have applied, including after a
1238
+ timeout, cancellation or lost connection. `outcome: refused` means this attempt
1239
+ did not run (for example an invalid key, insufficient authority or full actor
1240
+ window); it does not undo an earlier attempt. Known task permission/validation
1241
+ errors retain their specific error. Explicit retries are refused before sending
1242
+ to older daemons that do not advertise the feature.
1243
+
1244
+ Each authenticated actor has its own 512-entry, 15-minute replay window across
1245
+ its operations and launch generations. One actor filling that window does not
1246
+ block another. Replies are capped at 64 KiB and cached overlap metadata checks
1247
+ up to 64 current task references. Expired/foreign-instance IDs refuse without
1248
+ applying again; restart recovery uses board reads. `delivery_complete` uses the
1249
+ existing journal idempotence keyed by delivery ID and the live socket/generation,
1250
+ so settlement consumes no replay-window capacity. `hub_delivery_done` takes only
1251
+ its delivery ID and generation; it has no request key argument.
1252
+
1253
+ The latency receipt and its synthetic-only scope are in
1254
+ [control-write verification](verification/2026-11-02-control-write-outcomes.md).
1255
+
1256
+
1257
+ ## Control write timing and loop delay (#1126)
1258
+
1259
+ After upgrading to a release containing #1126 and restarting through the normal
1260
+ upgrade workflow, `ahub status --full` shows aggregate control-write timing
1261
+ counters. An older hub omits them. They are process-local diagnostics, reset at
1262
+ restart, not durable success receipts or a count of unique business effects.
1263
+ This feature does not change a request timeout or make a retry safe.
1264
+
1265
+ A slow tracked operation (at least 1,000 ms on a monotonic clock) writes a
1266
+ `control_write_slow` JSON record to hub.log. The record contains a closed request
1267
+ kind, an optional public task number, elapsed time, outcome and phase durations.
1268
+ `returned`/`threw` describes the handler, not business success or whether a write applied.
1269
+ Client-side timing is opt-in with `AHUB_CONTROL_WRITE_TIMING=1` in the client or
1270
+ MCP server environment. Its slow records go to stderr, never stdout, and omit task
1271
+ numbers because the client has no authoritative privacy view. No request/rid,
1272
+ actor/session identifier, path, task text, argument, external output or error
1273
+ message enters a timing record. Host privacy is checked again before logging.
1274
+ An unresolved task identity (including a noncanonical string id) suppresses daemon
1275
+ diagnostics while the operation keeps its normal validation and behavior.
1276
+
1277
+ The daemon samples its event loop every 100 ms. A missed tick delayed by at least
1278
+ 250 ms can write `control_write_loop_delay`, including bounded observed spans.
1279
+ Recently completed synchronous intervals are retained so a span that blocked the
1280
+ loop can be identified after its callback has returned. An open asynchronous span
1281
+ is only a concurrent wait, not proof that it occupied the CPU; untracked occupancy
1282
+ is reported as unknown. Background snapshot, queue/status and memory boundaries
1283
+ use the fixed `background` kind rather than inventing a tool request.
1284
+
1285
+ Per observer, retained work is capped at 128 operations, 256 active span registrations, 512
1286
+ recent intervals and 8 emitted phase/span items. Phase totals use only the closed
1287
+ phase set; a full active-registration map never discards a finished phase or recent
1288
+ synchronous interval. There is no per-request cutoff that can hide a late block.
1289
+ The slow and loop categories each allow at most one line per second, with a shared
1290
+ 60-line/minute ceiling; each JSON record is at most 2,048 UTF-8 bytes. Dropped work
1291
+ and logs are counted. Normal ring rotation and settle-time cleanup are excluded
1292
+ from the full-status dropped total. A stopped probe cannot be started again. Nested durations are not additive. Status counters include
1293
+ background observations and do not identify a blocking cause on their own.
1294
+
1295
+ Unanswered diagnostic requests expire after five monotonic minutes, reclaiming
1296
+ their operation and span slots without cancelling the underlying work or changing
1297
+ its eventual result. Full status lists `expired` separately from capacity/log drops
1298
+ and normal ring rotation. Argument screening starts only after socket admission;
1299
+ model-input diagnostic screens run once only when tracing can admit the observation.
1300
+
1301
+ Board-write, queue/status, memory and external-call phases locate an observed
1302
+ boundary; they do not weaken its existing authority, privacy or execution gates.
1303
+ The probe is unref'd and stopped at daemon shutdown or failed startup. The
1304
+ [historical window correlation](verification/2026-11-03-control-write-stall-windows.md)
1305
+ separates one 60-second all-event gap from review waits with continuing periodic
1306
+ callbacks. The instrumentation is intended to diagnose the next naturally
1307
+ occurring stall; it has not been run against the live daemon by that task.
1308
+
1309
+
1127
1310
  ## A two-agent task and review
1128
1311
 
1129
1312
  Choose an owner explicitly, then inspect the reviewer selected by the project
@@ -1307,6 +1490,18 @@ take it.
1307
1490
  for. Dependencies are fixed when a task is proposed and can only name tasks that
1308
1491
  already exist, so they cannot form a cycle.
1309
1492
 
1493
+ `hub_task_list` pages (#1083), because a 628-task board was 2.4 MB and over every agent tool-output limit.
1494
+ An agent's reply is `{tasks, total, next?}`: newest updated first (ties by id, descending), at most `limit`
1495
+ rows (default 50, 1 to 200), each compact (`id`, `state`, `owner`, `reviewer`, `title`). Pass `next` back as
1496
+ `cursor` for the following page; the cursor is the last row's `updated.id`, so a task that changes between
1497
+ calls neither repeats nor skips an older row, and the last page has no `next`. `ids` (1 to 200 distinct task
1498
+ numbers) returns the full public row of exactly those tasks, in the order given, and cannot be combined with
1499
+ `limit` or `cursor`; `detail: true` returns full rows for a page. `state`, `ready` and `release` filter before
1500
+ paging. PII tasks stay `[pii]` stubs in every form. Every refusal names its cause and what to pass instead.
1501
+ The person's console keeps the full bare array unless it names `limit`, `cursor`, `ids` or `detail`, so
1502
+ `ahub board`, `ahub tasks` and their `--json` output are unchanged. A peer that read the whole board as a
1503
+ bare array (the benchmark runner did) reads `tasks` instead, or asks for its task by `ids`.
1504
+
1310
1505
  An owner named for a task that waits (`owner` with `after`, `--owner` with
1311
1506
  `--after`) is its reserved owner: the task still waits, and when it is ready
1312
1507
  routing offers it to that peer first, not to the first idle peer of the class.
@@ -2062,6 +2257,16 @@ A newly initialized `ahub init` project inherits a trusted machine opt-in and sa
2062
2257
 
2063
2258
  `ahub setup` selects the explicit `CLAUDE_CONFIG_DIR`, otherwise an existing default Claude home (or the not-yet-created default when no other home is selected), plus distinct homes proven in use by live registered Claude launcher records. It does not scan arbitrary profile directories. Each native plugin command receives that home's environment. One confirmation covers plugin setup and a project-neutral PreToolUse hook while preserving unrelated hooks and statusLine settings. The profile fallback requires the inherited hub state and peer identity and does nothing for a managed launch that already has its session hook, preventing duplicate delivery. Installing a hook proves configuration, not live enforcement.
2064
2259
 
2260
+ Setup manages only the user-scope `agent-hub@agent-hub` plugin row (`claude
2261
+ plugin install|uninstall ... --scope user`). A project- or local-scope row of that
2262
+ plugin is kept and named in a notice (`... plugin row preserved ...; setup never
2263
+ removes non-user scopes`). While such a row exists, setup is blocked rather than
2264
+ removing an agent-hub marketplace that points at another source (#1073). The
2265
+ semver check on installed plugin versions applies only to agent-hub rows, so
2266
+ another plugin recorded with a git SHA no longer fails the inventory (#1074). When
2267
+ a Claude home step fails, setup still records the `start.agents` answer before it
2268
+ reports the failure.
2269
+
2065
2270
  `ahub autonomy status [--json]` includes the machine default and each discovered Claude home's plugin/hook state. Missing, unsafe, stale, unavailable or incomplete evidence is explicit; a missing selected home is reported without starting a native inventory command that could create it. The native Orca zero-prompt receipt remains T4.
2066
2271
 
2067
2272
 
@@ -2489,6 +2694,100 @@ Rules:
2489
2694
  - Person only: an agent shell or a hub tool is refused (`ahub settings edit` is a console command in the identity gate, and the three control requests answer only the console role). Recovery or shutdown holds every edit.
2490
2695
  - The hub must run this release: an older hub answers `unknown message` and the command says to upgrade and restart it.
2491
2696
 
2697
+ ## Design spec inspection
2698
+
2699
+ `ahub specs list` discovers Markdown specs whose flat frontmatter names an `id`
2700
+ and `type`. `ahub specs covers <file...>` finds their path scopes.
2701
+ `ahub specs check` validates graph links, types and stamps without a daemon;
2702
+ it prints nothing and exits 0 when there are no specs or stamped documents.
2703
+ `--json` returns structured reports, and `--require-fresh` also fails on stale
2704
+ documents. `--base <ref>` reports changed paths whose covering spec stayed
2705
+ unchanged without making that report an error.
2706
+
2707
+ The `specs` config block defaults to
2708
+ `{ "enabled": true, "review": "report", "roots": [], "stamps_file": "" }`.
2709
+ Roots are repository-relative directories; an empty list scans the repository.
2710
+ The optional stamps file uses the schema of `docs/verified.json` for other
2711
+ documents. A scan stops at 2,000 Markdown files and 8 KiB of frontmatter per
2712
+ file; a truncated report names `specs.roots` as the way to narrow the scan.
2713
+ Symlinked files are skipped. Freshness counts commits touching covered paths
2714
+ since the stamp and detects uncommitted changes without running Git filters
2715
+ or external diffs. These commands never refresh a stamp.
2716
+
2717
+ The Switches settings `specs.enabled` and `specs.review` are machine-local
2718
+ settings, and the specs config fields are tier 2 structural settings. Setting
2719
+ `specs.enabled` to false suppresses this machine's notices and status summary,
2720
+ leaving the shared config unchanged. Explicit CLI and `hub_specs` inspection
2721
+ still work when notices are disabled.
2722
+
2723
+ `ahub init --specs` (the default) and `--no-specs` set the shared key
2724
+ `specs.enabled` and add or remove the `AGENT_HUB_SPECS` instruction block in
2725
+ `AGENTS.md` (the text is `templates/specs.block.md`). `--spec-review
2726
+ off|report|require` sets `specs.review`. Both are rows of the init settings
2727
+ table: on an existing project an explicit flag shows as one old -> new row in
2728
+ `--dry-run`, a flag equal to the current value is a no-op, and a plain `ahub
2729
+ init` follows the project's `specs.enabled` (on unless the project set it to false):
2730
+ it adds or refreshes the block when specs are on and removes it when they are off. Text outside
2731
+ the markers is never touched. A block with only one of its two markers is
2732
+ refused, naming the cause, and nothing is written.
2733
+
2734
+ `ahub specs stamp <id-or-document...> [--yes]` is a person's record that they
2735
+ reviewed a spec or a stamped document against HEAD. It is person only: an agent
2736
+ identity is refused before the command runs (it is a console command, and
2737
+ `--live-test` delegation refuses it too), and a run with no terminal is refused
2738
+ even with `--yes`. It shows `old -> new` per document, asks, and then changes
2739
+ only the spec's `verified-against` line (adding it before the closing `---` when
2740
+ absent) or the document's `verifiedAgainst` value in the stamps file, keeping
2741
+ every other byte, the line endings, a byte order mark and the file mode. The new
2742
+ stamp is the full HEAD SHA. It is refused, writing nothing for any target, while
2743
+ a document's covered paths have uncommitted changes, when the document has no
2744
+ paths, when a document's frontmatter cannot take a stamp, or when a name matches
2745
+ no or several documents; all refusals are listed together, and a document that
2746
+ cannot be stamped is refused before the question is asked. The answer may take
2747
+ minutes, so after it HEAD, each covered path's state and every target file's
2748
+ bytes are checked again, and any change refuses the whole run: what you approved
2749
+ is what is written. Removing the instruction block with `--no-specs` takes out
2750
+ only the block and one separating blank line, so the whitespace of the text
2751
+ around it is kept. Stamping changes a working-tree file: review the diff and commit it.
2752
+ Agents never edit `verified-against`. `docs/verified.json` is still restamped
2753
+ only in a release PR (AGENTS.md "Verified stamps").
2754
+
2755
+ `scripts/check-docs.mjs` stays a self-contained Node script, because the release
2756
+ workflow runs it on Node without Bun or an install and `src/hub/specs.ts` is
2757
+ TypeScript that depends on repository packages. `test/check-docs-parity.test.ts`
2758
+ holds it to the same state, commit count and dirty flag as the shared engine on
2759
+ the same stamps.
2760
+
2761
+ Task proposal and acceptance replies name the specs covering the task's refs
2762
+ paths. At completion the hub reads spec metadata from the submitted commit,
2763
+ compares the host-verified changed-file inventory with those scopes, and adds
2764
+ `spec not updated: <id>` and `spec errors: <n>` to the result and review brief.
2765
+ Dirty spec edits in another checkout do not satisfy this check. `report` records
2766
+ the report; `off` suppresses task notices. Under `require`, unchanged covering
2767
+ specs must be updated or named in `spec_ack`, for example
2768
+ `[{ "id": "bus", "reason": "Only an internal allocation changed; the contract is unchanged." }]`.
2769
+ Acknowledgements are screened, recorded at the exact head, and shown to the
2770
+ reviewer. A new head invalidates the old report. Unknown or truncated coverage
2771
+ cannot satisfy `require`; a refusal names the next step. A call accepts up to
2772
+ 20 acknowledgements; larger changes can update the specs or split the task.
2773
+ Tasks without a submitted commit or with no changed files have no coverage
2774
+ obligation. Existing spec errors are reported on tasks touching covered paths
2775
+ or spec documents. A privacy change during a check suppresses its spec report
2776
+ without discarding the check result. If an applicable review policy changes
2777
+ to `require` while that check runs, the pass is retained and the owner is
2778
+ asked to refile completion under the current policy.
2779
+
2780
+ `hub_specs` offers read-only `check` and `covers` actions with at most 20 report
2781
+ items. These explicit reads and status inspect the daemon checkout; task notices
2782
+ inspect the owner's execution root and completion reports inspect its submitted
2783
+ commit. Immutable Markdown blobs are read in bounded batches and parsed results
2784
+ are cached by object id across submitted heads. PII tasks and private turns do not publish spec paths through task
2785
+ notices. Completion snapshots skip Git symlinks and cap each committed spec
2786
+ body at 4 MiB; an incomplete read stays unknown. Status shows a specs count,
2787
+ error count and stale count only when specs or stamped documents exist, with
2788
+ one hint when the specs instruction block is missing. Empty projects stay
2789
+ silent.
2790
+
2492
2791
  ## Start modes: TUI by default, headless as opt-in
2493
2792
 
2494
2793
  A peer that has a TUI the hub attaches to starts in it by default (issue #269).
@@ -2502,6 +2801,19 @@ A peer that has a TUI the hub attaches to starts in it by default (issue #269).
2502
2801
  | jcode (optional) | `tui` on the hub-owned ACP session (`ahub jcode`) | `ahub jcode --mode headless`, or `peers.jcode.start_mode: headless` |
2503
2802
  | local | headless (hub-native) | not applicable |
2504
2803
 
2804
+ Daemon-owned peer launches, native version-helper probes and process-table helpers drop
2805
+ every inherited `ORCA_*` variable. They pass `CLAUDE_CONFIG_DIR` and `CODEX_HOME` through
2806
+ unchanged; Claude uses the former and Codex the latter, while other peers ignore
2807
+ selectors they do not own. The hub does not choose a different account,
2808
+ synthesize a home, or copy native credentials. A person-started native process
2809
+ launched from `ahub claude`, `ahub codex`, `ahub kimi` or another native launcher
2810
+ keeps its pane's `ORCA_*` context while its own peer markers replace inherited
2811
+ agent identity. Manager and person console wrappers keep their existing
2812
+ environment, routing and recovery rules. Adapter-owned fresh homes take
2813
+ precedence; Pi's fixed environment carries `CODEX_HOME` but not
2814
+ `CLAUDE_CONFIG_DIR`, and the local worker's fixed shell environment carries
2815
+ neither.
2816
+
2505
2817
  - **You type the command:** `ahub pi` opens Pi's TUI in that terminal.
2506
2818
  `--headless` and the start mode setting are the two ways to ask for the
2507
2819
  background peer. `ahub settings set peers.pi.start_mode headless` writes the
@@ -2520,7 +2832,14 @@ A peer that has a TUI the hub attaches to starts in it by default (issue #269).
2520
2832
  `{command}` is quoted for a POSIX shell; `{title}` and `{cwd}` are raw,
2521
2833
  so give each an argv element of its own.
2522
2834
  2. Orca, when the hub runs in an Orca worktree or an earlier `ahub <peer>`
2523
- launch of this project recorded one (the path recovery uses).
2835
+ launch of this project recorded one (the path recovery uses). The terminal opens in the
2836
+ Orca worktree whose root is exactly this project's root, found with `orca
2837
+ worktree list` (#1105); it never uses the worktree of whichever terminal
2838
+ started the hub, which is another project's after `ahub --project`, a
2839
+ dashboard manager start or an upgrade. The start is refused, naming the cause
2840
+ and `terminal.open` as the alternative, when Orca cannot list its worktrees,
2841
+ has no worktree with this root, has more than one, or cut its list off at 500
2842
+ rows first.
2524
2843
  - **No provider:** the start is refused with the command to run by hand. It is
2525
2844
  never downgraded to headless. `ahub settings` lists each start mode, and the
2526
2845
  dashboard's Start control says which mode a start will use and where its
@@ -2920,6 +3239,13 @@ the history entry reads `by: hub` with that planner and the reason
2920
3239
  is paused for recovery pulls nothing, also when the setting is turned off or recovery begins while a pull is already waiting on its screening: that pull writes nothing, and a recovery waits for pulls in flight. A committed `config.json` cannot turn it on: like
2921
3240
  the other machine-local policies it is ignored there with a log line.
2922
3241
 
3242
+ A backlog or unowned proposed card whose `plannedStart` is still in the future is
3243
+ not started automatically (#1092). Until that time the hub sends no
3244
+ `backlog_ready` line, makes no auto-pull, makes no ready-queue or queue-pressure
3245
+ owner offer, and the task sweep reports no stall. Planner grooming lists the card
3246
+ as `scheduled`. The card becomes eligible at the planned start itself. A planner's
3247
+ explicit pull or a person's assignment is unchanged.
3248
+
2923
3249
  ## Resetting a project's hub
2924
3250
 
2925
3251
  `ahub reset` returns a project's hub to a clean state. Without `--yes` it is a
@@ -3126,7 +3452,7 @@ version is older than the selected running daemon; it installs nothing.
3126
3452
  | Runtime | Control protocol window | Legacy status field |
3127
3453
  | --- | --- | --- |
3128
3454
  | 0.12.20 through 0.25.0 | 16 only | `protocol: 16` |
3129
- | 0.26.0 through 0.27.0 | 16 through 17 | `protocol: 16`, `protocolMin: 16`, `protocolMax: 17` |
3455
+ | 0.26.0 through 0.29.0 | 16 through 17 | `protocol: 16`, `protocolMin: 16`, `protocolMax: 17` |
3130
3456
 
3131
3457
  New clients choose the highest common protocol. The runtime supports N-1, so the
3132
3458
  published protocol-16 client and channel plugin remain usable; unrelated feature
@@ -3731,6 +4057,20 @@ crash of one is not reported this way.
3731
4057
  - Codex's app-server died with the hub; run `ahub codex` again. Claude Code's
3732
4058
  plugin reconnects by itself while that session is open.
3733
4059
 
4060
+ The report in `ahub status` shows only what still stands (#1082), worked out when
4061
+ status is asked and not frozen at startup. A line that asks something of a peer
4062
+ (`run ahub codex again`, `reconnects by itself`, a Pi that is still down) goes as
4063
+ soon as that peer is attached again, whoever attached it. The headline's count of
4064
+ deliveries still in `needs_review` counts only the ones this crash left, read
4065
+ live, so it equals what `ahub queue list` shows for them and falls as each is
4066
+ resolved. At zero the count and its pointer to the queue are gone and the headline
4067
+ (`the previous hub run stopped without shutting down`) stays as the record that a
4068
+ crash was found. Lines that say what recovery found or did (`kimi resumed`,
4069
+ `pi not resumed`, `pi.auto_start started a fresh session`) are history and stay for
4070
+ the run. The hub log keeps every line as it was written. A row that becomes
4071
+ `needs_review` later in the run is in `ahub queue list` and is not part of this
4072
+ count.
4073
+
3734
4074
  ## Evidence and limits
3735
4075
 
3736
4076
  The 0.6.4 release has real measurements in [the smoke checklist](smoke.md):
@@ -4081,7 +4421,7 @@ has appeared for `AHUB_CHECK_QUIET_S` seconds (300 by default, set to 300 in CI)
4081
4421
  slow suite that is still passing tests is never cut. `scripts/progress-tee.sh` passes bun's
4082
4422
  output through unchanged and appends one byte to a progress file for each of those lines;
4083
4423
  the watchdog polls that file's size. On a stop it prints where the run is and the process
4084
- table below it first. `AHUB_CHECK_HANG_S` (960 by default) is only the backstop for a run
4424
+ table below it first. `AHUB_CHECK_HANG_S` (2250 by default, sized in #1086 from two measured full runs on dgx02 of 1483 s and 1487 s of test time, 1.5 times the longer; it was 960) is only the backstop for a run
4085
4425
  that keeps printing results while hung; hosted CI sets 1500 on Linux inside a 35-minute
4086
4426
  job limit (#378, #593, #752, task 516). The macOS leg runs as four shards (#548): `AHUB_CHECK_SHARD=i/n`
4087
4427
  makes `scripts/check.sh` run slice `i` of the full suite through `bun test --shard=i/n --timings=scripts/test-timings.json`, which puts
@@ -4409,6 +4749,16 @@ Use `ahub roster add jcode`, then `ahub jcode [--model <alias>] [--provider-prof
4409
4749
 
4410
4750
  The native TUI opens only after Jcode has saved the selected session. On 0.98.1, `session/new` does not save it; the first real hub turn does. The launcher prints `jcode's TUI opens after its first hub turn; send it work, or Ctrl-C` once and waits for the same owned launch's saved session. Send Jcode work from the hub, or press Ctrl-C to end only the wait. No artificial prompt is sent. A console `peer_start` uses a terminal provider to run the same launcher, even while ACP is running; a conductor or dashboard start of an attached Jcode answers `already` and opens nothing (#559). Without a provider, run its displayed command in your own terminal. Options with no terminal need that manual path.
4411
4751
 
4752
+ A refused TUI launch names its cause and the next step (#1093): Jcode was
4753
+ restarted during the launch, its owned server is not running, its private socket
4754
+ was replaced, no session has been reported yet, the saved session failed the hub's
4755
+ safety check, the hub is busy stopping or recovering, Jcode is not running under
4756
+ this hub, or the project MCP entry is stale. The daemon logs only the cause code,
4757
+ never a path or id. After a crash or controlled recovery a Jcode whose start mode
4758
+ is `tui` runs headless. Until a TUI launch is served, `ahub status` and the
4759
+ launcher say `jcode is running headless after recovery; run ahub stop jcode, then
4760
+ ahub jcode to get its TUI back`.
4761
+
4412
4762
  `ahub jcode --dry-run` stays pure and redacted. A person's `--print-command` can show the actual top-level native argv and closed native environment for an already-saved running session; it starts nothing, and like the launch it names a `--model`, `--provider-profile` or `--session-id` that differs from the running session (#574). The private socket/session tuple is not returned to hub tools or status. Native saved-session evidence is read-only and capped at 32 MiB; unsafe, mismatched or larger evidence refuses the TUI launch.
4413
4763
 
4414
4764
  A busy jcode takes an `important` message into its running turn (#572): the hub sends it as a soft interrupt through jcode's own private server socket, which jcode injects at its next tool boundary, so the message no longer waits for the turn to end. The hub never cancels a turn to deliver. Headless Kimi and Qwen have no such path: an `important` message waits, and `ahub status` and the console show `important messages waiting on a busy ACP peer for N min` once it has waited past `budget.stale_min`, so you can decide to stop the peer.
@@ -4690,6 +5040,14 @@ admission reads, and the split changes no admission decision. An older hub shows
4690
5040
  acceptance and self-claim in every lifecycle mode, so 0 refuses new acceptances; a
4691
5041
  current owner's redo of sent-back work keeps its slot (#481 S9).
4692
5042
 
5043
+ A refusal at the limit separates what the owner carries from what it was only offered (#1080):
5044
+ `planner WIP capacity for codex: limit 2; open tasks #621, #624 (accepted #621; offered, not yet accepted #624); <next step>`.
5045
+ An `urgent` task (priority 0, or the `urgent` signal) that is the owner's own offer may take back the
5046
+ newest offers the owner has not accepted, only as many as it needs, when that is all that keeps it at the
5047
+ limit. The hub returns each one ownerless to To Do (history `unassigned`, by the hub, naming the urgent
5048
+ task; a planner-pulled offer also loses the reservation that counted toward the owner's WIP), withdraws its offer, and tells the planners and the console. After the displacement the hub checks that the owner is under its limit and refuses the accept with the cause and a next step if not. It never takes back accepted work,
5049
+ another urgent offer, a person-pinned offer or an offer with work evidence, and a refusal changes nothing.
5050
+
4693
5051
  Pending unowned, unstarted pull can be undone at its latest owned revision via
4694
5052
  `hub_backlog_undo` or the existing planning undo control, restoring its earlier
4695
5053
  reservation. An ordinary return to Backlog also restores the pre-pull reservation unless a later explicit reservation edit superseded it. Normal release routing offers work only while that reserved peer
@@ -5073,7 +5431,7 @@ A reviewer has a review scope (#852): `full` (reads the code, runs the affected
5073
5431
 
5074
5432
  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 task 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.
5075
5433
 
5076
- Codex requires a verified `codex-cli` (0.146.0, 0.163.0 or 0.164.0) or one the person trusts on this machine, and a passing bounded runtime receipt before its model child starts. 0.146.0 and 0.163.0 passed the [2026-10-22 model-free production admission](verification/2026-10-22-codex-reviewer-isolation.md) and 0.164.0 the [2026-10-24 one](verification/2026-10-24-codex-0164-reviewer-isolation.md); 0.166.0 passed the [2026-10-31 model-free procedure](verification/2026-10-31-codex-0166-reviewer-isolation.md), but its [one authorized model review](verification/2026-10-31-codex-0166-reviewer-tool-call.md) missed the required synthetic marker in the persisted verdict, so the version remains unverified and its proposed verified bump was reverted. 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). Each launch approves exactly `hub_review` and `hub_task_show` per tool (`approval_mode = "approve"`), never through a server-wide default: without it Codex 0.164.0 listed the tools but refused the call, so reviews ended with no verdict (#783). A model-free receipt proves the inventory, not that a verdict reaches the hub; a [model-backed 0.164.0 run](verification/2026-10-25-codex-0164-reviewer-tool-call.md) recorded one verdict through that path. 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.
5434
+ Codex requires a verified `codex-cli` (0.146.0, 0.163.0, 0.164.0 or 0.166.0) or one the person trusts on this machine, and a passing bounded runtime receipt before its model child starts. 0.146.0 and 0.163.0 passed the [2026-10-22 model-free production admission](verification/2026-10-22-codex-reviewer-isolation.md) and 0.164.0 the [2026-10-24 one](verification/2026-10-24-codex-0164-reviewer-isolation.md); 0.166.0 passed the [2026-10-31 model-free procedure](verification/2026-10-31-codex-0166-reviewer-isolation.md). Its [first authorized model review](verification/2026-10-31-codex-0166-reviewer-tool-call.md) missed a marker hidden by the fixture's accepted plan, so that admission was reverted. After a committed model-free envelope regression and a fixture correction, the separately authorized [2026-11-02 single model review](verification/2026-11-02-codex-0166-reviewer-tool-call.md) persisted one approved verdict with the required marker and exact head; 0.166.0 is now verified. 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). Each launch approves exactly `hub_review` and `hub_task_show` per tool (`approval_mode = "approve"`), never through a server-wide default: without it Codex 0.164.0 listed the tools but refused the call, so reviews ended with no verdict (#783). A model-free receipt proves the inventory, not that a verdict reaches the hub; a [model-backed 0.164.0 run](verification/2026-10-25-codex-0164-reviewer-tool-call.md) recorded one verdict through that path. 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.
5077
5435
 
5078
5436
  Kimi requires a compat-admitted Kimi Code on the hub's PATH (receipt-verified 2.2.0, 2.3.0, 2.3.1, 2.3.2 and 2.4.1), 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](verification/2026-10-22-kimi-qwen-compat.md), [2.3.1](verification/2026-10-25-kimi-231-reviewer-compat.md), [2.3.2](verification/2026-10-26-kimi-232-reviewer-compat.md) and [2.4.1](verification/2026-10-31-kimi-241-reviewer-compat.md) receipts are model-free and do 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.
5079
5437
 
package/docs/profiles.md CHANGED
@@ -54,6 +54,7 @@
54
54
  # Project profile overrides
55
55
 
56
56
  - Acceptance criteria are profile-independent (#507b): a task's recorded criteria checklist (docs/sdlc.md, Acceptance-criteria checklist) is listed in every profile's review request, ahead of the profile's `review_checklist` lines, and an unmet item that starts with a criterion id names it; a profile only changes the wording of its checklist, never the criteria or their ids.
57
+ - Every shipped profile's `review_checklist` carries one error-message item (#1095): the reviewer names each new or changed error, refusal or failure message that lacks its cause or its next step. Because `review_checklist` replaces as a unit, a project that sets its own `profile_overrides.review_checklist`, and a pack (whose rubric lines become that override), does not get the item unless it adds one. The shipped lists are what a project without an override sees.
57
58
  - Recurring unmet criteria become a candidate, never a change (#514a): when reviews name the same criterion id unmet on at least 3 tasks of one profile and class within 30 days, and those tasks make up at least 2 separate pieces of work (tasks sharing a PR or a branch count once), the hub files one backlog card with the `profile_overrides.review_checklist` line to consider. The line is text, and nothing applies it on its own. By hand: `profile_overrides` is not machine-local, so you choose the shared `.agenthub/config.json` (every checkout) or `config.local.json` (this machine), and add the line to your current checklist because `review_checklist` replaces as a unit. With the command (#514b): `ahub learn list` shows the open cards, `ahub learn accept <id>` adds that one line to your current checklist in `.agenthub/config.local.json` (this machine only, never the shared or any tracked file) through the settings writer, closes the card, and says it applies after a restart; `ahub settings undo` puts the previous file back; `ahub learn reject <id>` only closes the card. The line is rebuilt from the key line of the card and the board's own evidence, never parsed from the card text. Person only: an agent shell and the configured conductor are refused. PII tasks and evidence-only verdicts never count.
58
59
  - `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.
59
60
  - 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.
@@ -17,7 +17,7 @@ ahub setup # installs the channel plugin and a pr
17
17
  Or use the matching GitHub release:
18
18
 
19
19
  ```bash
20
- bun add -g github:STAIxBWLB/agent-hub#v0.27.0
20
+ bun add -g github:STAIxBWLB/agent-hub#v0.29.0
21
21
  ahub setup
22
22
  ```
23
23
 
@@ -31,6 +31,20 @@ move a running project to a newer release later, run `ahub upgrade` in a
31
31
  terminal in that project: it reviews the plan with you, applies it and follows
32
32
  it; other projects keep their version.
33
33
 
34
+ ## Upgrading to 0.29.0
35
+
36
+ Upgrade every `amem` that shares your memory home together with the hub: the hub now exact-pins `@staix/agent-mem` 0.20.0, and an `amem` 0.19.0 hook or CLI cannot drain 0.20.0's new hook spool records and can retire them after seven days (#1131). Formats, shards, candidates and indexes are unchanged.
37
+
38
+ A hub-sandboxed peer (Pi, the local worker) can no longer commit, branch, tag, stash, switch, merge, rebase or reset, nor `git add` in a linked worktree: the sandbox refuses writes to refs, HEAD, packed-refs and worktree admin files (#1123). Instead the hub makes a task worktree for its write work (`implement`, `bulk_edit`) at accept and commits and pushes it at `hub_task_done` (#1115, #1129). Where the hub cannot make one (no git repository, no first commit, or a repository config that names a helper, filter or include), that work is not offered to Pi or the local worker, and an assignment to them is refused with the cause; run `git init` and make a first commit, or give the work to Claude, Codex or Kimi.
39
+
40
+ `ahub status --full` shows control-write timing counters, and a slow control write or a blocked event loop leaves a metadata-only record in `hub.log` (#1126). Set `AHUB_CONTROL_WRITE_TIMING=1` for client-side timing on stderr.
41
+
42
+ ## Upgrading to 0.28.0
43
+
44
+ `ahub init` now takes flags for the main project settings (#1100, #1107): `--agents <a,b,...|none>`, `--sdlc off|advise|enforce`, `--coordination advisory|turn-free`, `--memory`/`--no-memory`, `--snapshots`/`--no-snapshots`, `--pi`/`--no-pi`, `--specs`/`--no-specs`, `--spec-review off|report|require` and, for a new project only, `--autonomy inherit|off`. On an existing project only a flag you give changes anything, `--dry-run` shows each change as old -> new, and an invalid value is refused with the allowed values. Run at a terminal on a new project with none of these flags and no `--yes`, `ahub init` asks each setting with its default in brackets and shows the plan before it writes anything; `--yes` takes the defaults, and a pipe, a peer or `--dry-run` is never asked (#1110). Design specs are new and on by default (#1097, #1102, #1107). A design spec is a Markdown file with `id` and `type` frontmatter. `ahub specs list|check|covers` reads the specs and checks their links and freshness without a daemon, agents get the read-only `hub_specs`, and `ahub status` adds a specs line when the project has specs. `ahub specs stamp` is your own record, made at a terminal, that you reviewed a spec against HEAD (see [design spec inspection](operations.md#design-spec-inspection)). The next `ahub init` in an existing project adds an `AGENT_HUB_SPECS` block to `AGENTS.md` unless the project set `specs.enabled` to false; `ahub init --no-specs` turns specs off and removes the block.
45
+
46
+ Agents now read the task board in pages: `hub_task_list` returns 50 compact rows by default with a `next` cursor, and `ids` or `detail: true` give full rows (#1083); `ahub board` from your terminal is unchanged. A task can declare `needs_network` or `needs_worktree` (`ahub task propose --needs-network|--needs-worktree`), and such work is no longer offered to a peer that cannot meet the need, such as a sandboxed Pi or local worker (#1081). An urgent task can take back offers its owner never accepted when they are all that fill its WIP limit (#1080). When the reply to a `hub_task_propose` or `hub_review` is lost, the call reports `outcome: unknown` with a `request_id` to reuse, so a retry does not file a duplicate (#1078). A busy agent can settle its own `needs_review` delivery with `hub_delivery_resolve`, and `hub_inbox` reads new messages queued behind a held one (#1079). `ahub queue discard --peer <id>` without `--state` now also clears the peer's unsettled failed deliveries, `--state failed` names failed deliveries alone, and `ahub queue list` hides rows a resolution already settled unless `--all` (#1103). Crash-recovery notices in `ahub status` clear once their condition is gone (#1082), and a start refused during a recovery hold says why and what to run (#1104). A backlog card with a planned start is not offered before that date (#1092). Agents the hub starts no longer inherit the Orca pane's routing (#1094), and their Orca terminals open in the project root's worktree (#1105). A Jcode session that recovery restarted headless gets its TUI back with `ahub stop jcode`, then `ahub jcode` (#1093). `ahub setup` keeps plugin rows it did not install (#1073, #1074). The reviewer checklist in the shipped profiles now asks that error messages state the cause and the next step; a profile override or a pack replaces that checklist and does not get the new item (#1109). Codex 0.166.0 is now a verified reviewer version (#1071), and the hub exact-pins `@staix/agent-mem` 0.19.0, which shares its index and hook records with 0.18.0 (#1072). As for 0.27.0, update the global CLI and the plugin yourself afterwards with `bun add -g @staix/agent-hub@0.28.0`, then `ahub setup`, and restart Claude sessions so they load the 0.28.0 plugin, which adds `hub_specs` and `hub_delivery_resolve`.
47
+
34
48
  ## Upgrading to 0.27.0
35
49
 
36
50
  Nothing changes until you use something new. Release consent can now come from your explicit instruction to the conductor: say in its own session which release to ship (for example "release 0.27.0"), and the configured conductor runs `ahub sdlc authorize-release <version>` for you, under the same admission as `sdlc merge`; you can still run it yourself. The approval keeps every binding it had (exact commit, version, origin and fresh docs; a restart revokes it), its record names who ran it, and a hub message, task text, review note, file or another peer's relay never counts as the instruction (#1063, #1066). Research experiments gain a read and a write: `ahub study experiments` and `hub_experiment_show` read a campaign's attempts, one attempt or one comparison (#602), and an owner records protocols, plans, attempts, events and receipts with `hub_experiment_record`, refused during a private turn (#615). Concurrent starts of one project no longer fail on a `runtime.json` replaced while it was being read (#616). As for 0.26.0, update the global CLI and the plugin yourself afterwards: `bun add -g @staix/agent-hub@0.27.0`, then `ahub setup`.
@@ -157,7 +171,7 @@ The restarted Claude planner gets the release-train tools (`hub_release_new`, `h
157
171
 
158
172
  Jcode must be 0.98.0 or later, below 1.0.0; 0.94.0 is no longer accepted, so `ahub doctor` fails its Jcode row for an older install and the peer does not start (#529). Every owned jcode process runs with `JCODE_NO_TELEMETRY=1`.
159
173
 
160
- Since 0.12.37 the hub opens a split memory home through its shards (#534); the current exact `@staix/agent-mem` pin is 0.18.0. Before anyone runs `amem migrate --shards` on a memory home, every hub using that home must run 0.12.37 or later: an older hub does not see the split and keeps writing the old layout. `ahub memory profile-check` is split-aware and compares each hub with its own project's shard (#536). See [memory profiles and rollout](memory.md#selected-memory-profile).
174
+ Since 0.12.37 the hub opens a split memory home through its shards (#534); the current exact `@staix/agent-mem` pin is 0.20.0. Before anyone runs `amem migrate --shards` on a memory home, every hub using that home must run 0.12.37 or later: an older hub does not see the split and keeps writing the old layout. `ahub memory profile-check` is split-aware and compares each hub with its own project's shard (#536). See [memory profiles and rollout](memory.md#selected-memory-profile).
161
175
 
162
176
  From 0.12.30 or earlier, existing Jcode project MCP entries need one refresh before conductor or dashboard starts. Once per project, the person runs:
163
177
 
@@ -181,6 +195,8 @@ ahub console # use in another terminal if up used --no-console; Tab opens
181
195
  ahub kimi # Kimi in its own pull-only TUI; --mode headless for the hub-driven peer
182
196
  ```
183
197
 
198
+ On a new project at a terminal, `ahub init` first asks each main setting (profile, agents, specs, SDLC mode, coordination, memory, snapshots and the rest) with its default in brackets. Enter keeps a default, and nothing is written until you answer y to the plan. `ahub init --yes` takes every default without asking.
199
+
184
200
  `ahub up` also starts a default set of agents, `claude` and `codex` unless you choose others, each in a terminal the hub opens (`terminal.open`, or Orca). It prints one line per agent, and an agent it cannot open is named with the command to run yourself (`ahub claude`, `ahub codex`). Choose the set once with `ahub setup --agents kimi,codex` (or answer the question in `ahub setup`), change one project with `ahub settings set start.agents <a,b>`, and skip it for a run with `ahub up --no-agents`. Without a terminal provider nothing is opened and the hub keeps running; start them by hand:
185
201
 
186
202
  ```bash
@@ -230,7 +246,7 @@ More: `ahub help`. How it is built and why: [`docs/specs/2026-09-19-agent-hub-de
230
246
 
231
247
  ## Shared memory
232
248
 
233
- The hub exact-pins the first-party `@staix/agent-mem` 0.18.0 core, including deterministic capture; no claude-mem worker is needed. Existing shared-v1 memory remains the default; upgrading does not migrate it. Managed agents use screened `hub_memory_search`, `hub_memory_show` and `hub_memory_graph` tools, or propose a correction with `hub_memory_correct`; only a person accepts candidates through the supported `amem review` path. Since agent-mem 0.7.0, `amem review` also accepts candidates on a selected generation.
249
+ The hub exact-pins the first-party `@staix/agent-mem` 0.20.0 core, including deterministic capture; no claude-mem worker is needed. Existing shared-v1 memory remains the default; upgrading does not migrate it. Managed agents use screened `hub_memory_search`, `hub_memory_show` and `hub_memory_graph` tools, or propose a correction with `hub_memory_correct`; only a person accepts candidates through the supported `amem review` path. Since agent-mem 0.7.0, `amem review` also accepts candidates on a selected generation.
234
250
 
235
251
  Before any real generation migration, upgrade every hub sharing that memory home to 0.12.30 or later and check its authenticated binding with `ahub memory profile-check` in each custom registry. Ready bindings prove compatibility, not an atomic live cutover: quiesce consumers and close their handles before migrating, then reopen through the supported session-preserving lifecycle. Invalid or changed profiles disable native memory without falling back to legacy writes; the bus and board continue. See [memory operations](operations.md#hub-memory) for the remaining rollout gates.
236
252