@staix/agent-hub 0.18.0 → 0.20.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 (90) hide show
  1. package/CHANGELOG.md +30 -0
  2. package/README.md +4 -4
  3. package/docs/agent-notes/README.md +6 -0
  4. package/docs/agent-notes/adapters.md +13 -10
  5. package/docs/agent-notes/bus.md +1 -1
  6. package/docs/agent-notes/daemon.md +11 -4
  7. package/docs/agent-notes/local-worker.md +3 -1
  8. package/docs/agent-notes/tasks.md +6 -1
  9. package/docs/agent-notes/tests.md +8 -4
  10. package/docs/dogfood.md +13 -4
  11. package/docs/memory.md +1 -1
  12. package/docs/operations.md +98 -28
  13. package/docs/quickstart.md +13 -5
  14. package/docs/sdlc.md +12 -0
  15. package/docs/security.md +24 -15
  16. package/docs/smoke.md +3 -3
  17. package/docs/specs/2026-09-19-agent-hub-design.md +101 -8
  18. package/docs/verification/2026-10-25-remote-docker-transport.md +10 -0
  19. package/docs/verification/2026-10-26-agy-1.4.7.md +69 -0
  20. package/docs/verification/2026-10-26-claude-implementer-2.1.310.md +69 -0
  21. package/docs/verification/2026-10-26-headless-claude-shell-context.md +62 -0
  22. package/docs/verification/2026-10-26-jcode-0.99.1.md +49 -0
  23. package/docs/verification/2026-10-26-kimi-232-reviewer-compat.md +53 -0
  24. package/docs/verification/2026-10-26-pi-140-compat.md +41 -0
  25. package/docs/verification/2026-10-26-qwen-0261-acp.md +60 -0
  26. package/docs/verification/2026-10-27-0.19.0.md +86 -0
  27. package/docs/verification/2026-10-27-0.20.0.md +75 -0
  28. package/docs/verification/claude-permission-transport-native.py +187 -0
  29. package/docs/verification/claude-shell-wrapper-host.ts +20 -0
  30. package/docs/verification/claude-shell-wrapper-native.py +145 -0
  31. package/docs/verification/pi-140-model-free.ts +57 -0
  32. package/docs/verification/pi-140-native.mjs +21 -0
  33. package/docs/verified.json +25 -17
  34. package/package.json +2 -2
  35. package/plugins/agent-hub/.claude-plugin/plugin.json +1 -1
  36. package/plugins/agent-hub/server.js +373 -72
  37. package/src/adapters/acp.ts +8 -0
  38. package/src/adapters/agy/entry.ts +1 -1
  39. package/src/adapters/channel-peer.ts +7 -5
  40. package/src/adapters/claude-channel.ts +7 -3
  41. package/src/adapters/claude-implementer.ts +6 -3
  42. package/src/adapters/codex-appserver.ts +11 -1
  43. package/src/adapters/compat.ts +32 -18
  44. package/src/adapters/conformance/enrollment.ts +4 -4
  45. package/src/adapters/conformance/factories.ts +2 -1
  46. package/src/adapters/conformance/fakes/pi-rpc.ts +1 -0
  47. package/src/adapters/conformance/kit.ts +10 -0
  48. package/src/adapters/definitions/builtin-metadata.ts +2 -2
  49. package/src/adapters/definitions/native-factories.ts +7 -2
  50. package/src/adapters/definitions/reviewer-variants.ts +2 -2
  51. package/src/adapters/local-worker.ts +2 -0
  52. package/src/adapters/pi.ts +46 -3
  53. package/src/adapters/qwen-dual.ts +25 -0
  54. package/src/adapters/registry-types.ts +6 -2
  55. package/src/adapters/registry.ts +1 -0
  56. package/src/adapters/reviewer-print.ts +1 -0
  57. package/src/cli/agents.ts +69 -7
  58. package/src/cli/claude-shell-wrapper.ts +54 -0
  59. package/src/cli/main.ts +30 -24
  60. package/src/cli/output.ts +4 -0
  61. package/src/hub/agent-drift.ts +61 -0
  62. package/src/hub/board.ts +14 -2
  63. package/src/hub/check-runner.ts +148 -18
  64. package/src/hub/ci-watch.ts +140 -0
  65. package/src/hub/claude-launch-proof.ts +67 -2
  66. package/src/hub/claude-shell-wrapper.ts +349 -0
  67. package/src/hub/config-apply.ts +2 -2
  68. package/src/hub/config-edit.ts +2 -2
  69. package/src/hub/config-tier.ts +2 -2
  70. package/src/hub/config-trust.ts +1 -0
  71. package/src/hub/daemon.ts +282 -113
  72. package/src/hub/host-executable.ts +20 -1
  73. package/src/hub/instances.ts +18 -2
  74. package/src/hub/lifecycle.ts +1 -1
  75. package/src/hub/operational-authority.ts +7 -0
  76. package/src/hub/operational-view.ts +6 -10
  77. package/src/hub/peers.ts +34 -0
  78. package/src/hub/read-only-shell-proof.ts +7 -6
  79. package/src/hub/review-scope.ts +13 -0
  80. package/src/hub/sdlc-metrics.ts +6 -4
  81. package/src/hub/settings.ts +1 -0
  82. package/src/hub/task-shell-command.ts +85 -0
  83. package/src/hub/task-shell-default.ts +79 -0
  84. package/src/hub/task-shell-proof.ts +171 -0
  85. package/src/hub/task-shell-sandbox.ts +61 -0
  86. package/src/hub/tasks.ts +274 -16
  87. package/src/local/deny.ts +112 -11
  88. package/src/local/sandbox.ts +70 -29
  89. package/src/local/shell-environment.ts +10 -0
  90. package/src/ui/index.html +1 -0
