@staix/agent-hub 0.12.35 → 0.12.37

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 (71) hide show
  1. package/CHANGELOG.md +25 -0
  2. package/README.md +15 -8
  3. package/docs/agent-notes/adapters.md +9 -2
  4. package/docs/agent-notes/budget.md +3 -2
  5. package/docs/agent-notes/bus.md +3 -0
  6. package/docs/agent-notes/daemon.md +11 -3
  7. package/docs/agent-notes/tasks.md +14 -1
  8. package/docs/agent-notes/tests.md +6 -2
  9. package/docs/memory.md +3 -1
  10. package/docs/operations.md +74 -18
  11. package/docs/quickstart.md +7 -9
  12. package/docs/sdlc.md +72 -5
  13. package/docs/security.md +11 -3
  14. package/docs/smoke.md +18 -2
  15. package/docs/specs/2026-09-19-agent-hub-design.md +108 -3
  16. package/docs/verification/2026-10-19-0.12.36.md +63 -0
  17. package/docs/verification/2026-10-19-0.12.37.md +60 -0
  18. package/docs/verification/2026-10-19-jcode-0.98.1.md +30 -0
  19. package/docs/verification/2026-10-19-reviewer-isolation.md +119 -0
  20. package/docs/verified.json +23 -15
  21. package/package.json +2 -2
  22. package/plugins/agent-hub/.claude-plugin/plugin.json +1 -1
  23. package/plugins/agent-hub/server.js +2843 -1632
  24. package/src/adapters/acp.ts +3 -2
  25. package/src/adapters/claude-print.ts +92 -0
  26. package/src/adapters/codex-review-isolation.ts +276 -0
  27. package/src/adapters/definitions/builtin-metadata.ts +20 -1
  28. package/src/adapters/definitions/native-cli-entry-claude.ts +17 -3
  29. package/src/adapters/definitions/native-cli-entry-jcode.ts +3 -2
  30. package/src/adapters/definitions/native-cli-entry-kimi.ts +3 -2
  31. package/src/adapters/definitions/native-cli-entry-types.ts +2 -0
  32. package/src/adapters/definitions/native-factories.ts +24 -0
  33. package/src/adapters/definitions/native-policy-aux.ts +1 -1
  34. package/src/adapters/definitions/reviewer-variants.ts +32 -0
  35. package/src/adapters/jcode.ts +20 -5
  36. package/src/adapters/registry-types.ts +13 -3
  37. package/src/adapters/registry.ts +29 -5
  38. package/src/adapters/reviewer-print.ts +319 -0
  39. package/src/cli/help.ts +7 -0
  40. package/src/cli/identity.ts +2 -1
  41. package/src/cli/launch.ts +19 -6
  42. package/src/cli/main.ts +57 -4
  43. package/src/cli/memory.ts +13 -12
  44. package/src/cli/reset.ts +4 -2
  45. package/src/cli/statusline-tee.ts +5 -2
  46. package/src/cli/terminal-recovery.ts +7 -2
  47. package/src/cli/upgrade-runtime.ts +18 -15
  48. package/src/hub/board.ts +61 -11
  49. package/src/hub/bus.ts +11 -5
  50. package/src/hub/claude-state.ts +37 -0
  51. package/src/hub/config-apply.ts +3 -2
  52. package/src/hub/control-client.ts +1 -1
  53. package/src/hub/cost-governance.ts +76 -0
  54. package/src/hub/daemon.ts +269 -87
  55. package/src/hub/envelope.ts +1 -1
  56. package/src/hub/host-executable.ts +1 -1
  57. package/src/hub/hub-tools.ts +25 -1
  58. package/src/hub/instances.ts +29 -5
  59. package/src/hub/memory-profile.ts +67 -11
  60. package/src/hub/planning.ts +2 -0
  61. package/src/hub/release-admission.ts +64 -0
  62. package/src/hub/review-brief.ts +175 -0
  63. package/src/hub/reviewer-pool-config.ts +11 -0
  64. package/src/hub/reviewer-pool.ts +70 -0
  65. package/src/hub/routing.ts +58 -4
  66. package/src/hub/sdlc-release.ts +113 -0
  67. package/src/hub/sdlc.ts +205 -4
  68. package/src/hub/status-roster.ts +10 -0
  69. package/src/hub/task-sweep.ts +143 -0
  70. package/src/hub/tasks.ts +788 -52
  71. package/src/hub/usage.ts +57 -20
@@ -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.12.35
20
+ bun add -g github:STAIxBWLB/agent-hub#v0.12.37
21
21
  ahub setup
22
22
  ```
23
23
 
@@ -30,17 +30,15 @@ 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.12.35
33
+ ## Upgrading to 0.12.37
34
34
 
35
- A Claude session started with `ahub claude` now gets its hub messages while it is busy (#493). Its tool results report how many queued and important messages wait, `hub_inbox` reads the daemon queue even in a channel session, and an important message reaches the running turn instead of waiting for the turn to end. Nothing needs configuring. A Claude session keeps the plugin server it started with, so restart it after the upgrade to get this. `ahub jcode` no longer refuses every start as off roster (#484).
35
+ Restart Claude sessions after upgrading: each keeps the plugin server it started with, and the planner gets the release-train tools (`hub_release_new`, `hub_release_assign`, `hub_release_configure`, `hub_release_cut`, `hub_release_report`); the person uses `ahub sdlc train report|configure|cut` (#481 S11). A due date asks for a cut review and never clears unfinished work; a cut freezes scope and publishes nothing. See [release scheduling](operations.md#planner-release-scheduling).
36
36
 
37
- If `.agenthub/sdlc.toml` sets `workflow` or names a `phase` gate, `hub.log` now warns: remove `workflow`, rename the gate to `criteria`, and record `criteria` receipts, because 0.13.0 refuses all three. An enabled lifecycle now shows playbook stages, and stored `implement`/`verify` receipts read as `build`/`test` (#481).
37
+ Reviews can rotate across fresh Claude and Codex reviewer sessions (#519). Declare headless reviewer-only instances in `.agenthub/config.local.json`; Codex joins only with `review_mode: "fresh"` and an installed Codex 0.146.0 (select it with `codex_bin` when PATH resolves another version). A headless reviewer instance without `review_mode` keeps its native behavior and stays out of the pool. Kimi has no fresh reviewer yet (#533). agy will not be a reviewer: it is planned as an implementer peer (#485) and is not in this release. Naming either 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).
38
38
 
39
- To run extra peer instances, declare them in `.agenthub/config.local.json` after upgrading and restart the hub; an extra implementer needs an existing registered worktree at `.worktrees/<id>` with a branch (#450 I2). See [operations](operations.md).
39
+ 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`.
40
40
 
