@staix/agent-hub 0.17.0 → 0.19.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (109) hide show
  1. package/CHANGELOG.md +35 -0
  2. package/README.md +4 -4
  3. package/docs/agent-notes/README.md +11 -0
  4. package/docs/agent-notes/adapters.md +10 -6
  5. package/docs/agent-notes/benchmarks.md +1 -0
  6. package/docs/agent-notes/daemon.md +6 -2
  7. package/docs/agent-notes/local-worker.md +3 -1
  8. package/docs/agent-notes/tasks.md +11 -2
  9. package/docs/agent-notes/tests.md +7 -3
  10. package/docs/dogfood.md +22 -2
  11. package/docs/memory.md +1 -1
  12. package/docs/operations.md +162 -35
  13. package/docs/profiles.md +36 -0
  14. package/docs/quickstart.md +15 -5
  15. package/docs/sdlc.md +29 -3
  16. package/docs/security.md +44 -13
  17. package/docs/smoke.md +3 -3
  18. package/docs/specs/2026-09-19-agent-hub-design.md +31 -10
  19. package/docs/verification/2026-10-25-0.18.0.md +76 -0
  20. package/docs/verification/2026-10-25-codex-review-live-verdict.md +35 -0
  21. package/docs/verification/2026-10-25-remote-docker-transport.md +56 -0
  22. package/docs/verification/2026-10-26-agy-1.4.7.md +69 -0
  23. package/docs/verification/2026-10-26-claude-implementer-2.1.310.md +69 -0
  24. package/docs/verification/2026-10-26-headless-claude-shell-context.md +62 -0
  25. package/docs/verification/2026-10-26-jcode-0.99.1.md +49 -0
  26. package/docs/verification/2026-10-26-kimi-232-reviewer-compat.md +53 -0
  27. package/docs/verification/2026-10-26-pi-140-compat.md +41 -0
  28. package/docs/verification/2026-10-26-qwen-0261-acp.md +60 -0
  29. package/docs/verification/2026-10-27-0.19.0.md +86 -0
  30. package/docs/verification/claude-permission-transport-native.py +187 -0
  31. package/docs/verification/claude-shell-wrapper-host.ts +20 -0
  32. package/docs/verification/claude-shell-wrapper-native.py +145 -0
  33. package/docs/verification/pi-140-model-free.ts +57 -0
  34. package/docs/verification/pi-140-native.mjs +21 -0
  35. package/docs/verification/profiles/YYYY-MM-DD-general.md +46 -0
  36. package/docs/verification/profiles/YYYY-MM-DD-research.md +61 -0
  37. package/docs/verification/profiles/YYYY-MM-DD-writing.md +46 -0
  38. package/docs/verified.json +24 -18
  39. package/package.json +2 -2
  40. package/plugins/agent-hub/.claude-plugin/plugin.json +1 -1
  41. package/plugins/agent-hub/server.js +364 -98
  42. package/src/adapters/agy/entry.ts +1 -1
  43. package/src/adapters/claude-channel.ts +7 -3
  44. package/src/adapters/claude-implementer.ts +5 -3
  45. package/src/adapters/compat.ts +32 -18
  46. package/src/adapters/conformance/fakes/pi-rpc.ts +1 -0
  47. package/src/adapters/definitions/builtin-metadata.ts +3 -3
  48. package/src/adapters/definitions/native-factories.ts +7 -2
  49. package/src/adapters/definitions/reviewer-variants.ts +2 -2
  50. package/src/adapters/local-worker.ts +5 -3
  51. package/src/adapters/pi.ts +39 -3
  52. package/src/adapters/qwen-dual.ts +25 -0
  53. package/src/adapters/registry-types.ts +6 -2
  54. package/src/adapters/registry.ts +1 -0
  55. package/src/cli/agents.ts +69 -7
  56. package/src/cli/claude-shell-wrapper.ts +54 -0
  57. package/src/cli/console-state.ts +9 -2
  58. package/src/cli/help.ts +29 -28
  59. package/src/cli/identity.ts +87 -6
  60. package/src/cli/main.ts +198 -71
  61. package/src/cli/output.ts +11 -2
  62. package/src/cli/sdlc-output.ts +242 -0
  63. package/src/cli/task-output.ts +212 -0
  64. package/src/cli/upgrade-output.ts +57 -0
  65. package/src/hub/agent-drift.ts +61 -0
  66. package/src/hub/board.ts +31 -2
  67. package/src/hub/check-runner.ts +385 -25
  68. package/src/hub/claude-shell-wrapper.ts +349 -0
  69. package/src/hub/conductor.ts +5 -0
  70. package/src/hub/config-apply.ts +16 -7
  71. package/src/hub/config-edit-host.ts +1 -1
  72. package/src/hub/config-edit.ts +2 -2
  73. package/src/hub/config-tier.ts +3 -3
  74. package/src/hub/daemon.ts +530 -87
  75. package/src/hub/dogfood-profile.ts +248 -0
  76. package/src/hub/dogfood-read.ts +77 -14
  77. package/src/hub/dogfood.ts +5 -1
  78. package/src/hub/events.ts +1 -1
  79. package/src/hub/host-executable.ts +21 -1
  80. package/src/hub/hub-tools.ts +2 -2
  81. package/src/hub/instances.ts +18 -2
  82. package/src/hub/lifecycle.ts +1 -1
  83. package/src/hub/operational-authority.ts +67 -0
  84. package/src/hub/operational-view.ts +137 -0
  85. package/src/hub/orphan-process.ts +69 -0
  86. package/src/hub/permission-mode.ts +1 -1
  87. package/src/hub/read-only-shell-proof.ts +81 -0
  88. package/src/hub/read-only-shell.ts +162 -0
  89. package/src/hub/review-scope.ts +13 -0
  90. package/src/hub/reviewer-pool-config.ts +3 -2
  91. package/src/hub/reviewer-pool.ts +3 -0
  92. package/src/hub/routing.ts +23 -6
  93. package/src/hub/sdlc-merge.ts +299 -0
  94. package/src/hub/sdlc-metrics.ts +6 -4
  95. package/src/hub/sdlc-mirror.ts +129 -0
  96. package/src/hub/sdlc.ts +58 -12
  97. package/src/hub/settings.ts +11 -5
  98. package/src/hub/snapshots.ts +60 -0
  99. package/src/hub/task-repository.ts +31 -5
  100. package/src/hub/task-shell-command.ts +85 -0
  101. package/src/hub/task-shell-default.ts +78 -0
  102. package/src/hub/task-shell-proof.ts +171 -0
  103. package/src/hub/task-shell-sandbox.ts +61 -0
  104. package/src/hub/tasks.ts +378 -27
  105. package/src/local/deny.ts +91 -11
  106. package/src/local/sandbox.ts +67 -28
  107. package/src/local/shell-environment.ts +10 -0
  108. package/src/local/tools.ts +17 -4
  109. package/src/ui/index.html +3 -1
@@ -1,6 +1,6 @@
1
1
  # Operations guide
2
2
 
3
- This guide describes ahub 0.13.0 and control protocol 16. Live verification
3
+ This guide describes ahub 0.19.0 and control protocol 16. Live verification
4
4
  results and remaining prerequisites are recorded separately in [the smoke ledger](smoke.md).
5
5
 
6
6
  ## Agent-operated live verification
@@ -67,6 +67,9 @@ smoke ledger; the presence of this option proves no outstanding #273 live leg.
67
67
  `status --all` uses the projects table. `queue show`, `models status` and `budget execution status` show labelled fields; add `--json` for the raw fetched document.
68
68
  `report --by task` keeps the attribution and coverage sentences with section headings and aligned counters.
69
69
  Use `--json` for scripts; it returns the fetched data without color, and `status --json` keeps its existing document.
