@staix/agent-hub 0.18.0 → 0.19.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 (73) hide show
  1. package/CHANGELOG.md +19 -0
  2. package/README.md +4 -4
  3. package/docs/agent-notes/README.md +6 -0
  4. package/docs/agent-notes/adapters.md +10 -8
  5. package/docs/agent-notes/daemon.md +5 -2
  6. package/docs/agent-notes/local-worker.md +2 -1
  7. package/docs/agent-notes/tasks.md +4 -1
  8. package/docs/agent-notes/tests.md +6 -3
  9. package/docs/dogfood.md +9 -3
  10. package/docs/memory.md +1 -1
  11. package/docs/operations.md +66 -28
  12. package/docs/quickstart.md +9 -5
  13. package/docs/security.md +22 -13
  14. package/docs/smoke.md +3 -3
  15. package/docs/specs/2026-09-19-agent-hub-design.md +16 -5
  16. package/docs/verification/2026-10-25-remote-docker-transport.md +10 -0
  17. package/docs/verification/2026-10-26-agy-1.4.7.md +69 -0
  18. package/docs/verification/2026-10-26-claude-implementer-2.1.310.md +69 -0
  19. package/docs/verification/2026-10-26-headless-claude-shell-context.md +62 -0
  20. package/docs/verification/2026-10-26-jcode-0.99.1.md +49 -0
  21. package/docs/verification/2026-10-26-kimi-232-reviewer-compat.md +53 -0
  22. package/docs/verification/2026-10-26-pi-140-compat.md +41 -0
  23. package/docs/verification/2026-10-26-qwen-0261-acp.md +60 -0
  24. package/docs/verification/2026-10-27-0.19.0.md +86 -0
  25. package/docs/verification/claude-permission-transport-native.py +187 -0
  26. package/docs/verification/claude-shell-wrapper-host.ts +20 -0
  27. package/docs/verification/claude-shell-wrapper-native.py +145 -0
  28. package/docs/verification/pi-140-model-free.ts +57 -0
  29. package/docs/verification/pi-140-native.mjs +21 -0
  30. package/docs/verified.json +14 -14
  31. package/package.json +2 -2
  32. package/plugins/agent-hub/.claude-plugin/plugin.json +1 -1
  33. package/plugins/agent-hub/server.js +273 -68
  34. package/src/adapters/agy/entry.ts +1 -1
  35. package/src/adapters/claude-channel.ts +7 -3
  36. package/src/adapters/claude-implementer.ts +5 -3
  37. package/src/adapters/compat.ts +32 -18
  38. package/src/adapters/conformance/fakes/pi-rpc.ts +1 -0
  39. package/src/adapters/definitions/builtin-metadata.ts +2 -2
  40. package/src/adapters/definitions/native-factories.ts +7 -2
  41. package/src/adapters/definitions/reviewer-variants.ts +2 -2
  42. package/src/adapters/pi.ts +39 -3
  43. package/src/adapters/qwen-dual.ts +25 -0
  44. package/src/adapters/registry-types.ts +6 -2
  45. package/src/adapters/registry.ts +1 -0
  46. package/src/cli/agents.ts +69 -7
  47. package/src/cli/claude-shell-wrapper.ts +54 -0
  48. package/src/cli/main.ts +30 -24
  49. package/src/cli/output.ts +4 -0
  50. package/src/hub/agent-drift.ts +61 -0
  51. package/src/hub/board.ts +14 -2
  52. package/src/hub/check-runner.ts +148 -18
  53. package/src/hub/claude-shell-wrapper.ts +349 -0
  54. package/src/hub/config-edit.ts +2 -2
  55. package/src/hub/config-tier.ts +2 -2
  56. package/src/hub/daemon.ts +197 -35
  57. package/src/hub/host-executable.ts +20 -1
  58. package/src/hub/instances.ts +18 -2
  59. package/src/hub/lifecycle.ts +1 -1
  60. package/src/hub/operational-authority.ts +7 -0
  61. package/src/hub/operational-view.ts +6 -10
  62. package/src/hub/read-only-shell-proof.ts +7 -6
  63. package/src/hub/review-scope.ts +13 -0
  64. package/src/hub/sdlc-metrics.ts +6 -4
  65. package/src/hub/task-shell-command.ts +85 -0
  66. package/src/hub/task-shell-default.ts +78 -0
  67. package/src/hub/task-shell-proof.ts +171 -0
  68. package/src/hub/task-shell-sandbox.ts +61 -0
  69. package/src/hub/tasks.ts +139 -13
  70. package/src/local/deny.ts +91 -11
  71. package/src/local/sandbox.ts +67 -28
  72. package/src/local/shell-environment.ts +10 -0
  73. package/src/ui/index.html +1 -0
@@ -1,6 +1,6 @@
1
1
  # Operations guide
2
2
 
3
- This guide describes ahub 0.18.0 and control protocol 16. Live verification
3
+ This guide describes ahub 0.19.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
@@ -638,6 +638,11 @@ Pi starts in its TUI by default; `ahub pi --headless`, or the start mode
638
638
  setting (`ahub settings set peers.pi.start_mode headless`), asks for the
639
639
  background peer instead. The project uses its existing OmniRoute connection for DGX;
640
640
  `pi.dgx_coding` and `pi.dgx_fast` select the gateway's configured model IDs.