package/docs/dogfood.md CHANGED
@@ -13,8 +13,11 @@ agent-hub is developed with agent-hub. This page lists every feature, whether it
13
13
  | Message bus, digests, priorities | on | `ahub report`: messages, digests, overflow, undeliverable; delivery latency from events |
14
14
  | Compact board (#724) | on | `ahub board` at 80, 120 and 200 columns: one line per task (test/board-compact.test.ts) |
15
15
  | Backlog list table (#399) | on | `ahub backlog list` at a terminal width: one line per card by rank with the reserved owner and dependencies, and the count by owner (test/backlog-list-output.test.ts); `--json` for scripts |
16
- | Native branch ratchet and fixed builtin proof gate (#625 slice 2a) | active in source/CI after merge; running daemon gate takes effect after release and upgrade | reviewed predicate/function/count/reason rows equal source; foreign native proof products and nativeRevert declarations refused; reserved ACP ports leg remains unverified until 2b |
17
- | Remote isolated check transport (#793) | off here; fake checks and a bounded local Docker transport receipt, dependency preparation and person opt-in/SSH/live receipt follow separately | remote tests capability unavailable on failed SSH/Docker/image/transfer probe and while dependencies are unprepared; exact Git receipt, non-root hardened argv, zero Mac dependency copies and confirmed Docker cleanup before lease release; Darwin tar ignores PATH shims, probe warnings do not pollute JSON, bounded probes do not become unknown task-run orphans |
16
+ | Native branch ratchet and host proof ports (#625) | active in source/CI after merge; running daemon gate takes effect after release and upgrade | reviewed predicate/function/count/reason rows equal source; foreign native proof products and nativeRevert declarations refused; ACP delivery-only/owned/headless admission and generic delivery/stop/form/roster ports; plain-peer daemon tests cover session binding and recovery settlement, builtin context/provenance parity and ACP/channel notices; roster-detached Pi seed proves the recovery fence; Claude launch/generation proof uses the fixed-builtin real channel owner, with legacy hello/facts and non-native generation refusal tests; fixed-builtin frontend proxy/notice/revert ports have plain-peer and native debt/restore parity tests, with the action-policy Codex predicate intentionally retained |
17
+ | Review scope for sandboxed reviewers (#852) | on after the release for the fresh Codex and Kimi reviewers (default `code`); a person may set `review_scope` on an instance in `config.local.json` and restart the hub daemon | `ahub status` shows `review scope` on each reviewer row; a code reviewer's review request carries the one fixed line and no check or CI line (test/review-scope.test.ts); its approval history entry carries `reviewScope: code`; a `changes_requested` with no open finding is recorded `evidenceOnly`, adds nothing to `rejections`, and the conductor gets one ids-only notice (count the `evidenceOnly` history entries against the 7 verification-only rejections seen on #467, #512 and #510, and watch that no task escalates on them) |
18
+ | Headless Claude host shell grants (#845) | source/fixture work; active only after release and hub/plugin upgrade with `ask-when-needed` on the declared implementer | automatic wrapper grants versus person requests; native poisoned-environment and replay receipt; unknown/expired/reused/stale task, branch, root or child tickets execute zero commands |
19
+ | Optional model for a headless Claude implementer (#844) | on after the release and once the person sets `model` on `instances.claude-impl` in `config.local.json` (a hub daemon restart applies it); unset it changes nothing | `ahub status` shows `model source` on the instance row; the native argv carries `--model <value>` on every turn and none when unset (test/claude-implementer.test.ts, test/claude-implementer-daemon.test.ts); a tracked-file or non-implementer declaration is refused or ignored with a reason (test/instances.test.ts, test/jcode-instances.test.ts) |
20
+ | Remote isolated check transport (#793) | off here; fake checks, a bounded local Docker transport receipt and a local run of the lockfile-keyed dependency volume; person opt-in, SSH and the live receipt follow separately | remote tests capability unavailable on failed SSH/Docker/image/transfer probe; one networked install per new lockfile key, none on later runs, the check mounting the volume read-only with no network; exact Git receipt, non-root hardened argv, zero Mac dependency copies and confirmed Docker cleanup before lease release; Darwin tar ignores PATH shims, probe warnings do not pollute JSON, bounded probes do not become unknown task-run orphans |
18
21
  | Task board, routing, roles | on (claude planner, reviewer and implementer; codex, jcode, agy implementers) | board history: lead time from propose to approve, review rounds, escalations |
19
22
  | Readable `sdlc` output by default (#814 T1) | on after the release: `ahub sdlc status\|explain\|queue\|release\|metrics\|costs\|authorize-release` print tables and labelled fields here; scripts pass `--json` | the person reading `ahub sdlc status` and `sdlc metrics` without `--json`; `test/cli-json-guard.test.ts` lists the CLI paths that still print JSON by default (target: only named data formats) |
20
23
  | Readable restart, upgrade, recovery and model output by default (#814 T3) | on after the release: `ahub restart --dry-run`, `upgrade --dry-run`, `recovery status`, `models start`, `queue resolve` and `budget execution configure\|disable` print text here; scripts pass `--json` | the person reading a dry-run plan and a recovery status without `--json`; `test/cli-json-guard.test.ts` lists the CLI paths that still print JSON by default (target: only named data formats) |
@@ -53,6 +56,10 @@ agent-hub is developed with agent-hub. This page lists every feature, whether it
53
56
  | Lifecycle identity linked by the hub (#694, #723) | on in advise; links at propose and accept from the title and `refs.repo`, with a groom backfill as the safety net | open tasks still showing `issue identity missing` right after propose (should be only titles that name no issue or a qualified title without `refs.repo`) |
54
57
  | Planner plan review (#481 S9) | advise | plans reviewed before build |
55
58
  | Planner board-changed notice (#758) | on here, with the lifecycle in `advise`: `claude` is the planner, so a card another peer proposes or re-plans reaches it once at task weight, and its own writes, state moves, reviews and assignments cost it no turn | per planner and week: `task`-kind hub envelopes whose body starts `The board changed` (target: one per planning change, none after the planner's own write), the same envelopes at `fyi` priority (recorded, no turn: at most one per 10 minutes), and the count still queued at once (target: at most one). Read from `ahub log` and the event log; the 12 copies in 44 minutes seen on 2026-10-24 are the baseline |
59
+ | Planner capacity and dependency notices (#785 P1a) | source/fixture coverage; active after release and hub upgrade | write-triggered `capacity_released` and `backlog_ready` lines in `Planner work ready:` task envelopes; at most one queued envelope per effective planner, peer/WIP/card ids only; no automatic pull |
60
+ | Release preparation and guarded tag (#787 P3b) | scratch-repo fixtures with a local bare remote; the real history was dry-run in a disposable clone; first real use is the next release | `scripts/release-prepare.ts` run on a `release/<version>` branch and a release PR that carries the version, CHANGELOG, report and stamps; `scripts/release-tag.ts` refusing without the person's consent and pushing one tag after it |
61
+ | Opt-in auto pull (#785 P1c) | source/fixture coverage and a real daemon test; off by default, active only where a person sets `tasks.auto_pull` in `config.local.json` | `backlog pulled` history entries with `by: hub` and an `auto_pull:` note; at most one per capacity release; no pull with the setting off, for unreserved, PII, pinned, frozen or dependency-blocked cards |
62
+ | Per-head CI watcher and `ci_completed` (#785 P1b) | source/fixture coverage with a fake gh; active after release and hub upgrade | one `ci_completed:` task notice per head to owner and reviewer, a premature review re-offered once green; watchers started per `hub_task_done` head and gone at the result, deadline, newer head, verdict or shutdown; no gh read outside a watcher |
56
63
  | Plan conformance (#481 S2) | advise | PRs with files outside the plan |
57
64
  | Test integrity for bug fixes (#481 S4) | advise | bug PRs without a red-first receipt, modified pre-existing tests |
58
65
  | Required checks | advise (`check (ubuntu-latest)`, `check (macos-latest)`, `seeded guards`) | first-pass CI rate, same-head reruns (`hub_sdlc_costs`) |
@@ -82,6 +89,8 @@ agent-hub is developed with agent-hub. This page lists every feature, whether it
82
89
  | Permission modes | on, person-chosen | approval prompts and their wait times |
83
90
  | Pi/local read-only shell class (#816 T1) | source/fixture coverage; person setting and live Pi receipt (AC4) remain unverified | automatic read-only class versus approval requests, parser/allowlist/proof revalidation tests, unchanged Seatbelt/path-guard byte checks |
84
91
  | Pi and the local worker on DGX | off: DGX unreachable recently; Pi is off the roster | turns served on-prem, relay errors |
92
+ | Pi native version gate (#843 AC2) | source/fixture and isolated model-free 1.4.0 receipt; active after release and hub upgrade, Pi stays off this repository's roster | `ahub agents` pi verdict, one version probe on each real launch, rejected versions naming the exact trust command; account-backed inference remains unverified |
93
+ | Local agent CLI drift notice (#843 AC3) | source/fixture and injected-probe daemon test; active on this repository after release and hub upgrade | one planner notice per (id, version), the `agent CLI drift` line in `ahub status`, no repeat after a hub restart |
85
94
  | PII routing | on | PII tasks and where they ran |
86
95
  | Multiple Jcode instances (#685) | pending | turns per instance |
87
96
 
@@ -93,7 +102,7 @@ agent-hub is developed with agent-hub. This page lists every feature, whether it
93
102
  | Profile scorecard (#799) | source/fixture coverage; live writing/general/research pilot remains unverified | ahub report --dogfood profile rows; pure and reader dev byte goldens, private-text exclusion, research declarations/unknown verdicts and per-source unknown tests; anonymous weekly receipt templates |
94
103
  | Agent-shell read-only commands (#812 T1) | source/fixture coverage; active after release | the kept Allowed forms succeed from an agent shell and the demoted forms are refused with the console line (identity tests plus the live table in the PR body) |
95
104
  | Public agent CLI reads (#812 T1b) | source/fixture coverage; active after release | ordinary tools and agent-shell read success, private/secret projection tests, mutating-neighbour and planner-only refusals |
96
- | Conductor operational CLI (#812 T2) | source/fixture coverage; active after release | peer-attributed agent_cli admissions, conductor/non-conductor command matrix, public projection and authority-revocation checks |
105
+ | Conductor operational CLI (#812 T2) | source/fixture coverage; active after release | peer-attributed effect admissions, authority/domain-error separation, delayed-handshake and incomplete-output CLI tests, cached privacy projections and revocation checks |
97
106
  | Review request CI evidence (#824) | on after release: each review request carries one `Hub-verified at <head>` line from the admission it already read | review requests to a review-only reviewer; the count of reviews that still list test execution or CI evidence as unmet (should fall); no new gh reads |
98
107
  | Verdict mirror (#786 T3) | `auto_merge = "dry-run"` first (same setting as the guarded merge): the dry-run notice names the one comment review it would post; `on` after one dry-run notice matches a verdict | one `agent-hub verdict:` comment review per (task, head, verdict) on each PR; none at a stale head; the headline alone for a private task |
99
108
  | Guarded merge and result recording (#786 T1/T2) | `auto_merge = "dry-run"` first in this repository's `.agenthub/sdlc.toml`; `on` after one dry-run report matches a manual merge | `ahub sdlc merge <task> --dry-run` reports the decision and the argv it would run; merges the hub made against merges a person or reviewer made by hand; host merge/cleanup records and owner-notice status; fake-gh ordering/idempotency proof, no live cleanup claim |
@@ -104,7 +113,7 @@ agent-hub is developed with agent-hub. This page lists every feature, whether it
104
113
  | Settings reload | on | reloads applied, pending restarts |
105
114
  | Config editing from the console and dashboard (#689) | on after release; `ahub settings edit` and console key `c` run here, the dashboard Structure group needs `ahub ui --settings`; executable keys stay console only | `config_edit` events (edits applied by tier and source), refusals shown with the loader reason, restarts left pending |
106
115
  | Upgrade and recovery | on, per release | upgrade receipts in `docs/verification/` |
107
- | Agent CLI version policy and trust (#691) | on after the release: Codex 0.163.0 verified by the [2026-10-22 receipt](verification/2026-10-22-codex-reviewer-isolation.md) and 0.164.0 by the [2026-10-24 receipt](verification/2026-10-24-codex-0164-reviewer-isolation.md); Kimi 2.3.0 and Qwen 0.26.0 verified by the [model-free receipt](verification/2026-10-22-kimi-qwen-compat.md), Kimi 2.3.1 by the [2026-10-25 model-free receipt](verification/2026-10-25-kimi-231-reviewer-compat.md), with account/provider live legs unverified; agy 1.4.4 in-line | model-free isolation receipts, `ahub agents` verdicts, versions trusted per machine, lead time from an `agent-compat` issue to a receipt |
116
+ | Agent CLI version policy and trust (#691) | on after the release: Codex 0.163.0 verified by the [2026-10-22 receipt](verification/2026-10-22-codex-reviewer-isolation.md) and 0.164.0 by the [2026-10-24 receipt](verification/2026-10-24-codex-0164-reviewer-isolation.md); Kimi 2.3.0 and Qwen 0.26.0 verified by the [model-free receipt](verification/2026-10-22-kimi-qwen-compat.md), Kimi 2.3.1 by the [2026-10-25 model-free receipt](verification/2026-10-25-kimi-231-reviewer-compat.md) and 2.3.2 by the [2026-10-26 one](verification/2026-10-26-kimi-232-reviewer-compat.md), with account/provider live legs unverified; agy 1.4.4 in-line | model-free isolation receipts, `ahub agents` verdicts, versions trusted per machine, lead time from an `agent-compat` issue to a receipt |
108
117
  | Qwen's own TUI over dual output (#286 Part 3) | off here: this repository's hub runs no Qwen peer (roster.qwen is off), and the limits (a hub deny ends the turn without an answer; a stalled turn is stopped only with Esc in the Qwen terminal) are for the person at that terminal to accept. Verified on Qwen 0.26.1 under an isolated HOME by the [2026-10-25 receipt](verification/2026-10-25-qwen-dual-output-t0.md); the live account leg is the person's | `test/qwen-dual.test.ts` against a fake dual-output Qwen; in real use `ahub status` stalled/detached notices per peer and the `needs_review` count of Qwen deliveries (an Esc or an exited terminal ends a turn that way; a deny completes) |
109
118
  | Benchmarks (#251, #637) | off: `bench.enabled = false`; routing gate pending | bench receipts |
110
119
 
package/docs/memory.md CHANGED
@@ -1,6 +1,6 @@
1
1
  # Hub memory integration
2
2
 
3
- Agent-hub consumes the exact-pinned first-party [`@staix/agent-mem`](https://github.com/STAIxBWLB/agent-mem) library (current pin `0.8.0`, initial pin `0.1.1`). The package owns portable storage, tiers, graph, goals, sync, import and raw commands. Agent-hub owns caller authority, current project privacy policy, task lifecycle integration and presentation. Claude-mem is an optional, explicit import source only; runtime recall and capture make no worker HTTP requests.
3
+ Agent-hub consumes the exact-pinned first-party [`@staix/agent-mem`](https://github.com/STAIxBWLB/agent-mem) library (current pin `0.11.0`, initial pin `0.1.1`). The package owns portable storage, tiers, graph, goals, sync, import and raw commands. Agent-hub owns caller authority, current project privacy policy, task lifecycle integration and presentation. Claude-mem is an optional, explicit import source only; runtime recall and capture make no worker HTTP requests.
4
4
 
5
5
  ## Portable core and data directory
6
6
 
@@ -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.20.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,20 @@ 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`. The shared object store takes
918
+ only new loose objects (#864): `objects/info` (alternates, commit-graph) and
919
+ `objects/pack` are closed and an existing object cannot be changed, removed or
920
+ replaced, so `git gc`, `repack`, `fetch` and `maintenance` do not run inside
921
+ the sandbox either. `git add`, `commit`, `stash` and `checkout` work, and
909
922
  `.gitignore` and `.github/` stay writable; a commit that your git configuration
910
923
  signs with a key under `~/.ssh` or `~/.gnupg` fails, because the sandbox cannot
911
924
  read those directories. Run a refused command yourself, in a terminal.
@@ -1354,7 +1367,7 @@ ahub memory forget 1a2b3c4d # a person's; also promote, demote, sup
1354
1367
 
1355
1368
  `"memory": { "native": false }` in `.agenthub/config.json` turns it off for a project.
1356
1369
 
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.
1370
+ The exact-pinned `@staix/agent-mem` 0.11.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
1371
 
1359
1372
  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
1373
 
@@ -1901,6 +1914,7 @@ A guarded file edit that the daemon keeps at its accepted value remains visible
1901
1914
  | Switches | `task_sweep.enabled`, `.auto_reassign`, `.interval_s`, `.unaccepted_min`, `.idle_min`, `.review_min`, `.ready_min`, `.ladder_min`, `.no_writes_turns` | `config.local.json` | live; `.no_writes_turns` sets the turn threshold independently; other keys reschedule the sweep timer |
1902
1915
  | Routing | `tasks.wip_limits.<peer>` | `config.local.json` | live |
1903
1916
  | Routing | `tasks.escalate_after` (1-10, default 3: consecutive `changes_requested` before a task moves to the next `escalate_to` peer) | `config.local.json` | live; the next verdict uses it |
1917
+ | Switches | `tasks.auto_pull` (default off: pull the top explicitly reserved ready card when a planner slot frees) | `config.local.json` | live; the next freed planner slot uses it |
1904
1918
  | Roles | `planner` (one or up to eight planner holders) | `config.local.json` | at once |
1905
1919
 
1906
1920
  - Writes go only to `.agenthub/config.local.json` and `.agenthub/routing.local.toml`,
@@ -1972,7 +1986,7 @@ Instances, the review and approval blocks and the lifecycle can be edited withou
1972
1986
 
1973
1987
  1. **Registry settings** (everything above) are unchanged.
1974
1988
  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`;
1989
+ - `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
1990
  - `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
1991
  - `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
1992
 
@@ -2391,6 +2405,40 @@ moves, reviews and assignments are recorded at `fyi` weight (no turn), at most
2391
2405
  once per planner per ten minutes. A newer notice replaces an unsettled copy, and
2392
2406
  the hub's own bookkeeping tells nobody.
2393
2407
 
2408
+ A task write also wakes the task's rostered planners when it frees capacity or
2409
+ releases backlog work (#785): a lower WIP for the owner or reserved peer adds a
2410
+ `capacity_released: <peer> WIP <n>; reserved ready #<id>|none` line, and an
2411
+ approval that leaves a backlog card with no unresolved dependency adds
2412
+ `backlog_ready: #<id>`. The lines go in one `Planner work ready:` task notice
2413
+ per planner, ids only; a newer one merges and replaces the unsettled copy, which
2414
+ survives a restart. By default nothing is pulled automatically: the planner reads the
2415
+ board and decides. With `tasks.auto_pull` on (opt-in, see below) the hub itself pulls
2416
+ the top card explicitly reserved for the peer whose slot just freed.
2417
+
2418
+ When `hub_task_done` leaves a review whose exact-head CI is not yet green (the
2419
+ lifecycle must be on, the task linked to a PR and not private), the hub starts one watcher
2420
+ for that head (#785): one `gh pr view` a minute, ended by the CI result, by a
2421
+ 45 minute deadline, by a newer head, a verdict or a cancellation, or by hub
2422
+ shutdown; a restart does not restore it, and the next `hub_task_done` starts a new
2423
+ one. On the result the owner and the reviewer each get one
2424
+ `ci_completed: #<id> head <sha12> passed|failed|incomplete` task notice (ids only, the
2425
+ head cut to 12 characters; a person reviewer gets a console line instead), and
2426
+ unless CI failed, a review the hub held as premature for that head is offered
2427
+ once more after a fresh readiness read. A deadline tells the console only. No peer turn waits for
2428
+ CI, and nothing is polled outside a watcher.
2429
+
2430
+ `tasks.auto_pull` (default off, machine-local, live) lets the hub act on that
2431
+ wake-up (#785). When a write frees a peer's planner slot, the hub pulls the one top
2432
+ backlog card explicitly reserved for that peer, through the same planner pull a
2433
+ planner makes: revision, dependency, reservation, pinned, frozen-release, P0 order
2434
+ and WIP checks all apply unchanged, and a refusal becomes a console notice naming
2435
+ why. Unreserved cards, cards reserved for another peer and private (PII) cards are
2436
+ never pulled. One card per release, as the hub for the card's own planner of record:
2437
+ the history entry reads `by: hub` with that planner and the reason
2438
+ `auto_pull: capacity released for <peer>; hub acted for planner <planner>`. A hub that
2439
+ 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
2440
+ the other machine-local policies it is ignored there with a log line.
2441
+
2394
2442
  ## Resetting a project's hub
2395
2443
 
2396
2444
  `ahub reset` returns a project's hub to a clean state. Without `--yes` it is a
@@ -2509,7 +2557,7 @@ Rows without a live process are stale registrations; forget them with
2509
2557
 
2510
2558
  ## Agent CLI versions
2511
2559
 
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).
2560
+ 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
2561
 
2514
2562
  - `ahub agents [--json]` lists each gated CLI with its version, verdict and verified versions.
2515
2563
  - **verified:** a receipt exists.
@@ -2521,7 +2569,9 @@ Agent CLIs update themselves often, and a hub release cannot follow every one. O
2521
2569
  - 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
2570
  - What trust cannot prove is printed when you trust.
2523
2571
  - `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.
2572
+ - 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.
2573
+ - **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.
2574
+ - **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
2575
 
2526
2576
  ## Upgrade and crash recovery
2527
2577
 
@@ -2542,7 +2592,7 @@ section is what they run.
2542
2592
 
2543
2593
  Upgrade running projects with the target release's own coordinator. It accepts
2544
2594
  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
2595
+ 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.20.0), and only
2546
2596
  a target on its own protocol, so the target's coordinator fits every supported
2547
2597
  source and carries every recovery fix released up to it. Protocol 8 and older
2548
2598
  (0.5.x and earlier) are refused as `manual-bootstrap-required`. Run from the
@@ -2556,7 +2606,7 @@ bunx --package @staix/agent-hub@0.12.43 ahub upgrade --to 0.12.43 --yes
2556
2606
  | Running now | Coordinator to use |
2557
2607
  | --- | --- |
2558
2608
  | 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 |
2609
+ | 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.20.0 (protocol 16) | the target's, through `bunx` as above |
2560
2610
  | any supported source, with the installed CLI already at the target | `ahub upgrade` below, which is the same coordinator |
2561
2611
  | 0.5.x or earlier (protocol 8 and older) | not supported: bootstrap by hand with the matching CLI |
2562
2612
 
@@ -3453,9 +3503,15 @@ unreadable result runs the main gate again and refuses release. A manual
3453
3503
  `prepare_bundle` dispatch builds reviewable plugin assets without publishing;
3454
3504
  it is never accepted as full-gate evidence. A `timing_runs=20` dispatch runs only the daemon, PII screen and ACP
3455
3505
  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).
3506
+ `scripts/check.sh` runs the suite under `scripts/hang-watch.sh`, which stops it when
3507
+ no test result line (`(pass)`, `(fail)`, `(skip)` or `(todo)`) and no test file header
3508
+ has appeared for `AHUB_CHECK_QUIET_S` seconds (300 by default, set to 300 in CI), so a
3509
+ slow suite that is still passing tests is never cut. `scripts/progress-tee.sh` passes bun's
3510
+ output through unchanged and appends one byte to a progress file for each of those lines;
3511
+ the watchdog polls that file's size. On a stop it prints where the run is and the process
3512
+ table below it first. `AHUB_CHECK_HANG_S` (960 by default) is only the backstop for a run
3513
+ that keeps printing results while hung; hosted CI sets 1500 on Linux inside a 35-minute
3514
+ job limit and 1800 on macOS inside a 40-minute one (#378, #593, #752, #516).
3459
3515
  The `closing-refs` workflow fails a PR whose title, body or commit messages would make
3460
3516
  GitHub close an issue the text only mentions (`Refs #N`, a negated or qualified keyword);
3461
3517
  an issue linked only in the sidebar passes (#760).
@@ -3715,13 +3771,13 @@ selected roster and existing capabilities; the TUI is always a person's start.
3715
3771
  `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
3772
 
3717
3773
  - 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`.
3774
+ - **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
3775
  - **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
3776
  - 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
3777
  - 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
3778
  - 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
3779
 
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
3780
+ 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
3781
  is insufficient: startup verifies its native identity/version and advertised mode
3726
3782
  ids before any prompt. The earlier local installation reported CLI 0.25.0 while
3727
3783
  its package/ACP source said 0.24.1; that was not a ready identity. The 0.26.0 receipt
@@ -3758,11 +3814,11 @@ TUI input may interleave with an ACP turn; there is no single-writer guard. Clos
3758
3814
  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
3815
 
3760
3816
 
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.
3817
+ 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
3818
 
3763
3819
  ## Optional Antigravity (agy) peer
3764
3820
 
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.
3821
+ 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
3822
 
3767
3823
  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
3824
 
@@ -4339,11 +4395,23 @@ A headless Claude implementer (#525-4) is declared separately:
4339
4395
  "claude-impl": { "kind": "claude", "roles": ["implementer"], "start_mode": "headless", "worktree": ".worktrees/claude-impl", "permission_mode": "ask" }
4340
4396
  ```
4341
4397
 
4398
+ 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.
4399
+
4342
4400
  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
4401
 
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.
4402
+ 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.
4403
+
4404
+ 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.
4405
+
4406
+ 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
4407
 
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.
4408
+ 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.
4409
+
4410
+ Default-branch capture runs only at daemon startup when a Claude implementer is declared. If the first implementer is added through config reload, automatic task-command grants remain unavailable and those commands ask. Restart the daemon with that implementer declared to capture the remote default branch. Reload does not retry a failed startup probe or refresh the captured default branch.
4411
+
4412
+ 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.
4413
+
4414
+ 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
4415
 
4348
4416
  A headless Claude reviewer is run by the hub instead (#223): declare
4349
4417
  `"claude-review": { "kind": "claude", "roles": ["reviewer"], "start_mode": "headless" }`
@@ -4377,11 +4445,13 @@ From 0.12.37 reviews can rotate across fresh headless Claude and Codex reviewers
4377
4445
 
4378
4446
  `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
4447
 
4448
+ 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.
4449
+
4380
4450
  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
4451
 
4382
4452
  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
4453
 
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.
4454
+ 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
4455
 
4386
4456
  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
4457
 
@@ -4462,7 +4532,7 @@ Native launcher connections retry transport timeouts/unclean closes with bounded
4462
4532
 
4463
4533
  ## amem runtime (agent-mem#13 P4)
4464
4534
 
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.
4535
+ The hub exact-pins @staix/agent-mem 0.11.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
4536
 
4467
4537
  ## Backlog issue identity and notes
4468
4538
 
@@ -4496,7 +4566,7 @@ Optional code work runs only while Memory is open and visible. A cold console co
4496
4566
 
4497
4567
  ### Conductor operations (#812 T2)
4498
4568
 
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.
4569
+ 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
4570
 
4501
4571
  | Command | Conductor scope |
4502
4572
  | --- | --- |
@@ -4510,7 +4580,7 @@ The selected project's configured conductor may run the commands below through i
4510
4580
 
4511
4581
  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
4582
 
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.
4583
+ 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
4584
 
4515
4585
  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
4586
 
@@ -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.20.0
21
21
  ahub setup
22
22
  ```
23
23
 
@@ -30,6 +30,14 @@ 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.20.0
34
+
35
+ The hub now exact-pins `@staix/agent-mem` 0.11.0. Every `amem` that shares a memory home must run 0.11.0 or later to keep consolidation review receipts, so upgrade a global `amem` CLI too. When `hub_task_done` leaves a review whose exact-head CI is not yet green, the hub watches that PR head once a minute for up to 45 minutes and tells the owner and the reviewer once with a `ci_completed:` task notice; no peer turn waits for CI (#785). The new `tasks.auto_pull` setting is opt-in and off by default, and a committed config cannot turn it on: set it in `.agenthub/config.local.json` or a settings session, and when a peer's planner slot frees the hub pulls the top backlog card explicitly reserved for that peer (#785; see [the planner](operations.md#the-planner-455)). The local worker and Pi sandbox now lets a command add only new loose objects to the shared git object store (#864): `git gc`, `repack`, `fetch` and `maintenance` no longer run inside it, so run them yourself in a terminal. A task's host-observed branch binding no longer turns a running check result into "check finished late" (#880). Upgrade running hubs with `ahub upgrade` and restart Claude sessions so they load the 0.20.0 plugin. Coming from 0.18.0 or older, the notes below apply too.
36
+
37
+ ## Upgrading to 0.19.0
38
+
39
+ 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.
40
+
33
41
  ## Upgrading to 0.18.0
34
42
 
35
43
  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 +108,9 @@ Upgrading from 0.12.37 or earlier, the 0.12.38 notes below also apply.
100
108
 
101
109
  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
110
 
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).
111
+ 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
112
 
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).
113
+ 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
114
 
107
115
  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
116
 
@@ -112,7 +120,7 @@ The restarted Claude planner gets the release-train tools (`hub_release_new`, `h
112
120
 
113
121
  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
122
 
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).
123
+ Since 0.12.37 the hub opens a split memory home through its shards (#534); the current exact `@staix/agent-mem` pin is 0.11.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
124
 
117
125
  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
126
 
@@ -179,7 +187,7 @@ More: `ahub help`. How it is built and why: [`docs/specs/2026-09-19-agent-hub-de
179
187
 
180
188
  ## Shared memory
181
189
 
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.
190
+ The hub exact-pins the first-party `@staix/agent-mem` 0.11.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
191
 
184
192
  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
193
 
package/docs/sdlc.md CHANGED
@@ -357,6 +357,18 @@ Before other release commands, agents run `ahub sdlc release-guard -- <literal a
357
357
 
358
358
  Tracked npm cleanliness compares the cached index to HEAD and raw tracked blob bytes without `git status`, clean/process filters or text converters. It refuses unknown evidence, gitlinks, transformed files and inventories above 4,096 entries, 4 MiB per file, 32 MiB total or 250 ms; untracked files remain outside this check and the eventual tarball is not attested.
359
359
 
360
+ ## Release preparation and the guarded tag (#787 P3b)
361
+
362
+ Release preparation is an agent task; the release decision is not. They are two records and two commands, and consent stays person-only.
363
+
364
+ `bun scripts/release-prepare.ts [--version X.Y.Z] [--dry-run]` is the mechanical half of AGENTS.md "Releasing" steps 1 and 2. On a `release/<version>` branch (it refuses `main`, a detached HEAD, a dirty tree and any other branch name) it reads the commits since the last `v*` tag, applies the rule (any `feat` subject bumps the minor, otherwise the patch; a version below the rule or not newer than the last tag is refused, so is a missing or empty `## Unreleased`), and writes `package.json`, the README `Status:` line, the CHANGELOG section (the Unreleased entries move under the version, Unreleased stays empty), the bundle (`bun run build`) and a verification report skeleton `docs/verification/<date>-<version>.md`. The skeleton lists the pull requests the commits name and the native acceptance receipts, which it reads from `docs/verification/` files added since the tag, never from what an agent writes. It leaves `TODO(agent)` markers for what only a review can state: each PR's review and CI, the document review, the checks and the known gaps.
365
+
366
+ It restamps nothing by itself. A stamp in `docs/verified.json` records that someone read what changed under a document, so `bun scripts/release-prepare.ts restamp <document>... --reviewed` sets `verifiedAgainst` to HEAD for the named documents only, refuses without `--reviewed`, refuses an unnamed or unknown document and refuses with staged changes. The agent runs it per document after reading the source commits, commits `docs/verified.json`, opens the release PR and merges it with a merge commit (never squash).
367
+
368
+ `bun scripts/release-tag.ts <version> [--execute]` is the one guarded tag run. Every run repeats the whole preflight and reports every refusal at once: a valid release version; `package.json`, the README Status line and a CHANGELOG section at HEAD all naming it; HEAD on `main`, clean, and the merge commit of a `release/<version>` PR (a squash is refused); HEAD an ancestor of `origin/main` (fetched, not assumed); no `v<version>` tag locally or on the remote; `scripts/check-docs.mjs --require-fresh --committed` clean; and exactly one `docs/verification/<date>-<version>.md` with no `TODO(agent)` left. Without `--execute` it only reports, and whether the person's consent matches (exit 2 when it does not). With `--execute` it reads the consent through `ahub sdlc release-guard -- git push <remote> <commit>:refs/tags/v<version>` (read only; the ledger `ahub sdlc authorize-release` fills, person only, revoked by a hub restart), repeats the preflight, checks the commit did not move, reads the consent once more, and then makes the single push of that exact commit. A rejected push is reported and never retried, and the script passes neither `--force` nor `--no-verify`; the pre-push hook from `ahub init --release-guard` still runs on that push. The push creates a lightweight tag (`<commit>:refs/tags/v<version>`), the exact form `ahub sdlc release-guard` parses and the consent receipt binds; the repository's tags are mixed (v0.19.0 and v0.18.0 are annotated, v0.17.0 is not) and `.github/workflows/release.yml` accepts either (`v*`, then `--verify-tag`), so this is a convention difference, not a gap. If the project wants annotated release tags, create the tag object first and push the tag ref, which the guard also accepts (its source object is peeled to the commit); that is a later change to this script. `AHUB_RELEASE_ROOT` points both scripts at a checkout other than the one they live in (the tests use it so nothing outside a scratch repository is touched). Nothing here calls `authorize-release`, and no agent shell can: the report only names the command. Publication (npm, the GitHub release) stays with the tag-triggered workflow, which has its own gate (`.github/workflows/release.yml`).
369
+
370
+ The scripts and their pure helpers live in `scripts/release-prepare.ts`, `scripts/release-tag.ts` and `scripts/lib/release-plan.ts`; they are maintainer tooling and are not part of the published package. The deterministic recovery barriers of #787 AC3 are not needed by either leg and are not part of this change.
371
+
360
372
  ## Deliverable records (#509a)
361
373
 
362
374
  A report, translation or slide deck has no commit, so the artifact identity is the file's own content hash, recorded by the hub.