70
+ `sdlc status`, `sdlc explain`, `sdlc queue`, `sdlc release`, `sdlc metrics`, `sdlc costs` and `sdlc authorize-release` print readable output by default (#814: aligned columns, wrapped text, readable times, `--color=auto|always|never`). A script that parsed their JSON must pass `--json`, which prints exactly the document they printed before. `sdlc export` is a data format and stays JSON, as do `export`, `research export` and `bench export`.
71
+ `restart` and `upgrade` off an interactive terminal (a pipe, `--dry-run`), `recovery status` and the results of `recovery reattach`, `resume`, `abort` and `dispose`, `models setup` and `start`, `queue resolve`, and `budget execution configure` and `disable` print readable text by default (#814); a script that parsed their JSON must pass `--json`, which prints exactly the document they printed before, including `restart --json --yes`. A readable plan lists each project, each peer with how it runs and what happens to it, every blocker, every reconnect-only session and every fresh start, and says nothing was changed; `--json` keeps those as stderr lines. A pipe wraps at `COLUMNS` when it is set, else at 80 columns.
72
+ `task show`, `task cancel`, `backlog groom`, `backlog return|plan|pin|split|undo|pull-ready` and the `release` commands (`list`, `show`, `new`, `plan`, `activate|freeze|ship|drop`) print a readable block by default (#814): the task or release as the hub holds it, a table for lists, every identifier whole, long text wrapped to the terminal. A script that parsed their JSON must pass `--json`, which prints the hub tool's text exactly as before. A private task arrives from the hub as its stub and is shown as that stub. Because these commands also take free text, `--json` and `--color=` count as flags only at the start or the end of the arguments: `--json` in the middle of a reason is part of the reason, and a value flag's value (`task cancel --reason --json`) is never a flag. A reason that ends in a bare `--json` loses it to the flag. `backlog add|pull`, `task escalate|assign|unassign` already answered one sentence and are unchanged.
70
73
  In `projects --json` and `status --all --json`, an unavailable hub that explicitly refuses an inspection for SQLite contention also carries `failure: { "code": "database_busy" }`; it is additive metadata alongside the neutral `error` sentence.
71
74
  Text columns are a human interface, not a scripting contract. An identifier accepted as a command argument is whole wherever its available column can hold its own length:
72
75
  turn ids, queue ids, project ids, execution budget ids and ids in labelled views. Only status's informational settlement and hold ids are shortened;
@@ -136,6 +139,12 @@ asking, under the same path guard and sandbox; a Pi restart or replacement, or a
136
139
  hub restart, drops it. The hub log records each granted call by tool name only.
137
140
  The dashboard can only deny Pi's requests.
138
141
 
142
+ In `ask-when-needed`, Pi and the local worker also grant bounded read-only shell calls at the host permit boundary (#816). The command must parse as one literal command and satisfy a closed program/flag table, trusted executable lookup and the existing root/path guard. Unknown flags, operators, expansions, pipelines, output files, unsafe helper/follow/file-list options and outside-root paths keep the ordinary approval path. Single quotes preserve literal data; double quotes containing `$`, backticks or backslashes are not automatically granted.
143
+
144
+ The fixed program set is `ls`, `cat`, `head`, `tail`, `wc`, `rg`, `grep`, `stat`, `file`, `tree`, `pwd`, `find` and supported read-only `git` forms. Git patch forms need both `--no-ext-diff` and `--no-textconv` where supported; a configured helper-bearing key (`core.fsmonitor`, `log.showSignature`, `gpg.program`, `gpg.<format>.program`, `diff.external`, `diff.<driver>.command` or `diff.<driver>.textconv`, `format.pretty` or `pretty.<name>`) outside the hub's own command scope keeps every git form asking. Custom `--format` and `--pretty` options also ask because signature placeholders can invoke the default GPG program. The host revalidates executable and path proof after each await, immediately before execution. A changed proof requires an explicit approval, including in unattended mode; persistent instability runs nothing. Neither argv nor environment is rewritten. Scoped read-only grants log the tool and class without command arguments.
145
+
146
+ `ask` retains shell approval behavior; `never-ask`, existing Pi tool-wide grants and the always-ask action policy retain their precedence. Seatbelt, the path guard, read allowlists, home and network access are unchanged. The person's Pi setting and live triage receipt are separate AC4 work; this feature's tests do not certify that rollout.
147
+
139
148
  Tab toggles stream and panels; `ahub console --panels` starts in panels. A panel
140
149
  screen has a header (the project's directory name, then the tabs), the panel and
141
150
  a three-line footer, separated by ASCII `-` rules; the stream has one rule above
@@ -518,6 +527,41 @@ resume, budget, queue, permit, permission, ask, remember, route, turns, undo,
518
527
  check-path and report operations. It executes an argument vector with closed stdin. Lifecycle,
519
528
  launch, nested console, setup, UI, logs and tail commands are refused.
520
529
 
530
+ ## Authority by command
531
+
532
+ `classifyPeerCommand` (src/cli/identity.ts) is the gate for an `ahub` command
533
+ run from an agent shell. It gives every command one of three classes:
534
+ **Allowed** means any agent may run it; **Conductor** means only the
535
+ configured conductor peer may (actor-preserving and audited); **Console** means
536
+ only the person's own terminal. A command the gate does not name is Console,
537
+ and so is every help entry the gate has no class for yet;
538
+ `test/identity.test.ts` pins each classed form below.
539
+
540
+ ### Allowed (any agent)
541
+
542
+ | Command | Class | Why |
543
+ | --- | --- | --- |
544
+ | `ahub doctor [--json] [--full] [--color=...]` | Allowed | a read-only check of tools, daemon, plugin, gateway, models and memory |
545
+ | `ahub doctor --stale-anchors [--json]` | Allowed | a bounded read-only report of memory symbol anchors at current HEAD |
546
+ | `ahub queue list [--peer <id>] [--json] [--full] [--color=...]` | Allowed | public queue metadata only; held bodies and resolution remain conductor operations |
547
+ | `ahub roster [--json] [--color=...]` | Allowed | public roster status; add/remove retain conductor admission |
548
+ | `ahub settings [list] [--json]`, `ahub settings get <key> [--json]` | Allowed | public registry values; secret-locating values, paths, undo records and config-editor details are withheld |
549
+ | `ahub sdlc status\|explain [<task>] [--json] [--color=...]` | Allowed | public lifecycle status; private tasks stay stubs, without private evidence or receipts |
550
+ | `ahub sdlc queue <owner/repo> [--json] [--color=...]`, `ahub sdlc release <owner/repo> [live-issue...] [--json] [--color=...]` | Allowed | public delivery/release readback for the checkout's GitHub origin, explicit SDLC config repos or person-imported public external records; no release authorization or mutation |
551
+ | `ahub release show <name>` | Allowed | public release metadata; private scope and internal history remain withheld |
552
+ | `ahub projects [--json] [--color=...]` | Allowed | lists registered projects; `projects remove` stays Console |
553
+ | `ahub models status [--json] [--color=...]` | Allowed | reads local model state; `setup\|start\|stop` stay Console |
554
+ | `ahub recovery status [<op-id>] [--json]` | Allowed | reads a preflight operation; the bare interactive screen, `resume`, `abort` and `reattach` stay Console |
555
+ | `ahub upgrade ... --dry-run` (no `--yes`) | Allowed | reviews the upgrade plan and applies nothing; `--yes` stays Console |
556
+ | `ahub restart --dry-run [--json]` (no `--yes`) | Allowed | reviews the restart plan and applies nothing; `--yes` stays Console |
557
+
558
+ `backlog list`, `release list` and `sdlc costs|metrics` retain their existing
559
+ planner/person policy. Their CLI forms stay Console for ordinary agents; a
560
+ configured planner can use the existing planner tools without borrowing console
561
+ authority. All newly admitted reads use the ordinary tools connection and public
562
+ views, never console identity. The person's full readback stays unchanged.
563
+ Conductor operations are listed in [the operational command table](#conductor-operations-812-t2).
564
+
521
565
  ## Conducting a team from Claude Code or Codex
522
566
 
523
567
  Start the daemon with `ahub up --no-console`, set exactly one conductor in the
@@ -594,6 +638,11 @@ Pi starts in its TUI by default; `ahub pi --headless`, or the start mode
594
638
  setting (`ahub settings set peers.pi.start_mode headless`), asks for the
595
639
  background peer instead. The project uses its existing OmniRoute connection for DGX;
596
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>`.
597
646
 
598
647
  ```bash
599
648
  ahub models setup # prepare bounded Qwen3.5 4B MLX in an existing Ollama service
@@ -623,13 +672,17 @@ does not automatically hand its task to a cloud peer.
623
672
  For assigned, non-private work, `hub_task_spec` reads the named GitHub issues
624
673
  through the hub's authenticated, bounded read path. `hub_check_start` runs an
625
674
  exact Git ref's permitted tests or typecheck in the opt-in isolated container
626
- runner (`checks.runner: "container"`); `hub_check_result` and `hub_check_cancel`
675
+ runner (`checks.runner: "container"`, or the optional `"remote"` backend); `hub_check_result` and `hub_check_cancel`
627
676
  read or cancel that owned run. Missing spec/check capability refuses the
628
677
  assignment early; it never grants host commands, credentials or broader sandbox
629
678
  access. Receipts distinguish exact source/tree/lockfile, platform, exit and
630
679
  uncertain or cancelled outcomes. See [bounded spec and check access](security.md#bounded-task-specs-and-isolated-checks-418)
631
680
  for the authorization and resource bounds.
632
681
 
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.
683
+
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.
685
+
633
686
  Retention bounds inactive valid check receipts to 200 records and the newest 20
634
687
  full outputs, excluding active runs. Full output is also consumed by the owner
635
688
  result read or expires after 24 hours.
@@ -852,12 +905,16 @@ the OS temp cleanup), and nothing else; the shared temp dirs are closed.
852
905
  Some names stay closed to those commands, and to Pi's, wherever they may
853
906
  otherwise write: anything named `.git` at any depth, the `config` and `hooks`
854
907
  directly under a `.git`, `commondir` below it, and `.agenthub` anywhere in the
855
- project. A submodule's git directory under `.git/modules/` is not covered yet
856
- (#281). A command can therefore not create, rename or remove a `.git`:
857
- `git init`, `git clone` and `git worktree add` do not run inside the sandbox,
858
- and neither do `git worktree remove`, `git worktree prune` of a stale record,
859
- `git clean -ffd` over a nested repository or `rm -rf` of a directory that
860
- holds a `.git`. `git add`, `commit`, `stash`, `checkout` and `gc` work, and
908
+ project, plus `config.worktree` beside each such `config`. A submodule's git
909
+ directory gets the same `config` and `hooks` refusal under every `modules/`
910
+ tree the project can reach: the common dir's, the worktree's own and every
911
+ linked worktree's (#281, #518). Nothing else may be written under such a tree
912
+ outside a discovered submodule git dir (#865). A command can therefore not
913
+ create, rename or remove a `.git`: `git init`, `git clone` and
914
+ `git worktree add` do not run inside the sandbox, and neither do
915
+ `git worktree remove`, `git worktree prune` of a stale record,
916
+ `git submodule update --init`, `git clean -ffd` over a nested repository or
917
+ `rm -rf` of a directory that holds a `.git`. `git add`, `commit`, `stash`, `checkout` and `gc` work, and
861
918
  `.gitignore` and `.github/` stay writable; a commit that your git configuration
862
919
  signs with a key under `~/.ssh` or `~/.gnupg` fails, because the sandbox cannot
863
920
  read those directories. Run a refused command yourself, in a terminal.
@@ -1028,7 +1085,9 @@ includes), read the check result, and list what is
1028
1085
  unmet (`hub_review` takes `unmet`, `ahub review ... --unmet <item>`). A reviewer
1029
1086
  that missed the request, and the owner, read the done summary and check result
1030
1087
  with `hub_task_show {id}`: the task's public view with its history, as the
1031
- conductor sees it, and only a stub for a PII task. Other peers are refused. Each
1088
+ conductor sees it, and only a stub for a PII task. The configured planner reads any
1089
+ task outside the backlog the same way (#812); a staged backlog row stays hidden from agent
1090
+ task tools and is read through the planner tools. Other peers are refused. Each
1032
1091
  request also carries the hub's review brief for the task's commit: reviewable and
1033
1092
  excluded files with reasons, the `REVIEW.md` and `ahub-review-rules` rules read at
1034
1093
  the merge base, and a cross-check list; `ahub review brief <id>` prints it.
@@ -1304,7 +1363,7 @@ ahub memory forget 1a2b3c4d # a person's; also promote, demote, sup
1304
1363
 
1305
1364
  `"memory": { "native": false }` in `.agenthub/config.json` turns it off for a project.
1306
1365
 
1307
- The exact-pinned `@staix/agent-mem` 0.7.0 resolves `AMEM_HOME`, then `$AGENTHUB_HOME/memory`, then `~/.agenthub/memory`. An absent `M/profile.json` selects shared v1; an explicit generation selects `M/generations/G` with the package's readonly legacy bridge. All daemon memory consumers and the hub memory/goal CLI use that selected profile. Invalid metadata or a missing selected generation makes native memory unavailable while the bus and board continue; there is no fallback to legacy writes. Inspect the daemon's memory binding/availability before interpreting missing memory as an empty store.
1366
+ The exact-pinned `@staix/agent-mem` 0.10.0 resolves `AMEM_HOME`, then `$AGENTHUB_HOME/memory`, then `~/.agenthub/memory`. An absent `M/profile.json` selects shared v1; an explicit generation selects `M/generations/G` with the package's readonly legacy bridge. All daemon memory consumers and the hub memory/goal CLI use that selected profile. Invalid metadata or a missing selected generation makes native memory unavailable while the bus and board continue; there is no fallback to legacy writes. Inspect the daemon's memory binding/availability before interpreting missing memory as an empty store.
1308
1367
 
1309
1368
  The daemon checks its selector at hub operation entry and before candidate/counter writes. It does not hot-switch handles or provide an atomic fence against selection changes during an in-flight core call. Quiesce consumers and close their handles before migration or selection changes; reopen through the supported session-preserving lifecycle afterward. Candidate/counter storage remains `M/candidates.db`, independent of `G`; since agent-mem 0.7.0 `amem review` also lists and accepts candidates on a selected generation. Every running hub using this home needs an authenticated receipt: a matching ready binding, or a generation-capable unbound receipt (`disabled` when native memory is off, `unavailable` when opening failed) proving it holds no memory handle. An unbound daemon and zero running hubs are quiesced for memory; a stale bound handle, unreadable registry or unknown runtime is not ready. Existing JSON memory/goal results remain unchanged; the additive binding receipt belongs to daemon status, profile-check and the decay object. This source integration does not authorize installing a new runtime, migrating real memory, restarting sessions or claiming real-machine sync/capture receipts. Migration/deactivation and their limits belong to [agent-mem](https://github.com/STAIxBWLB/agent-mem); see [the hub profile contract](memory.md#selected-memory-profile).
1310
1369
 
@@ -1654,7 +1713,7 @@ denylist (Pi and local), or native vendor boundaries (Claude and Codex).
1654
1713
  | Kimi Code CLI | ACP yolo | ACP auto | ACP default |
1655
1714
  | Codex | on-request for every outgoing turn/start | never for every outgoing turn/start | Restore the known native policy once, then stop overriding |
1656
1715
  | Claude | Allow resolving project file tools outside .agenthub/.git/.claude/.codex/.qwen/.kimi/.kimi-code/.jcode/.pi/.mcp.json/.agents/.agent/_agents/_agent/.gemini/.grok/.claude.json | Hook allow for every tool | No hook decision |
1657
- | Pi and local | Grant write/edit outside native agent configuration paths; read never asks; shell/git writes ask | Grant each tool once within the existing sandbox/guard | Normal approval handling |
1716
+ | Pi and local | Grant write/edit outside native agent configuration paths and proven read-only shell calls; other shell/git writes ask | Grant each tool once within the existing sandbox/guard | Normal approval handling |
1658
1717
 
1659
1718
  Kimi's map applies only to the actual Kimi Code CLI identity, not another ACP
1660
1719
  agent configured in kimi_cmd. Qwen and other ACP agents start fresh/resumed
@@ -1922,8 +1981,8 @@ Instances, the review and approval blocks and the lifecycle can be edited withou
1922
1981
 
1923
1982
  1. **Registry settings** (everything above) are unchanged.
1924
1983
  2. **Structural keys**, editable in the console and in a dashboard settings session:
1925
- - `instances.<id>` with `kind`, `roles`, `start_mode`, `review_mode`, `readOnly`, `worktree` and `permission_mode`, in `config.local.json`;
1926
- - `review.adaptive`, `review.min_reviews`, `review.pool`, `review.rerotate`, `snapshots.keep`, `approvals.timeout_s` and `context.gate`, in `config.local.json`;
1984
+ - `instances.<id>` with `kind`, `roles`, `start_mode`, `review_mode`, `readOnly`, `worktree` and `permission_mode`, in `config.local.json` (`review_scope` is tier 3 and not editable here);
1985
+ - `review.adaptive`, `review.min_reviews`, `review.pool`, `review.rerotate`, `review.same_family`, `snapshots.keep`, `approvals.timeout_s` and `context.gate`, in `config.local.json`;
1927
1986
  - `mode` and `required_checks` in `.agenthub/sdlc.toml`. Only those two lines change: comments and every other table stay as written, and a layout the editor cannot change faithfully (a key repeated, or one inside a multi-line string) is refused with its own reason.
1928
1987
 
1929
1988
  `roster.*`, `tasks.wip_limits.*`, `permission_modes`, `peers` and the rest of the registry keep their own `ahub settings set` path.
@@ -2341,6 +2400,15 @@ moves, reviews and assignments are recorded at `fyi` weight (no turn), at most
2341
2400
  once per planner per ten minutes. A newer notice replaces an unsettled copy, and
2342
2401
  the hub's own bookkeeping tells nobody.
2343
2402
 
2403
+ A task write also wakes the task's rostered planners when it frees capacity or
2404
+ releases backlog work (#785): a lower WIP for the owner or reserved peer adds a
2405
+ `capacity_released: <peer> WIP <n>; reserved ready #<id>|none` line, and an
2406
+ approval that leaves a backlog card with no unresolved dependency adds
2407
+ `backlog_ready: #<id>`. The lines go in one `Planner work ready:` task notice
2408
+ per planner, ids only; a newer one merges and replaces the unsettled copy, which
2409
+ survives a restart. Nothing is pulled automatically: the planner reads the
2410
+ board and decides.
2411
+
2344
2412
  ## Resetting a project's hub
2345
2413
 
2346
2414
  `ahub reset` returns a project's hub to a clean state. Without `--yes` it is a
@@ -2459,7 +2527,7 @@ Rows without a live process are stale registrations; forget them with
2459
2527
 
2460
2528
  ## Agent CLI versions
2461
2529
 
2462
- Agent CLIs update themselves often, and a hub release cannot follow every one. One policy decides each gated CLI: the Codex and Kimi fresh reviewers, Jcode, agy and Qwen (#691). Qwen has two entries: `qwen` for headless ACP and `qwen-dual` for the dual-output TUI (#286).
2530
+ Agent CLIs update themselves often, and a hub release cannot follow every one. One policy decides each gated CLI: the Codex and Kimi fresh reviewers, the headless Claude implementer, Jcode, agy, Qwen and Pi (#691, #847). Qwen has two entries: `qwen` for headless ACP and `qwen-dual` for the dual-output TUI (#286).
2463
2531
 
2464
2532
  - `ahub agents [--json]` lists each gated CLI with its version, verdict and verified versions.
2465
2533
  - **verified:** a receipt exists.
@@ -2471,7 +2539,9 @@ Agent CLIs update themselves often, and a hub release cannot follow every one. O
2471
2539
  - For the reviewers, trust lifts only the version pin. The MCP inventory, Apps/plugins/hooks readback, sandbox denial fixtures and binary fence still run before every child, and a failed leg still refuses.
2472
2540
  - What trust cannot prove is printed when you trust.
2473
2541
  - `ahub doctor` shows one line for each installed CLI that is not on a verified version.
2474
- - A weekly workflow (`.github/workflows/agent-compat.yml`) compares the latest npm versions of Codex, Kimi Code and Qwen Code with the verified sets, keeps one `agent-compat` issue up to date while one is ahead and closes it when none is. The next step for each listed version is a model-free receipt in `docs/verification/`, then adding it to `verified` in the same PR. Jcode, agy and `qwen-dual` are not watched.
2542
+ - A daily workflow (`.github/workflows/agent-compat.yml`) runs `scripts/agent-compat-watch.ts`, which reads the latest published version of every gated CLI that has a public source: the npm packages `@anthropic-ai/claude-code`, `@openai/codex`, `@moonshot-ai/kimi-code`, `@qwen-code/qwen-code` (for both `qwen` and `qwen-dual`) and `@earendil-works/pi-coding-agent` (for `pi`, since #847 gave it a COMPAT entry), and the GitHub releases of Jcode. agy has no public source, so its row reads "no public source; local drift only" and is never a failure. Every COMPAT entry names the receipt to repeat (`receipt`), and a test requires that file to exist and the entry to have a watcher source. A version that cannot be read reads as unknown, not as a crash. The workflow keeps one `agent-compat` issue current while a source is ahead and closes it when none is.
2543
+ - **Maintenance loop (#843):** the daily upstream watch is the trigger; `ahub doctor` is the local drift notice for versions installed on a machine, and the hub itself tells the planner (below). The planner files one receipt task per (id, version): the implementer repeats the procedure in the entry's last `docs/verification/` receipt, writes a new one, and adds the version to `verified` in that same PR; the next release ships the bump. A person's `ahub agents trust` stays the stopgap that keeps a machine running meanwhile, and the person may revoke it once the bump lands.
2544
+ - **Local drift notice (#843 AC3):** at hub start and once a day the daemon reads each gated CLI's installed version with the probe `ahub agents` uses (no network, no install). For every installed CLI on a version that is not `verified` (in-line, trusted or refused) the effective planner gets one notice per (id, version): the id, the version, the state and the last receipt path, nothing the CLI printed. A CLI that is not installed, or whose version cannot be read, is not drift. The marker is kept per (id, version, planner) in `hub.db` (`agent_drift_notices`), so a restart repeats nothing, a planner whose notice could not be queued (or who joins later) is retried alone without telling the others again, and a new version is a new notice; with no rostered planner nothing is recorded. `ahub status` shows one `agent CLI drift` attention line per drifted id until the installed version is verified.
2475
2545
 
2476
2546
  ## Upgrade and crash recovery
2477
2547
 
@@ -2492,7 +2562,7 @@ section is what they run.
2492
2562
 
2493
2563
  Upgrade running projects with the target release's own coordinator. It accepts
2494
2564
  a running source on control protocol 9 (0.6.x), 10 (0.7.0 through 0.12.0),
2495
- 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.12.43), and only
2565
+ 11 (0.12.1 and 0.12.2), 12 (0.12.3), 13 (0.12.4 through 0.12.15), 14 (0.12.16), 15 (0.12.17 through 0.12.19) or 16 (0.12.20 through 0.19.0), and only
2496
2566
  a target on its own protocol, so the target's coordinator fits every supported
2497
2567
  source and carries every recovery fix released up to it. Protocol 8 and older
2498
2568
  (0.5.x and earlier) are refused as `manual-bootstrap-required`. Run from the
@@ -2506,7 +2576,7 @@ bunx --package @staix/agent-hub@0.12.43 ahub upgrade --to 0.12.43 --yes
2506
2576
  | Running now | Coordinator to use |
2507
2577
  | --- | --- |
2508
2578
  | 0.6.x (protocol 9) | the target's, through `bunx` as above |
2509
- | 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.12.43 (protocol 16) | the target's, through `bunx` as above |
2579
+ | 0.7.0 through 0.12.0 (protocol 10), 0.12.1 and 0.12.2 (protocol 11), 0.12.3 (protocol 12), 0.12.4 through 0.12.15 (protocol 13), 0.12.16 (protocol 14), 0.12.17 through 0.12.19 (protocol 15), 0.12.20 through 0.19.0 (protocol 16) | the target's, through `bunx` as above |
2510
2580
  | any supported source, with the installed CLI already at the target | `ahub upgrade` below, which is the same coordinator |
2511
2581
  | 0.5.x or earlier (protocol 8 and older) | not supported: bootstrap by hand with the matching CLI |
2512
2582
 
@@ -2655,15 +2725,14 @@ plugin, daemon, or terminal mutation.
2655
2725
 
2656
2726
  `ahub upgrade` and `ahub restart` are interactive when standard input and output
2657
2727
  are a terminal and neither `--dry-run` nor `--yes` is given. With either flag, or
2658
- through a pipe, the JSON plan, the `ahub: blocker:` lines and the exit codes are
2659
- unchanged. The screens add no way to change an operation: every choice runs one
2728
+ through a pipe, the plan is printed as readable text (#814) and `--json` keeps the JSON plan and the `ahub: blocker:` lines; the exit codes are unchanged. The screens add no way to change an operation: every choice runs one
2660
2729
  of the commands in this section for you, so that command's refusals decide.
2661
2730
 
2662
2731
  The screens use the console palette and `--color=auto|always|never`, with the
2663
2732
  same `NO_COLOR` and `TERM=dumb` precedence. Auto colours a suitable TTY; explicit
2664
2733
  always has the console's override behaviour. Raw JSON bypasses colour and width
2665
- handling. Default and never preserve the existing pipe, dry-run, status and
2666
- dispose output byte for byte. Presentation flags go after the command, for
2734
+ handling. Default and never print the same readable pipe, dry-run, status and
2735
+ dispose text, and `--json` keeps the previous documents byte for byte. Presentation flags go after the command, for
2667
2736
  example `ahub recovery --color=never` or
2668
2737
  `ahub recovery status <operation-id> --json`. An explicit `--json` returns only
2669
2738
  the raw document, without a human suffix. `upgrade --json` reviews a JSON plan;
@@ -3271,7 +3340,7 @@ The person can retain superseded work as closed history:
3271
3340
  ahub task cancel <id> --reason <text> [--superseded-by <ref>] [--revision N]
3272
3341
  ```
3273
3342
 
3274
- The console also offers cancellation with the selected task's flow revision. Approved tasks cannot be cancelled. The planner's `hub_task_cancel` tool is limited to its own unaccepted backlog/proposed work; previously accepted work requires the person. Cancellation sends the former owner/reviewer a stop notice, releases overlap/reservation participation and keeps dependents blocked with a cancelled-dependency notice. It does not stop a native turn, delete a worktree or write an outcome memory note. Cancelled work is closed history, counted separately from approved work; it does not satisfy an approval dependency. A still-running private turn keeps its privacy fence after cancellation. Use the displayed revision to avoid acting on a task that changed while it was selected.
3343
+ The console also offers cancellation with the selected task's flow revision. Approved tasks cannot be cancelled. The planner's `hub_task_cancel` tool is limited to work it proposed that is in backlog or proposed with no execution evidence: never started, or accepted by an owner and then declined or returned untouched (no branch, commit, PR, local check, done, verdict or reopen, the same test as `hub_backlog_return_untouched`, #812). Work with any such evidence requires the person. Cancellation sends the former owner/reviewer a stop notice, releases overlap/reservation participation and keeps dependents blocked with a cancelled-dependency notice. It does not stop a native turn, delete a worktree or write an outcome memory note. Cancelled work is closed history, counted separately from approved work; it does not satisfy an approval dependency. A still-running private turn keeps its privacy fence after cancellation. Use the displayed revision to avoid acting on a task that changed while it was selected.
3275
3344
 
3276
3345
  The daemon advertises `task_cancel`; a current CLI refuses cancellation against older daemons. Recovery retains verified target features beside its immutable root/digest, reuses them on resume and on each lease renewal before a terminal is closed, and checks only sources not yet past commit; a legacy receipt without that evidence needs a target probe only before commit. Upgrade/restart refuses a target without this feature while cancelled rows exist and names their ids. No cancelled row is rewritten as approved. An old binary launched by hand remains unsupported: it can count cancelled work as open or fail to move it. Use a compatible coordinator instead. Live task cancellation and sweep enablement are separate post-release operations.
3277
3346
 
@@ -3404,9 +3473,15 @@ unreadable result runs the main gate again and refuses release. A manual
3404
3473
  `prepare_bundle` dispatch builds reviewable plugin assets without publishing;
3405
3474
  it is never accepted as full-gate evidence. A `timing_runs=20` dispatch runs only the daemon, PII screen and ACP
3406
3475
  test files twenty times on macOS, in its own concurrency group; it does not replace the full gate. Each PR has its own CI concurrency group, so PRs run side by side; within a PR a newer pending run replaces the older pending one and an admitted run is never cancelled; main pushes and dispatches keep queueing every run. Two groups admitting the same tree at once may run it twice, never a false green (#609). `scripts/check.sh --changed [<base>]` runs every non-test step but only the affected test files, and the full suite when it cannot tell; the full gate stays CI's (#610).
3407
- `scripts/check.sh` stops a suite still running after `AHUB_CHECK_HANG_S` seconds
3408
- (960 by default); hosted CI sets 1080 on Linux inside a 25-minute job limit and 1440
3409
- on macOS inside a 30-minute one (#378, #593, #752).
3476
+ `scripts/check.sh` runs the suite under `scripts/hang-watch.sh`, which stops it when
3477
+ no test result line (`(pass)`, `(fail)`, `(skip)` or `(todo)`) and no test file header
3478
+ has appeared for `AHUB_CHECK_QUIET_S` seconds (300 by default, set to 300 in CI), so a
3479
+ slow suite that is still passing tests is never cut. `scripts/progress-tee.sh` passes bun's
3480
+ output through unchanged and appends one byte to a progress file for each of those lines;
3481
+ the watchdog polls that file's size. On a stop it prints where the run is and the process
3482
+ table below it first. `AHUB_CHECK_HANG_S` (960 by default) is only the backstop for a run
3483
+ that keeps printing results while hung; hosted CI sets 1500 on Linux inside a 35-minute
3484
+ job limit and 1800 on macOS inside a 40-minute one (#378, #593, #752, #516).
3410
3485
  The `closing-refs` workflow fails a PR whose title, body or commit messages would make
3411
3486
  GitHub close an issue the text only mentions (`Refs #N`, a negated or qualified keyword);
3412
3487
  an issue linked only in the sidebar passes (#760).
@@ -3666,13 +3741,13 @@ selected roster and existing capabilities; the TUI is always a person's start.
3666
3741
  `ahub qwen --mode tui` runs `qwen --input-file <in> --json-file <out> --mcp-config <hub server> --approval-mode default` in the terminal you ran it from. The daemon prepares the two files (mode 0600, under `<state dir>/qwen/qwen-dual/`) and returns the launch; the hub then appends one `submit` line per delivery and tails the event file from a saved offset. It needs a Qwen the dual-output compat entry verifies (`ahub agents` shows `qwen-dual`, verified 0.26.1 only; the headless `qwen` entry is separate and unchanged) and refuses another version before it creates a file. The TUI is explicit only: `ahub qwen` without `--mode` is still headless, Qwen is not a start-mode peer, and the hub never opens this terminal by itself. The TUI start is refused for an extra Qwen instance, off roster, outside a terminal, and while a headless Qwen runs (`ahub stop qwen` first); a headless start is refused while the TUI holds its terminal.
3667
3742
 
3668
3743
  - What the hub does: a delivery becomes a `submit`; the last assistant message of the turn (an `assistant` event whose `stop_reason` is not `tool_use`, then `message_stop`) is shared as the answer; busy and idle follow the events. A `can_use_tool` request goes to the console approval path (`ahub permit`, the dashboard), and the hub's own tools are approved only by their exact `mcp__agent-hub__<tool>` name. If you answer a prompt in the Qwen terminal first, the hub's pending approval is withdrawn.
3669
- - **Limit 1, a deny ends the turn without an answer.** Qwen emits nothing when the hub denies: it cancels the whole turn. The hub ends the turn itself, shares no answer, settles the delivery as `completed` (a deny is a known outcome, and a `needs_review` would hold every later message to Qwen until a person resolved it) and says so in a notice. A turn whose outcome is not known (Qwen exited, an Esc followed by a new prompt, an event file that became unreadable) is the one that settles `needs_review`.
3744
+ - **Limit 1, a deny ends the turn without an answer.** Qwen emits nothing when the hub denies: it cancels the whole turn. The hub ends the turn itself, shares no answer, settles the delivery as `completed` (a deny is a known outcome, and a `needs_review` would hold every later message to Qwen until a person resolved it) and says so in a notice. A turn whose outcome is not known (Qwen exited, an Esc followed by a new prompt, an event file that became unreadable) is the one that settles `needs_review`. Qwen's own notice that a background shell finished (a `user` event that is wholly one or more `<task-notification>` envelopes) is not a new prompt: it ends no hub turn and leaves an idle peer idle (#517).
3670
3745
  - **Limit 2, the hub cannot stop a Qwen turn.** The input protocol has only `submit` and `confirmation_response`, and a signal does not stop a running turn. The inactivity watchdog therefore never cancels: after `watchdog_ms` of silence it marks the turn stalled (`ahub status`, the console and the dashboard say "press Esc in the Qwen terminal") and keeps the peer busy. The hub never types into your terminal and never signals Qwen to stop a turn.
3671
3746
  - A Qwen write or shell command that names the hub state directory is refused (see [security](security.md): the input file is a prompt channel).
3672
3747
  - If the hub cannot read the event file the peer is `detached`, shown as offline with a notice, never idle: Qwen itself keeps working, and the hub reattaches when the file is readable again.
3673
3748
  - Restart: if the hub restarts while your Qwen keeps running, it reads on from its saved offset (no event twice, no launch). If Qwen is gone, `ahub qwen --mode tui` starts `qwen -r <session id>` on the same two files; the earlier submits are not replayed and the recorded answers are not shared again. Recovery never restores this terminal as a headless worker, and `ahub stop qwen` refuses: end Qwen in its own terminal.
3674
3749
 
3675
- Receipt-verified native ACP identities are qwen-code 0.25.0 and 0.26.0 ([dated receipt](verification/2026-10-22-kimi-qwen-compat.md)). A CLI version label alone
3750
+ Receipt-verified native ACP identities are qwen-code 0.25.0 and 0.26.0 ([dated receipt](verification/2026-10-22-kimi-qwen-compat.md)) and 0.26.1 ([0.26.1 receipt](verification/2026-10-26-qwen-0261-acp.md)). A CLI version label alone
3676
3751
  is insufficient: startup verifies its native identity/version and advertised mode
3677
3752
  ids before any prompt. The earlier local installation reported CLI 0.25.0 while
3678
3753
  its package/ACP source said 0.24.1; that was not a ready identity. The 0.26.0 receipt
@@ -3709,11 +3784,11 @@ TUI input may interleave with an ACP turn; there is no single-writer guard. Clos
3709
3784
  After upgrading to the recipient-retry release, a Jcode project MCP entry written before `AGENTHUB_DELIVERY_ONLY` was added is intentionally stale. Once per project, the person runs `ahub jcode --mode headless` before using conductor/dashboard starts. The launcher rewrites only the owned entry with the delivery-only marker, preserving foreign entries and refusing collisions; recovery relaunches already use this CLI path. Readiness checks never rewrite the file, and an explicit false marker or any other drift remains refused. Until that rewrite, daemon starts keep their actionable stale-entry refusal rather than advertising a retry tool Jcode cannot use.
3710
3785
 
3711
3786
 
3712
- Native policy is unmanaged and has NO hub sandbox; no permission_modes.jcode or runtime mode mapping is invented. Quota and consumption remain unknown. Supported versions run from 0.98.0 up to, not including, 1.0.0 (#529); another version is refused before the owned daemon starts, with the version named and the trust command (#691). Every owned jcode process (version probe, daemon, ACP and TUI) runs with `JCODE_NO_TELEMETRY=1`. Native session/load (verified on 0.94.0 and 0.98.1) cannot prove identity, so attached Jcode blocks controlled recovery before effects and crash recovery asks for deliberate native reopening. A requested load without an echoed identity refuses; no fresh session is presented as restored. Provider/tool/native teardown receipts remain unverified in the pinned contract report.
3787
+ Native policy is unmanaged and has NO hub sandbox; no permission_modes.jcode or runtime mode mapping is invented. Quota and consumption remain unknown. Supported versions run from 0.98.0 up to, not including, 1.0.0 (#529); another version is refused before the owned daemon starts, with the version named and the trust command (#691). Every owned jcode process (version probe, daemon, ACP and TUI) runs with `JCODE_NO_TELEMETRY=1`. Native session/load (verified on 0.94.0, 0.98.1 and 0.99.1) cannot prove identity, so attached Jcode blocks controlled recovery before effects and crash recovery asks for deliberate native reopening. A requested load without an echoed identity refuses; no fresh session is presented as restored. Provider/tool/native teardown receipts remain unverified in the pinned contract report.
3713
3788
 
3714
3789
  ## Optional Antigravity (agy) peer
3715
3790
 
3716
- Antigravity is optional and off roster by default. Use `ahub roster add agy`, then run `ahub agy` in a terminal: it runs the Antigravity CLI as a fresh pull-only TUI in that terminal. It accepts the 1.4.x line: 1.4.2 is the verified version, any other 1.4.x launches with a stderr warning (agy updates itself), and other versions are refused (#589) unless the person trusts that exact version with `ahub agents trust agy <version>` (#691). The running hub must advertise `native_hook_launch`; an older hub refuses before any write with an upgrade-or-restart remedy. Only the person's launcher starts it: conductor starts are refused, there is no controlled restore or resume, and a declared agy extra instance is refused when the config loads (#625). Headless/print, resume (`--conversation`, `--continue`, `--new`), `--mode`, `--dangerously-skip-permissions` and `--unattended` are refused until their native mapping is verified; `--print-command` previews without writing.
3791
+ Antigravity is optional and off roster by default. Use `ahub roster add agy`, then run `ahub agy` in a terminal: it runs the Antigravity CLI as a fresh pull-only TUI in that terminal. It accepts the 1.4.x line: 1.4.2 and 1.4.7 are the verified versions ([1.4.7 receipt](verification/2026-10-26-agy-1.4.7.md)), any other 1.4.x launches with a stderr warning (agy updates itself), and other versions are refused (#589) unless the person trusts that exact version with `ahub agents trust agy <version>` (#691). The running hub must advertise `native_hook_launch`; an older hub refuses before any write with an upgrade-or-restart remedy. Only the person's launcher starts it: conductor starts are refused, there is no controlled restore or resume, and a declared agy extra instance is refused when the config loads (#625). Headless/print, resume (`--conversation`, `--continue`, `--new`), `--mode`, `--dangerously-skip-permissions` and `--unattended` are refused until their native mapping is verified; `--print-command` previews without writing.
3717
3792
 
3718
3793
  The launcher writes exactly `plugin.json`, `mcp_config.json` and `hooks.json` in `.agents/plugins/agent-hub/`, refusing linked or foreign entries before any write, and removes only its own matching files when the TUI ends. It never writes project-level `.agents/hooks.json` or the person's `~/.gemini` settings. Run `ahub init` afterwards to ignore the untracked plugin directory; a tracked plugin is written with a warning. The native model sees the hub tools under the server name `agent-hub_agent-hub`.
3719
3794
 
@@ -3952,7 +4027,12 @@ Capacity defaults to one per peer. Each declared instance has its own `tasks.wip
3952
4027
  uses the existing machine-local registry path; integers 0..32 apply live (the next
3953
4028
  admission reads the current value), with 0 disabling planner admission for that
3954
4029
  peer. Status and the agent legend count
3955
- owned unfinished work plus unowned planner-pull reservations. The same limit gates
4030
+ owned unfinished work plus unowned planner-pull reservations, and show how that
4031
+ count splits (#784): `ahub status` prints `<peer> planner WIP 3/3 (active 2, review-wait 1, reserved 0)`,
4032
+ the console Peers detail (Enter) has a `planner WIP` line, and the dashboard legend shows the same split.
4033
+ `active` is owned work that is not waiting for review, `review-wait` is owned work in review (it still
4034
+ holds its slot), `reserved` is a planner-pull reservation nobody owns yet; the three add up to the count
4035
+ admission reads, and the split changes no admission decision. An older hub shows the plain count. The same limit gates
3956
4036
  acceptance and self-claim in every lifecycle mode, so 0 refuses new acceptances; a
3957
4037
  current owner's redo of sent-back work keeps its slot (#481 S9).
3958
4038
 
@@ -4209,6 +4289,15 @@ restart the hub. For example, a reviewer can use:
4209
4289
  Start them with `ahub codex --as codex-review` and
4210
4290
  `ahub kimi --as kimi-review`. Codex keeps its native TUI and its own hub-owned
4211
4291
  app-server proxy; extra Kimi, Qwen, Jcode, Pi and local instances use headless starts.
4292
+
4293
+ An extra instance of any kind that holds the `implementer` role needs its own
4294
+ registered worktree on an attached branch, at exactly `.worktrees/<id>`
4295
+ (`"worktree": ".worktrees/<id>"`). Configuration validation refuses a missing one
4296
+ only for Claude channel instances and headless writers. For other kinds, Codex and
4297
+ Jcode included, the hub accepts the declaration at startup and then refuses the start
4298
+ with `an extra implementer requires an explicit managed worktree`. Create and
4299
+ register the worktree before the start; the hub never creates it.
4300
+
4212
4301
  Extra Jcode instances are supported (#685), in `.agenthub/config.local.json` only:
4213
4302
 
4214
4303
  ```json
@@ -4276,11 +4365,21 @@ A headless Claude implementer (#525-4) is declared separately:
4276
4365
  "claude-impl": { "kind": "claude", "roles": ["implementer"], "start_mode": "headless", "worktree": ".worktrees/claude-impl", "permission_mode": "ask" }
4277
4366
  ```
4278
4367
 
4368
+ A headless Claude implementer may name its model (#844): `"model": "claude-sonnet-5-5"` on its declaration passes `--model <value>` on every native turn, resumed ones included. It is one bounded word (letters, digits and `._:/@+-`, up to 128 bytes, no spaces and no leading dash) and is accepted only on a headless implementer, only from the untracked `config.local.json`. A tracked-file declaration is ignored with a reason, and the key is tier 3: the dashboard and a settings session cannot read or change it, only the console or your editor. Unset, the argv is unchanged and Claude uses its own default. `ahub status` shows the model on the instance row as `model source`. Declarations are captured once when the hub daemon starts, so a changed or newly added `model` takes effect after a hub daemon restart (`ahub restart`); restarting only the instance keeps the old model.
4369
+
4279
4370
  Create and register the attached-branch worktree first; the hub never creates it implicitly. `ahub claude --as claude-impl` asks the hub to start the instance and returns. Idle holds no native child. Task deliveries use a persisted UUID per task: first `--session-id`, later `--resume`. Unscoped chat reuses a session only when exactly one task is in progress for the instance; otherwise it is ephemeral. Positive missing-transcript evidence starts fresh with a notice; unknown read evidence retains the id. Reset includes the per-peer session map. A child inbox read refuses a queued batch attributed to another task and leaves it queued for that task's own session.
4280
4371
 
4281
- Both permission modes use native `default`, managed settings/hooks and the instance's strict tools-mode MCP binding. `ask` routes writes and shell calls to the person's approval. `ask-when-needed` may explicitly allow safe project file tools through the facts hook in the execution root; protected configuration, paths outside that worktree and shell/git writes still ask. There is no `acceptEdits` or `never-ask` bypass. A failed hook decides nothing and may be followed by a valid person allow. Prompt failure, timeout, disconnect and a stale child binding never grant permission. Approvals use the existing console and `approvals.timeout_s`; no answer returns deny. A stop holds uncertain delivery until the owned process group is confirmed gone; a failed cleanup keeps the peer busy.
4372
+ Both permission modes use native `default`, managed settings/hooks and the instance's strict tools-mode MCP binding. `ask` routes writes and shell calls to the person's approval. `ask-when-needed` may explicitly allow safe project file tools through the facts hook in the execution root. For Bash, #845 also permits proven read-only commands and the closed task command forms below, through a host-owned execution wrapper. Protected configuration, paths outside that worktree and unproven shell commands still ask. There is no `acceptEdits` or `never-ask` bypass. A failed hook decides nothing and may be followed by a valid person allow. Prompt failure, timeout, disconnect and a stale child binding never grant permission. Approvals use the existing console and `approvals.timeout_s`; no answer returns deny. A stop holds uncertain delivery until the owned process group is confirmed gone; a failed cleanup keeps the peer busy.
4373
+
4374
+ Under #845, automatic Bash grants require a current public task and the instance's registered worktree. The exact task forms are `scripts/check.sh --changed`, `bun run build`, `bun test <explicit test/*.test.ts paths>`, `git add <named paths>`, `git commit -m <message>`, `git push origin <bound branch>` or `git push -u origin <bound branch>`, and `gh pr view|checks <bound branch>`. `gh pr create` requires `--base <default> --head <bound branch> --title <title>` and exactly one `--body <text>` or `--body-file <relative path>`. Operators, expansion, extra flags, force/refspec/delete pushes, another remote/branch, background execution or a model-supplied cwd ask. Enabled publish/destroy/install/external-write policy classes still ask. Unknown default-branch evidence also asks.
4282
4375
 
4283
- 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.
4376
+ The task's `refs.branch` is authoritative. Otherwise the first eligible task-command request records the current non-default/non-main/master attached branch through `Tasks`, provided no other open task uses it. A host history entry binds the worktree too; restarting preserves it, and a changed branch or root asks rather than rebinding. Each automatic allow contains only the fixed wrapper and one short-lived single-use ticket. The host rechecks the task, child, root, branch and command proof before executing with its fixed environment and absolute executable; native shell cwd/PATH/config variables do not carry over. Unknown, expired, reused or stale tickets run nothing. Read-only and executable code commands (check/test/build) use the existing local-worker Seatbelt profile: writes only in the worktree and own temp, with all project Git metadata read-only, credential/hub-state denial and the configured network policy. These commands ask if macOS sandbox-exec is unavailable. Only closed host Git add/commit/push and gh operations run outside that profile. Push execution pins both source and destination as refs/heads/<bound>:refs/heads/<bound>; configured remote.origin.push/mirror asks. PATH excludes project/worktree directories, gh child Git uses the core hardening config, and body-file uploads use bounded screened snapshots. An older plugin without the wrapper protocol keeps person approval. [The native context receipt](verification/2026-10-26-headless-claude-shell-context.md) records which native legs have actually been observed.
4377
+
4378
+ Check/test/build cannot write the project Git common/current directory at all, including objects, HEAD/index, refs/logs, other worktrees and either submodule tree. Closed host add/commit reuse core Git hardening, ask with configured filters/external drivers/textconv, and force signing/automatic maintenance off. A global Git LFS installation does not disable push/gh grants; only add/commit keep person approval so LFS conversions are not silently bypassed. Automatic push still does not run pre-push hooks. Config includes and partial clones refuse privileged Git/gh. Their command/paths/current task/branch are re-proved before execution. The host pins the default branch from the remote at startup, using a bounded isolated-config Git symref probe against the captured GitHub repository. Local origin/HEAD changes cannot redefine it. No credentials are supplied to that probe; unavailable/private-remote evidence or an unsupported Git layout asks. local.bash_network "direct" gives code direct network access. A setsid-detached code child can outlive owned-group cleanup while retaining its sandbox and permitted worktree writes.
4379
+
4380
+ Project Git retains the core policy that clears credential.helper and core.askpass; an HTTPS push needing those helpers may fail, rather than importing credentials or relaxing that policy. Task branch/config proof reads are bounded synchronous reads (1.5 seconds per reader); slow or unknown evidence asks. The existing sandbox profile discovery is also synchronous; moving it to an asynchronous inventory proof with a shared deadline remains a follow-up. The nonce is visible in a permission response, so it proves a bounded grant rather than native-dispatch provenance.
4381
+
4382
+ The [native permission receipt](verification/2026-10-23-headless-claude-permissions.md) covers Claude Code 2.1.309, and the [2.1.310 receipt](verification/2026-10-26-claude-implementer-2.1.310.md) repeats it on 2.1.310 (with 2.1.309 as a control), each with a deterministic loopback fixture and no real account/model call. It is not sandbox proof or a runtime receipt gate. The real account/model/person interaction remains a separate live leg.
4284
4383
 
4285
4384
  A headless Claude reviewer is run by the hub instead (#223): declare
4286
4385
  `"claude-review": { "kind": "claude", "roles": ["reviewer"], "start_mode": "headless" }`
@@ -4308,15 +4407,19 @@ From 0.12.37 reviews can rotate across fresh headless Claude and Codex reviewers
4308
4407
  "codex-review": { "kind": "codex", "roles": ["reviewer"], "start_mode": "headless", "review_mode": "fresh" },
4309
4408
  "kimi-review": { "kind": "kimi", "roles": ["reviewer"], "start_mode": "headless", "review_mode": "fresh" }
4310
4409
  },
4311
- "review": { "pool": ["claude", "codex", "kimi"], "rerotate": false }
4410
+ "review": { "pool": ["claude", "codex", "kimi"], "rerotate": false, "same_family": "fallback" }
4312
4411
  }
4313
4412
  ```
4314
4413
 
4414
+ `review.same_family` (#820) is `"fallback"` (the default) or `"never"`. With `"fallback"` a reviewer of the owner's family is chosen only when no other family is eligible, and the board records a same-family fallback. With `"never"` routing never selects one, in the pool and in the legacy path: the planner reviews when it holds the reviewer role, is not the owner, is of another family and is not blocked, otherwise the person does (`reviewer = "user"`, recorded as blocked), including when every reviewer is offline: an approval without review never happens under `"never"`. A manual `ahub task assign <id> <peer> --role reviewer` of a same-family peer is refused with `same family as <owner>; review.same_family is never`. `ahub route explain <id>` names the rule in its trace. Use `"never"` when the workspace review gate needs the reviewer to be a different AI than the implementer; a Jcode running a Claude model is still family `jcode`.
4415
+
4416
+ A reviewer has a review scope (#852): `full` (reads the code, runs the affected tests, reads CI) or `code` (reads the code only). The fresh Codex and Kimi reviewers run in a sandbox that cannot run `bun test` or read CI, so they default to `code`; every other reviewer is `full`. A `code` reviewer's review request replaces the check and exact-head CI lines with one: its sandbox cannot run tests or read CI, review the code against the plan, and the merger gates on exact-head CI. Findings are only defects in the reviewed code, docs or tests; a missing check result, CI result, test run or PR/branch identity belongs in `unmet`, never in `findings`. A `code` reviewer's approval and verdicts record `reviewScope: "code"` in the task history. Its `changes_requested` is always recorded and never refused: with at least one structured finding whose disposition is `open` it counts as a rejection as usual; without one it is recorded as evidence-only, which does not count toward `rejections` or escalation, still returns the task to its owner, and sends the conductor one task notice with ids only. The review request therefore asks a `code` reviewer to report every code defect as a structured finding with an id. A person's verdict is never evidence-only. The scope is a fixed property of the definition. To change it for one instance, set `"review_scope": "full"` or `"code"` on that fresh-reviewer instance in the untracked `config.local.json` (a tracked declaration is ignored with a reason; the key is tier 3, so the dashboard and settings sessions cannot read or change it, and a hub daemon restart applies it). `ahub status` shows `review scope` on each reviewer row. A task approved by a `code` reviewer is merged only after exact-head CI is green and no review thread is unresolved, and the merge note names the CI run.
4417
+
4315
4418
  Start them with `ahub claude --as claude-review`, `ahub codex --as codex-review` and `ahub kimi --as kimi-review`; stop with `ahub stop <id>`. For the Claude and Codex reviewers the command asks the running hub to start the reviewer and returns: nothing runs in that terminal, it takes no launch arguments, and `--print-command` or `--dry-run` prints a hub-start note instead of a launch (#720). Fresh Claude and Codex variants share the project checkout; the Kimi variant runs in a clean temporary working directory and reviews only the screened brief and host facts. All fresh variants refuse a custom worktree or permission mode. Codex and Kimi require `review_mode: "fresh"`; without it, an existing headless instance with the reviewer role keeps its native app-server/ACP behavior, worktree and permission options and does not join this pool. Claude retains its #223 automatic headless-reviewer declaration. The agy reviewer slot remains automatically unavailable (`agy is an implementer peer (#485)`). A failed or unsupported fresh start never downgrades to a native continuing session. Ordinary implementer instances retain their own adapters. The default logical pool order is Claude, Codex, Kimi. Only configured kinds with at least one declared reviewer count as slots, including declared ones that are offline, paused, quota-limited or failing, so availability never renumbers them; configured kinds with no declaration are listed after the walk as `no declared reviewer`. `review.rerotate` defaults to false. Pool selection starts at PR number modulo the number of declared kind slots, or task id when there is no PR, then skips the implementer's family and unavailable candidates. Delta review prefers the preceding eligible independent family in a fresh child. A preceding same-family fallback does not outrank a newly available independent family. `ahub route explain` shows the same pure walk.
4316
4419
 
4317
4420
  Codex requires a verified `codex-cli` (0.146.0, 0.163.0 or 0.164.0) or one the person trusts on this machine, and a passing bounded runtime receipt before its model child starts. 0.146.0 and 0.163.0 passed the [2026-10-22 model-free production admission](verification/2026-10-22-codex-reviewer-isolation.md) and 0.164.0 the [2026-10-24 one](verification/2026-10-24-codex-0164-reviewer-isolation.md). A refusal names the installed and verified versions. Either set `codex_bin` to a verified executable, or run `ahub agents trust codex-review <version>`: trust lifts only the version pin, and every proof below still runs before each child (#691). Each launch approves exactly `hub_review` and `hub_task_show` per tool (`approval_mode = "approve"`), never through a server-wide default: without it Codex 0.164.0 listed the tools but refused the call, so reviews ended with no verdict (#783). A model-free receipt proves the inventory, not that a verdict reaches the hub; a [model-backed 0.164.0 run](verification/2026-10-25-codex-0164-reviewer-tool-call.md) recorded one verdict through that path. The producer checks native binary/config stability, configured MCP inventory with only the two hub tools, effective Apps/plugins/hooks disablement, an explicit empty legacy `notify` command and a named filesystem profile. Apps needs separate feature readback because configured inventory omits runtime connectors. Web search is requested disabled; its effective turn mode is not read back and managed requirements can select Cached (see the verification ceiling). Each launch proves public-read, exact/glob/symlink private-denial, standard host credential denial and write-denial in temporary fixtures. Standard credential and configured tool-home/XDG subtrees are denied independently of project `local.deny`. Its sandboxed shell is filesystem read-only with private paths denied and network configured disabled; it is not Claude's Read/Grep/Glob-only tool whitelist. No credential home is copied.
4318
4421
 
4319
- Kimi requires a compat-admitted Kimi Code on the hub's PATH (receipt-verified 2.2.0, 2.3.0 and 2.3.1), signed in through its managed OAuth provider, and a passing runtime preparation before each child; an unverified/untrusted version or a failed preparation is unavailable with its reason. The [2.3.0](verification/2026-10-22-kimi-qwen-compat.md) and [2.3.1](verification/2026-10-25-kimi-231-reviewer-compat.md) receipts are model-free and do not verify real login/provider readiness. Each review runs a new `kimi -p` child without Seatbelt, in a private `KIMI_CODE_HOME` with managed-provider-only config, a clean temporary working directory and an empty skills directory. Its agent may call only `hub_review` and `hub_task_show`, through a task-bound loopback MCP session that never carries the hub's control token, and has no native read, file or shell tool: it reviews the screened brief and host facts only. Its `credentials/`, `oauth/` and `device_id` link to the person's native Kimi files, so it shares the native login, refresh lock and the Kimi account's budget; no token is copied. A stop revokes its verdict authority first, then waits briefly while an OAuth refresh holds the lock; a forced stop mid-refresh may require a native re-login. The Kimi backend records no measured token counts here, so its usage stays unknown; the 2.3.0 receipt makes no usage claim. This is a native tool-authority boundary, not filesystem or network isolation.
4422
+ Kimi requires a compat-admitted Kimi Code on the hub's PATH (receipt-verified 2.2.0, 2.3.0, 2.3.1 and 2.3.2), signed in through its managed OAuth provider, and a passing runtime preparation before each child; an unverified/untrusted version or a failed preparation is unavailable with its reason. The [2.3.0](verification/2026-10-22-kimi-qwen-compat.md), [2.3.1](verification/2026-10-25-kimi-231-reviewer-compat.md) and [2.3.2](verification/2026-10-26-kimi-232-reviewer-compat.md) receipts are model-free and do not verify real login/provider readiness. Each review runs a new `kimi -p` child without Seatbelt, in a private `KIMI_CODE_HOME` with managed-provider-only config, a clean temporary working directory and an empty skills directory. Its agent may call only `hub_review` and `hub_task_show`, through a task-bound loopback MCP session that never carries the hub's control token, and has no native read, file or shell tool: it reviews the screened brief and host facts only. Its `credentials/`, `oauth/` and `device_id` link to the person's native Kimi files, so it shares the native login, refresh lock and the Kimi account's budget; no token is copied. A stop revokes its verdict authority first, then waits briefly while an OAuth refresh holds the lock; a forced stop mid-refresh may require a native re-login. The Kimi backend records no measured token counts here, so its usage stays unknown; the 2.3.0 receipt makes no usage claim. This is a native tool-authority boundary, not filesystem or network isolation.
4320
4423
 
4321
4424
  A dangling or unreadable standard/configured credential root makes the Codex reviewer unavailable with only its path class in the reason (for example `~/.docker` or `$DOCKER_CONFIG`, never an expanded path or contents); repair the root and start the reviewer again.
4322
4425
 
@@ -4397,7 +4500,7 @@ Native launcher connections retry transport timeouts/unclean closes with bounded
4397
4500
 
4398
4501
  ## amem runtime (agent-mem#13 P4)
4399
4502
 
4400
- The hub exact-pins @staix/agent-mem 0.7.0 for recall, curated evidence and local deterministic capture. No runtime call reaches a claude-mem worker; `memory.worker_url` is ignored. The person's `ahub memory import claude-mem` remains explicit and off by default. Existing profile, alias, PII and private-turn fences still apply. `ask --remember` and context checkpoints propose machine-local candidates, never automatic curated evidence. See [memory integration](memory.md#in-process-recall-and-capture-agent-mem13-p4) for changed behavior and bounded-view ceilings.
4503
+ The hub exact-pins @staix/agent-mem 0.10.0 for recall, curated evidence and local deterministic capture. No runtime call reaches a claude-mem worker; `memory.worker_url` is ignored. The person's `ahub memory import claude-mem` remains explicit and off by default. Existing profile, alias, PII and private-turn fences still apply. `ask --remember` and context checkpoints propose machine-local candidates, never automatic curated evidence. See [memory integration](memory.md#in-process-recall-and-capture-agent-mem13-p4) for changed behavior and bounded-view ceilings.
4401
4504
 
4402
4505
  ## Backlog issue identity and notes
4403
4506
 
@@ -4426,3 +4529,27 @@ In the dashboard Memory view (#684 phase 4), select a symbol and enable Code nei
4426
4529
  `hub_memory_graph` includes the same bounded derived context for symbol starts within its existing 6 KB output budget. Task briefs prioritize attached fails and decisions within `brief_items`; the console related-note count refers to that bounded selection. Code facts remain optional: no index is copied into memory, no graph edges are persisted and no PII task/turn starts a query.
4427
4530
 
4428
4531
  Optional code work runs only while Memory is open and visible. A cold console count reports unknown immediately; it does not prepare code facts. Direct task memory remains available when optional code facts are ineligible, interrupted or unavailable. Review/completion queries pre-empt optional native work after its owned process/tree cleanup. Unknown lookups have a five-second retry floor; topology caches are commit-bound and note bodies are refreshed under current policy. These are advisory coverage bounds, not empty-result proofs.
4532
+
4533
+ ## Authority by command
4534
+
4535
+ ### Conductor operations (#812 T2)
4536
+
4537
+ The selected project's configured conductor may run the commands below through its ordinary peer identity. Other peers retain the guidance "the person runs it in ahub console or a terminal". A missing or dead descriptor refuses promptly. For a live descriptor the connect and local authorization handshakes each keep a 3-second bound; connection unavailability is not presented as proof that the peer lacks the conductor role. Actual authority refusals are audited as refused, while ordinary operation failures retain their original cause. The daemon checks the authenticated connection, current role and project membership before effects and after asynchronous boundaries. Roster handoffs carry that same authority check through plan admission, owner delivery and each later task; a saved assignment is never rolled back when delivery is interrupted. Effectful operations and operational history reads retain an `agent_cli` admission with the initiating peer; audit failure refuses those operations. Authorization negotiation, `settings_preview` and `queue show` still check current authority but emit no extra admission record or conductor notice. Admission records are not a unique invocation counter.
4538
+
4539
+ | Command | Conductor scope |
4540
+ | --- | --- |
4541
+ | roster add/remove | Existing roster lifecycle and confirmations; no role or permission-mode grant |
4542
+ | stop peer | Existing hub-owned peer stop, ownership and recovery guards |
4543
+ | queue show/resolve/discard | Public queue projection and existing revision/confirmation guards |
4544
+ | task cancel | Existing hub_task_cancel with the real peer; eligibility follows the task-board policy |
4545
+ | doctor --orphans --kill | Selected project's positively identified hub-owned process only; no cross-project registrations or unsigned process files |
4546
+ | logs / export | Bounded daemon projections, with unsupported/private records withheld; logs -f ends when authority ends |
4547
+ | settings set | Only the twelve existing live registry keys listed below |
4548
+
4549
+ The exact settings keys are `task_sweep.enabled`, `task_sweep.auto_reassign`, `task_sweep.no_writes_turns`, `task_sweep.interval_s`, `task_sweep.unaccepted_min`, `task_sweep.idle_min`, `task_sweep.review_min`, `task_sweep.ready_min`, `task_sweep.ladder_min`, `snapshots.enabled`, `research.enabled` and `approvals.notify`. New keys receive no authority by prefix. Generic roster setting writes still use the dedicated roster lifecycle. Conductor setting records name the initiating peer, and conductor writes preserve the person's pending undo record. An intervening write can still invalidate that undo through the existing file-digest guard; preserving the record does not bypass that guard. Permission modes, roles, `pet.window`, arbitrary configuration reload and undo remain person commands.
4550
+
4551
+ Log history is limited to the last 100 retained lines and export to the last 1000 retained events, each within a 128 KiB read. Truncation is explicit. An incomplete public projection reports one bounded reason on stderr, preserving export JSON on stdout. The complete privacy inventory has no 500-task cutoff and is cached until board or privacy/config revisions change, so log-follow does not rebuild it for every line. Export accepts closed metadata shapes; unknown fields and nested records are withheld rather than copied. Task titles, paths and private delivery bodies are not part of this projection. It does not change the person's ordinary logs/export output.
4552
+
4553
+ Orphan cleanup requires a missing selected root, signed process identity and the host-known hub executable/entrypoint and argv. An inaccessible root or unavailable proof remains unknown; there is no offline config-based role grant. The daemon rechecks authority and process ownership before signals. A confirmed self candidate uses the existing lifecycle stop after its reply. A roster write already committed before authority ends is acknowledged as saved, without authorizing further lifecycle work or commands.
4554
+
4555
+ `restart --yes` and `recovery resume/abort` await a project-scoped continuation proof. `task unassign` follows #481, and legacy models start/stop awaits a project that needs it. This slice adds no task eligibility rule and no MLX/recovery-driver changes. Person authority and the separately authorized `--live-test` path retain their existing behavior.
package/docs/profiles.md CHANGED
@@ -56,3 +56,39 @@
56
56
  - `role_text` is bounded guidance for existing flow roles (2000 characters each); it grants no authority. `catalog_roles` supplies bounded compatible guidance ids; the existing explicit catalog selections take precedence. Actual flow maps remain in `roles` and retain their existing validation.
57
57
  - `agents` replaces only the managed AGENTS block (16000 characters maximum); surrounding instructions remain. Init previews contain paths/actions only. `example_checks` is at most 16 display strings of 2000 characters, shown as advisory examples by init and never selected as executable checks. `review_checklist` is at most 16 non-empty strings of 500 characters, customizing the checklist items in review requests with `{target}` and `{check}` interpolations. Existing `checks` trust remains authoritative.
58
58
  - No override mutates shipped profile data or another project. Unknown fields, base remapping and invalid/oversized data are refused. Headers are quoted. Daemon/MCP restart is required for config changes; no native model consumption or research measurement is claimed.
59
+
60
+ # Profile scorecard (#799)
61
+
62
+ The existing ahub report --dogfood command adds a profile section when the selected profile is non-dev, or bound non-dev tasks have evidence in the requested window. Dev-only JSON and readable output retain their prior bytes. Existing flow, quality, cost and other fields keep their meanings; the profile section does not require merged PRs.
63
+
64
+ Rows include profile totals and class-alias groups with the stored base class. A task keeps its recorded profile across later configuration changes. Public custom aliases remain bounded identifiers; private custom aliases are withheld. Task titles, paths, notes, checklist prose and model text are not exported. Private pii/pii_path tasks are counted by id and number.
65
+
66
+ A missing, incomplete or unsupported source produces JSON null, rendered as unknown in the readable report. A complete source can prove zero recorded events; it cannot prove zero events for a category that has no recorder. Source completeness and flow scope distinguish history-backed totals from recorded-window observations. Profile bindings, including event-only fallback, are capped at 500; a capped window reports incomplete/unknown totals rather than extrapolating. Automatic approvals are flow transitions, not reviewer rounds or first-pass review samples.
67
+
68
+ | Row group | Existing evidence | Unmeasured ceiling |
69
+ | --- | --- | --- |
70
+ | Flow | Bound task history and complete task-event windows; observed automatic approvals | Undated approvals, incomplete history and first-pass review without history remain unknown |
71
+ | Review | Explicit verdicts and structured public unmet-criteria ids | Profile checklist prose has no item-id recorder; private criterion ids are withheld |
72
+ | Overlap | Recorded path/section notices and later matching pair/path edit facts | Observed path-edit pairs imply no authorship or causation; section edit confirmation remains unknown |
73
+ | Deliverables | Deduplicated ids in recorded deliverable histories | No new file reads; changed-since-approval and current unknown states remain unknown |
74
+ | Privacy | Private task flags and generic attributed PII note-screen records | Screen records do not identify the operation; memory drops, refusals and capture totals remain unknown without operation-specific counters |
75
+ | Undo | No additional recorder introduced | Request, completion and guard-refusal totals remain unknown |
76
+ | Snapshots | Already-recorded, attributed snapshot durations | No new recording; budget exhaustion and absent durations remain unknown |
77
+ | Profile errors | No additional recorder introduced | Unknown/foreign alias-refusal totals remain unknown |
78
+ | Cost | Existing task-attributed token/usage summaries | Missing usage and attribution remain unknown; no provider/model prose is exported |
79
+
80
+ Research rows also expose bounded current-plan source and claim counts per task. Claim slots are anonymous ordinal numbers; declaration text and identifiers are never exported. In a stored board plan a key the plan omits counts as zero declarations, as an explicit empty list does; a cut or malformed plan leaves that one task's declarations unknown without affecting the rest of the board. These are current-plan counts, not a reconstruction of past plans.
81
+
82
+ Each research class alias retains its own recorded task-check pass/fail counts under `observedCheckOutcomes`. Those history events do not record which check key ran. Consequently `factCheck` pass/fail, check selection and missing-key counts per run remain unknown until that provenance is recorded. No new telemetry is collected. Model labels, person verdicts, claims reaching a person verdict and per-label agreement also remain unknown because no claim-level verdict source exists. Task approval never supplies a claim verdict. Research uses the same recorded section-overlap rows as writing.
83
+
84
+ The weekly writing, general and research receipt templates are in the verification/profiles directory. Copy only the emitted profile subsection, preserve null values, and include no project/person names or paths. A template is not a completed measurement or a live acceptance claim.
85
+
86
+ The following verification claim ids collect the still-unverified statements above. A weekly receipt gives each a verdict of supported, partly supported or not supported, tied to recorded ids and remaining unknowns. Profile counts alone do not prove native ingestion, context retention or account acceptance; those verdicts need corresponding native/account receipt ids and independent judgment. None becomes verified merely because this reporting section exists:
87
+
88
+ - writing-live-example: an observed writing workflow.
89
+ - general-live-example: an observed general workflow.
90
+ - research-live-example: the live three-source/two-agent research example.
91
+ - native-model-ingestion: native peers consuming profile guidance and tasks.
92
+ - context-retention: profile/task context retained through the relevant native turns.
93
+ - native-account-research: a real native/model/account research workflow.
94
+ - research-measurement: observed research-profile measurements.