641
+ Every Pi start (headless, TUI, console or auto start) first runs the configured
642
+ `pi.cmd` with `--version` (#847). Pi 1.4.0 is verified
643
+ ([model-free receipt](verification/2026-10-26-pi-140-compat.md)); another
644
+ version, or one that cannot be read, is refused before Pi starts, unless the
645
+ person trusts that exact version with `ahub agents trust pi <version>`.
641
646
 
642
647
  ```bash
643
648
  ahub models setup # prepare bounded Qwen3.5 4B MLX in an existing Ollama service
@@ -674,9 +679,9 @@ access. Receipts distinguish exact source/tree/lockfile, platform, exit and
674
679
  uncertain or cancelled outcomes. See [bounded spec and check access](security.md#bounded-task-specs-and-isolated-checks-418)
675
680
  for the authorization and resource bounds.
676
681
 
677
- Remote runner configuration (#793) is permitted only in the person's untracked `config.local.json`: select `checks.runner = "remote"` and supply `checks.remote.host`, `checks.remote.image` (immutable SHA-256 image reference) and `checks.remote.root` (reserved absolute metadata for the later dependency-preparation slice). No remote destination belongs in tracked project files. The runner uses the person's host SSH capability (including the hub's authentication agent socket) in BatchMode, with TTY allocation, agent/X11 forwarding, configured forwardings and local SSH commands disabled, and fixed Docker argv; it never installs host tools, mounts host paths or falls back to this Mac. A failed SSH, Docker, image or hardened network-none/transfer probe marks sandboxed test capability unavailable with a generic remote-runner reason. A successful platform line names only Linux architecture and numeric Docker version.
682
+ Remote runner configuration (#793) is permitted only in the person's untracked `config.local.json`: select `checks.runner = "remote"` and supply `checks.remote.host`, `checks.remote.image` (immutable SHA-256 image reference) and `checks.remote.root` (reserved absolute metadata, unused by any current command). No remote destination belongs in tracked project files. The runner uses the person's host SSH capability (including the hub's authentication agent socket) in BatchMode, with TTY allocation, agent/X11 forwarding, configured forwardings and local SSH commands disabled, and fixed Docker argv; it never installs host tools, mounts host paths or falls back to this Mac. A failed SSH, Docker, image or hardened network-none/transfer probe marks sandboxed test capability unavailable with a generic remote-runner reason. A successful platform line names only Linux architecture and numeric Docker version.
678
683
 
679
- The remote source is the recorded Git commit, streamed into a hardened non-root container. Mac dependencies are excluded; the project-scoped read-only named dependency volume is currently an unprepared placeholder, so the runner reports `remote dependencies unprepared` and withholds tests capability until the separate admitted preparation slice lands, even when its transport probe succeeds. That future step must key the volume by lockfile and Bun version; the run itself performs no install. Remote `root` is validated but unused in this transport-only slice because no host path operation is needed. The bounded [local Docker transport receipt](verification/2026-10-25-remote-docker-transport.md) records the extraction and cleanup leg. Actual remote-host opt-in, SSH and pi's live check receipt remain unverified follow-up work. Existing local Apple-container behavior remains available by explicit selection.
684
+ The remote source is the recorded Git commit, streamed into a hardened non-root container. Mac dependencies are excluded. Before a check, the runner proves or prepares a Docker volume named from the commit's lockfile hash, its manifests and install configuration (`package.json`, `bunfig.toml`, `.npmrc`, `patches/`), the Bun version and architecture of the pinned image, and its digest (#793b): the first run for a new lockfile spends extra time and is the only moment a container has a network, running exactly `bun install --frozen-lockfile` into the volume. Later runs find the volume, verify it read-only offline and install nothing. The check itself never installs, has no network and mounts the volume read-only. The ten-minute run budget includes preparation, so a very large first install can time out and is simply retried. A commit whose root or workspace `package.json` declares an install lifecycle script (`preinstall`, `install`, `postinstall`, `prepublish`, `preprepare`, `prepare`, `postprepare`) is refused the same way, since such a script can read source the volume key does not cover. If the volume cannot be proven the run ends with `remote dependencies unavailable`; there is no writable shared cache and no fallback to this Mac. The newest three prepared volumes per project are kept and older ones removed after a verified run. Remote `root` is validated but unused because no host path operation is needed. The bounded [local Docker transport receipt](verification/2026-10-25-remote-docker-transport.md) records the extraction, cleanup and dependency-volume legs. Actual remote-host opt-in, SSH and pi's live check receipt remain unverified follow-up work. Existing local Apple-container behavior remains available by explicit selection.
680
685
 
681
686
  Retention bounds inactive valid check receipts to 200 records and the newest 20
682
687
  full outputs, excluding active runs. Full output is also consumed by the owner
@@ -900,12 +905,16 @@ the OS temp cleanup), and nothing else; the shared temp dirs are closed.
900
905
  Some names stay closed to those commands, and to Pi's, wherever they may
901
906
  otherwise write: anything named `.git` at any depth, the `config` and `hooks`
902
907
  directly under a `.git`, `commondir` below it, and `.agenthub` anywhere in the
903
- project. A submodule's git directory under `.git/modules/` is not covered yet
904
- (#281). A command can therefore not create, rename or remove a `.git`:
905
- `git init`, `git clone` and `git worktree add` do not run inside the sandbox,
906
- and neither do `git worktree remove`, `git worktree prune` of a stale record,
907
- `git clean -ffd` over a nested repository or `rm -rf` of a directory that
908
- holds a `.git`. `git add`, `commit`, `stash`, `checkout` and `gc` work, and
908
+ project, plus `config.worktree` beside each such `config`. A submodule's git
909
+ directory gets the same `config` and `hooks` refusal under every `modules/`
910
+ tree the project can reach: the common dir's, the worktree's own and every
911
+ linked worktree's (#281, #518). Nothing else may be written under such a tree
912
+ outside a discovered submodule git dir (#865). A command can therefore not
913
+ create, rename or remove a `.git`: `git init`, `git clone` and
914
+ `git worktree add` do not run inside the sandbox, and neither do
915
+ `git worktree remove`, `git worktree prune` of a stale record,
916
+ `git submodule update --init`, `git clean -ffd` over a nested repository or
917
+ `rm -rf` of a directory that holds a `.git`. `git add`, `commit`, `stash`, `checkout` and `gc` work, and
909
918
  `.gitignore` and `.github/` stay writable; a commit that your git configuration
910
919
  signs with a key under `~/.ssh` or `~/.gnupg` fails, because the sandbox cannot
911
920
  read those directories. Run a refused command yourself, in a terminal.
@@ -1354,7 +1363,7 @@ ahub memory forget 1a2b3c4d # a person's; also promote, demote, sup
1354
1363
 
1355
1364
  `"memory": { "native": false }` in `.agenthub/config.json` turns it off for a project.
1356
1365
 
1357
- The exact-pinned `@staix/agent-mem` 0.8.0 resolves `AMEM_HOME`, then `$AGENTHUB_HOME/memory`, then `~/.agenthub/memory`. An absent `M/profile.json` selects shared v1; an explicit generation selects `M/generations/G` with the package's readonly legacy bridge. All daemon memory consumers and the hub memory/goal CLI use that selected profile. Invalid metadata or a missing selected generation makes native memory unavailable while the bus and board continue; there is no fallback to legacy writes. Inspect the daemon's memory binding/availability before interpreting missing memory as an empty store.
1366
+ The exact-pinned `@staix/agent-mem` 0.10.0 resolves `AMEM_HOME`, then `$AGENTHUB_HOME/memory`, then `~/.agenthub/memory`. An absent `M/profile.json` selects shared v1; an explicit generation selects `M/generations/G` with the package's readonly legacy bridge. All daemon memory consumers and the hub memory/goal CLI use that selected profile. Invalid metadata or a missing selected generation makes native memory unavailable while the bus and board continue; there is no fallback to legacy writes. Inspect the daemon's memory binding/availability before interpreting missing memory as an empty store.
1358
1367
 
1359
1368
  The daemon checks its selector at hub operation entry and before candidate/counter writes. It does not hot-switch handles or provide an atomic fence against selection changes during an in-flight core call. Quiesce consumers and close their handles before migration or selection changes; reopen through the supported session-preserving lifecycle afterward. Candidate/counter storage remains `M/candidates.db`, independent of `G`; since agent-mem 0.7.0 `amem review` also lists and accepts candidates on a selected generation. Every running hub using this home needs an authenticated receipt: a matching ready binding, or a generation-capable unbound receipt (`disabled` when native memory is off, `unavailable` when opening failed) proving it holds no memory handle. An unbound daemon and zero running hubs are quiesced for memory; a stale bound handle, unreadable registry or unknown runtime is not ready. Existing JSON memory/goal results remain unchanged; the additive binding receipt belongs to daemon status, profile-check and the decay object. This source integration does not authorize installing a new runtime, migrating real memory, restarting sessions or claiming real-machine sync/capture receipts. Migration/deactivation and their limits belong to [agent-mem](https://github.com/STAIxBWLB/agent-mem); see [the hub profile contract](memory.md#selected-memory-profile).
1360
1369
 
@@ -1972,7 +1981,7 @@ Instances, the review and approval blocks and the lifecycle can be edited withou
1972
1981
 
1973
1982
  1. **Registry settings** (everything above) are unchanged.
1974
1983
  2. **Structural keys**, editable in the console and in a dashboard settings session:
1975
- - `instances.<id>` with `kind`, `roles`, `start_mode`, `review_mode`, `readOnly`, `worktree` and `permission_mode`, in `config.local.json`;
1984
+ - `instances.<id>` with `kind`, `roles`, `start_mode`, `review_mode`, `readOnly`, `worktree` and `permission_mode`, in `config.local.json` (`review_scope` is tier 3 and not editable here);
1976
1985
  - `review.adaptive`, `review.min_reviews`, `review.pool`, `review.rerotate`, `review.same_family`, `snapshots.keep`, `approvals.timeout_s` and `context.gate`, in `config.local.json`;
1977
1986
  - `mode` and `required_checks` in `.agenthub/sdlc.toml`. Only those two lines change: comments and every other table stay as written, and a layout the editor cannot change faithfully (a key repeated, or one inside a multi-line string) is refused with its own reason.
1978
1987
 
@@ -2391,6 +2400,15 @@ moves, reviews and assignments are recorded at `fyi` weight (no turn), at most
2391
2400
  once per planner per ten minutes. A newer notice replaces an unsettled copy, and
2392
2401
  the hub's own bookkeeping tells nobody.
2393
2402
 
2403
+ A task write also wakes the task's rostered planners when it frees capacity or
2404
+ releases backlog work (#785): a lower WIP for the owner or reserved peer adds a
2405
+ `capacity_released: <peer> WIP <n>; reserved ready #<id>|none` line, and an
2406
+ approval that leaves a backlog card with no unresolved dependency adds
2407
+ `backlog_ready: #<id>`. The lines go in one `Planner work ready:` task notice
2408
+ per planner, ids only; a newer one merges and replaces the unsettled copy, which
2409
+ survives a restart. Nothing is pulled automatically: the planner reads the
2410
+ board and decides.
2411
+
2394
2412
  ## Resetting a project's hub
2395
2413
 
2396
2414
  `ahub reset` returns a project's hub to a clean state. Without `--yes` it is a
@@ -2509,7 +2527,7 @@ Rows without a live process are stale registrations; forget them with
2509
2527
 
2510
2528
  ## Agent CLI versions
2511
2529
 
2512
- 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). Qwen has two entries: `qwen` for headless ACP and `qwen-dual` for the dual-output TUI (#286).
2530
+ 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, the headless Claude implementer, Jcode, agy, Qwen and Pi (#691, #847). Qwen has two entries: `qwen` for headless ACP and `qwen-dual` for the dual-output TUI (#286).
2513
2531
 
2514
2532
  - `ahub agents [--json]` lists each gated CLI with its version, verdict and verified versions.
2515
2533
  - **verified:** a receipt exists.
@@ -2521,7 +2539,9 @@ Agent CLIs update themselves often, and a hub release cannot follow every one. O
2521
2539
  - 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.
2522
2540
  - What trust cannot prove is printed when you trust.
2523
2541
  - `ahub doctor` shows one line for each installed CLI that is not on a verified version.
2524
- - 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, agy and `qwen-dual` are not watched.
2542
+ - A daily workflow (`.github/workflows/agent-compat.yml`) runs `scripts/agent-compat-watch.ts`, which reads the latest published version of every gated CLI that has a public source: the npm packages `@anthropic-ai/claude-code`, `@openai/codex`, `@moonshot-ai/kimi-code`, `@qwen-code/qwen-code` (for both `qwen` and `qwen-dual`) and `@earendil-works/pi-coding-agent` (for `pi`, since #847 gave it a COMPAT entry), and the GitHub releases of Jcode. agy has no public source, so its row reads "no public source; local drift only" and is never a failure. Every COMPAT entry names the receipt to repeat (`receipt`), and a test requires that file to exist and the entry to have a watcher source. A version that cannot be read reads as unknown, not as a crash. The workflow keeps one `agent-compat` issue current while a source is ahead and closes it when none is.
2543
+ - **Maintenance loop (#843):** the daily upstream watch is the trigger; `ahub doctor` is the local drift notice for versions installed on a machine, and the hub itself tells the planner (below). The planner files one receipt task per (id, version): the implementer repeats the procedure in the entry's last `docs/verification/` receipt, writes a new one, and adds the version to `verified` in that same PR; the next release ships the bump. A person's `ahub agents trust` stays the stopgap that keeps a machine running meanwhile, and the person may revoke it once the bump lands.
2544
+ - **Local drift notice (#843 AC3):** at hub start and once a day the daemon reads each gated CLI's installed version with the probe `ahub agents` uses (no network, no install). For every installed CLI on a version that is not `verified` (in-line, trusted or refused) the effective planner gets one notice per (id, version): the id, the version, the state and the last receipt path, nothing the CLI printed. A CLI that is not installed, or whose version cannot be read, is not drift. The marker is kept per (id, version, planner) in `hub.db` (`agent_drift_notices`), so a restart repeats nothing, a planner whose notice could not be queued (or who joins later) is retried alone without telling the others again, and a new version is a new notice; with no rostered planner nothing is recorded. `ahub status` shows one `agent CLI drift` attention line per drifted id until the installed version is verified.
2525
2545
 
2526
2546
  ## Upgrade and crash recovery
2527
2547
 
@@ -2542,7 +2562,7 @@ section is what they run.
2542
2562
 
2543
2563
  Upgrade running projects with the target release's own coordinator. It accepts
2544
2564
  a running source on control protocol 9 (0.6.x), 10 (0.7.0 through 0.12.0),
2545
- 11 (0.12.1 and 0.12.2), 12 (0.12.3), 13 (0.12.4 through 0.12.15), 14 (0.12.16), 15 (0.12.17 through 0.12.19) or 16 (0.12.20 through 0.18.0), and only
2565
+ 11 (0.12.1 and 0.12.2), 12 (0.12.3), 13 (0.12.4 through 0.12.15), 14 (0.12.16), 15 (0.12.17 through 0.12.19) or 16 (0.12.20 through 0.19.0), and only
2546
2566
  a target on its own protocol, so the target's coordinator fits every supported
2547
2567
  source and carries every recovery fix released up to it. Protocol 8 and older
2548
2568
  (0.5.x and earlier) are refused as `manual-bootstrap-required`. Run from the
@@ -2556,7 +2576,7 @@ bunx --package @staix/agent-hub@0.12.43 ahub upgrade --to 0.12.43 --yes
2556
2576
  | Running now | Coordinator to use |
2557
2577
  | --- | --- |
2558
2578
  | 0.6.x (protocol 9) | the target's, through `bunx` as above |
2559
- | 0.7.0 through 0.12.0 (protocol 10), 0.12.1 and 0.12.2 (protocol 11), 0.12.3 (protocol 12), 0.12.4 through 0.12.15 (protocol 13), 0.12.16 (protocol 14), 0.12.17 through 0.12.19 (protocol 15), 0.12.20 through 0.18.0 (protocol 16) | the target's, through `bunx` as above |
2579
+ | 0.7.0 through 0.12.0 (protocol 10), 0.12.1 and 0.12.2 (protocol 11), 0.12.3 (protocol 12), 0.12.4 through 0.12.15 (protocol 13), 0.12.16 (protocol 14), 0.12.17 through 0.12.19 (protocol 15), 0.12.20 through 0.19.0 (protocol 16) | the target's, through `bunx` as above |
2560
2580
  | any supported source, with the installed CLI already at the target | `ahub upgrade` below, which is the same coordinator |
2561
2581
  | 0.5.x or earlier (protocol 8 and older) | not supported: bootstrap by hand with the matching CLI |
2562
2582
 
@@ -3453,9 +3473,15 @@ unreadable result runs the main gate again and refuses release. A manual
3453
3473
  `prepare_bundle` dispatch builds reviewable plugin assets without publishing;
3454
3474
  it is never accepted as full-gate evidence. A `timing_runs=20` dispatch runs only the daemon, PII screen and ACP
3455
3475
  test files twenty times on macOS, in its own concurrency group; it does not replace the full gate. Each PR has its own CI concurrency group, so PRs run side by side; within a PR a newer pending run replaces the older pending one and an admitted run is never cancelled; main pushes and dispatches keep queueing every run. Two groups admitting the same tree at once may run it twice, never a false green (#609). `scripts/check.sh --changed [<base>]` runs every non-test step but only the affected test files, and the full suite when it cannot tell; the full gate stays CI's (#610).
3456
- `scripts/check.sh` stops a suite still running after `AHUB_CHECK_HANG_S` seconds
3457
- (960 by default); hosted CI sets 1080 on Linux inside a 25-minute job limit and 1440
3458
- on macOS inside a 30-minute one (#378, #593, #752).
3476
+ `scripts/check.sh` runs the suite under `scripts/hang-watch.sh`, which stops it when
3477
+ no test result line (`(pass)`, `(fail)`, `(skip)` or `(todo)`) and no test file header
3478
+ has appeared for `AHUB_CHECK_QUIET_S` seconds (300 by default, set to 300 in CI), so a
3479
+ slow suite that is still passing tests is never cut. `scripts/progress-tee.sh` passes bun's
3480
+ output through unchanged and appends one byte to a progress file for each of those lines;
3481
+ the watchdog polls that file's size. On a stop it prints where the run is and the process
3482
+ table below it first. `AHUB_CHECK_HANG_S` (960 by default) is only the backstop for a run
3483
+ that keeps printing results while hung; hosted CI sets 1500 on Linux inside a 35-minute
3484
+ job limit and 1800 on macOS inside a 40-minute one (#378, #593, #752, #516).
3459
3485
  The `closing-refs` workflow fails a PR whose title, body or commit messages would make
3460
3486
  GitHub close an issue the text only mentions (`Refs #N`, a negated or qualified keyword);
3461
3487
  an issue linked only in the sidebar passes (#760).
@@ -3715,13 +3741,13 @@ selected roster and existing capabilities; the TUI is always a person's start.
3715
3741
  `ahub qwen --mode tui` runs `qwen --input-file <in> --json-file <out> --mcp-config <hub server> --approval-mode default` in the terminal you ran it from. The daemon prepares the two files (mode 0600, under `<state dir>/qwen/qwen-dual/`) and returns the launch; the hub then appends one `submit` line per delivery and tails the event file from a saved offset. It needs a Qwen the dual-output compat entry verifies (`ahub agents` shows `qwen-dual`, verified 0.26.1 only; the headless `qwen` entry is separate and unchanged) and refuses another version before it creates a file. The TUI is explicit only: `ahub qwen` without `--mode` is still headless, Qwen is not a start-mode peer, and the hub never opens this terminal by itself. The TUI start is refused for an extra Qwen instance, off roster, outside a terminal, and while a headless Qwen runs (`ahub stop qwen` first); a headless start is refused while the TUI holds its terminal.
3716
3742
 
3717
3743
  - What the hub does: a delivery becomes a `submit`; the last assistant message of the turn (an `assistant` event whose `stop_reason` is not `tool_use`, then `message_stop`) is shared as the answer; busy and idle follow the events. A `can_use_tool` request goes to the console approval path (`ahub permit`, the dashboard), and the hub's own tools are approved only by their exact `mcp__agent-hub__<tool>` name. If you answer a prompt in the Qwen terminal first, the hub's pending approval is withdrawn.
3718
- - **Limit 1, a deny ends the turn without an answer.** Qwen emits nothing when the hub denies: it cancels the whole turn. The hub ends the turn itself, shares no answer, settles the delivery as `completed` (a deny is a known outcome, and a `needs_review` would hold every later message to Qwen until a person resolved it) and says so in a notice. A turn whose outcome is not known (Qwen exited, an Esc followed by a new prompt, an event file that became unreadable) is the one that settles `needs_review`.
3744
+ - **Limit 1, a deny ends the turn without an answer.** Qwen emits nothing when the hub denies: it cancels the whole turn. The hub ends the turn itself, shares no answer, settles the delivery as `completed` (a deny is a known outcome, and a `needs_review` would hold every later message to Qwen until a person resolved it) and says so in a notice. A turn whose outcome is not known (Qwen exited, an Esc followed by a new prompt, an event file that became unreadable) is the one that settles `needs_review`. Qwen's own notice that a background shell finished (a `user` event that is wholly one or more `<task-notification>` envelopes) is not a new prompt: it ends no hub turn and leaves an idle peer idle (#517).
3719
3745
  - **Limit 2, the hub cannot stop a Qwen turn.** The input protocol has only `submit` and `confirmation_response`, and a signal does not stop a running turn. The inactivity watchdog therefore never cancels: after `watchdog_ms` of silence it marks the turn stalled (`ahub status`, the console and the dashboard say "press Esc in the Qwen terminal") and keeps the peer busy. The hub never types into your terminal and never signals Qwen to stop a turn.
3720
3746
  - A Qwen write or shell command that names the hub state directory is refused (see [security](security.md): the input file is a prompt channel).
3721
3747
  - If the hub cannot read the event file the peer is `detached`, shown as offline with a notice, never idle: Qwen itself keeps working, and the hub reattaches when the file is readable again.
3722
3748
  - Restart: if the hub restarts while your Qwen keeps running, it reads on from its saved offset (no event twice, no launch). If Qwen is gone, `ahub qwen --mode tui` starts `qwen -r <session id>` on the same two files; the earlier submits are not replayed and the recorded answers are not shared again. Recovery never restores this terminal as a headless worker, and `ahub stop qwen` refuses: end Qwen in its own terminal.
3723
3749
 
3724
- 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
3750
+ 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)) and 0.26.1 ([0.26.1 receipt](verification/2026-10-26-qwen-0261-acp.md)). A CLI version label alone
3725
3751
  is insufficient: startup verifies its native identity/version and advertised mode
3726
3752
  ids before any prompt. The earlier local installation reported CLI 0.25.0 while
3727
3753
  its package/ACP source said 0.24.1; that was not a ready identity. The 0.26.0 receipt
@@ -3758,11 +3784,11 @@ TUI input may interleave with an ACP turn; there is no single-writer guard. Clos
3758
3784
  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.
3759
3785
 
3760
3786
 
3761
- 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.
3787
+ 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, 0.98.1 and 0.99.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.
3762
3788
 
3763
3789
  ## Optional Antigravity (agy) peer
3764
3790
 
3765
- 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.
3791
+ 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 and 1.4.7 are the verified versions ([1.4.7 receipt](verification/2026-10-26-agy-1.4.7.md)), 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.
3766
3792
 
3767
3793
  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`.
3768
3794
 
@@ -4339,11 +4365,21 @@ A headless Claude implementer (#525-4) is declared separately:
4339
4365
  "claude-impl": { "kind": "claude", "roles": ["implementer"], "start_mode": "headless", "worktree": ".worktrees/claude-impl", "permission_mode": "ask" }
4340
4366
  ```
4341
4367
 
4368
+ A headless Claude implementer may name its model (#844): `"model": "claude-sonnet-5-5"` on its declaration passes `--model <value>` on every native turn, resumed ones included. It is one bounded word (letters, digits and `._:/@+-`, up to 128 bytes, no spaces and no leading dash) and is accepted only on a headless implementer, only from the untracked `config.local.json`. A tracked-file declaration is ignored with a reason, and the key is tier 3: the dashboard and a settings session cannot read or change it, only the console or your editor. Unset, the argv is unchanged and Claude uses its own default. `ahub status` shows the model on the instance row as `model source`. Declarations are captured once when the hub daemon starts, so a changed or newly added `model` takes effect after a hub daemon restart (`ahub restart`); restarting only the instance keeps the old model.
4369
+
4342
4370
  Create and register the attached-branch worktree first; the hub never creates it implicitly. `ahub claude --as claude-impl` asks the hub to start the instance and returns. Idle holds no native child. Task deliveries use a persisted UUID per task: first `--session-id`, later `--resume`. Unscoped chat reuses a session only when exactly one task is in progress for the instance; otherwise it is ephemeral. Positive missing-transcript evidence starts fresh with a notice; unknown read evidence retains the id. Reset includes the per-peer session map. A child inbox read refuses a queued batch attributed to another task and leaves it queued for that task's own session.
4343
4371
 
4344
- Both permission modes use native `default`, managed settings/hooks and the instance's strict tools-mode MCP binding. `ask` routes writes and shell calls to the person's approval. `ask-when-needed` may explicitly allow safe project file tools through the facts hook in the execution root; protected configuration, paths outside that worktree and shell/git writes still ask. There is no `acceptEdits` or `never-ask` bypass. A failed hook decides nothing and may be followed by a valid person allow. Prompt failure, timeout, disconnect and a stale child binding never grant permission. Approvals use the existing console and `approvals.timeout_s`; no answer returns deny. A stop holds uncertain delivery until the owned process group is confirmed gone; a failed cleanup keeps the peer busy.
4372
+ Both permission modes use native `default`, managed settings/hooks and the instance's strict tools-mode MCP binding. `ask` routes writes and shell calls to the person's approval. `ask-when-needed` may explicitly allow safe project file tools through the facts hook in the execution root. For Bash, #845 also permits proven read-only commands and the closed task command forms below, through a host-owned execution wrapper. Protected configuration, paths outside that worktree and unproven shell commands still ask. There is no `acceptEdits` or `never-ask` bypass. A failed hook decides nothing and may be followed by a valid person allow. Prompt failure, timeout, disconnect and a stale child binding never grant permission. Approvals use the existing console and `approvals.timeout_s`; no answer returns deny. A stop holds uncertain delivery until the owned process group is confirmed gone; a failed cleanup keeps the peer busy.
4373
+
4374
+ Under #845, automatic Bash grants require a current public task and the instance's registered worktree. The exact task forms are `scripts/check.sh --changed`, `bun run build`, `bun test <explicit test/*.test.ts paths>`, `git add <named paths>`, `git commit -m <message>`, `git push origin <bound branch>` or `git push -u origin <bound branch>`, and `gh pr view|checks <bound branch>`. `gh pr create` requires `--base <default> --head <bound branch> --title <title>` and exactly one `--body <text>` or `--body-file <relative path>`. Operators, expansion, extra flags, force/refspec/delete pushes, another remote/branch, background execution or a model-supplied cwd ask. Enabled publish/destroy/install/external-write policy classes still ask. Unknown default-branch evidence also asks.
4375
+
4376
+ The task's `refs.branch` is authoritative. Otherwise the first eligible task-command request records the current non-default/non-main/master attached branch through `Tasks`, provided no other open task uses it. A host history entry binds the worktree too; restarting preserves it, and a changed branch or root asks rather than rebinding. Each automatic allow contains only the fixed wrapper and one short-lived single-use ticket. The host rechecks the task, child, root, branch and command proof before executing with its fixed environment and absolute executable; native shell cwd/PATH/config variables do not carry over. Unknown, expired, reused or stale tickets run nothing. Read-only and executable code commands (check/test/build) use the existing local-worker Seatbelt profile: writes only in the worktree and own temp, with all project Git metadata read-only, credential/hub-state denial and the configured network policy. These commands ask if macOS sandbox-exec is unavailable. Only closed host Git add/commit/push and gh operations run outside that profile. Push execution pins both source and destination as refs/heads/<bound>:refs/heads/<bound>; configured remote.origin.push/mirror asks. PATH excludes project/worktree directories, gh child Git uses the core hardening config, and body-file uploads use bounded screened snapshots. An older plugin without the wrapper protocol keeps person approval. [The native context receipt](verification/2026-10-26-headless-claude-shell-context.md) records which native legs have actually been observed.
4345
4377
 
4346
- The [native permission receipt](verification/2026-10-23-headless-claude-permissions.md) covers Claude Code 2.1.309 with a deterministic loopback fixture and no real account/model call. It is not sandbox proof or a runtime receipt gate. The real account/model/person interaction remains a separate live leg.
4378
+ Check/test/build cannot write the project Git common/current directory at all, including objects, HEAD/index, refs/logs, other worktrees and either submodule tree. Closed host add/commit reuse core Git hardening, ask with configured filters/external drivers/textconv, and force signing/automatic maintenance off. A global Git LFS installation does not disable push/gh grants; only add/commit keep person approval so LFS conversions are not silently bypassed. Automatic push still does not run pre-push hooks. Config includes and partial clones refuse privileged Git/gh. Their command/paths/current task/branch are re-proved before execution. The host pins the default branch from the remote at startup, using a bounded isolated-config Git symref probe against the captured GitHub repository. Local origin/HEAD changes cannot redefine it. No credentials are supplied to that probe; unavailable/private-remote evidence or an unsupported Git layout asks. local.bash_network "direct" gives code direct network access. A setsid-detached code child can outlive owned-group cleanup while retaining its sandbox and permitted worktree writes.
4379
+
4380
+ Project Git retains the core policy that clears credential.helper and core.askpass; an HTTPS push needing those helpers may fail, rather than importing credentials or relaxing that policy. Task branch/config proof reads are bounded synchronous reads (1.5 seconds per reader); slow or unknown evidence asks. The existing sandbox profile discovery is also synchronous; moving it to an asynchronous inventory proof with a shared deadline remains a follow-up. The nonce is visible in a permission response, so it proves a bounded grant rather than native-dispatch provenance.
4381
+
4382
+ The [native permission receipt](verification/2026-10-23-headless-claude-permissions.md) covers Claude Code 2.1.309, and the [2.1.310 receipt](verification/2026-10-26-claude-implementer-2.1.310.md) repeats it on 2.1.310 (with 2.1.309 as a control), each with a deterministic loopback fixture and no real account/model call. It is not sandbox proof or a runtime receipt gate. The real account/model/person interaction remains a separate live leg.
4347
4383
 
4348
4384
  A headless Claude reviewer is run by the hub instead (#223): declare
4349
4385
  `"claude-review": { "kind": "claude", "roles": ["reviewer"], "start_mode": "headless" }`
@@ -4377,11 +4413,13 @@ From 0.12.37 reviews can rotate across fresh headless Claude and Codex reviewers
4377
4413
 
4378
4414
  `review.same_family` (#820) is `"fallback"` (the default) or `"never"`. With `"fallback"` a reviewer of the owner's family is chosen only when no other family is eligible, and the board records a same-family fallback. With `"never"` routing never selects one, in the pool and in the legacy path: the planner reviews when it holds the reviewer role, is not the owner, is of another family and is not blocked, otherwise the person does (`reviewer = "user"`, recorded as blocked), including when every reviewer is offline: an approval without review never happens under `"never"`. A manual `ahub task assign <id> <peer> --role reviewer` of a same-family peer is refused with `same family as <owner>; review.same_family is never`. `ahub route explain <id>` names the rule in its trace. Use `"never"` when the workspace review gate needs the reviewer to be a different AI than the implementer; a Jcode running a Claude model is still family `jcode`.
4379
4415
 
4416
+ A reviewer has a review scope (#852): `full` (reads the code, runs the affected tests, reads CI) or `code` (reads the code only). The fresh Codex and Kimi reviewers run in a sandbox that cannot run `bun test` or read CI, so they default to `code`; every other reviewer is `full`. A `code` reviewer's review request replaces the check and exact-head CI lines with one: its sandbox cannot run tests or read CI, review the code against the plan, and the merger gates on exact-head CI. Findings are only defects in the reviewed code, docs or tests; a missing check result, CI result, test run or PR/branch identity belongs in `unmet`, never in `findings`. A `code` reviewer's approval and verdicts record `reviewScope: "code"` in the task history. Its `changes_requested` is always recorded and never refused: with at least one structured finding whose disposition is `open` it counts as a rejection as usual; without one it is recorded as evidence-only, which does not count toward `rejections` or escalation, still returns the task to its owner, and sends the conductor one task notice with ids only. The review request therefore asks a `code` reviewer to report every code defect as a structured finding with an id. A person's verdict is never evidence-only. The scope is a fixed property of the definition. To change it for one instance, set `"review_scope": "full"` or `"code"` on that fresh-reviewer instance in the untracked `config.local.json` (a tracked declaration is ignored with a reason; the key is tier 3, so the dashboard and settings sessions cannot read or change it, and a hub daemon restart applies it). `ahub status` shows `review scope` on each reviewer row. A task approved by a `code` reviewer is merged only after exact-head CI is green and no review thread is unresolved, and the merge note names the CI run.
4417
+
4380
4418
  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.
4381
4419
 
4382
4420
  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). 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.
4383
4421
 
4384
- Kimi requires a compat-admitted Kimi Code on the hub's PATH (receipt-verified 2.2.0, 2.3.0 and 2.3.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) and [2.3.1](verification/2026-10-25-kimi-231-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.
4422
+ Kimi requires a compat-admitted Kimi Code on the hub's PATH (receipt-verified 2.2.0, 2.3.0, 2.3.1 and 2.3.2), 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) and [2.3.2](verification/2026-10-26-kimi-232-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.
4385
4423
 
4386
4424
  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.
4387
4425
 
@@ -4462,7 +4500,7 @@ Native launcher connections retry transport timeouts/unclean closes with bounded
4462
4500
 
4463
4501
  ## amem runtime (agent-mem#13 P4)
4464
4502
 
4465
- The hub exact-pins @staix/agent-mem 0.8.0 for recall, curated evidence and local deterministic capture. No runtime call reaches a claude-mem worker; `memory.worker_url` is ignored. The person's `ahub memory import claude-mem` remains explicit and off by default. Existing profile, alias, PII and private-turn fences still apply. `ask --remember` and context checkpoints propose machine-local candidates, never automatic curated evidence. See [memory integration](memory.md#in-process-recall-and-capture-agent-mem13-p4) for changed behavior and bounded-view ceilings.
4503
+ The hub exact-pins @staix/agent-mem 0.10.0 for recall, curated evidence and local deterministic capture. No runtime call reaches a claude-mem worker; `memory.worker_url` is ignored. The person's `ahub memory import claude-mem` remains explicit and off by default. Existing profile, alias, PII and private-turn fences still apply. `ask --remember` and context checkpoints propose machine-local candidates, never automatic curated evidence. See [memory integration](memory.md#in-process-recall-and-capture-agent-mem13-p4) for changed behavior and bounded-view ceilings.
4466
4504
 
4467
4505
  ## Backlog issue identity and notes
4468
4506
 
@@ -4496,7 +4534,7 @@ Optional code work runs only while Memory is open and visible. A cold console co
4496
4534
 
4497
4535
  ### Conductor operations (#812 T2)
4498
4536
 
4499
- The selected project's configured conductor may run the commands below through its ordinary peer identity. Other peers retain the guidance "the person runs it in ahub console or a terminal". A daemon round trip is permitted, but an unreachable hub refuses promptly rather than waiting for the ordinary connection timeout. Refused operations are audited as refused. The daemon checks the authenticated connection, current role and project membership before effects and after asynchronous boundaries. Roster handoffs carry that same authority check through plan admission, owner delivery and each later task; a saved assignment is never rolled back when delivery is interrupted. It records an `agent_cli` admission with the initiating peer before proceeding; audit failure refuses the operation. Admission records are not a unique invocation counter.
4537
+ The selected project's configured conductor may run the commands below through its ordinary peer identity. Other peers retain the guidance "the person runs it in ahub console or a terminal". A missing or dead descriptor refuses promptly. For a live descriptor the connect and local authorization handshakes each keep a 3-second bound; connection unavailability is not presented as proof that the peer lacks the conductor role. Actual authority refusals are audited as refused, while ordinary operation failures retain their original cause. The daemon checks the authenticated connection, current role and project membership before effects and after asynchronous boundaries. Roster handoffs carry that same authority check through plan admission, owner delivery and each later task; a saved assignment is never rolled back when delivery is interrupted. Effectful operations and operational history reads retain an `agent_cli` admission with the initiating peer; audit failure refuses those operations. Authorization negotiation, `settings_preview` and `queue show` still check current authority but emit no extra admission record or conductor notice. Admission records are not a unique invocation counter.
4500
4538
 
4501
4539
  | Command | Conductor scope |
4502
4540
  | --- | --- |
@@ -4510,7 +4548,7 @@ The selected project's configured conductor may run the commands below through i
4510
4548
 
4511
4549
  The exact settings keys are `task_sweep.enabled`, `task_sweep.auto_reassign`, `task_sweep.no_writes_turns`, `task_sweep.interval_s`, `task_sweep.unaccepted_min`, `task_sweep.idle_min`, `task_sweep.review_min`, `task_sweep.ready_min`, `task_sweep.ladder_min`, `snapshots.enabled`, `research.enabled` and `approvals.notify`. New keys receive no authority by prefix. Generic roster setting writes still use the dedicated roster lifecycle. Conductor setting records name the initiating peer, and conductor writes preserve the person's pending undo record. An intervening write can still invalidate that undo through the existing file-digest guard; preserving the record does not bypass that guard. Permission modes, roles, `pet.window`, arbitrary configuration reload and undo remain person commands.
4512
4550
 
4513
- Log history is limited to the last 100 retained lines and export to the last 1000 retained events, each within a 128 KiB read. Truncation is explicit. Export accepts closed metadata shapes; unknown fields and nested records are withheld rather than copied. Task titles, paths and private delivery bodies are not part of this projection. It does not change the person's ordinary logs/export output.
4551
+ Log history is limited to the last 100 retained lines and export to the last 1000 retained events, each within a 128 KiB read. Truncation is explicit. An incomplete public projection reports one bounded reason on stderr, preserving export JSON on stdout. The complete privacy inventory has no 500-task cutoff and is cached until board or privacy/config revisions change, so log-follow does not rebuild it for every line. Export accepts closed metadata shapes; unknown fields and nested records are withheld rather than copied. Task titles, paths and private delivery bodies are not part of this projection. It does not change the person's ordinary logs/export output.
4514
4552
 
4515
4553
  Orphan cleanup requires a missing selected root, signed process identity and the host-known hub executable/entrypoint and argv. An inaccessible root or unavailable proof remains unknown; there is no offline config-based role grant. The daemon rechecks authority and process ownership before signals. A confirmed self candidate uses the existing lifecycle stop after its reply. A roster write already committed before authority ends is acknowledged as saved, without authorizing further lifecycle work or commands.
4516
4554
 
@@ -17,7 +17,7 @@ ahub setup # installs the Claude Code channel plu
17
17
  Or use the matching GitHub release:
18
18
 
19
19
  ```bash
20
- bun add -g github:STAIxBWLB/agent-hub#v0.18.0
20
+ bun add -g github:STAIxBWLB/agent-hub#v0.19.0
21
21
  ahub setup
22
22
  ```
23
23
 
@@ -30,6 +30,10 @@ and upgrade or crash recovery, see the [operations guide](operations.md). To
30
30
  move running projects to a newer release later, run `ahub upgrade` in a
31
31
  terminal: it reviews the plan with you, applies it and follows it.
32
32
 
33
+ ## Upgrading to 0.19.0
34
+
35
+ Pi is now a gated CLI (#843): `ahub agents` lists it, 1.4.0 is the verified version, and any other version is refused at every launch path with the exact `ahub agents trust pi <version>` command. If your Pi is not 1.4.0, trust that version (person only) or install 1.4.0 before Pi work resumes. At hub start and once a day the hub reads each gated CLI's installed version without network, tells the planner once per id and version that is not verified, and `ahub status` shows an `agent CLI drift` line until it is (#843). Newly verified: agy 1.4.7, Jcode 0.99.1, headless Qwen 0.26.1, the Kimi fresh reviewer 2.3.2 and the headless Claude implementer 2.1.310. In `ask-when-needed`, a headless Claude implementer now runs proven read-only shell commands and a closed set of task commands (`scripts/check.sh --changed`, `bun run build`, `bun test <test files>`, `git add|commit`, `git push` and `gh pr view|checks|create` on the task's bound branch) through a host-owned wrapper with single-use tickets; check, test and build run in the local worker's sandbox, any other command and any call in an enabled always-ask class still ask, and an older plugin keeps asking (#845; see [additional headless peer instances](operations.md#additional-headless-peer-instances)). It may also name its model with `"model"` on its declaration in untracked `config.local.json`, applied at the next hub daemon restart (#844). The fresh Codex and Kimi reviewers now declare review scope `code`: they review code only, a `changes_requested` with no open structured finding is recorded as evidence-only and does not count toward escalation, `ahub status` shows `review scope` on each reviewer row, and `"review_scope"` on the instance in `config.local.json` overrides it (#852). Planners get one `Planner work ready:` notice when a write frees WIP capacity or makes backlog work ready; nothing is pulled for them (#785). The remote check runner installs dependencies once per lockfile key into a read-only volume and refuses a commit whose `package.json` declares an install lifecycle script (#793). A conductor operation whose hub connection or authorization handshake is unavailable now says so instead of reporting a role refusal (#812). The hub sandbox also protects a linked worktree's submodule git dirs and `config.worktree`, and refuses writes under a modules root outside a discovered git dir (#518, #865). A Qwen TUI background-task notice no longer ends a hub turn as uncertain (#517). `@staix/agent-mem` is pinned at 0.10.0. Upgrade running hubs with `ahub upgrade` and restart Claude sessions so they load the 0.19.0 plugin. Coming from 0.17.0 or older, the notes below apply too.
36
+
33
37
  ## Upgrading to 0.18.0
34
38
 
35
39
  The `ahub sdlc` read commands (`status`, `explain`, `queue`, `release`, `metrics`, `costs`, `authorize-release`) now print readable output by default, no longer a JSON document (#814). A script that parses their output must pass `--json`: it prints exactly what the command printed before. `sdlc export` stays JSON. `restart` and `upgrade` off a terminal (a pipe or `--dry-run`), `recovery status` and its results, `models setup|start`, `queue resolve` and `budget execution configure|disable` follow (#814): pass `--json` for the old document. `task show|cancel`, `backlog groom|return|plan|pin|split|undo|pull-ready` and `release list|show|new|plan|activate|freeze|ship|drop` follow (#814): pass `--json` for the hub tool's text. More commands follow in later releases.
@@ -100,9 +104,9 @@ Upgrading from 0.12.37 or earlier, the 0.12.38 notes below also apply.
100
104
 
101
105
  The 0.12.38 plugin reports a reply stopped by the hop cap as not delivered instead of as sent (#546); the restart above loads it.
102
106
 
103
- Antigravity (`agy`) can join as an optional pull-only implementer (#485). It is off roster by default: install the Antigravity CLI 1.4.x (1.4.2 verified; other 1.4.x patches launch with a warning; on 1.4.3 the hub tools appear from the second TUI turn), run `ahub roster add agy`, then run `ahub agy` in a terminal. Its native permissions are unmanaged and it has no hub sandbox. See [Antigravity operations](operations.md#optional-antigravity-agy-peer).
107
+ Antigravity (`agy`) can join as an optional pull-only implementer (#485). It is off roster by default: install the Antigravity CLI 1.4.x (1.4.2 and 1.4.7 verified; other 1.4.x patches launch with a warning; the hub tools appear from the second TUI turn), run `ahub roster add agy`, then run `ahub agy` in a terminal. Its native permissions are unmanaged and it has no hub sandbox. See [Antigravity operations](operations.md#optional-antigravity-agy-peer).
104
108
 
105
- Reviews can rotate across fresh Claude, Codex and Kimi reviewer sessions (#519, #533). Declare headless reviewer-only instances in `.agenthub/config.local.json`; Codex and Kimi join only with `review_mode: "fresh"`. Codex also needs a compat-admitted Codex (receipt-verified 0.146.0 and 0.163.0; select it with `codex_bin` when needed), and Kimi needs a compat-admitted Kimi Code on PATH (receipt-verified 2.2.0, 2.3.0 and 2.3.1), signed in with its own OAuth login. Run `ahub agents` for the current version policy; every fresh reviewer still needs a passing runtime preparation. The [2.3.0](verification/2026-10-22-kimi-qwen-compat.md) and [2.3.1](verification/2026-10-25-kimi-231-reviewer-compat.md) receipts are model-free and do not establish real login or model readiness. A headless reviewer instance without `review_mode` keeps its native behavior and stays out of the pool. agy is not a reviewer: naming it in `review.pool` shows an unavailable skip in `ahub route explain`, never a review. Once a train is active or frozen, or `review.pool` is set or fresh reviewer instances are declared, `ahub upgrade` refuses a target release without that support, so an older release cannot take the project over. See [fresh reviewer pool](operations.md#fresh-reviewer-pool-519).
109
+ Reviews can rotate across fresh Claude, Codex and Kimi reviewer sessions (#519, #533). Declare headless reviewer-only instances in `.agenthub/config.local.json`; Codex and Kimi join only with `review_mode: "fresh"`. Codex also needs a compat-admitted Codex (receipt-verified 0.146.0 and 0.163.0; select it with `codex_bin` when needed), and Kimi needs a compat-admitted Kimi Code on PATH (receipt-verified 2.2.0, 2.3.0, 2.3.1 and 2.3.2), signed in with its own OAuth login. Run `ahub agents` for the current version policy; every fresh reviewer still needs a passing runtime preparation. The [2.3.0](verification/2026-10-22-kimi-qwen-compat.md), [2.3.1](verification/2026-10-25-kimi-231-reviewer-compat.md) and [2.3.2](verification/2026-10-26-kimi-232-reviewer-compat.md) receipts are model-free and do not establish real login or model readiness. A headless reviewer instance without `review_mode` keeps its native behavior and stays out of the pool. agy is not a reviewer: naming it in `review.pool` shows an unavailable skip in `ahub route explain`, never a review. Once a train is active or frozen, or `review.pool` is set or fresh reviewer instances are declared, `ahub upgrade` refuses a target release without that support, so an older release cannot take the project over. See [fresh reviewer pool](operations.md#fresh-reviewer-pool-519).
106
110
 
107
111
  A guarded config edit that the hub keeps at its running value now shows as `config retained: <keys>; run ahub settings reload` in status, the console and the dashboard's Settings, and starting a peer whose launch keys are retained says it uses the running value (#542). See [settings](operations.md#settings-from-the-dashboard-and-the-terminal).
108
112
 
@@ -112,7 +116,7 @@ The restarted Claude planner gets the release-train tools (`hub_release_new`, `h
112
116
 
113
117
  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`.
114
118
 
115
- Since 0.12.37 the hub opens a split memory home through its shards (#534); the current exact `@staix/agent-mem` pin is 0.8.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).
119
+ Since 0.12.37 the hub opens a split memory home through its shards (#534); the current exact `@staix/agent-mem` pin is 0.10.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).
116
120
 
117
121
  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:
118
122
 
@@ -179,7 +183,7 @@ More: `ahub help`. How it is built and why: [`docs/specs/2026-09-19-agent-hub-de
179
183
 
180
184
  ## Shared memory
181
185
 
182
- The hub exact-pins the first-party `@staix/agent-mem` 0.8.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.
186
+ The hub exact-pins the first-party `@staix/agent-mem` 0.10.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.
183
187
 
184
188
  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.
185
189