41
- The upgrade runs with the target release's own coordinator, so an upgrade from 0.12.32 with cancelled tasks and a managed terminal, or with a notice the target sends right after it starts, no longer blocks (#478). If the operation blocks because a restored Codex, Claude or Pi terminal disappeared, the person runs `ahub recovery reattach <operation-id> codex|claude|pi` to relaunch that peer with its recorded session (one relaunch per receipt), then `ahub recovery resume` to finish.
42
-
43
- A task now moves to another peer after the third consecutive `changes_requested` instead of the second (#488). To keep the old threshold, run `ahub settings set tasks.escalate_after 2` in a terminal (1 to 10, applies at the next verdict), or set `"tasks": { "escalate_after": 2 }` in `.agenthub/config.json`. In `ask-when-needed`, Claude, Pi and `local` now ask before writing under `.agent`, `_agents`, `_agent`, `.gemini` or `.grok`, like other agent configuration (#485).
41
+ The hub now pins `@staix/agent-mem` 0.4.0 and opens a split memory home through its shards (#534). 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 not split-aware yet and reports a mismatch for every correctly bound hub on a split home (#536). See [memory profiles and rollout](memory.md#selected-memory-profile).
44
42
 
45
43
  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:
46
44
 
@@ -104,7 +102,7 @@ More: `ahub help`. How it is built and why: [`docs/specs/2026-09-19-agent-hub-de
104
102
 
105
103
  ## Shared memory
106
104
 
107
- The hub exact-pins the first-party `@staix/agent-mem` 0.3.1 core. 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. Generation candidate acceptance is still refused.
105
+ The hub exact-pins the first-party `@staix/agent-mem` 0.4.0 core. 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. Generation candidate acceptance is still refused.
108
106
 
109
107
  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.
110
108
 
package/docs/sdlc.md CHANGED
@@ -12,9 +12,9 @@ The built-in lifecycle follows the [AI-Native SDLC Playbook](https://claude.com/
12
12
 
13
13
  | Stage id | Playbook stage | Acting role | Artifact or exit gate | Current enforcement |
14
14
  | --- | --- | --- | --- | --- |
15
- | intent | Plan | person/planner | intent or backlog identity | shown; acceptance gate pending S1 |
16
- | spec | Design | person/planner | issue identity and scope | shown; acceptance/drift gate pending S1 |
17
- | plan | Build | planner/owner | plan, branch, existing predecessor gates | existing Tasks rules; conformance pending S2 |
15
+ | intent | Plan | person/planner | intent or backlog identity | person spec acceptance required at build entry in enforce |
16
+ | spec | Design | person/planner | issue identity and scope | spec acceptance/body drift checked at build entry |
17
+ | plan | Build | planner/owner | plan, branch, existing predecessor gates | current planner review required at build admission; conformance pending S2 |
18
18
  | build | Build | owner | PR identity/current head and local check on that head | entering review, when enabled |
19
19
  | test | Test | verifier/reviewer | required checks and local gate on the reviewed head | approval, when enabled; integrity/evals pending S4/S5 |
20
20
  | review | Deploy | assigned reviewer | current head-bound verdict, no pending change request/open thread; AC checklist under Compliance | approval, when enabled; structured findings pending S3 |
@@ -29,7 +29,7 @@ Stored `implement` and `verify` stage ids read as `build` and `test`. A write th
29
29
  | Slice | Playbook play | Delivery in this slice |
30
30
  | --- | --- | --- |
31
31
  | S0 | Plan, Design, Build, Test, Deploy, Maintain stage model | Nine built-in bases, canonical ids, stored receipt aliases and playbook labels |
32
- | S1 | Accepted intent/spec and spec drift | Pending; issue metadata acceptance is not yet a hub gate |
32
+ | S1 | Accepted intent/spec and spec drift | Person-only `ahub sdlc accept`, bound to the issue's body edit time; build entry needs it in enforce (see Spec acceptance below) |
33
33
  | S2 | Accepted plan and diff conformance | Pending; existing task plans remain authoritative without a conformance readback |
34
34
  | S3 | Bugs, Security and Compliance review passes | Pending structured findings; existing reviewer separation and head-bound review remain |
35
35
  | S4 | Red-first tests and test integrity | Pending receipts/gates; repository test-integrity rules still apply |
@@ -37,6 +37,8 @@ Stored `implement` and `verify` stage ids read as `build` and `test`. A write th
37
37
  | S6 | Production approval gate | Pending person-only release authorization; release report stays read-only |
38
38
  | S7 | Metrics, deterministic control bands and follow-up intent | Pending; no automated metric triage or security baseline claimed |
39
39
  | S8 | Compliance acceptance-criteria checklist | Head-bound checklist retained; legacy workflow and phase compatibility deprecated; task import removed |
40
+ | S9 | Planner grooming and plan review | Read-only digest, current plan-review receipt at build admission, all-mode WIP and accepted-untouched return |
41
+ | S10 | Planner cost governance | Typed review/CI scope, premature-review admission, identical-tree CI reuse and read-only cost report |
40
42
 
41
43
  S0/S8 establish vocabulary and checklist compatibility only. Later slices must retain the same authority contract: the issue is the spec, Tasks owns mutations, GitHub owns PR/check/tag facts, and the hub stores bounded identities, timestamps, hashes and counts instead of remote bodies. No slice writes GitHub or grants capabilities. Advise shows readiness without blocking; enforce applies only the named implemented gates. A full live advise cycle through S1, merge, release and S6 authorization remains pending and cannot be certified by S0/S8 source tests.
42
44
 
@@ -62,7 +64,7 @@ The acceptance-criteria checklist belongs to the Compliance pass over the same b
62
64
 
63
65
  ## Delivery and verification state
64
66
 
65
- Head-bound review/approval gates, bounded custom definitions, read-only merge/release reports and the S0/S8 vocabulary/checklist migration are implemented in source. S1-S7 and the native advise-through-merge/release cycle remain pending. Installation, native acceptance and release authorization require separate receipts.
67
+ Head-bound review/approval gates, bounded custom definitions, read-only merge/release reports, S0/S8 vocabulary/checklist migration, S1 spec acceptance and S9 planner admission and S10 cost governance are implemented in source. S11 release-train governance ships in 0.12.37. S2-S7 and the native advise-through-merge/release cycle remain pending. Installation, native acceptance and release authorization require separate receipts.
66
68
 
67
69
  ## Initial commands
68
70
 
@@ -72,6 +74,12 @@ A person records ids and a claimed local receipt with `ahub sdlc record 12 '{"re
72
74
 
73
75
  `ahub sdlc status` lists bounded local projections without polling every remote PR. `ahub sdlc explain 12` or `status 12` performs a fresh structured readback, with a console-only boundary and no remote body retention. Shared console/dashboard projections explicitly show unknown external evidence; they do not certify freshness from cached data. Default task rows and contracts remain unchanged when off.
74
76
 
77
+ # Spec acceptance (#481 S1)
78
+
79
+ - The person accepts a task's spec issue with `ahub sdlc accept <task> --issue <n> [--repo owner/repository]` (the repository defaults to the task's receipt). Agent shells and live tests are refused. The hub reads the issue's `lastEditedAt` and `updatedAt` through a fixed GraphQL query and stores them with the issue identity and the time of acceptance; it never reads or stores the body. `hub_sdlc_record` cannot write an acceptance, and relinking the receipt to another repository or issue drops it.
80
+ - Build entry is the `implement` or `bulk_edit` task's acceptance (`hub_task_accept`), or a recorded stage change into a build stage by anyone but the person. In enforce, build entry needs a current acceptance: the hub rereads the issue and refuses with `spec acceptance missing`, `spec changed after acceptance` (the body was edited; comments only move `updatedAt` and do not count), `spec edit time unknown` (the readback failed) or `spec issue unknown` (the receipt names no issue). Advise posts the same text as a notice and lets the work start; off reads nothing.
81
+ - `ahub sdlc status <task>` and `explain` show `spec: {issue, state}` (`missing`, `current`, `changed` or `unknown`) when the lifecycle is enabled and the receipt names an issue. The spec state is not a merge-readiness blocker: its gate is build entry.
82
+
75
83
  # Acceptance-criteria checklist
76
84
 
77
85
  - A task's reviewer or the person records a head-bound `criteria` receipt through `hub_sdlc_record` or `ahub sdlc record` (an off or older daemon's tool schema still offers `phase`). Its ids name the issue's acceptance criteria. The board owns the receipt and its actor/history; no writable STATE/PLAN/HANDOFF file is introduced.
@@ -193,3 +201,62 @@ After dashboard authentication, those two delivery operations get a 35-second HT
193
201
  The connection idle setting is not a guaranteed 10-second deadline for an active handler. The actual HTTP fixture records that an ordinary action also completes after 11.5 seconds; that legacy behavior is retained. The owner approved correcting only the newly added test's mistaken ordinary-action failure expectation. Manager control request caps are separate from HTTP connection idle behavior. Delivery HTTP results omit the internal control-request rid.
194
202
 
195
203
  - Runtime doc checker ships explicitly in the npm tarball and package verification asserts its presence. Merge queue criteria readiness derives each holder's current board checklist view; absent or blocked required checklists remain unknown/blocked.
204
+
205
+ # Planner plan review (#481 S9)
206
+
207
+ A configured planner reads `hub_planner_groom` (or the person runs `ahub backlog groom`) before choosing build work. The digest reports missing text-only dependencies, repository-scoped duplicate issues and path/symbol overlaps, over-WIP owners, recorded supersession, issue edit/closed-state evidence and capability, queue or offline blockers. It proposes no board mutation. Missing repository or issue evidence stays unknown; the planner still evaluates issue rulings and the person's priorities. Private task text and references are withheld.
208
+
209
+ The planner records `hub_plan_review` with the task id, current `planningRevision` and a reason. The person's CLI is `ahub sdlc plan-review <task> <revision> <reason...>`. Tasks records the actor, planning revision, observation time and host-read issue identity/edit time. The fixed GraphQL query requests metadata only, never an issue body or comments; comment-only activity does not invalidate the body-edit receipt. Caller-supplied timestamps are refused. A changed revision requires another review except for the complete contiguous release-only history described under S11; issue identity/edit and cost-scope checks still apply. An issue edit requires another review; unavailable metadata never counts as a current review. The native CLI cannot delegate this person-attributed write through `--live-test`.
210
+
211
+ For implementation/bulk-edit tasks and an explicit build-base stage, enforce refuses an offer or acceptance without a current receipt. Advise reports the gap and off performs no lifecycle gate. Spec acceptance and planner review are independent readbacks bound to one task snapshot before acceptance. The ordinary synchronous acceptance path cannot bypass the enforce readback. A self-claim can remain assigned and proposed while its prerequisites are missing, with no offer or accepted event. A saved planner review can release previously held proposed work; delivery failure leaves that saved receipt intact.
212
+
213
+ Two board rules apply in every lifecycle mode:
214
+
215
+ - Acceptance and self-claim, including a `done` that implicitly accepts proposed work, respect the owner's effective planner WIP limit. The target task is excluded when checking capacity, and updating an already accepted plan or resubmitting changes_requested work does not consume another slot, provided its current owner accepted it after the latest ownership change. Another unaccepted offer cannot block that redo. In enforce, a changes_requested resubmission still revalidates spec acceptance and the plan review; an issue body edit requires the person to accept the revised spec and the planner to review it again before re-acceptance or done. A refusal names the owner, limit and open task ids before a board write. The existing planner capacity accounting includes owned open work and active planner-pull reservations.
216
+ - A planner or the person can return accepted but untouched work with `hub_backlog_return_untouched`, or `ahub backlog return <task> <revision> <reason...>`. A done/check/review/integration entry or recorded branch, commit, PR or local/review head refuses the return. History and planning fields remain; former role requests are withdrawn through the existing delivery path. A successful return permits replanning and pulling again without erasing historical acceptance. General unassignment and cancellation of accepted work remain person-only. Planner changes to pinned or frozen work remain refused.
217
+
218
+ Enabled lifecycles send an attached planner one grooming pointer for a changed board revision; a queued pointer is not duplicated. Proposal, approval and planning writes flow through the existing revision and task-sweep notice path. External issue changes are read during grooming, offering and accepting, rather than by a separate watcher.
219
+
220
+ The digest is bounded to 500 task rows (open work plus structured cancelled supersession evidence) and 256 findings, with at most eight issue metadata reads sharing four seconds. Spec acceptance and planner-review staleness are compared independently. Truncation and unavailable evidence are explicit. Qualified foreign repositories are compared within their own key. Without a proven repository key an absolute outside-project path remains unknown; the dedicated repository-overlap resolver is tracked in #476.
221
+
222
+ S9 ceilings: a private build task with an issue reference cannot satisfy enforce admission: privacy forbids the external issue metadata read, and no person override for a metadata-free plan receipt exists. Grooming pointers are coalesced only while queued, per board revision; there is no minimum time interval after delivery, so a busy board may produce one delivered pointer per revision.
223
+
224
+ # Planner review and CI cost governance (#481 S10)
225
+
226
+ At plan review, batch small related fixes into one area branch and PR; keep release preparation separate from feature work. A plan can name `review: {scope: "none" | "delta" | "full", reviewers: N}` and `ci: "focused" | "full" | "reuse"`. None requires zero reviewers; delta/full require one to eight. These are expected costs, not permission to bypass the task's reviewer, CI or privacy gates. Use one reviewer for a small change; split a large review by area with one receipt owner. After a fix, review the delta against the previous findings. S2 will compare planned scope with completed coverage.
227
+
228
+ The planner can provide scope with `hub_plan_review`; the person can run `ahub sdlc plan-review <task> <revision> <reason...> --review full --reviewers 1 --ci full`. Tasks records the explicit scope on the plan and its revision-bound receipt. A file-list update retains omitted cost fields; an explicit scope change cannot replace a reviewed scope during enforce acceptance. Old plans and receipts remain unspecified and are never filled by guessing. Advise reports missing scope. In enforce, every newly recorded receipt needs both scopes; an existing scope-less receipt remains valid until a non-release planning revision changes, with the same complete-history exception under S11. Enforce acceptance cannot add owner-supplied cost keys to that legacy receipt; the planner must record an explicit scope first.
229
+
230
+ A review request is premature if its task head does not match the PR head, configured required checks are not green, or a pending bot thread is unanswered. A resolved thread is settled; an unresolved one needs a user reply after its last bot comment. The hub reads identity, timestamps, authorship type and resolution only, never comment bodies. Without explicit required checks the native floor is both OS checks plus `seeded guards`. Advise reports the gap; enforce refuses before the requesting done/cohort writes. A private turn may finish its own PII task in advise with a generic unknown-readiness notice and no remote metadata read; the private-turn refusal for ordinary work remains. Delayed checks revalidate before completing. Background roster/quota role changes remain fact, but enforce holds a new review envelope without an admission readback; refresh it through a manual reviewer assignment after CI and replies are ready. The brief and verdict coverage belong to #520, separate from this admission.
231
+
232
+ CI controllers are serialized across the repository for the entire workflow, without canceling an admitted controller. `queue: max` preserves up to 100 waiting controllers instead of replacing the previous pending run ([GitHub concurrency documentation](https://docs.github.com/en/actions/how-tos/write-workflows/choose-when-workflows-run/control-workflow-concurrency)). This is intentionally stronger than a tree-only lock and retains the required job names and read-only token permissions. Before PR or push gates, `scripts/ci-reuse.mjs` reuses successful full proof for the identical Git tree, including its workflow. On reuse the two OS matrix jobs still expand and report their exact required names, while the seeded aggregate reports its exact name. They run only a `Reuse verified identical-tree full gate` marker step naming the original proof; full setup and gates are skipped. Skipped jobs and successful reuse-reporting jobs never count as fresh full proof. A running identical-tree gate refuses duplicate admission; unknown active coverage refuses safely. Metadata reads are bounded to 100 recent successes/active runs and 240 calls within 60 seconds. Pending serialized controllers are not admitted gates. Run focused tests locally and one full gate at the final head; a known flake warrants only one rerun of its failed job with recorded evidence.
233
+
234
+ `hub_sdlc_costs` and the person's `ahub sdlc costs [owner/repo]` report linked merged PRs without changing the board. A review round is a board done-to-verdict handoff, not each reviewer in a multi-reviewer round. An observed handoff still counts when its declared scope was none. The report counts distinct check-workflow run ids, extra attempts (`run_attempt - 1`), full re-reviews, delta rounds and summed review intervals, with GitHub review counts alongside them. Unspecified scope and unavailable evidence stay unknown/incomplete. Only current public links are projected; a private counterpart withholds the whole PR. Up to 16 linked PR reads share 20 seconds; each PR covers at most 100 commits, reviews and recent check-workflow runs, and the pure aggregator bounds 500 tasks, 200 PRs and 10,000 history entries. Truncation is explicit. S10 exposes pure band-breach proposal data; S7 owns rolling baselines, band configuration, automatic scheduling and board-card creation.
235
+
236
+ S10 policy ceilings are tracked in hub task #230: metadata failures remain fail-closed; the repository-wide queue can delay newer heads and releases; unknown thread author kinds remain unknown in enforce; and advise still retries concurrent state/authority changes. These policies are deferred, with no silent relaxation in this slice.
237
+
238
+ # Planner release trains (S11)
239
+
240
+ Release scope stays on each task's existing `release` planning field. `hub_release_new` creates a future planning row; `hub_release_configure` activates it, sets its planned cut date, or records an enumerated hotfix cause. Only one train may be active or frozen. Future planning rows remain available so unfinished work has a next-train destination.
241
+
242
+ `hub_release_assign` changes only a task's release, including approved or in-flight work. It retains execution state, owner, reservations and dependencies. It requires the current planning revision and a reason. Public reasons are screened; private or pinned work requires the person. A private reason stays on the board with a withheld history marker and never goes to the model screen, including when approved work is rolled out of a train. Public readiness/cut still refuses a scope containing private work; frozen or settled scope cannot move through this operation. Release-only planning revisions retain the S9 plan review only when the complete intervening planning history changes exactly the release field; other planning changes still invalidate it. The existing general backlog planning rules remain unchanged.
243
+
244
+ Use `ahub sdlc train report <release-id>` to see planned task identities, approval readiness, due trigger and recorded release metrics. `ahub sdlc release-assign <task> <revision> <release-id|none> <reason...>` is the person's CLI for assignment. A planner uses the corresponding hub tools under its own identity. `ahub sdlc train configure <release-id> <revision> '{"state":"active","cut":1792500000000}'` activates a future train and optionally sets the UTC epoch-millisecond cut date.
245
+
246
+ A train becomes due when its tasks are approved or its cut date arrives. A date never clears an unfinished task: move it to the next planning row with a reason before cutting. `hub_release_cut`, or the person's `ahub sdlc train cut <release-id> <revision>`, freezes a nonempty, fully approved scope. Scope and authority are checked again after cost/admission reads and in the final database transaction. Cancellation does not count as approval.
247
+
248
+ The cut obtains a short repository lease through the installed host `dot admit` controller, checks the shared gate lock, and retains the lease until scope freeze. The controller is resolved outside the project, its command is fixed, and missing/expired/unknown admission defers. The daemon checks fresh account quota headroom for attached peers with a real source (a registered `budget.reader`, or an enabled soft-token limit), including shared instance accounts; unknown/stale readings from those sources defer. Registered accounts without a configured source report `no quota source` rather than deferring on absent readings; an unknown external account still requires evidence. Budget-record and manual pauses still defer for every attached peer. This metadata lease does not authorize a build. Each later heavy preparation step needs its own admission. The cut neither publishes nor tags, and it never substitutes for S6's person authorization or native verification.
249
+
250
+ A hotfix records one cause and why it cannot wait: `loss`, `security_privacy`, `upgrade_start_blocked`, or `core_flow_no_workaround`. These mean data/message loss, a security/privacy defect, blocked upgrade/start, or a broken core flow without a workaround. Hotfixes keep every scope, quota, resource, review and CI gate. Their branch starts at main and contains the fix and regression only; unrelated work belongs in a future train.
251
+
252
+ Release history records the frozen task ids and actual cut/shipment times. CI resource counts read only the planned scope's PRs at cut (at most 16 distinct PRs in one repository, run attempts included); failed reads or a larger/mixed-repository scope leave the count unknown; board review verdict counts are recorded separately. Missing PR links, incomplete reads and legacy history remain unknown. Metrics derive shipment intervals, approval-to-shipment lead time, hotfix fraction among shipped releases, and recorded resource counts. They do not infer success from an absent record. Release-preparation PR costs and future S7 band automation are separate evidence, not guessed counts. The per-release "gates run" record is deferred to S6/S7; frozen scope and resource counts do not prove that gates ran.
253
+
254
+ Legacy release rows omit train metadata. The old off-mode planning APIs keep their shape; explicit train operations always enforce their stated board/resource rules. Enabled lifecycle freeze, and any configured train freeze, must use the admitted cut path. Ship/drop remain person-only. The existing person-selected upgrade/recovery operation and its readiness/completion checks remain separate from a cut; a source gate is not an upgrade receipt.
255
+
256
+ Person ruling 2026-10-19 supersedes the earlier S11 upgrade-admission ruling: upgrades remain outside S11 host-admission, quota and additional all-idle gates. Recovery uses its pre-S11 `recoveryReady()` checks: no busy peers, pending approvals resolved and completion checks settled, with the existing operation-identity and lifecycle fences. A quota/manual pause, missing or shadowed `dot`, or an outer same-repository admission lease adds no S11 upgrade refusal. The person-selected operation remains the confirmation; no extra receipt or agent upgrade authority is added. Release-cut host and quota admission remains unchanged.
257
+
258
+ Configured cut dates must be positive safe UTC millisecond timestamps; the train tool does not accept null to clear a date. A configured active/frozen train also requires a recovery target advertising `release_trains`, so an older daemon cannot silently drop the safeguards after restart. Target proof is checked before preparation and again before commit; queued future planning rows and legacy rows retain their existing compatibility.
259
+
260
+ Release-cut refusals name the applicable exits: stop or resume a paused peer, adjust `ahub budget set`, or drop the train with `ahub release drop`. Install the resource controller or fix PATH when `dot` is unavailable or shadowed; retry the cut outside an outer admission lease when nested admission is refused.
261
+
262
+ Train history retains at most 256 events. Configuration no-ops retain the revision and history; meaningful configuration reserves the final two slots for cut and shipment. Capacity refusals occur before effects; an already exhausted legacy train requires a person-selected replacement.
package/docs/security.md CHANGED
@@ -23,7 +23,7 @@ agent-hub connects agents that can each run commands. This page says what the hu
23
23
 
24
24
  ## Native memory authority and profile selection
25
25
 
26
- The portable core is the exact-pinned first-party `@staix/agent-mem` 0.3.1 package; it does not replace the hub's caller identity, PII policy or task-write authority. Deny, hardened git, real paths and process identity are single-sourced in its Node-safe platform entry. Current project privacy is applied by the hub before promotion and again before model-facing reads. Standalone person reads are unscreened, and the standalone `--policy-free` acknowledgement grants no hub or peer authority.
26
+ The portable core is the exact-pinned first-party `@staix/agent-mem` 0.4.0 package; it does not replace the hub's caller identity, PII policy or task-write authority. Deny, hardened git, real paths and process identity are single-sourced in its Node-safe platform entry. Current project privacy is applied by the hub before promotion and again before model-facing reads. Standalone person reads are unscreened, and the standalone `--policy-free` acknowledgement grants no hub or peer authority.
27
27
 
28
28
  `hub_memory_search`, `hub_memory_show` and `hub_memory_graph` take the project from the daemon, require `memory_read`, refuse private turns and validate UTF-8 input before logs or storage work. Complete records and graph metadata are screened before counts and truncation. Search is at most 20 lines/4 KiB (default 8); show accepts at most 8 ids and returns at most 8 KiB; graph is at most 120 nodes/240 edges/6 KiB (default 60 nodes), with no edge lacking an endpoint. Unknown, ambiguous and withheld ids share one neutral refusal. Arguments and results are excluded from hub-managed capture; this does not govern a vendor's independent transcript or memory hooks.
29
29
 
@@ -33,6 +33,8 @@ The hub binds the selected root/profile at open. Invalid/missing generation meta
33
33
 
34
34
  Before migrating any real home, every hub sharing it must first be on 0.12.30 or later with positive authenticated binding or unbound/quiesced proof from `ahub memory profile-check` in each relevant registry. An unknown or unreadable runtime is not proof of absence, and a matching ready binding still holds a handle that must be closed. Profile-check reports only its named registry inventory. Root paths, selector receipts and malformed-legacy counts are person-local diagnostics, excluded from ordinary peer status and allowed agent CLI reads. Source/fixture checks do not establish native capture, real migration or account/device-sync success.
35
35
 
36
+ On a home split by `amem migrate --shards`, the hub opens only its own project's shard, with the user shard as overlay, under the alias-resolved project key (#534, #537); other projects' private notes are not in that store. Each memory entry rechecks the shard identity, and an alias that later remaps the key latches the binding unavailable. A split that is not active refuses and is never read as an empty or legacy store. A hub on 0.12.36 or earlier does not detect a split and keeps writing the old layout, so every hub on the home must run 0.12.37 or later, or be stopped, before splitting. `ahub memory profile-check` is not split-aware yet (#536): on a split home its mismatch reports are not evidence either way.
37
+
36
38
  ## What is not defended
37
39
 
38
40
  - **A malicious local process running as you.** It can read the control token, `hub.db`, the project and your home directory without the hub's help. The token keeps web pages and other users out, not your own processes.
@@ -169,7 +171,7 @@ Please report vulnerabilities privately through GitHub's "Report a vulnerability
169
171
  - A settings session is as strong as the console for these settings and no stronger elsewhere. A mode change is the console's own request with the same refusals; tool approvals stay deny-only for Pi and local; there is still no shell, file, task-detail or launch-command API. `ahub ui --settings` and `ahub settings` pass the same identity gate as `ahub permission`, which is an operating policy, not authentication. The hostile-agent boundary above applies unchanged: an agent with an unrestricted shell as the same user can impersonate the console, and so can open a settings session.
170
172
  - Every change writes a `settings` event (the key, the value before and after, the source `dashboard` or `terminal`) and a line in `hub.log` and open consoles; opening a settings session is logged too. The unified dashboard (`ahub ui --all`) forwards settings actions as an ordinary session and opens no settings session.
171
173
  - Configuration reload (issue #439). The daemon watches `config.json`, `config.local.json`, `routing.toml` and `routing.local.toml` and re-reads the complete candidate after a change, as the dashboard's Reload configuration and `ahub settings reload` do; an invalid candidate applies nothing. Each changed key is then authorized on its own against its file's actual git provenance. A privacy tightening (local-only PII, the model screen on, added PII patterns, narrower roles or capability lists, unless it moves the planner or conductor) and a safe registered setting apply from any file. Every other change, machine-local fields included, keeps its running value when its file is tracked or git cannot answer. From an untracked file such a guarded change is adopted only by `ahub settings reload` in an interactive terminal, after it lists each guarded key with its full before and after values and the person types `y` within 30 seconds; the confirmation is bound to the file contents and accepted state it was shown for. File watches and dashboard reloads, a settings session's included, never confirm, and no reload changes the roster: membership moves only through the roster setting. Keys whose consumers are bound at start (memory, the gateway and MLX, checks, the privacy and sandbox policy of existing sessions, the terminal provider and others) stay pending until a restart.
172
- - Start modes. A start the hub makes itself (the dashboard's Start control, `peer_start` from a console child, the conductor's `hub_peer_start`, `pi.auto_start`) takes the peer's start mode. Through settings only a settings session or the terminal can set it to headless; a config file can also set `peers.<id>.start_mode`, the shared one included (it is not a machine-local field: headless was the only form for Pi, Kimi and the local worker before this change, a hub-made headless Codex start is the app-server the hub already ran for it, and a start mode grants nothing a permission mode or a capability controls). A declared extra instance accepts only `headless` from any surface. For a TUI the hub opens a terminal running `<runtime> <its own entry point> --project <this project> <peer>` (and `--mode tui` for Pi): every word is the hub's own, the command line is those words each quoted for a POSIX shell, and no request field, tool argument or agent message is part of it. In a `terminal.open` template `{command}` is that quoted line and `{title}` and `{cwd}` are raw text for argv elements of their own; each placeholder is substituted once, so a project path that itself contains a placeholder, a quote or `$` stays one word. A provider's own error text goes to `hub.log`, not to the caller. The terminal comes from `terminal.open`, a machine-local command template that config trust refuses from a committed file and that no settings surface can write, or from Orca. The opened terminal's environment carries no agent marker, so `ahub` there has the console's identity. Without a provider the start is refused with the command to run by hand; it is never downgraded to headless. A conductor can start a peer but cannot choose its mode.
174
+ - Start modes. A start the hub makes itself (the dashboard's Start control, `peer_start` from a console child, the conductor's `hub_peer_start`, `pi.auto_start`) takes the peer's start mode. Through settings only a settings session or the terminal can set it to headless; a config file can also set `peers.<id>.start_mode`, the shared one included (it is not a machine-local field: headless was the only form for Pi, Kimi and the local worker before this change, a hub-made headless Codex start is the app-server the hub already ran for it, and a start mode grants nothing a permission mode or a capability controls). A declared extra instance accepts only `headless` from any surface; a Claude channel instance accepts no start mode, and a headless Claude instance must have roles exactly `["reviewer"]` and no worktree. For a TUI the hub opens a terminal running `<runtime> <its own entry point> --project <this project> <peer>` (and `--mode tui` for Pi): every word is the hub's own, the command line is those words each quoted for a POSIX shell, and no request field, tool argument or agent message is part of it. In a `terminal.open` template `{command}` is that quoted line and `{title}` and `{cwd}` are raw text for argv elements of their own; each placeholder is substituted once, so a project path that itself contains a placeholder, a quote or `$` stays one word. A provider's own error text goes to `hub.log`, not to the caller. The terminal comes from `terminal.open`, a machine-local command template that config trust refuses from a committed file and that no settings surface can write, or from Orca. The opened terminal's environment carries no agent marker, so `ahub` there has the console's identity. Without a provider the start is refused with the command to run by hand; it is never downgraded to headless. A conductor can start a peer but cannot choose its mode.
173
175
 
174
176
  ## Multi-project manager (protocol 7)
175
177
 
@@ -238,7 +240,7 @@ never-ask still requires the person's confirmation. qwen_cmd is machine-local an
238
240
  never editable through settings. Legacy benchmark adapters remain unmanaged by
239
241
  this production table. Startup diagnostics withhold native host/payload details.
240
242
 
241
- Jcode is optional, source/version-fenced and runs native tools without a hub sandbox or hub permission-mode mapping. .jcode joins the scoped-grant exclusion, including lexical/resolved aliases. Only a person writes its unique project MCP entry; foreign/override/symlink entries refuse. The hub owns a private daemon/socket and ACP group, never the global daemon, and copies no credentials. Native diagnostics withhold payloads/hosts. Unproved conversation loads refuse; no fresh replacement is called a restore.
243
+ Jcode is optional, source/version-fenced and runs native tools without a hub sandbox or hub permission-mode mapping. .jcode joins the scoped-grant exclusion, including lexical/resolved aliases. Only a person writes its unique project MCP entry; foreign/override/symlink entries refuse. The hub owns a private daemon/socket and ACP group, never the global daemon, and copies no credentials. It accepts only a plain jcode release from 0.98.0 up to, not including, 1.0.0 (#529), and runs every owned jcode process (version probe, daemon, ACP) with `JCODE_NO_TELEMETRY=1`, because jcode's telemetry can report the provider account's email. Native diagnostics withhold payloads/hosts. Unproved conversation loads refuse; no fresh replacement is called a restore.
242
244
 
243
245
  # Transient ACP activity (#286)
244
246
 
@@ -341,6 +343,12 @@ Stall causes are persisted and deduplicated by cause and replanning revision. Pl
341
343
 
342
344
  `hub_delivery_retry` grants an eligible recipient one retry of its own `needs_review` work, never completion/discard authority or pause removal. Durable per-original evidence survives journal pruning; unknown retry history or a used retry requires the person. Hub presence prefaces are excluded from attempted-work identity, so a shared context preface does not consume an unrelated message's retry. Delivery-only local and ACP peers (including Jcode) cannot invoke recipient retry; tool exposure hints do not replace the daemon's actual-adapter guard. Their held work requires person resolution. Before transport handoff, storage failure preserves the queue and preface; after handoff an uncertain effect is never automatically replayed.
343
345
 
346
+ A declared instance whose only role is reviewer gets review requests, task messages addressed to it and the person's messages; agent chat, digests and presence are discarded and journaled, and it gets no memory recall (#223). The headless Claude reviewer runs one new `claude -p` per review with `--setting-sources ""`, `--strict-mcp-config`, `--no-session-persistence`, tools limited to Read, Grep, Glob, `hub_review` and `hub_task_show`, and `dontAsk`. Its prompt holds hub facts and a bounded diff, never the builder's summary, and its `hub_task_show` drops the builder's notes. Changes touching `local.deny` or `pii.paths` are withheld and passed as `Read` deny rules, which Claude enforces, not the hub. Private tasks are never given to it. Its `hub_review` counts only while the child started for that task still runs; a stop or exit revokes it first, and surviving processes hold the delivery as `needs_review` and keep the reviewer busy.
347
+
348
+ The fresh Codex reviewer (#519) needs `review_mode: "fresh"` and an installed Codex 0.146.0; any other version is unavailable. Before each review it requires a model-free isolation receipt: binary and config unchanged, only the two hub tools in the configured MCP inventory, plugins, hooks and Apps disabled (Apps by a separate feature readback), and a named filesystem profile whose fixtures prove public reads, private-path denial (exact, glob and symlink), standard host credential denial and write denial. Credential and tool-home roots are denied even with an empty `local.deny`; that list does not cover every personal file. Its shell is sandboxed read-only, not Claude's Read/Grep/Glob whitelist. Network and web search are configured disabled but not observed: managed requirements can still select cached web search, and no run has shown the review selecting the profile through `default_permissions`. A failed receipt or start never falls back to a continuing native session, and a child that ends without a saved verdict is never approval. Kimi and agy reviewers are unavailable placeholders and never run.
349
+
350
+ The review brief (#520) withholds private changes whole (both sides of a move) and counts them; a rule file that fails the private-data screen is named, not shown. Attributes and rules are read at the merge base, so a change cannot loosen its own review. Spec acceptance, plan review, premature-review and cost reads fetch GitHub metadata only (edit times, check states, thread authorship and resolution), never issue bodies or comments. The CI workflow keeps `contents: read` and `actions: read`.
351
+
344
352
  ## Bounded task specs and isolated checks (#418)
345
353
 
346
354
  `hub_task_spec` reads only the GitHub issues named by a current owner's or reviewer's task in this project's origin repository, including the validated canonical GitHub intake source when its title/body has no issue token. Bare `#N` references count only in the title; detail references require `owner/repo#N` or `https://github.com/owner/repo/issues/N`. Bare detail numbers denote hub tasks, and arbitrary file paths are not parsed as issue references. A qualified reference cannot begin after a slash or a name character, so `src/hub/tasks.ts#12` is a file path. Routing uses this same parser. Without a GitHub project identity, bare title numbers do not require spec access, but explicit qualified/URL references, `refs.repo` and imported GitHub identity still fail closed. The daemon resolves authenticated and container executables outside the project, pins their absolute paths and uses fixed arguments; repository-writable PATH shims are refused. Runner Git resolves its host executable through a restricted PATH. For specs, the model receives no credential or arbitrary authenticated URL capability. Foreign/transferred issues, more than three references, bodies over 64 KiB and aggregate results over 80 KiB fail explicitly. Spec fetches share a 25-second budget inside the control client's 30-second cap; each gh call remains bounded to 15 seconds. Current PII, secret and private-path policy withholds the complete spec rather than returning a partial contract. PII turns and PII tasks refuse these tools. The independent `spec_read` and `check_run` capabilities apply when a peer has a narrowed capability list. Arguments and results are excluded from capture. Proven deterministic pre-effect guards return PreEffectToolRefusal so Pi settles the exact refusal receipt; generic transport/storage failures and failures after durable receipt/output changes remain uncertain, never reclassified from error text.
package/docs/smoke.md CHANGED
@@ -1709,9 +1709,9 @@ the native version and observed outcome here. No hub-driven Qwen TUI is offered.
1709
1709
 
1710
1710
  Qwen recovery source/fake proof: saved ids pass through the driver and CLI to ACP session/load, and the crash loader restores the recorded id/model. Actual native/provider recovery remains unverified.
1711
1711
 
1712
- ## Jcode 0.94.0 peer (#332)
1712
+ ## Jcode peer, 0.98.0 to 1.0.0 (#332, #529)
1713
1713
 
1714
- Source/fixture proof is recorded in verification/2026-10-11-jcode-peer-contract.md. Unverified: real provider/model, native project MCP discovery and hub-tool receipt, native tool policy and stop/child teardown. Native automatic restoration is withheld because the pinned ACP load does not prove the id. No benchmark comparison or default replacement ran.
1714
+ Source/fixture proof is recorded in verification/2026-10-11-jcode-peer-contract.md; the live owned-daemon and ACP handshake on 0.94.0 and 0.98.1 in verification/2026-10-19-jcode-0.98.1.md. Unverified: real provider/model, native project MCP discovery and hub-tool receipt, native tool policy and stop/child teardown. Native automatic restoration is withheld because the pinned ACP load does not prove the id. No benchmark comparison or default replacement ran.
1715
1715
 
1716
1716
 
1717
1717
  ## Project board (#263)
@@ -1935,3 +1935,19 @@ hub instance stays running with its queue and pauses intact. Record the source
1935
1935
  head, terminal and daemon readbacks separately before claiming AC5 native smoke.
1936
1936
 
1937
1937
  For the guarded CLI leg, edit a disposable untracked WIP limit, run `ahub settings reload`, inspect its complete before -> after preview, decline and verify the running limit is retained. Repeat and confirm y, then verify the same instance and queue/pause state. Record this separately from the scripted CLI fixture; the token proves freshness within console authority, and the CLI supplies the person-presence checks.
1938
+
1939
+
1940
+ ## Reviewer pool native legs (#519, 2026-10-19)
1941
+
1942
+ Native account/model acceptance is **UNVERIFIED**. See [reviewer isolation evidence](verification/2026-10-19-reviewer-isolation.md) for the exact source/version pins and offline probes. Those probes used temporary fixtures and no model prompt, account login or credential copy. Fake-CLI tests and CI are separate host-contract evidence. Existing #223 Claude behavior is retained without a new live Claude claim.
1943
+
1944
+ Complete these legs in a disposable project with authorized accounts after source integration and exact-head CI:
1945
+
1946
+ - Declare headless Claude and Codex reviewer-only instances, adding `review_mode: "fresh"` to Codex. Claude retains the automatic #223 declaration. For Codex, select the proved 0.146.0 binary and verify its runtime isolation receipt before a model turn; a 0.163.0 PATH executable must remain unavailable.
1947
+ - Submit a small public change with a pinned reviewed head. Confirm a different-family reviewer, fresh native session, expected read-only policy, the fixed two hub tools and a verdict carrying `reviewContext: fresh`, `reviewerFamily` and `reviewerReason`. Confirm builder done narrative is absent from both prompt and `hub_task_show`.
1948
+ - Request a delta review and confirm the same eligible independent reviewing family receives a new session with prior findings. After an earlier same-family fallback, a newly available independent family still takes precedence. Exercise configured re-rotation separately. Stop or exhaust one reviewer and confirm the pure routing walk chooses the next eligible candidate; no failed start or child ending without a saved verdict becomes approval. Check the next candidate even when the missing verdict followed a vendor quota error; no text-based quota classifier is assumed.
1949
+ - Make all pool candidates unavailable and confirm a blocked review awaiting planner/person action. Then test a different fresh session in same-family fallback only when no other family can run. Verify stale-child, changed-head and private-review verdict refusals.
1950
+ - Verify compatibility separately: a Codex/Kimi headless reviewer-role declaration without `review_mode` keeps its native app-server/ACP behavior and existing worktree/permission options, and stays outside the fresh pool. An explicit fresh declaration must refuse an unsupported backend without downgrading.
1951
+ - Keep Kimi and agy out of the default pool. Explicitly requesting either currently yields unavailable and a skip, not a model launch or pass. No Kimi/agy launch code is included in #229. Their follow-up #533 (hub task #234) needs strict MCP/config, private-read, write-denial and fresh-session receipts before native smoke can count; the future agy launcher belongs in `src/adapters/agy/`.
1952
+
1953
+ Record source head, exact executable version/hash, isolation receipt, task/head identities, route walk, distinct native session evidence and persisted verdict separately. Do not record credentials or raw transport environments. Codex sandboxed shell must not be described as Claude's Read/Grep/Glob-only whitelist. Network policy is configured disabled; no network-denial probe or native LLM isolation result has been recorded by this investigation.
@@ -1696,7 +1696,11 @@ distinct from the completed-message baseline recorded at that start. Later tool
1696
1696
  activity does not move this turn-start boundary. Missing start evidence or a
1697
1697
  start from another session, launch or channel claim leaves completion unknown.
1698
1698
  Accepted completion consumes that start before the peer becomes idle, so another
1699
- message cannot reuse it. A valid bound Stop request receives one prompt
1699
+ message cannot reuse it. The completion and the baseline are read from the last
1700
+ 4 MiB of the transcript, whatever its size (#517): the 32 MiB cap applies only to
1701
+ usage accounting, so a long session still ends its turns. A Stop refused because
1702
+ no completion is found tells the console once per turn start that the peer stays
1703
+ busy and its messages wait in its queue. A valid bound Stop request receives one prompt
1700
1704
  acknowledgement with pending=true, within the existing two-second hook deadline.
1701
1705
  The acknowledgement records observation only, never completion. After the hook
1702
1706
  can return, a deferred observer waits up to 1200 monotonic milliseconds for its
@@ -2450,11 +2454,12 @@ the queue.
2450
2454
 
2451
2455
  ## Amendment: optional owned Jcode peer (#332)
2452
2456
 
2453
- - jcode is optional on the roster, default off, with implementer/verifier guidance and no default routing replacement. Its source-pinned native identity is jcode 0.94.0.
2457
+ - jcode is optional on the roster, default off, with implementer/verifier guidance and no default routing replacement. Its native identity is jcode from 0.98.0 (the minimum; verified live on 0.98.1) up to, not including, 1.0.0 (the assumed breaking version); the minor is not pinned. Lower the breaking version when a release is shown to break the contract (#529).
2454
2458
  - A private foreground daemon/socket and ACP client belong to this peer. The global daemon is never adopted or stopped. No automatic update, self-dev or credential copy. The native MCP store must already exist to avoid implicit Codex import.
2455
2459
  - ACP session MCP arrays are ignored natively, so the person launcher manages only agent-hub-jcode, shared:false, in project .jcode/mcp.json. Later override layers, foreign names and links refuse. Daemon starts verify read-only. .jcode is outside scoped grants.
2456
2460
  - Native policy is unmanaged and has no hub sandbox. Hub permission mappings refuse; native quota/consumption are not inferred. Every task mutation stays in Tasks and PII remains at existing redaction/routing boundaries.
2457
- - Native 0.94.0 load lacks identity proof. Controlled recovery refuses before effects, crash recovery is manual and a load without an exact echoed id refuses before a prompt or restored claim. Real provider/tool/teardown evidence and comparative measurement remain unverified.
2461
+ - The private socket name ends in `.sock`: jcode names its debug socket by replacing that suffix. Every owned jcode process runs with `JCODE_NO_TELEMETRY=1` (#529).
2462
+ - Native ACP load (verified on 0.94.0 and 0.98.1) lacks identity proof. Controlled recovery refuses before effects, crash recovery is manual and a load without an exact echoed id refuses before a prompt or restored claim. Real provider/tool/teardown evidence and comparative measurement remain unverified.
2458
2463
 
2459
2464
  # ACP watch amendment, 2026-10-11 (#286)
2460
2465
 
@@ -3554,6 +3559,55 @@ keep their exact scope. It performs no filesystem or package lookup.
3554
3559
  - `ahub sdlc export`: a plain off lifecycle (no retired setting) keeps the #257 export bytes (schema 1, `workflow: "none"`, `phase: null`). An enabled or retired-setting lifecycle emits schema 2: sanitized state (id, state, stage, criteria view or a private stub), `stateHash` and `sourceHash`, without the workflow field. In off, a `phase` receipt write also keeps its #257 bytes (`tasks: []`); task import stays retired in every mode, so a non-empty list is refused.
3555
3560
  - Contract amendment, seeds (419 before and after: 414 byte-unchanged, 3 modified, 2 removed, 2 added; the registry baseline is unchanged). Modified, each with the same id, test file, test name and mutation intent: `workflow-reviewer-only` (the criteria/phase check), plus `override-local-schema` and `lifecycle-native-schema`. The person approved the last two on 2026-10-18, because the `lifecycleCriteria` plumbing changed their call sites. Removed with the code they mutated: `workflow-exact-after` and `lifecycle-phase-view-required`. Added for the new guards: `criteria-task-import-retired` and `sdlc-workflow-window-closes`. Tests that pinned the retired gsd-lite behaviour (task import, phase naming, the workflow projection, the implement id stored in an enabled config) are amended in the same PR; every other test is unchanged.
3556
3561
  - S0/S8 implement none of S1-S7 and S9-S11. The issue stays the spec, Tasks owns mutations and GitHub owns PR, check and tag facts.
3562
+ ## Reviewer separation amendment (#450, hub task #223)
3563
+
3564
+ Owner decision 2026-10-18 (#450 comment 6248184754), R1-R5. R3's mechanism, chosen by the person: a new headless Claude session per review request.
3565
+
3566
+ - **Declaration.** `"claude-review": { "kind": "claude", "roles": ["reviewer"], "start_mode": "headless" }` declares a headless reviewer. `start_mode: "headless"` on a Claude instance is accepted only when the roles are exactly `["reviewer"]`; `worktree` stays refused. It resolves to a fixed builtin variant of the claude definition (hub-native transport, env MCP identity, a headless factory); the builtin `claude` keeps its terminal-only definition, and external definitions cannot declare a variant. A channel instance (I3, no `start_mode`) may still hold reviewer; it is started from a terminal and its verdicts are recorded as `continuing`.
3567
+ - **Start.** `ahub claude --as <id>` asks the running hub to start the reviewer (the console and dashboard Start do the same); it takes no launch arguments, runs nothing in that terminal, and `ahub stop <id>` stops it. It holds no process while idle.
3568
+ - **R3, one session per verdict.** For each `review` envelope the peer runs one `claude -p` child (own process group, watchdog, `peerChildEnv(<id>)`) with: `--output-format stream-json --verbose --no-session-persistence --session-id <new uuid> --strict-mcp-config --mcp-config <agent-hub tools-mode server for <id>> --setting-sources "" --tools Read,Grep,Glob --allowedTools mcp__agent-hub__hub_review mcp__agent-hub__hub_task_show Read Grep Glob --permission-mode dontAsk`, prompt on stdin. It never passes `--resume` or `--continue`, so no session sees an earlier round. No user or project settings, hooks or plugins load. A delta review also runs fresh (R3 allows continuing; not needed). The child's final text is not published; the verdict is the `hub_review` call.
3569
+ - **R2, review-only delivery.** A declared instance whose effective roles are exactly `["reviewer"]` (headless or channel) receives only hub `review` envelopes, hub `task` envelopes addressed to it and the person's own messages. Every other item (agent chat, digests, presence, budget, `[FYI]`) is discarded and journaled with that reason; an agent that addressed it directly is told why. A headless reviewer starts a session only for a review request; anything else it receives settles without one. The headless reviewer's prompt is built by the hub from facts, not from the envelope text: the task title and detail, the linked spec issue, refs (branch, head commit), check results, the earlier `changes_requested` notes and unmet items, and a diff of the head bounded in size (ponytail: a cap, the reviewer reads files with Read/Grep/Glob for the rest). The implementer's done summary is builder narrative and is left out.
3570
+ - **R4, recorded context.** `hub_review` writes `reviewContext` on its history entry, set by the hub: `fresh` when the verdict comes from a headless reviewer's child started for that task's review, `continuing` from any other peer, absent from the person.
3571
+ - **R1/R5, routing.** `assign()` prefers a dedicated reviewer (holds `reviewer`, not `planner`) among the eligible peers, busy or not; family independence and the PII rule (the person reviews) are unchanged. When a dedicated reviewer is declared but not eligible (offline, not started, paused) the planner reviews as before and the assignment note says `reviewer=planner (fallback)`. With no dedicated reviewer declared nothing changes. The reviewer is chosen at assignment, as before (ponytail: a task assigned during a fallback keeps the planner; re-pick at done if that matters).
3572
+ - **Budget.** The reviewer shares the `claude` account and its pauses; usage from each child's `result` event is added to the peer's tokens. No quota is classified from its text.
3573
+ - **Privacy.** The diff lists changed paths first, with rename and copy detection, and leaves out every change touching a path that `local.deny` or `pii.paths` covers (a move or copy whole, when either side is private), noting only how many were withheld (ponytail: git's 50% similarity decides what counts as a move); a diff that matches a private-data pattern is withheld whole. Those entries are also passed to the child as Claude `Read` deny rules (Claude evaluates them, not the hub's path policy). A private review is never given to it.
3574
+ - **Narrative on the read tool.** `hub_task_show` from a review-only instance returns the task with history notes kept only on `approved` and `changes_requested` entries; the done summary and other notes are dropped.
3575
+ - **Verdict authority.** A headless reviewer's `hub_review` is accepted only while the child that started for that task still runs (a per-child token, so a replacement child for the same task is a new authority). It is captured when the call starts and rechecked before every write, after each await. The leader's exit, a watchdog or a stop revokes it first. A review ends only when the child's whole process group is confirmed gone; survivors hold the delivery as `needs_review` and keep the peer busy with its handle: a stop fails and leaves it busy (never offline), and a start is refused, until they exit.
3576
+ - **Settlement.** A start failure before any review in a delivery ran is `failed_safe`; after one ran, the delivery is held as `needs_review`. A failing facts or verdict-check callback is logged, and the review counts as without a recorded verdict.
3577
+ - **Never an owner.** The reviewer variant carries `routing.reviewOnly`; assignment refuses it as an owner for every task class and when named.
3578
+
3579
+ ## Reviewer pool amendment (#519, hub task #229)
3580
+
3581
+ Planner ruling, 2026-10-19: #229 implements R3-R6 for Claude and Codex; admission is receipt-gated. Only proved reviewer profiles ship as runnable variants. The existing #223 Claude variant is retained; Codex is restricted to its verified 0.146.0 profile and a passing runtime receipt. Kimi and agy remain unavailable with metadata reasons and outside the default pool; #229 adds no launch code for either. All Kimi/agy launch, isolation and receipt work is in #533 (hub task #234), with the future agy launcher in `src/adapters/agy/`. The [isolation evidence report](../verification/2026-10-19-reviewer-isolation.md) records primary sources, offline probes and untested native legs. Source integration and model-free admission are verified; final-head verification remains the required PR gate.
3582
+
3583
+ - **Pool and routing.** The default logical kind order is Claude, Codex as fixed configured kind slots; undeclared/unavailable slots are skipped after modulo without renumbering. `review.pool` accepts registered reviewer kinds (Claude, Codex, Kimi and agy), including the unavailable placeholders; local/Pi are refused. An explicit pool with no declared candidates blocks for the person. An omitted pool with no fresh reviewer declaration retains legacy routing. With an omitted pool, exhaustion retains the #223 R5 eligible-planner fallback, preserving the exhausted pool walk and reason in trace/history; absent an eligible planner, the person reviews with a blocked result. This applies also to unavailable-only declarations outside the default pool. An explicit required pool remains blocked for the person on exhaustion; explicit nominations cannot bypass backend unavailability. Unavailable never means pass. Pure `assign()` starts at PR number modulo the fixed configured kind count when linked, otherwise task id modulo that count, walks unavailable/paused/quota/failing candidates and skips the implementer's family while another can run. Delta review keeps the preceding reviewing family by default when it remains eligible and independent, with configurable re-rotation. Family independence still takes precedence over continuity after an earlier same-family fallback. A start failure or any child ending without a saved verdict walks the remaining candidates for that review; no quota category is inferred from vendor text. Explicit named reviewer choices remain outside automatic pool replacement; a failed pinned review stays held for the person. Board transitions still go through Tasks. A same-family fallback uses a different fresh child and is recorded. Exhaustion records `blocked: no reviewer can run` and leaves planner/person review available; it never auto-approves.
3584
+ - **Variants and authority.** Fresh variants require a declared headless instance with roles exactly `["reviewer"]`. Codex and Kimi additionally require `review_mode: "fresh"`; without that opt-in, existing headless reviewer-role instances retain their native app-server/ACP transport, worktree and permission semantics and do not join the fresh pool. Claude retains #223 automatic selection for its headless reviewer-only declaration; agy remains an automatically unavailable metadata-only declaration. An unsupported fresh backend refuses without falling back to a native continuing session. Ordinary implementer instances retain their native adapters. The common lifecycle starts a fresh child per review, never resumes, filters builder narrative/shared memory, retains private-diff/read/verdict fences and permits only `hub_review` and `hub_task_show` from the hub. Process-group cleanup and the per-child verdict token retain #223 semantics. A missing receipt refuses before a model starts.
3585
+ - **Codex receipt.** The producer resolves a host executable outside the project and binds its native binary hash and config metadata. It checks the exact supported version, performs bounded model-free MCP inventory/readback, disables ambient stdio/HTTP servers with self-contained stubs and verifies the fixed hub's two-tool allowlist. Plugins, hooks and Apps are disabled and the physical project is untrusted. Because `mcp list` omits runtime ChatGPT Apps contributions, a separate bounded `features list` readback must prove Apps disabled; inventory alone is not a strict-MCP receipt. The policy requests disabled web search and is passed to exec. There is no effective-turn web-search readback; managed requirements can override this request to Cached at the pin, as recorded in the verification ceiling. A named filesystem profile is read-only with portable core deny rules, explicit private-path/subtree rules, standard host credential directories/files (including configured XDG/tool-home overrides, deepest-existing-ancestor canonical paths for absent descendants and existing real-path aliases), the hub state directory and network disabled; a native temp-fixture proof, including synthetic gh/kube/docker credential reads, precedes the ephemeral exec launch. These standard credential denies supplement the portable core and project policy; they do not classify arbitrary personal files elsewhere on the host. The binary is hashed once during preparation, with dev/ino/size/mtime/ctime rechecked before spawn along with config metadata. PATH Codex 0.163.0 is unsupported, not an inferred pass. No credential copy or blanket approval bypass is added.
3586
+ - **Read-only meaning.** Codex retains sandboxed shell under that filesystem/network policy; it does not have Claude's Read/Grep/Glob-only whitelist. The offline fixture measured filesystem denial, not a model turn or network-denial result. No recorded run shows exec selecting `ahub-review` through `default_permissions`; the configured exec argument and explicit-profile sandbox proof remain separate evidence. Post-check same-user mutation remains a documented ceiling. Kimi plan mode and agy plan mode alone are not isolation receipts.
3587
+ - **Registry contract.** The data-only agy declaration is separate from the native runtime catalog and does not extend the legacy supported-peer or optional-peer lists. Their existing pinned expectations and the empty registry-guard baseline stay intact. Only declared unavailable reviewer instances can name that metadata; it adds no agy native adapter or launcher.
3588
+ - **Recovery compatibility.** A configured pool or declared fresh reviewer requires target feature `reviewer_pool`; an older target cannot drop `review_mode: "fresh"` or silently restore it under ordinary adapter policy.
3589
+ - **History compatibility.** Keep `reviewContext` as its existing `fresh`/`continuing` string. Add host-recorded `reviewerFamily` and `reviewerReason` fields for rotation, implementer skip, unavailable skip and same-family fallback. `ahub route explain` renders the same pure routing walk; agent prose never supplies routing authority or a passing receipt.
3590
+
3591
+ ## Review brief amendment (#520, hub task #228)
3592
+
3593
+ ahub builds the review brief that the workspace gate used to take from `ocr delegate preview` and `rule`, and gives the same brief to every reviewer.
3594
+
3595
+ - **Range.** The task's commit (`refs.commit`, resolved to its full SHA) against its merge base with the remote default branch (`origin/HEAD`, else `origin/main`, else `origin/master`). There is no fallback to the checkout's `HEAD`, which may be the head itself or another session's branch: without a default branch, or when the commit has no changes against it, the brief is unavailable. Any failed git read makes it unavailable too, never partial. git only, bounded (500 files, 8 rule files of at most 32 KB each); the brief never touches the board.
3596
+ - **Files.** Each change comes with its status and line counts; a rename or copy lists both sides. A change touching a `local.deny` or `pii.paths` path, or a path that fails the private-data screen, is withheld whole (both sides of a move) and only counted. Excluded only when every side of a change is (a move keeps its source's obligation), with the reason, status and both sides shown: files marked `linguist-generated` in `.gitattributes`, lockfiles (a fixed list) and binary files. They are listed so a reviewer can still read them. Unlike `ocr delegate preview`, markdown and test files stay reviewable: the workspace gate required reading them anyway, and coverage now records it. Compared on PR #506 (head e305a17e against 3f258a41), OCR excluded 3 markdown and 3 test files and kept the generated bundle; ahub keeps those 6 and excludes the bundle once the `.gitattributes` this PR adds is on the default branch.
3597
+ - **Rules.** `REVIEW.md` applies to every reviewable file. A path whose `.gitattributes` entry sets `ahub-review-rules=<repository path>` also gets that file, and a move gets the rule files of both sides. Files are grouped by the rule files that apply. Attributes and rule files are read at the merge base, not the head, and every diff the brief runs uses `git --attr-source=<merge base>`, so a checkout at the head cannot reclassify files either (for example `* -diff` making them binary): a change cannot lift its own review obligations (marking everything generated, weakening `REVIEW.md`); its own edits apply from the next change after it merges. A rule file's name and whole body pass the private-data screen before any cut; a file that fails is named but not shown. A configured rule file the brief cannot load (missing at the merge base, or beyond the 8-file cap) is listed with the reason; a private or non-nameable rule path is left out unnamed. Deviation from the issue: per-path settings are git attributes, versioned with the change, instead of new `review.exclude` and `review.rules` config keys.
3598
+ - **Cross-check.** The plain `--name-only --no-renames` list of the same range is compared with the brief. A path it shows that the brief does not list is named for a direct read.
3599
+ - **Contract.** The review request already carries the spec issue reference, the task plan and the detail. The brief does not fetch the issue body: the hub reads issue metadata only (S1), so the acceptance checklist stays in the issue.
3600
+ - **Delivery.** `sendReview` appends the rendered brief to the review request (after #215's admission, body only). `hub_task_show` adds a `brief` field while the task is `in_review`. `ahub review brief <id>` prints it for a person. A task without a commit, or a private task, has no brief.
3601
+ - **Coverage gate.** `hub_review` takes `coverage: { reviewed: [paths], skipped: [{ path, reason }] }`. An approval must account for every reviewable path. The gate follows the lifecycle mode (deviation from the issue, which refuses always): `enforce` refuses before any write, naming the paths, and also refuses when the brief is unavailable (an unknown inventory is never an empty one); `advise` records `Coverage gap (N): ...` or `Coverage unknown: ...` in the verdict note; `off` and a person's own verdict skip it. The commit under review is the task's commit, else the head the verdict names, else the head its lifecycle receipt records. Code work (a task with a commit, branch or PR) for which none of these is known, or whose brief cannot be built, has an unavailable brief. Work with no commit, branch or PR is exempt, with nothing to account for. So is a `Tasks` built without a brief provider (library use and tests; the daemon always provides one). One head throughout: the head the verdict names, or the `reviewedHead` it would inherit from an earlier review, must equal the brief's head (with a PR, the lifecycle approval gate already binds the reviewed head to the PR head). The task's commit and its lifecycle receipt (which holds the inherited `reviewedHead`) are pinned when coverage is checked and rechecked before every write after an await, so a commit or receipt that moves during the verdict is refused.
3602
+
3603
+ ## Claude peer instances I3 amendment (#450)
3604
+
3605
+ - A declared instance of a channel-plugin kind (Claude) is admitted when its declaration names `roles`, reviewer and/or planner only. `start_mode` and `worktree` are refused: the instance starts only from the person's terminal with `ahub claude --as <id>`, so the hub never starts, watches or owns writes for it, and it is neither startable nor reserved from external admission.
3606
+ - The launcher sets the child's `AGENTHUB_PEER_ID` to the instance id; the channel plugin and the statusline tee already key on it. Launch, session and context state files stay `claude-launch.json`, `claude-session.json` and `claude-context.json` for the builtin and become `<base>.<id>.json` for an instance (`src/hub/claude-state.ts`). Account usage stays shared. The daemon's context and usage readers, the permission-launch check, the facts handler, terminal recovery (project root, no worktree), the upgrade blocker check and `ahub reset` read each instance's own files.
3607
+ - The Claude version probe stays on the builtin peer (ponytail: instances are reviewers started by hand); zero-turn detection reads each peer's own evidence, as below.
3608
+ - Status, which the console and dashboard read, lists each declared instance right after its kind. `ahub <kind> --as <id>` starts only an instance the running daemon bound at startup (status `activeInstances`); otherwise it asks for a restart or an upgrade before any effect.
3609
+ - Each Claude peer reads only its own `terminal-recovery.json` row. Transcript evidence comes from the path that peer's status line reported; an instance without one counts as persisted, because the hub cannot see its `CLAUDE_CONFIG_DIR`. Upgrade runs plugin setup once per distinct Claude profile, under that profile.
3610
+
3557
3611
  ## Headless peer instances I2 amendment (#450)
3558
3612
 
3559
3613
  `instances` is a person-authored, machine-local object in an untracked
@@ -3657,3 +3711,54 @@ and mutant result, per the reviewer's explicit ruling.
3657
3711
  - Launchers that check membership before starting (`ahub jcode`, `ahub kimi --mode tui`) read it through `rosterInStatus` in `src/hub/status-roster.ts` (no imports, so the registry's native CLI entries add no cycle). A reply without the field (an older hub) is unknown: the launcher stops with an upgrade-or-restart remedy and starts nothing. It never reads unknown as off (the jcode bug) or as on (the dead kimi guard; daemon admission still refused off-roster Kimi, so that one was harmless).
3658
3712
  - `settings_get` stays console-only.
3659
3713
  - With declared headless instances, status includes each startup-bound extra id as well as every built-in and optional peer, with its own roster membership. No undeclared suffix acquires a row.
3714
+
3715
+ ## Amendment: planner plan review and board governance, 2026-10-18 (#481 S9)
3716
+
3717
+ - The person's mode-boundary ruling separates board governance from lifecycle enforcement. WIP-at-accept and planner/person return of accepted work without work evidence apply in every `sdlc.mode`; only the current plan-review receipt gate uses enforce/refuse, advise/report and off/no gate. Existing lifecycle schema aliases and ordinary off-mode receipts are unchanged.
3718
+ - Tasks owns plan-review receipts, accepted-untouched returns and all offers/acceptances. Review evidence contains the planner, planning revision, recorded time and fixed-query GitHub issue metadata. An issue body or comments are never fetched or stored by this readback. A changed body edit time or planning revision invalidates the receipt, except the complete contiguous release-only revision history defined by S11; issue identity/edit and cost-scope checks still apply; missing evidence remains unknown. The synchronous acceptance API cannot bypass the fresh enforce readback.
3719
+ - S1 person spec acceptance and S9 planner review remain independent. `acceptReviewed` reads both against one task snapshot before the acceptance write. A `done` that implicitly accepts proposed work checks WIP and both readbacks before any cohort or board write; the missing-spec refusal retains precedence over missing-plan admission. Off keeps its synchronous ungated acceptance path apart from the all-mode WIP rule.
3720
+ - The accepted-untouched exception is a distinct, revision-bound return transition with a screened reason. It preserves history and planning fields, withdraws former role requests and enables replanning. Historical acceptance still protects cancellation and undo. Done/check/review/integration evidence or branch/commit/PR/local/review artifacts refuses this exception; it does not grant a general planner unassign permission.
3721
+ - Grooming is an ids/codes-only read projection with repository-scoped comparisons, explicit unknown/truncated evidence and person-only actions rendered as commands. It does not infer a ruling from arbitrary task prose, fetch issue bodies, mutate dependencies or invoke a model. Limits are 500 task rows, 256 findings and eight metadata reads within four seconds; a future paginated repository resolver supplies exhaustive coverage.
3722
+ - Existing seeds and the registry baseline remain byte-identical. Source guards retain the original ordinary backlog and dependency floors outside the explicit untouched-return branch. S10/S11 and the remaining lifecycle slices are not implemented here.
3723
+
3724
+ ## Amendment: the daemon's plugin bundle in status, 2026-10-18 (#502)
3725
+
3726
+ - The authenticated `status` reply carries `pluginBundle`, the absolute path of the plugin bundle the running daemon serves and checks against. It is additive, with no protocol bump.
3727
+ - `ahub jcode` and `ahub kimi --mode tui` write that path into their project MCP entries, read through `bundleInStatus` in `src/hub/status-roster.ts`, which accepts only an absolute path ending in `plugins/agent-hub/server.js`. Before this, they wrote the path of the CLI copy on PATH, which can be a different install than the daemon's (for example a global install against a retained release), so the daemon refused Jcode's entry as stale. A hub that reports no bundle gets the CLI's own copy, as before. Kimi's headless start removes the entry, so no path is involved.
3728
+
3729
+ ## Amendment: spec acceptance, 2026-10-18 (#481 S1)
3730
+
3731
+ - `ahub sdlc accept <task> --issue <n> [--repo owner/repository]` is person-only (console-role `sdlc` op `accept`; agent shells and live tests are refused). The daemon reads `lastEditedAt` and `updatedAt` with a fixed GraphQL query and `Tasks.acceptSpec` stores `{repo, issue, lastEditedAt, updatedAt, acceptedAt}` as the receipt's `acceptance`; no issue body is fetched or stored. `hub_sdlc_record` refuses the field, and a receipt relinked to another repository or issue drops it.
3732
+ - Build entry (an `implement` or `bulk_edit` task's acceptance, its `hub_task_done` without a separate accept, a self-claim, or a non-person stage change into a build base) in enforce needs a current acceptance. `Tasks.accept` refuses a missing one synchronously; the daemon awaits `Tasks.specGate` first, and `done` and `specStageGate` await the same reread, which refuses a changed body (`lastEditedAt` differs; comments move only `updatedAt`) or an unreadable one. A self-claim without an acceptance stays assigned to the claimant, still proposed, until the person accepts the spec and the claimant accepts the task. A task that changed during a readback (flow, link, acceptance, owner or privacy) is refused before any write. Advise notifies instead, stage changes included; off reads nothing. PII tasks never read the issue, so in enforce their accepted spec reads as unknown, like their PR evidence.
3733
+ - Status shows `spec: {issue, state}` when the lifecycle is enabled and the receipt names an issue. It is not a readiness blocker, so existing approval and merge gates are unchanged.
3734
+
3735
+ ### S9 redo admission clarification (#481, hub task #214)
3736
+
3737
+ An acceptance by the current owner since the latest ownership change retains its WIP slot for both in_progress updates and changes_requested redo, including re-accept and implicit done, in every mode. An unaccepted offer does not block that redo. Acceptance from a former ownership span gives no exemption. In enforce, changes_requested re-acceptance and done still re-read spec acceptance and plan-review freshness: an issue body edit requires renewed person acceptance and planner review before resubmission. This WIP exemption does not waive lifecycle review gates.
3738
+
3739
+ S9 privacy and notice ceilings: private build work with an issue ref remains inadmissible in enforce because its issue metadata cannot be read, with no metadata-free person override. Grooming pointers are coalesced per queued board revision but have no minimum delivery interval.
3740
+
3741
+ ### S10 cost governance amendment (#481, hub task #215)
3742
+
3743
+ - Task plans carry explicit review scope (none/delta/full, bounded reviewer count) and CI scope (focused/full/reuse); S9 receipts copy the planner-selected values. None uses zero reviewers; other scopes use one to eight. Legacy scope is unspecified, readable and never guessed. Advise reports the gap; new enforce receipts require both scopes, while old scope-less receipts remain valid to their next non-release planning revision under the complete-history S11 exception. Omitted cost keys on an owner file-list update retain the reviewed scope; explicit prospective drift refuses enforce acceptance. Scope does not change reviewer authority or bypass CI; S2 owns actual conformance comparison.
3744
+ - Review admission is separate from #520 brief/coverage. Before done/cohort writes, enforce requires the task's exact PR head, successful configured checks (native default: both OS checks and seeded guards), and no unanswered pending bot thread. Resolution settles a thread; an unresolved thread needs a user reply after the last bot comment. Only metadata is fetched. Advise reports gaps, including a generic unknown-readiness notice when a private turn finishes its own PII task without remote metadata; ordinary work remains refused during a private turn. Off adds no readback. Identity/privacy/owner and runtime authority are rechecked across awaits and each external read. Background role maintenance may retain a saved role while holding an unadmitted review envelope; a manual reviewer resend performs fresh admission.
3745
+ - Whole-workflow repository serialization closes the admission race, keeping required check names and read-only permissions; queue:max retains up to 100 pending controllers instead of canceling the previous pending one. Identical-tree successful full proof is reused for PR and push. Reuse expands both OS matrix names and reports the required seeded aggregate, using only a `Reuse verified identical-tree full gate` marker step with the original run id. These successful reporting jobs and skipped jobs are not fresh full proof; duplicate active full gates and unknown active coverage refuse admission. Pending controllers do not run gates. CI proof searches are bounded; missed older success can require a new gate, never invent green evidence.
3746
+ - Cost reporting is read-only over bounded current public board links plus merged-PR/check metadata: review handoff rounds and elapsed time, CI run ids and extra attempts, full re-reviews versus deltas, and unknown scope. Private/relinked counterparts are excluded at each read and projection. Pure band-breach proposal data is delivered here; the S7 baseline/configuration/automatic board-card scheduler is deliberately not implemented in S10.
3747
+ - Nonblocking policy ceilings stay unchanged and are recorded in hub task #230: fail-closed metadata errors, repository-wide queue fairness, unknown thread author kinds, and strict advise readback races. Their planner-reviewed alternatives are deferred.
3748
+
3749
+ ### S11 release-train amendment (#481, hub task #216)
3750
+
3751
+ - The existing `release` task field and release planning table remain authoritative. Additive optional train metadata records enumerated hotfix cause/reason, frozen task ids, and bounded actor/time/resource history. Legacy rows omit it.
3752
+ - "One open release" means one active/frozen train. Future planning rows are permitted as rollover destinations. A dedicated release-only assignment may move approved/in-flight tasks without modifying their execution state, owner, reservation, dependencies or person pins; every move needs a reason and current revision. Public reasons are screened; private scope moves are person-only and retain the reason solely in withheld board history without model screening. Public readiness/cut still refuses private scope. General backlog planning authority is unchanged.
3753
+ - The planned date is a due trigger, not a waiver. A cut requires nonempty approved scope; unfinished/cancelled tasks move out with a reason before cut. It pins the complete scope and release revision through all awaits and the final transaction. Planner cut is a dedicated operation; legacy planner freeze refusal stays intact. Enabled or configured-train freezes cannot bypass the admitted cut API. Ship/drop remain person-only; a changed frozen scope blocks shipment.
3754
+ - Cut admission holds a bounded five-second host-controller repository lease through the metadata freeze. The controller executable is host-resolved, its child command is fixed, and the shared gate lock is checked before and at freeze. Unknown admission or unknown/stale attached-peer account quota windows with a real quota source defer. A real source means the adapter definition supplies `budget.reader`, or it uses soft-token accounting with an enabled limit (Kimi: `kimi_tokens_5h > 0`); registered accounts without a configured source are reported as `no quota source` and do not defer for missing readings; unknown external accounts still require evidence. Budget-record and manual pauses still defer for every attached peer. Every later heavy step still needs its own admission.
3755
+ - Hotfix causes are closed: data/message loss, security/privacy defect, blocked upgrade/start, core flow without workaround. Reason is bounded and screened. No hotfix waives a gate, grants publish authority or starts an upgrade.
3756
+ - Actual cut/shipment event timestamps and frozen scope drive interval/approval lead-time metrics. Hotfix rate uses shipped releases. Bounded CI counts refer to planned-task PR metadata at cut, including attempts; review counts refer to board verdict history. Unavailable/legacy evidence remains unknown, and release-preparation PR costs require separate receipts. S7 owns bands/proposals; S6 owns authorization. The per-release "gates run" record is explicitly deferred to S6/S7; a cut record is not gate-execution evidence. No GitHub body, publish, merge or tag write is added.
3757
+
3758
+ - Person ruling 2026-10-19 supersedes the earlier planner ruling on S11 upgrade admission: upgrade is excluded from S11 host-admission, quota and additional all-idle gates. Recovery retains the pre-S11 `recoveryReady()` requirement (no busy peers, pending approvals resolved and completion checks settled), operation-identity checks and existing lifecycle fences. Quota/manual pauses, missing/shadowed `dot` and nested host admission add no S11 upgrade refusal. The person's existing recovery-operation selection remains the confirmation; no separate receipt or agent upgrade authority is added. Release-cut host/quota gates remain unchanged.
3759
+
3760
+ - Configured cut dates are positive safe UTC millisecond timestamps within the Date range; null, coercible values and invalid numbers refuse at the Tasks boundary before screening or board writes. The additive `release_trains` daemon feature is required of a recovery target while a configured train is active/frozen, before source holds/capture and again after awaits/before commit. Legacy and queued future release rows do not add this downgrade floor. Protocol 16 remains unchanged.
3761
+
3762
+ - Release-cut deferrals name their applicable exits: stop or resume paused peers, adjust the relevant `ahub budget set` setting, or drop the train with `ahub release drop`. Missing or shadowed `dot` requires installing the resource controller or fixing PATH; nested admission requires retrying the cut outside the outer admission lease. These host/quota gates do not apply to upgrade.
3763
+
3764
+ - S11 follow-up invariants: contiguous planning revisions changing exactly `["release"]` retain the S9 reviewed execution plan; other or missing planning history invalidates it. Cut-time CI cost reads are limited to the scoped PR ids (at most 16 in one repository); read errors or unbounded/mixed scope remain unknown and cannot abort a valid cut. Train history is capped at 256 events: configuration no-ops append nothing, meaningful configuration reserves cut/shipment slots, and capacity refusals are pre-effect.
@@ -0,0 +1,63 @@
1
+ # 0.12.36 release verification
2
+
3
+ This record covers release preparation and source review. It does not claim publication, installation, a running-hub restart or native/live acceptance.
4
+
5
+ ## Candidate
6
+
7
+ Version 0.12.36, control protocol 16 (unchanged), from merged main `6e39b616`. The person directed the release once every open PR had merged (2026-10-18/19).
8
+
9
+ - #502 (PR #504): the Jcode and Kimi TUI launchers write the running daemon's plugin bundle into their MCP entries; `status` reports the bundle.
10
+ - #481 S1 (PR #503): person-only spec acceptance bound to the issue edit time; in enforce, build work starts only from a current acceptance.
11
+ - #450 I3 (PR #505): declared Claude reviewer and planner channel instances with per-instance launch, session, context and recovery records.
12
+ - #481 S9 (PR #518): planner grooming, plan-review receipts, WIP admission in every lifecycle mode, untouched-work return.
13
+ - #450 / hub task #223 (PR #506): a headless Claude reviewer that runs a fresh `claude -p` session per review, with review-only delivery, verdict authority fenced to the running child, and process-group cleanup.
14
+ - #517 (PR #521): Claude turn completion read from the transcript's last 4 MiB; a console notice when no completed turn is found in that tail.
15
+ - #520 (PR #522): the hub-built review brief in every review request, `hub_review` coverage with a mode-following gate, and `ahub review brief`.
16
+ - #481 S10 (PR #523): plan review and CI scopes, the premature-review gate, identical-tree CI reuse with reporter jobs for the required check names, and the cost report.
17
+ - #450 / hub task #223 (PR #524, found by this release's document review): `ahub claude --as <id>` asks the hub to start a headless reviewer (`ahub start`, named by #506, never existed), `ahub stop` stops it, and stop replies name `ahub <kind> --as <id>` for every instance.
18
+ - Still open (unchanged): live native clients, installed managed-Pi #418 AC8 (#142), #438 AC7, the native console settings leg (#439 AC5), a real upgrade with reattach (#463), a real upgrade with a managed terminal and cancelled rows (#478), the live advise cycle of #481 AC9, the installed #493 D3 leg, and the live two-instance run (#450 I4, #138).
19
+ - New open legs:
20
+ - A live headless review with a real Claude account: fresh session, read-only tools, the `Read` deny rules for private paths, and the verdict recorded as `fresh` (#223).
21
+ - A live long session over 32 MiB going idle after a Stop (#517).
22
+ - Deferred to backlog: #230 (S10 P3s: fail-closed reuse decision, one repository-wide queue, ghost thread authors, advise races).
23
+
24
+ ## Reviews
25
+
26
+ | PR | Final head | Review | CI |
27
+ | --- | --- | --- | --- |
28
+ | #504 (#502) | b26ca1ea | codex, approved | exact head, success |
29
+ | #503 (#481 S1) | 5b22c841 | codex, approved | exact head, success |
30
+ | #505 (#450 I3) | 091689ac | codex: three rounds, then a merge-delta approval | exact head, success |
31
+ | #518 (#481 S9) | 1d2a9ac8 | planner via OCR delegation (Kimi out of quota, read-only Claude subagent): round 1 one P2 (redo blocked by WIP), round 2 approved | exact head, success |
32
+ | #506 (#223) | e305a17e | codex: four rounds (privacy, authority, lifecycle and process-group findings), then rebase-delta approval | exact head, success |
33
+ | #521 (#517) | 7ae00959 | codex: round 1 one P2 (newline-less cut tail), round 2 approved; bot P1/P2 fixed, delta approved | exact head, success |
34
+ | #522 (#520) | c35222f6 | codex: three rounds of findings (task escalated once by the hub's rejection rule; the person directed the planner to finish it), then approvals of the rebase and CI-fix deltas | exact head, success |
35
+ | #524 (#223 start/stop) | 9408ebde | codex: approved, plus a delta approval after the bot's P2 (offline reply command); codex also ran a real CLI/daemon fixture for start, refusal and stop | exact head, success |
36
+ | #523 (#481 S10) | 65ae2705 | planner via OCR delegation (read-only Claude subagent): round 1 P1 (advise PII done) and P2 (reuse missing required names), round 2 approved | exact head, success; a real identical-tree reuse run (39168948253) reported the required names and the PR merged on them |
37
+
38
+ ## Document review
39
+
40
+ Ten documents were stale against 03209040, and the agent-notes README became stale through `daemon.md`. Two fresh-context reviewers read them against the eight PRs above; the planner applied the corrections.
41
+
42
+ | Document | Result | Checked claims and correction |
43
+ | --- | --- | --- |
44
+ | `README.md` | corrected | Status 0.12.36; the Jcode entry sentence now says the command writes the running hub's plugin bundle (#502); the verification link and the open live legs (#223, #517, #523 added). |
45
+ | `docs/quickstart.md` | corrected | `v0.12.36` install. "Upgrading to 0.12.36": restart Claude sessions, the review brief and `coverage` (#520), WIP admission in every mode with `tasks.wip_limits` (#481 S9), the enforce-only gates (S1 acceptance, plan review, coverage, premature review), the #502 entry refresh, instances and #517. |
46
+ | `docs/operations.md` | corrected | Version and upgrade commands 0.12.36; the review brief and coverage in the two-agent review section; #517 in the busy-session section; WIP in every mode; the S1, S9 and S10 commands in the SDLC section; the #502 bundle path in the Jcode section. The headless reviewer's start and stop came with #524. `tasks.wip_limits` corrected to apply live (settings.ts, config-apply.ts and the per-admission read in daemon.ts), after the release review. |
47
+ | `docs/security.md` | corrected | Instance start-mode limits per kind (#505, #506); a new paragraph on review-only delivery and the headless reviewer's flags, read-only tools, `Read` deny rules and verdict fence (#223); the brief's privacy and merge-base rules, metadata-only GitHub reads, and the unchanged CI permissions (#520, #523). |
48
+ | `docs/agent-notes/adapters.md` | corrected | `reviewerVariant` joined the restricted-field list; rules for TUI launchers writing the hub's bundle (#502) and for `ClaudePrintPeer`'s authority and cleanup (#223). |
49
+ | `docs/agent-notes/budget.md` | corrected | Per-peer Claude context and session files through `claudeStateFile`; usage stays one shared file (#505). |
50
+ | `docs/agent-notes/bus.md` | corrected | `relevant` may return a discard reason; review-only delivery kept at send and at delivery (#223). |
51
+ | `docs/agent-notes/daemon.md` | corrected | Per-instance launch file name; instance transcript evidence; `claudeStateFile` and `ownTerminalRow`; channel extras never hub-started and `requireActiveInstance`; Stop verification only through the tail reader (#517). |
52
+ | `docs/agent-notes/tasks.md` | corrected | Scope adds `src/hub/review-brief.ts`; rules for `reviewAuthority` in `valid()`, the coverage gate's pins and merge-base reads, owner refusal of `routing.reviewOnly`, and the untouched-return exception. |
53
+ | `docs/agent-notes/tests.md` | corrected | The `seeded guards` job also passes on a verified identical-tree reuse (#523); headless-reviewer tests never run the real `claude`. |
54
+
55
+ | `docs/agent-notes/README.md` | no change | Stale only because `daemon.md`, a tracked path, changed; its console exact-row background still matches `daemon.md`. |
56
+
57
+ `AGENTS.md` lists `src/hub/review-brief.ts` under tasks and `src/hub/usage.ts` under daemon.
58
+
59
+ ## Checks
60
+
61
+ - Each merged PR's exact-head CI passed (table above). The release branch merges main `6e39b616` cleanly; the bundle is rebuilt with the pinned Bun 1.4.2 and is fresh; `tsc` is clean.
62
+ - `node scripts/check-docs.mjs --require-fresh` runs after the restamp and before the tag.
63
+