@staix/agent-hub 0.12.33 → 0.12.35

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 (126) hide show
  1. package/CHANGELOG.md +22 -0
  2. package/README.md +3 -3
  3. package/docs/agent-notes/adapters.md +9 -3
  4. package/docs/agent-notes/budget.md +7 -3
  5. package/docs/agent-notes/bus.md +2 -1
  6. package/docs/agent-notes/daemon.md +7 -3
  7. package/docs/agent-notes/local-worker.md +1 -1
  8. package/docs/agent-notes/tasks.md +6 -2
  9. package/docs/agent-notes/tests.md +2 -1
  10. package/docs/gsd-collaboration-analysis.md +5 -1
  11. package/docs/operations.md +80 -16
  12. package/docs/quickstart.md +10 -2
  13. package/docs/sdlc.md +59 -34
  14. package/docs/security.md +7 -4
  15. package/docs/smoke.md +1 -1
  16. package/docs/specs/2026-09-19-agent-hub-design.md +407 -10
  17. package/docs/verification/2026-10-18-0.12.34.md +48 -0
  18. package/docs/verification/2026-10-18-0.12.35.md +49 -0
  19. package/docs/verification/2026-10-18-493-claude-busy-channel.md +91 -0
  20. package/docs/verified.json +35 -19
  21. package/package.json +1 -1
  22. package/plugins/agent-hub/.claude-plugin/plugin.json +1 -1
  23. package/plugins/agent-hub/server.js +23567 -16606
  24. package/src/adapters/acp.ts +3 -1
  25. package/src/adapters/claude-channel.ts +59 -29
  26. package/src/adapters/codex-appserver.ts +82 -12
  27. package/src/adapters/definitions/builtin-metadata.ts +101 -7
  28. package/src/adapters/definitions/native-cli-claude-launch.ts +90 -0
  29. package/src/adapters/definitions/native-cli-claude-setup.ts +80 -0
  30. package/src/adapters/definitions/native-cli-codex-launch.ts +14 -0
  31. package/src/adapters/definitions/native-cli-doctor.ts +46 -0
  32. package/src/adapters/definitions/native-cli-entry-claude.ts +52 -0
  33. package/src/adapters/definitions/native-cli-entry-codex.ts +34 -0
  34. package/src/adapters/definitions/native-cli-entry-jcode.ts +27 -0
  35. package/src/adapters/definitions/native-cli-entry-kimi.ts +57 -0
  36. package/src/adapters/definitions/native-cli-entry-local.ts +15 -0
  37. package/src/adapters/definitions/native-cli-entry-pi.ts +53 -0
  38. package/src/adapters/definitions/native-cli-entry-qwen.ts +21 -0
  39. package/src/adapters/definitions/native-cli-entry-types.ts +41 -0
  40. package/src/adapters/definitions/native-cli-entry.ts +19 -0
  41. package/src/adapters/definitions/native-cli-jcode-mcp.ts +58 -0
  42. package/src/adapters/definitions/native-cli-launch-common.ts +17 -0
  43. package/src/adapters/definitions/native-cli-pi.ts +33 -0
  44. package/src/adapters/definitions/native-cli-preview-claude.ts +37 -0
  45. package/src/adapters/definitions/native-cli-preview-codex.ts +20 -0
  46. package/src/adapters/definitions/native-cli-preview-common.ts +57 -0
  47. package/src/adapters/definitions/native-cli-preview-jcode.ts +20 -0
  48. package/src/adapters/definitions/native-cli-preview-kimi.ts +28 -0
  49. package/src/adapters/definitions/native-cli-preview-pi.ts +36 -0
  50. package/src/adapters/definitions/native-cli-preview-qwen.ts +22 -0
  51. package/src/adapters/definitions/native-cli-preview.ts +16 -0
  52. package/src/adapters/definitions/native-config-defaults.ts +19 -0
  53. package/src/adapters/definitions/native-config-policy.ts +35 -0
  54. package/src/adapters/definitions/native-context.ts +35 -0
  55. package/src/adapters/definitions/native-daemon-kimi.ts +5 -0
  56. package/src/adapters/definitions/native-daemon-policy.ts +18 -0
  57. package/src/adapters/definitions/native-display.ts +18 -0
  58. package/src/adapters/definitions/native-factories.ts +48 -0
  59. package/src/adapters/definitions/native-policy-aux.ts +130 -0
  60. package/src/adapters/definitions/native-quota.ts +29 -0
  61. package/src/adapters/definitions/native-recovery-continuity.ts +12 -0
  62. package/src/adapters/definitions/native-recovery.ts +88 -0
  63. package/src/adapters/definitions/qwen-native.ts +2 -2
  64. package/src/adapters/definitions/qwen.ts +8 -4
  65. package/src/adapters/local-worker.ts +3 -2
  66. package/src/adapters/pi.ts +3 -1
  67. package/src/adapters/registry-types.ts +100 -10
  68. package/src/adapters/registry.ts +127 -11
  69. package/src/cli/console-state.ts +5 -5
  70. package/src/cli/identity.ts +8 -7
  71. package/src/cli/jcode-mcp.ts +2 -58
  72. package/src/cli/launch.ts +41 -102
  73. package/src/cli/main.ts +126 -290
  74. package/src/cli/output.ts +3 -2
  75. package/src/cli/preview.ts +26 -126
  76. package/src/cli/recovery-guidance.ts +43 -22
  77. package/src/cli/setup.ts +2 -80
  78. package/src/cli/shell-quote.ts +3 -0
  79. package/src/cli/terminal-recovery.ts +81 -22
  80. package/src/cli/upgrade-interactive.ts +4 -2
  81. package/src/cli/upgrade-restore-state.ts +17 -7
  82. package/src/cli/upgrade-runtime.ts +126 -147
  83. package/src/cli/upgrade.ts +44 -27
  84. package/src/hub/budget.ts +59 -31
  85. package/src/hub/bus.ts +1 -1
  86. package/src/hub/conductor.ts +19 -9
  87. package/src/hub/config-apply.ts +22 -15
  88. package/src/hub/config-reload.ts +18 -3
  89. package/src/hub/config-trust.ts +1 -1
  90. package/src/hub/context-window.ts +3 -27
  91. package/src/hub/control-client.ts +1 -1
  92. package/src/hub/crash.ts +15 -39
  93. package/src/hub/daemon.ts +506 -341
  94. package/src/hub/execution-budget.ts +3 -2
  95. package/src/hub/hub-tools.ts +19 -8
  96. package/src/hub/instance-budget.ts +73 -0
  97. package/src/hub/instance-config.ts +47 -0
  98. package/src/hub/instance-execution.ts +50 -0
  99. package/src/hub/instances.ts +100 -0
  100. package/src/hub/lifecycle-schema.ts +7 -3
  101. package/src/hub/peer-catalog.ts +4 -13
  102. package/src/hub/permission-mode.ts +14 -17
  103. package/src/hub/recovery-continuity.ts +6 -5
  104. package/src/hub/report.ts +9 -8
  105. package/src/hub/roster.ts +9 -9
  106. package/src/hub/routing.ts +94 -19
  107. package/src/hub/sdlc-queue.ts +2 -2
  108. package/src/hub/sdlc.ts +84 -38
  109. package/src/hub/settings.ts +39 -10
  110. package/src/hub/settled-roles.ts +1 -1
  111. package/src/hub/snapshots.ts +40 -9
  112. package/src/hub/start-mode.ts +13 -8
  113. package/src/hub/status-roster.ts +8 -0
  114. package/src/hub/tasks.ts +60 -27
  115. package/src/hub/text-projection.ts +7 -3
  116. package/src/hub/tooling.ts +4 -60
  117. package/src/hub/ui.ts +3 -1
  118. package/src/hub/workflow.ts +30 -52
  119. package/src/memory/recall.ts +4 -5
  120. package/src/models/route/signals.ts +3 -1
  121. package/src/pi/launch.ts +5 -3
  122. package/src/ui/index.html +4 -3
  123. package/src/ui/office-model.ts +3 -1
  124. package/src/ui/peer-palette.ts +16 -6
  125. package/templates/AGENTS.block.md +2 -1
  126. package/templates/routing.toml +2 -2
package/CHANGELOG.md CHANGED
@@ -2,6 +2,28 @@
2
2
 
3
3
  Issue and pull request numbers in the entries for 0.7.7 and earlier refer to the previous repository, archived on 2026-09-30 when this repository's history was rewritten; the one exception is the open smoke-check issue, formerly #12, which moved here as #1. Numbers in newer entries refer to this repository.
4
4
 
5
+ ## 0.12.35
6
+
7
+ A Claude session started with `ahub claude` gets its hub messages while it is busy (#493), `ahub jcode` starts again (#484), a kind can run several headless instances (#450 I2), and an enabled lifecycle follows the AI-Native SDLC Playbook with gsd-lite retired (#481 S0+S8). Restart Claude sessions after upgrading: each keeps the plugin server it started with.
8
+
9
+ - Let an attached channel session read the daemon queue with `hub_inbox` while busy, using the existing pull, holds and journal completion; tool results carry fresh queued and important counts in every MCP mode, and a zero clears a stale hint (#493).
10
+ - Steer important messages into a busy channel session's running turn instead of waiting for the turn to end; ordinary messages still wait (#493). Mid-turn reading was verified on Claude Code 2.1.302.
11
+ - Session instructions, tool texts and the AGENTS block forbid `[FYI]` for actionable messages (PR ready, done, submitted, ruling needed, blocker, changed contract or path) (#493).
12
+ - `ahub jcode` no longer refuses every start as off roster, and the `ahub kimi --mode tui` off-roster check works: `status` now carries each supported peer's roster membership, and a hub without it is reported as unknown with a remedy (#484).
13
+ - Declare extra headless peer instances (`<kind>-<name>`, for example a second Codex or Kimi reviewer) in untracked `.agenthub/config.local.json` and start them with `ahub <kind> --as <id>`. Extra implementers run in their own registered worktree, routing balances a kind across its instances, and quota pauses are shared per account (#450 I2).
14
+ - An enabled lifecycle (`mode` advise or enforce) defaults to the playbook stages intent, spec, plan, build, test, review, merge, release and maintain, and status and the dashboard name the playbook stage. Stored `implement`/`verify` receipts read as `build`/`test`. Off keeps its seven stages, receipts, tool schema, export and output unchanged (#481 S0).
15
+ - gsd-lite is retired into the acceptance-criteria checklist (`criteria`, part of the Compliance pass), which counts once recorded in an enabled lifecycle; checklist task import is removed. Until 0.13.0, `workflow = "gsd-lite"` and the `phase` gate load with a warning in `hub.log` and the `phase` receipt name is still accepted; from 0.13.0 all three are refused with a field-level reason (#481 S8).
16
+ - The protected-segment grant test checks every segment through the production grant decision and no longer grows with the set, ending its Ubuntu CI timeout (#219 task, #485 follow-up).
17
+
18
+ ## 0.12.34
19
+
20
+ Built-in native policy now lives behind the adapter registry (#451 A2). Native behaviour is unchanged except the disclosed Codex watchdog fence below; the core keeps no vendor tables, and a guard refuses new ones. The changes_requested threshold that moves a task to another peer is now the setting `tasks.escalate_after`, with a default of three instead of the old fixed two (#488). Antigravity, Gemini CLI and Grok project configuration directories are protected like other agent configuration (#485). Live native clients, the #418 AC8 managed-Pi leg (#142) and #438 AC7 remain unverified by this release preparation. Installation and the person's restart require separate receipts.
21
+
22
+ - Escalate a task after `tasks.escalate_after` consecutive changes_requested (1-10, default 3, applies live at the next verdict), replacing the fixed two (#488).
23
+ - Treat `.agent`, `_agents`, `_agent`, `.gemini` and `.grok` as protected agent configuration directories, so scoped grants ask instead of auto-allowing (#485).
24
+ - Move built-in native launch, permission, recovery, reporting and configuration policy behind the adapter registry, retaining host authority and native behavior; add transport conformance and reject new core vendor tables (#451).
25
+ - Fence late Codex turn reports and acceptance after watchdog cancellation so expired answers cannot reach a later audience (#451).
26
+
5
27
  ## 0.12.33
6
28
 
7
29
  Upgrades with cancelled tasks and a managed terminal no longer stop at the lease renewal, and a notice the target sends right after it starts no longer blocks queue verification (#478). The upgrade runs with the target release's own coordinator, so an upgrade from 0.12.32 to this release already uses these fixes. A real upgrade with a managed TUI and cancelled rows (#478) and installed managed-Pi #418 AC8 (#142) remain unverified by this release preparation. Installation and the person's restart require separate receipts.
package/README.md CHANGED
@@ -5,7 +5,7 @@ Codex, Kimi Code, Pi and a hub-owned local worker (Qwen Code and Jcode optional)
5
5
  share a message bus and a task board per project and one memory on the machine,
6
6
  each attached through its own native interface.
7
7
 
8
- Status: 0.12.33, control protocol 16. Bun + TypeScript, one loopback daemon per
8
+ Status: 0.12.35, control protocol 16. Bun + TypeScript, one loopback daemon per
9
9
  project. Site: [agent-hub.staix.net](https://agent-hub.staix.net).
10
10
 
11
11
  ## What it does today
@@ -77,13 +77,13 @@ cd <your project> && ahub init && ahub up
77
77
 
78
78
  After upgrading from 0.12.30 or earlier, run `ahub jcode --mode headless` once in each
79
79
  project that uses Jcode to refresh its owned MCP entry before conductor or dashboard
80
- starts of Jcode. The entry's format is unchanged in 0.12.33; a daemon start that still
80
+ starts of Jcode. The entry's format is unchanged in 0.12.35; a daemon start that still
81
81
  reports the entry missing or stale is fixed by the same command.
82
82
  Cancellation needs a daemon advertising `task_cancel`; controlled downgrades
83
83
  refuse while cancelled rows exist. For local/Pi implementation, opt in to an
84
84
  available `checks.runner = "container"`; issue-backed owners also need authenticated
85
85
  spec access. PII work and designated reviewers retain their separate exemptions.
86
- See [release verification](docs/verification/2026-10-18-0.12.33.md) for the live legs
86
+ See [release verification](docs/verification/2026-10-18-0.12.35.md) for the live legs
87
87
  still open, including #418 AC8, #438 AC7, the native console settings leg (#439 AC5),
88
88
  a real upgrade with reattach (#463) and a real upgrade with a managed terminal and
89
89
  cancelled tasks (#478).
@@ -2,10 +2,13 @@
2
2
 
3
3
  Scope: the Codex, ACP and Pi adapters and the Pi extension (turn `generation` and `activity` events), peer turn state, approvals, and the agent processes the hub spawns and stops. Read before editing `src/adapters/`, `src/pi/`, `src/hub/peers.ts`, `src/hub/permission-mode.ts`, `src/hub/tool-refusal.ts`, `src/hub/child-process.ts`, `src/hub/lifecycle.ts` (shutdown deadline), `src/hub/start-mode.ts`, `src/hub/terminal-open.ts`, `src/memory/capture.ts` (session end) or the process-tree stop in `scripts/benchmarks/teardown.ts`, or before adding a native peer.
4
4
 
5
+ - When an adapter lands, add its project config directories to `AGENT_CONFIG_SEGMENTS` (`src/hub/permission-mode.ts`); scoped automatic writes must ask there, while explicit approval and `never-ask` keep their existing behavior.
5
6
  - Kimi TUI is a pull-only WsPeer under the one kimi id, with no hub busy/watchdog/permission/quota machinery. Only the person's launcher merges or removes its project MCP entry; a daemon headless start refuses a stale entry and never edits the file. Scrub mode/channel markers from the native child; managed AGENTS instructions and tool descriptions carry the pull rules, not MCP initialize instructions.
7
+ - Keep per-kind native policy in `src/adapters/definitions/` and consume it through the registry. Private CLI implementations accept only fixed host-bound ports; keep identity, audit, lifecycle admission and effect ordering in the host. Do not add config-selected callbacks or runtime imports from registry leaves into eagerly initialized hub/CLI modules; use type-only ports for existing host planners.
8
+ - Retire a cancelled Codex native turn until its exact completion or session teardown. Late start acceptance, answer frames and completion must not revive its audience or relabel uncertainty as `failed_safe`. If cancellation has no provable native id, keep the peer offline until verified reattachment.
6
9
  - Codex: the adapter never sends its own `initialize`. It is a proxy; hub requests use negative ids and their responses must not reach the TUI.
7
10
  - A durable Codex steer stays pending while its native turn runs. Its RPC may answer after `turn/completed`: bound unanswered replies from that turn's end, requeue a definitive late refusal, and hold a late acceptance as uncertain without attaching it to a later turn's audience (#296).
8
- - Codex reports each thread it adopts, started or resumed (`onThread`, the `native_thread` event), before any turn on it starts: the upgrade planner (`codexZeroTurn` in `src/cli/upgrade-runtime.ts`, #215) gives each codex `turn_start` to the thread adopted last before it and plans a thread it saw start with no turn (and no rollout) as a fresh start.
11
+ - Codex reports each thread it adopts, started or resumed (`onThread`, the `native_thread` event), before any turn on it starts: the upgrade planner (Codex's `session.zeroTurn` in `src/adapters/definitions/native-recovery.ts`, read through `nativeZeroTurn` in `src/cli/upgrade-runtime.ts`, #215) gives each codex `turn_start` to the thread adopted last before it and plans a thread it saw start with no turn (and no rollout) as a fresh start.
9
12
  - Codex 0.154.0 `agentMessage` items carry `text` and `phase` (not `content[]`); only the last non-`commentary` message of a turn is shared.
10
13
  - Approval titles are agent-written text shown to a person: the daemon escapes control characters, and write/edit/bash show what will be written or run.
11
14
  - An ACP permission request may carry no `rawInput` (Kimi 2.0.1): the arguments were on the earlier `tool_call` update, which `acp.ts` remembers by `toolCallId`. Kimi 2.1.1 sends no `rawInput` before the answer at all: the argument JSON streams as `content` text on `tool_call_update`, and only complete JSON counts. A payload that still cannot be resolved, or is too long to show whole (marked `[cut, N chars]`), withholds its `allow_always` option - never render a bare tool name as if it described the call.
@@ -30,7 +33,7 @@ Scope: the Codex, ACP and Pi adapters and the Pi extension (turn `generation` an
30
33
  - Pi's bridge executes `/tool` only for the verified owner (`verifiedOwner()`: the claimed pid with its `processSignature`; a headless owner is pinned from the spawned process once `get_state` proved its session) with the current `sessionId` and turn `generation`, and admits `/budget` only for that owner and generation, checked again after the awaited admission. Refuse anything else before execution. `tool_abort` aborts that pending call, or fences its id when it arrives before the call (64 ids a turn, then every further call of that turn). A person's `!command` (`purpose: "idle_user_bash"`) is taken only in TUI mode from an idle owner on its one-use reservation, and it alone skips `piPermit`: never widen that to a model's bash call (#253).
31
34
  - Pi and local approvals (#253): `permit` answers a `ToolApproval` (contract: `local-worker.md`) and `requestPermission` (`src/hub/daemon.ts`) tells the adapter's `ApprovalObserver` what settled it. An expiry counts toward the stop only as `source: "expired"` with `eligibleExpiry` not false (no person answered a request of that peer while it was pending); `person` resets the count, `automatic` (a mode grant, Pi's `always`) never does, and a new turn starts it over. The second one ends the turn, never the peer: local throws `ApprovalWaitStop`, Pi sends `abort_budget` with `cause: "approval"`, and both settle the delivery `completed` with the reason; a Pi whose extension did not announce `approvalTurnAbort` goes offline with the delivery held. `requestPermission` deletes a request from `permissions` before it runs an observer, resolves or reports, and an aborted signal closes it as `cancelled`: a late answer must find nothing to grant, and a failed event, notice or console push never strands the caller.
32
35
  - `stop(reason?)` (`src/hub/peers.ts`): a reason marks a person's requested stop (`peer_stop`, rule: `daemon.md`). Fence before the first await (the adapter's stopping flag, or `offline`) so `deliver()` and buffered native callbacks do nothing more, hold the in-flight delivery as `needs_review` with that reason, and end `offline`. For ACP without a journal, return ordinary in-flight envelopes once through `onFailed` before a requested stop invalidates the turn; never return the watchdog-cancelled original prompt. A requested stop never completes, escalates or reports the turn it cut (`AcpPeer` bumps `turn`; `PiPeer.fail` skips the failure message and `onTurnFailure` while `requestedStopReason` is set). `PiPeer.stop()` waits for aborted tools only up to `stopGraceMs`: a tool past it never blocks the teardown, and the stop throws `PiToolSettlementFailure` once the owner is down.
33
- - A terminal the hub opens for a person (`src/hub/terminal-open.ts`, #269) gets `personEnv`, which strips every agent marker; never `peerChildEnv`, which sets one. Adding a native peer means registering its `AdapterDefinition` in `ADAPTERS` (`src/adapters/registry.ts`; `isSupportedPeer`, `isKnownPeer` and `defaultPeerRoles` in `src/hub/peer-catalog.ts` answer from `definitionOf`) and listing its id in `KNOWN_PEERS` or `OPTIONAL_PEERS` and `ROLE_ORDER` there (`STARTABLE_PEERS` in `src/hub/start-mode.ts` reuses that list); if it has both a TUI and a headless form, also add it to `START_MODE_PEERS`, with its fixed words in `startWords` (`src/hub/daemon.ts`).
36
+ - A terminal the hub opens for a person (`src/hub/terminal-open.ts`, #269) gets `personEnv`, which strips every agent marker; never `peerChildEnv`, which sets one. Adding a native peer means registering its `AdapterDefinition` in `ADAPTERS` and listing its id in `KNOWN_PEERS` or `OPTIONAL_PEERS` and `ROLE_ORDER`, all in `src/adapters/registry.ts` (`isSupportedPeer`, `isKnownPeer` and `defaultPeerRoles` answer from `definitionOf`; `STARTABLE_PEERS` reuses `SUPPORTED_PEERS`); if it has both a TUI and a headless form, also add it to the registry's `START_MODE_PEERS`, with its fixed words in its definition's `cli.startWords`. Mirror `KNOWN_PEERS`, `OPTIONAL_PEERS` and `START_MODE_PEERS` in the literal cross-release copies in `src/hub/peer-catalog.ts` and `src/hub/start-mode.ts` (`ROLE_ORDER` has none). Adding a peer is the one change that updates their pinned counts in `test/registry-guard.test.ts` and the literal in `test/adapter-catalog-a2.test.ts`; say so in the PR as an expected test change.
34
37
  - Spawn a native peer with `peerChildEnv(peer, source)` so its own `AGENTHUB_PEER_ID` replaces inherited agent markers. Sandbox tool environments carry their executing peer too. Preserve the native shell environment policy; Codex's `CODEX_THREAD_ID` supplies the fallback after filtering. Before changing marker detection or launcher environments, read `docs/verification/2026-10-09-agent-shell-t0.md` for pinned source and live-probe limits.
35
38
  - Emit Pi's ceiling event at the pre-effect rejection boundary (`src/pi/ceiling.ts`), before `agent_end`. Accept only a valid current-session/current-generation signal during a running turn; retain the first signal and attach it to that turn's failure. Never classify a ceiling from free text.
36
39
 
@@ -45,5 +48,8 @@ Scope: the Codex, ACP and Pi adapters and the Pi extension (turn `generation` an
45
48
 
46
49
  - Pi TUI hub prompts (#379; an idle snapshot ACK arriving before a personal start inserted a hub prompt into the person's turn): attribute a start, end or settle to a hub turn only by the command's `promptId`, which lasts until the logical `agent_settled`; treat any other start while a hub prompt is pending or running as personal activity that detaches Pi. Settle `failed_safe` only on a `prompt_not_started` refusal bound to the verified owner, session, command id and generation; everything else stays `needs_review`. Refuse roster return without `fencedPrompt`.
47
50
 
48
- - Adapter registry (#451): new kind-specific policy goes into that peer's `AdapterDefinition` under `src/adapters/definitions/` (the A1 compatibility definitions in `builtin-metadata.ts` leave the daemon the runtime authority for every peer but Qwen). Outside `src/adapters/`, resolve a peer through `kindOf`/`definitionOf` imported from `src/adapters/registry.ts`, never a raw peer-id comparison, a vendor-keyed object, a peer-name array or a closed-list import such as `SUPPORTED_PEERS`. `test/registry-guard.test.ts` holds every remaining site to `test/registry-guard.baseline.json`, which only shrinks: when a site migrates, delete its entry or lower its count in the same change; never add an entry, raise a count or regenerate the file. `LaunchContext.nativeCommand` carries only validated config argv (or its redacted preview), never model input; a definition's `permission.grants` text is what `permissionGrant` shows, ahead of the built-in wording. Keep native mode-id maps in `definitions/*-native.ts`, free of registry factories (`test/adapter-import-order.test.ts`).
51
+ - Adapter registry (#451): kind-specific policy lives in that peer's `AdapterDefinition` under `src/adapters/definitions/` (runtime definitions `NATIVE_BUILTIN_DEFINITIONS_WITHOUT_QWEN` in `builtin-metadata.ts` plus `qwen.ts`; the daemon constructs every native driver through `start.create` and `DAEMON_PORTS`; `BUILTIN_DEFINITIONS_WITHOUT_QWEN` is the frozen A1 export, read by no runtime consumer). Outside `src/adapters/`, read a semantic field of `definitionOf(id)`: never a raw peer-id comparison, a resolved kind (`kindOf`, `definitionOf(...).kind`) or a constant alias compared with a native name, a vendor-keyed object, a peer-name array, or a closed-list import from `peer-catalog.ts`, `start-mode.ts` or `permission-mode.ts`; the guard resolves aliases and import/re-export bindings. `test/registry-guard.baseline.json` is `[]` and stays empty: a new site moves into a definition, never into the baseline. The only exceptions are the cross-release literals `KNOWN_PEERS` (5), `OPTIONAL_PEERS` (2) and `START_MODE_PEERS` (3), pinned separately in `test/registry-guard.test.ts`. `LaunchContext.nativeCommand` carries only validated config argv (or its redacted preview), never model input; a definition's `permission.grants` text is what `permissionGrant` shows, ahead of the built-in wording. Keep native mode-id maps in `definitions/*-native.ts`, free of registry factories (`test/adapter-import-order.test.ts`).
52
+ - Registry load runs `assertDaemonEngine` and `assertNativeCapabilityScope` before publication. A definition that is not a fixed builtin (object identity in `fixedBuiltinDefinitions`, never a kind name) may not declare `start.acpCommand`, `attach.admission`, `budget.resetSoftTokensOnResume`, `session.crashIdentity`, a `pi-rpc` daemon port or `routing`. A new field that depends on fixed-builtin assumptions or host-global native state joins that list (`test/adapter-capability-scope.test.ts`). `trust.onPrem` admits PII work (rule: `tasks.md`).
49
53
  - `validatePiSession` (`src/pi/session.ts`) throws `PiSessionUnresumable` only on positive evidence (ENOENT, a file outside the managed session directory, an unparsable or mismatched header) and `PiSessionBookmarkMismatch` only when a valid header names another session id; every other read failure is rethrown unchanged, because the daemon retires a Pi continuity bookmark only on those two classes (#463). `PiPeer` rethrows a mismatch with the neutral header-id message and never claims a bookmark exists (`test/pi-session-validation.test.ts`).
54
+
55
+ - Spawn with `peerChildEnv(<instance id>)`, not the kind name. An ACP `kind` comes only from the host declaration, never from the id suffix or agent text.
@@ -1,6 +1,6 @@
1
1
  # Budget, pause and handoff
2
2
 
3
- Scope: quota and native context readings, checkpoints, pause, handoff and resume, and the status line tee. Read before editing `src/hub/budget.ts`, `src/hub/context-window.ts`, `src/cli/statusline-tee.ts`, `src/hub/bus.ts`, `src/hub/delivery-journal.ts` or `src/hub/restart.ts` (pause persistence).
3
+ Scope: quota and native context readings, checkpoints, pause, handoff and resume, and the status line tee. Read before editing `src/hub/budget.ts`, `src/hub/context-window.ts`, `src/cli/statusline-tee.ts`, `src/adapters/definitions/native-quota.ts`, `src/adapters/definitions/native-context.ts`, `src/hub/bus.ts`, `src/hub/delivery-journal.ts`, `src/hub/restart.ts` (pause persistence) or the per-account fanout in `src/hub/instance-budget.ts`.
4
4
 
5
5
  - Budget: checkpoint first, pause second (a paused peer receives nothing). A handoff that fails is left unmarked so the next tick or hub run retries it; never record it as done.
6
6
  - A handoff needs somebody to hand over to: `canHandOff` is false right after a restart, when no peer is attached yet, and the handoff waits for a later tick instead of stripping tasks of their owner.
@@ -9,10 +9,14 @@ Scope: quota and native context readings, checkpoints, pause, handoff and resume
9
9
  - The coordinator only lifts its own pauses: `manualPaused` in the daemon keeps a `ahub pause` in place, and `ahub resume` refuses while a budget record is open.
10
10
  - The status line tee must never fail or slow the render: no throw, original command run with the same stdin, 5 s cap.
11
11
 
12
- - Keep native context occupancy in `src/hub/context-window.ts` separate from quota accounting; never use Codex's accumulated `total` as occupancy or estimate Pi counters.
12
+ - Keep native context occupancy (`src/hub/context-window.ts`, readers in `src/adapters/definitions/native-context.ts`) separate from quota accounting; never use Codex's accumulated `total` as occupancy or estimate Pi counters.
13
13
  - Context checkpoint completion requires its request id and the current attached peer/native session. Invalidate its transport generation synchronously at a new peer claim, before asynchronous recall or attachment. Never pause, reassign or replace a session on context pressure. Refuse private-turn, PII-pattern and all associated open PII-task summaries (owner or reviewer, including in_review, with the current routing policy; use `isClosedTaskState` to exclude approved and cancelled work) before local persistence or cloud memory; never broadcast checkpoint text.
14
14
  - Invalid/stale context readings expose unknown and do not rearm a crossing. Latch a crossing only after its event is accepted; reconsider paused/recovery-held crossings after release with fresh current-session readings. Tee context fields contain finite numeric values or null only, bound to daemon instance, launcher and native session.
15
15
 
16
16
  - A provider hard refusal ends a reading override and supersedes a soft evaluation still awaiting its checkpoint; never let the obsolete checkpoint overwrite that pause. An ACP provider quota refusal reports a hard budget reading and confirms the live pause before the adapter requeues work, so the pause fences retries and the existing relay handles assignments. Use the observed window length for an unknown reset; repeated quota refusals while paused do not consume poison-envelope retries.
17
- - Kimi's ACP `{used, size}` occupancy is a context reading (`acpContext`, source `acp_usage_update`) bound to the adapter's own session id: never a `tokens` event or a budget window, and an invalid shape reports unknown, not the previous value. A gate crossing sends a checkpoint request to Claude and Codex only; an ACP peer gets the event and the notice (#285).
17
+ - Kimi's ACP `{used, size}` occupancy is a context reading (`acpContext`, source `acp_usage_update`) bound to the adapter's own session id: never a `tokens` event or a budget window, and an invalid shape reports unknown, not the previous value. A gate crossing sends a checkpoint request only to definitions with `budget.checkpoint` (Claude and Codex); an ACP peer gets the event and the notice (#285).
18
18
  - A quota resume notice replaces the peer's unread resume notices: carry their moves into the new one (`refs.budgetResumeMoves`, or the whole text of a notice that has none), publish it, and only then withdraw the ones it replaces, so a failed publish keeps them (#306).
19
+ - Quota handoff context reads `QUOTA_HANDOFF_PLATFORMS` (`src/adapters/registry.ts`, each definition's `hooks.recallPlatform`: claude, codex, kimi, qwen, jcode). It is independent of `hooks.recallInjected`: a handoff platform never joins the injected-platform exclusion in recall (`test/adapter-handoff-platforms.test.ts`).
20
+ - `Budget.report` skips `user`, `hub` and a registered kind with no `budget` block or with `budget.quotaExempt` (only local); an external id keeps quota accounting. `budget.instrumented` (execution budget) never exempts quota.
21
+
22
+ - Key quota readings, pause records, overrides, checkpoints and handoffs by the canonical account (`hooks.account`, idempotent); telemetry keeps the reporter id and native context occupancy stays per instance. `instanceBudgetHooks` fans pause, resume, checkpoint and handoff out to every member, and `canHandOff` excludes the paused account's siblings. Startup normalization merges old alias pause rows without dropping a row or marking an unfinished handoff done. A manual pause stays per member.
@@ -8,13 +8,14 @@ Scope: delivery, digests and condensation, reply addressing, priority and limits
8
8
  - Apply reloaded `batch_max`, `batch_ms` and `queue_cap` only through `Bus.configureScheduling` (#439): it validates the values and changes later batching and admission only. Never re-arm an existing batch deadline, trim or reorder queued work, or touch prefaces, pauses, recovery holds or in-flight delivery from it.
9
9
  - A failed digest is retried one envelope at a time, so a poison envelope cannot take its neighbours down with it.
10
10
  - An `important` envelope being steered is not in the queue while the steer is in flight; queue it first and an idle transition delivers it twice.
11
+ - `WsPeer.steer` exists only for a push channel session: set at attach, cleared on detach, roster detach and a pull-only attach, and it refuses anything but important work in the current busy turn (#493).
11
12
  - `replyParent()` decides what a reply answers (highest hop, never the `hub` preface). Use it for deliveries and steers alike, or the hop cap can be reset.
12
13
  - What a peer is handed (`out`) and what it stands for (`originals`) differ once a delivery is condensed: the bus keeps both, registers `out` so `reply_to` resolves, puts the originals back on a `failed_safe` receipt and holds any other failure (a thrown `deliver`, a later `onFailed`, a `needs_review` receipt) as `needs_review` with its originals, for `ahub queue resolve`, and counts no attempt when the peer merely got busy while the delivery was prepared. A delivery with an `important` envelope is never condensed.
13
14
  - A condensed digest is sent by `digest`, not `hub`: `replyParent()` skips only the hub's `presence` item, and a reply to a digest must keep the highest hop of what it replaced. On a `failed_safe` delivery or a `retry` resolution the bus puts back the originals, never the digest.
14
15
  - Limits admit what is sent: the envelope `newEnvelope` builds (a reply inherits its parent's sender, `digest` resolves to the originals, `capPriority` applies), never the raw `to` and priority. Admit first and build later, and implicit replies all count as broadcasts.
15
16
  - A reply is addressed, never broadcast. An adapter that answers a delivery passes `to: replyAudience(envs)`; anything else with an `inReplyTo` inherits that envelope's sender. Leaving `to` empty fans the message out to every peer.
16
17
  - `digest` is not a peer: addressing a reply at the envelopes the peer was handed sends it nowhere once a delivery was condensed. The bus resolves `digest` back through `lastDelivery.originals`; anything else that reads a reply's `to` has to do the same.
17
- - A pull-only peer (a Claude session without channel pushes or Kimi in its own TUI) is never drained; `Bus.pull` hands it what one push would take. Drain and pull share `held()` (recovery, an uncertain delivery, any pause, the lifecycle `deliveryHeld` fence) and `remember()` (so `reply_to` resolves), and a drain rechecks `deliverable()` after condensing because such a session can take the peer meanwhile. A pull is journaled `completed` in the same transaction as the bus snapshot, never `accepted`; a failed one puts back only that peer's queue and preface and never reloads the snapshot.
18
+ - A pull-only peer (a Claude session without channel pushes or Kimi in its own TUI) is never drained; `Bus.pull` hands it what one push would take. An attached channel session may also pull its own queue through `hub_inbox` while busy (#493); the same holds and journal completion apply. Drain and pull share `held()` (recovery, an uncertain delivery, any pause, the lifecycle `deliveryHeld` fence) and `remember()` (so `reply_to` resolves), and a drain rechecks `deliverable()` after condensing because such a session can take the peer meanwhile. A pull is journaled `completed` in the same transaction as the bus snapshot, never `accepted`; a failed one puts back only that peer's queue and preface and never reloads the snapshot.
18
19
  - Record exact task-offer reads only through `DeliveryJournal.completeDelivered` on an active-fenced adapter completion, correlated reply or actual pull, atomically with completion. Capture original offers before terminal pruning and record their ids, never condensed ids; accepted receipts, generic transitions, operator/hub dispositions and restored completed rows are not read evidence.
19
20
  - The `onImportantQueued` nudge for a pull-only peer (#398) is advisory: it never dequeues, steers, journals or settles, and a throw from it never stops an enqueue.
20
21
  - Merge recall and restored prefaces through `mergePrefaces`/`uniqueMemoryLines`; preserve distinct context and new notes, deduplicate memory identities, and keep colliding short prefixes distinct. Use `recallDisplayText`/`recallMemoryLines` before display caps: suppress only matching automatic approval outcomes, retain nonempty rejection details, preserve id headers and complete-word excerpts, and keep token trimming at line boundaries. Keep these changes display-only.
@@ -5,7 +5,7 @@
5
5
 
6
6
  - Console attention navigation selects a delivery only by an exact visible row id. A missing id or row prints read-only guidance; never select by peer or fall back to the first Queue row.
7
7
 
8
- Scope: the operator console, control WS and its protocol, state files, the dashboard listener and settings, the Claude channel's reconnect, `ahub setup`, git snapshots, upgrade and recovery. Read before editing `src/cli/console.ts`, `src/cli/console-state.ts`, `src/cli/attention.ts`, `src/ui/needs.ts`, `src/hub/daemon.ts`, `src/hub/ui.ts`, `src/hub/settings.ts`, `src/hub/control-client.ts`, `src/adapters/claude-channel.ts`, `src/hub/restart.ts`, `src/hub/recovery-store.ts`, `src/hub/snapshots.ts`, `src/hub/manager.ts`, `src/hub/lifecycle.ts`, `src/hub/crash.ts`, `src/hub/start-mode.ts`, `src/hub/terminal-open.ts`, `src/hub/recovery-error.ts`, `src/hub/db-timeout.ts`, `src/hub/config-reload.ts`, `src/hub/config-apply.ts`, `src/hub/recovery-continuity.ts`, `src/cli/recovery-presentation-options.ts`, `src/cli/setup.ts`, `src/cli/upgrade*.ts`, `src/cli/terminal-recovery.ts`, `src/cli/recovery-package.ts` or `src/cli/facts-hook.ts`, or before adding a native peer.
8
+ Scope: the operator console, control WS and its protocol, state files, the dashboard listener and settings, the Claude channel's reconnect, `ahub setup`, git snapshots, upgrade and recovery. Read before editing `src/adapters/definitions/native-recovery*.ts`, `src/adapters/definitions/native-policy-aux.ts`, `src/adapters/definitions/native-config-*.ts`, `src/adapters/definitions/native-cli-*.ts`, `src/cli/console.ts`, `src/cli/console-state.ts`, `src/cli/attention.ts`, `src/ui/needs.ts`, `src/hub/daemon.ts`, `src/hub/ui.ts`, `src/hub/settings.ts`, `src/hub/control-client.ts`, `src/adapters/claude-channel.ts`, `src/hub/restart.ts`, `src/hub/recovery-store.ts`, `src/hub/snapshots.ts`, `src/hub/manager.ts`, `src/hub/lifecycle.ts`, `src/hub/crash.ts`, `src/hub/start-mode.ts`, `src/hub/terminal-open.ts`, `src/hub/recovery-error.ts`, `src/hub/db-timeout.ts`, `src/hub/config-reload.ts`, `src/hub/config-apply.ts`, `src/hub/recovery-continuity.ts`, `src/cli/recovery-presentation-options.ts`, `src/cli/setup.ts`, `src/cli/upgrade*.ts`, `src/cli/terminal-recovery.ts`, `src/cli/recovery-package.ts`, `src/cli/facts-hook.ts` or the instance modules (`src/hub/instances.ts`, `src/hub/instance-config.ts`, `src/hub/instance-execution.ts`), or before adding a native peer.
9
9
 
10
10
  - The plugin bundle is installed apart from the daemon. Breaking a control WS message contract bumps `PROTOCOL` in `control-client.ts`; a new request answered as unknown or an optional field preserves it, as does additive optional public task metadata in `ui_snapshot` that older clients ignore (such as `deps` in #246).
11
11
  - `peer_stop` is console-only and stops only a hub-owned headless ACP, Pi or local runtime through its adapter. Refuse native-terminal peers and recovery-held mutations; serialize with starts and permission changes, and refuse a recovery `prepare` while a stop runs. An admitted stop holds delivery through `BusOptions.deliveryHeld` before any teardown await (never a console pause that resume can undo, or a global recovery hold that blocks unrelated peers) and withdraws the peer's pending approvals before the adapter stops. A requested stop holds an in-flight delivery as `needs_review` with an ids-only stop reason, suppresses restart/escalation, and never resolves that delivery automatically.
@@ -31,7 +31,7 @@ Scope: the operator console, control WS and its protocol, state files, the dashb
31
31
  - A target that stopped after it started is restarted only through `recordRestart` (`src/cli/upgrade.ts`), recorded before the new one starts and at most once per project per resume: each done or waiting `restored:<peer>` binding becomes `retired:<peer>`, whose terminal is closed (`closedRetired:<peer>`) or found absent before that peer is relaunched, and is never revalidated against the new instance. Keep repaired-session provenance across every target restart, disabling automatic fresh/zero-turn replacement. A person may explicitly accept loss of a repaired Codex/Claude conversation only on typed positive missing transcript/rollout evidence and a disposal-time recheck; refuse live-test repaired overrides after read-only receipt classification and before runner/lock or coordinator mutation, fail closed on unclassifiable scope, and repeat in core before lock and after under-claim read, preserving ordinary #408 delegation; record the one-use grant and freshOverride evidence, and consume the grant on exact attachment. Pi stays excluded: positively missing accepted Pi files get stop-and-archive plus the normal project-scoped ahub pi command; bookmark retirement never falls back fresh in the same start. Clear the repair fence only for a done or waiting binding retired from that target; an uncertain creation without a durable binding keeps its fence. Launcher evidence is read for every instance in `restarts`, and a session attached without an id is unknown, never gone (#225).
32
32
  - After a managed terminal is created, persist its binding before waiting up to `RECONNECT_WAIT_MS` for authenticated attachment to the expected target instance, operation and conversation (#414). Resume waits on the saved binding without creating another terminal. Count only attached, non-offline session ids; when the chosen terminal has no current launch record, a confirmed-ended row for another handle or an id-less attachment retains the bounded wait after older-launcher checks. A late current record must match the saved terminal; unknown or live replacement evidence still blocks. Timeout keeps the receipt blocked and records only terminal/hub metadata; a last native connect attempt that was never recorded is unknown, never inferred from terminal readiness or offline state. Final identity and preservation verification stays strict.
33
33
  - An operation receipt stays `schema: 1`. Only `abandoned()` writes 2, on an operation with a recorded `disposition` whose coordinator predates #215: in the same write as the disposition, or, for a disposition 0.12.20 recorded at 1, by the next command that holds the runner claim. `validateReceipt` accepts 2 only with a disposition (#227). Never write 2 on an operation that can still be resumed.
34
- - Four checks read another release's source text for an exact line: `coordinatorCurrent` (`export async function disposeRecovery` in `src/cli/upgrade.ts`), `targetReadsWaivers` (`export function readRecoveryWaivers` in `src/hub/restart.ts`), `coordinatorRestarts` (`export function recordRestart` in `src/cli/upgrade.ts`) and `supportsAction` (`export async function reattachRecovery` in `src/cli/upgrade.ts`). Renaming, moving or un-exporting one makes this release read as an older one: keep those lines until a capability list replaces the checks. The reattach CLI dispatch must use the same `supportsAction` check as its printed command; never redirect a fallback command back to a coordinator without that action.
34
+ - Four checks read another release's source text for an exact line: `coordinatorCurrent` (`export async function disposeRecovery` in `src/cli/upgrade.ts`), `targetReadsWaivers` (`export function readRecoveryWaivers` in `src/hub/restart.ts`), `coordinatorRestarts` (`export function recordRestart` in `src/cli/upgrade.ts`) and `supportsAction` (`export async function reattachRecovery` in `src/cli/upgrade.ts`). Renaming, moving or un-exporting one makes this release read as an older one: keep those lines until a capability list replaces the checks. The reattach CLI dispatch must use the same `supportsAction` check as its printed command; never redirect a fallback command back to a coordinator without that action. Recovery also reads a target's literal `OPTIONAL_PEERS` (`src/hub/peer-catalog.ts`, through `legacyTargetSupport`) and `START_MODE_PEERS` (`src/hub/start-mode.ts`, through `legacyHeadlessArgv`), declared in `src/adapters/definitions/native-recovery.ts`; keep both literal declarations until a published capability replaces them.
35
35
  - `reattachRecovery` (`src/cli/upgrade.ts`, #463) relaunches only the peer a blocked receipt's `blockedReattachment` names, once per its `reattachments` marker, after the lock, the runner claim and a re-read, and only against the same held target instance and version. The terminal proof is `inspectSavedTerminal` (`src/cli/terminal-recovery.ts`): only `orphaned` or `missing` (a typed stale handle and a complete, untruncated inventory) counts as gone; `unknown`, `changed` or a disconnected-only reading never authorizes a terminal. It counts as reattached only on a `done` restore receipt with the exact recorded session id, session file and project root, then leaves the operation blocked for `recovery resume`. It is person-only: `classifyPeerCommand` and `liveTestRefusal` refuse it from an agent shell.
36
36
  - The interactive screens (`src/cli/upgrade-interactive.ts`, #272) change an operation only by running an existing command (`recovery resume|abort|dispose|reattach`, `reset`) through `io.run`, and build their menu from `nextChoices`, whose text is `nextActions`: never call `abortRecovery`, `disposeRecovery` or a driver from a screen, and never offer a choice `next` does not have. The one addition is `cancellableWait` (a runner still at `prepare` with no effect): read the receipt again at the key press, stop only a runner `signedRunner` names (a claim without a matching process signature is never signalled), then run `recovery abort`, which decides again. Agents are ended (`endPlannedPeer`) only before an operation holds the lock, and only after the lock, the hub instance, the attached session and the terminal's handle and incarnation were read again: never on the plan as it was drawn. Ctrl+C leaves the screens and the terminal is not read after it; a prompt answers an interrupt with null, never text. What a hub says it waits for (`recovery.waiting`, the daemon's `blockers`) is shown, never decided on.
37
37
  - Recovery presentation (#466, `src/cli/upgrade-presentation*.ts`) only renders, and only when `humanRecovery` holds (a TTY and no `--json`); `--json` stays the raw document. Recognise `--json`, `--verbose` and `--color=` only through `recoveryPresentationFlag` (`src/cli/recovery-presentation-options.ts`), which `liveTestRefusal` and `classifyPeerCommand` share, so a presentation flag cannot hide reattach or a repaired dispose from authorization. A screen's Enter default (`preferred`) is read-only (refresh, follow the runner, read the receipt); apply, resume, reattach and destructive choices need their own key, and an item with a `disabledReason` never runs. The receipt's `waits` and `presentationCause` describe the current attempt only: written best-effort (a failed timing save warns, never fails the operation or replaces the wait's own error) and cleared at each resume. Without them show `timing unknown` or an unknown cause; never substitute `updatedAt` or guess a cause from error text. With no typed cause, `renderBlocked` may show only the first recorded error line through `terminalText`, controls stripped, capped at 512 code points and labelled `typed cause unknown`, with the uniform action; never classify it or choose an option from its text (#478). `endInspection` (the completion and stop-and-archive summary) only inspects, and a failed reading is `runtime unknown`.
@@ -46,7 +46,7 @@ Scope: the operator console, control WS and its protocol, state files, the dashb
46
46
  - Every `new Database(` under `src/` is followed at once by its own `PRAGMA busy_timeout` from a named `*_BUSY_TIMEOUT_MS` constant (`HUB_DB_BUSY_TIMEOUT_MS` in `src/hub/db-timeout.ts` for `hub.db`), and nothing sets `busy_timeout` again later: `test/db-busy-timeout.test.ts` reads the source and fails on any other opening idiom, so a new one extends that guard first (#303).
47
47
  - The manager forwards a dashboard `start_peer` with the terminal provider cap (`MAX_COMMAND_MS`) plus 5 s and reports a missed deadline as `unconfirmed`, never as a failure; it recognises the deadline by the exact text `ControlClient.request` resolves with, so change the two together. The hub's terminal reopen guard and the dashboard's retry hold share `PEER_START_RETRY_MS` (`src/hub/start-mode.ts`): never give either its own number (#298).
48
48
 
49
- - Use `KNOWN_PEERS` for the native catalog and `rosterAllows` for participation. Change live membership only at the authorized roster settings write or undo, applying exactly that key. Keep external ids and projects without roster settings compatible with the existing admission and lazy status rules. Fence starts and attachment before detaching or stopping a removed runtime; retain its queue, uncertain deliveries and independent pauses. Refuse removal of the conductor holder and require explicit confirmation and `--now` for an active turn.
49
+ - Use the registry's `KNOWN_PEERS` (`src/adapters/registry.ts`) for the native catalog (the `src/hub/peer-catalog.ts` literal is a cross-release probe only) and `rosterAllows` for participation. Change live membership only at the authorized roster settings write or undo, applying exactly that key. Keep external ids and projects without roster settings compatible with the existing admission and lazy status rules. Fence starts and attachment before detaching or stopping a removed runtime; retain its queue, uncertain deliveries and independent pauses. Refuse removal of the conductor holder and require explicit confirmation and `--now` for an active turn.
50
50
 
51
51
  - A Kimi one-launch TUI admission ends on its owning detach, roster removal, matched launcher cleanup or the unclaimed 30 s deadline. An old socket or launcher never clears a newer admission. Pin --mode tui in the hub-opened terminal argv.
52
52
 
@@ -57,3 +57,7 @@ Scope: the operator console, control WS and its protocol, state files, the dashb
57
57
  - Observe memory counters only through the machine-local candidate sidecar. Bind delivered counters to a positive journal acknowledgement and the original render context; skip private envelopes and current PII tasks/turns. Leave missing telemetry unknown, and never pass candidates or counters to evidence consumers. Require memory_correct for correction proposals, refuse PII turns, and keep project/identity authority server-side.
58
58
 
59
59
  - Keep full memory home, generation ID, selector revision and malformed-row totals in authenticated person status/CLI receipts, never disk status.json or agent CLI output; low-privilege receipts use a generation prefix and omit home, selector revision and malformed-row totals. Emit typed unbound disabled/unavailable receipts with no profile when no memory handle opened; profile-check may treat an authenticated same-home generation-capable unbound daemon as memory-quiesced, never a stale bound handle. Scope profile-check readiness to its named registry inventory; old, unknown or unreadable receipts refuse readiness, and other custom registries need separate checks.
60
+
61
+ - Bind instance declarations once at startup through the daemon-local `definitionOf` closure and the `extraIds` catalogs; never publish them to the global registry or take them from a request. `instances` is restart-class, and its settings rows come only from `projectSettings(declarations)`.
62
+ - Every start resolves `resolveInstanceExecutionCwd` and passes that `executionCwd` as adapter cwd, sandbox, grant and Pi session root; set `executionOwners` only for the adapter actually started. Extra-instance native state is per instance, while its MCP binding keeps the canonical hub state dir.
63
+ - Recovery: record an extra Codex launch row only after the start reply supplies `executionCwd`; keep `projectRoot`, `executionCwd` and `terminalRoot` separate and never bind an extra by kind metadata. Abort and archive validate receipts without current declarations; restore, fresh and reattach require them; refuse before effects when `peer_instances` is missing. Extra-instance turns write `turn_workspaces` in the `turns` transaction and leave the legacy snapshot fields unknown; undo requires the same trusted declaration and validated root.
@@ -1,6 +1,6 @@
1
1
  # Local worker and sandbox
2
2
 
3
- Scope: the hub-native local worker, its tools, path guard, denylist and seatbelt sandbox. Read before editing `src/adapters/local-worker.ts`, `src/local/`, `src/memory/capture.ts` or `src/hub/facts.ts` (both read the denylist), `src/hub/git.ts`, or the grant filter in `src/hub/permission-mode.ts`.
3
+ Scope: the hub-native local worker, its tools, path guard, denylist and seatbelt sandbox. Read before editing `src/adapters/local-worker.ts`, `src/local/`, `src/memory/capture.ts` or `src/hub/facts.ts` (both read the denylist), `src/hub/git.ts`, the grant filter in `src/hub/permission-mode.ts`, or the sandbox root in `src/hub/instance-execution.ts`.
4
4
 
5
5
  - Nothing the local worker executes may run outside `sandboxedExec`; a new tool that spawns a process goes through it, and a tool that touches a path goes through `guardPath`.
6
6
  - The sandbox denies home reads by default (toolchain dirs, the project and its real git dir excepted) and all network, loopback included: claude-mem and the Codex app-server listen on loopback without auth. Tests that bind a local port therefore fail when the worker runs them, also with `local.bash_network: true` (the egress proxy keeps loopback closed, and the proxy variables send a test's plain-HTTP `fetch` to the proxy, which refuses it); that is the intended trade-off, and `"direct"` is the switch until 0.13.0 removes it.
@@ -15,13 +15,14 @@ Scope: the task flow, assignment, the state machine and the hub's MCP tools. Rea
15
15
  - `.agenthub/routing.local.toml` (#269) may hold only `stay_switch` and per-class `peers`, `escalate_to`, `route` and `pi_backend` (`checkOverlay` in `src/hub/routing.ts`): never widen it to targets, routes, signals or constraints, which stay in the hand-written `routing.toml`. `parseRouting` is the one merge, and `assign()` reads `routing.sources` only for its trace line.
16
16
  - Conductor authority requires the explicit unique role before capability checks. The only exceptions run before that check in `Conductor.execute` and stay task-scoped: a task's current owner and reviewer read it with `hub_task_show` (#208), and its proposer redirects it with `hub_task_assign` while nobody ever accepted it and no person moved it (#207). Removing them strands reviewers that missed a review request. Assignments preserve the acting peer. A peer releases only its own conductor hold; operator release and manual, quota and recovery holds remain separate paths.
17
17
  - The PII screen (#198) runs in `propose` before anything else reads the task (no board row, envelope, triage or brief before its verdict), and `unknown` is PII. A call that screens free text (`done`, `review`, `remember`) screens only after its own checks pass, and `done` and `review` run them again after the awaited screen, because the task can change meanwhile. A history entry marked `withheld` stays withheld in public views, completed notices and review requests, whatever `nameable()` says; `ahub ask` shows it only on campus, like a note that matches a PII pattern.
18
- - `taskOp` (`src/hub/daemon.ts`) lists full PII rows only when `onPrem` holds: the in-process local worker inside a PII turn (#230). Every other caller, the local worker's ordinary turns included, gets stubs.
18
+ - `taskOp` (`src/hub/daemon.ts`) lists full PII rows only when `onPrem` holds: an in-process peer whose definition sets `trust.onPrem` (only the local worker) inside a PII turn (#230). Every other caller, the local worker's ordinary turns included, gets stubs. `assign()` (`src/hub/routing.ts`) is the PII gate: it admits PII work only for `trust.onPrem`, and an unknown id is never on-prem. `relayCandidates` only orders the preferred handoff peers by the same field; never treat its order as the gate.
19
+ - `review()` reads `escalateAfter()` (`tasks.escalate_after`, 1-10, default `ESCALATE_AFTER` = 3) at each `changes_requested` verdict, so a reload applies from the next verdict; never capture it at construction or compare against a literal (#488).
19
20
  - A guard in `tasks.ts` or `conductor.ts` passes `preEffect` (throws `PreEffectToolRefusal`, `src/hub/tool-refusal.ts`) only before the operation's first board or hold write; a throw after a write, a readback failure or a wrapped error stays a plain `Error` (#254). The Pi side of the rule is in `adapters.md`.
20
21
  - Supervision reasons use structured enums, never history notes, check output or approval titles. Round signatures survive restart; later summaries count only newly joined tasks.
21
22
 
22
23
  - Pass off-roster ids as data into `assign()` so its trace and every owner/reviewer choice share the membership filter. Refuse a manual off-roster assignment before a board write, including a reservation. Use `reassignForRoster` through the existing assignment delivery path on removal, including in-review ownership while keeping its review state; keep the old owner or reviewer when no eligible replacement exists and emit the guarded public-title notice. Preserve budget-relay behavior when sharing that implementation.
23
24
 
24
- - Phase checklists are written only through Tasks.recordLifecycle by that task's reviewer/person. Validate exact current Tasks.after edges and privacy before saving; never change dependencies, accepted owners or reservations from an import. Keep verified checklist claims separate from externally read CI, and bind them to the current reviewed head. Export only derived sanitized board/config projections.
25
+ - Criteria checklists (`criteria`, retired alias `phase`) are written only through Tasks.recordLifecycle by that task's reviewer/person, after the PII screen. Refuse a checklist task list before any write; never change dependencies, accepted owners or reservations from a checklist. Keep verified checklist claims separate from externally read CI, and bind them to the current reviewed head. Screen `checklistOf(next)`, never `next.criteria` alone: off stores a phase write under `phase`. Plain off (no retired `workflow`) keeps the #257 receipt bytes, the schema-1 export and the `phase` tool schema; advertise `criteria` only when daemon status carries `lifecycleCriteria`. Export only derived sanitized board/config projections.
25
26
 
26
27
  - A planner patch that names `reserved` at all, the same value included, is refused while `hasPersonReservation` holds; planner pull and grooming both ask it (#378: a same-value write laundered a person's reservation).
27
28
  - `done()` gates the SDLC transition once: pass `gated` to `complete()` when nothing was awaited since, and after any awaited gate readback refuse if another done already moved the task, in every mode (#382: advise recorded overlapping dones).
@@ -34,3 +35,6 @@ Scope: the task flow, assignment, the state machine and the hub's MCP tools. Rea
34
35
  - Keep candidate production in Tasks: screen review/correction payloads and current task/turn privacy before CandidateStore writes; recheck after asynchronous screening. Never make candidate failures roll back a saved review, or let a peer accept a candidate. Keep candidate tool arguments/results out of capture.
35
36
 
36
37
  - Feed the same current spec/test capability map into every `assign()` path, explicit nomination and reservation. Apply these capability refusals only to non-PII owner admission, preserving reviewer and on-prem PII routing exemptions; refuse a missing required capability before a manual owner assignment write. Bind spec/check authority and result delivery to the current task, owner, peer generation and privacy policy.
38
+
39
+ - Resolve any id that may be an extra instance with `definitionOf`/`kindOf`/`familyOf(id, this.d.instances?.())`; a bare `definitionOf(id)` answers builtins only. Only a kind's routing preference expands to its instances; manual ids, reservations and nominations stay exact. Reviewer independence compares families: take the own family only when no other is eligible, record it through `assignmentNote` (`sameFamilyFallback=true`), explicit nominations included; persist the `instance`/`family` refusal codes as `policy`.
40
+ - Admit a declared extra as owner of `implement`, `bulk_edit` or `test` work only with the current implementer role and `ownerIsolation[id] === true`, the daemon's proof that the live adapter started in `executionRoots[id]` outside the canonical cwd. Never derive it from the declaration's `worktree`, its roles or a reload.
@@ -7,7 +7,7 @@ Scope: tests, fakes and the test gate. Read before adding or changing a test or
7
7
  - The Switchyard sidecar, sandboxed commands and the MLX server get a fixed environment (`PATH`, `HOME` and their own keys), so test knobs for their fakes travel in a wrapper script or argv, not in `process.env`; native peer children inherit the hub's environment minus the agent markers (`peerChildEnv`).
8
8
  - Bind agent identity markers explicitly in native/CLI fixture children. The full gate starts the test runner without inherited agent markers (including `AGENTHUB_DELIVERY_ONLY`) and without an `ahub claude` session's hub variables (`AGENTHUB_PROJECT_DIR`, `AGENTHUB_STATE_DIR`, `AGENTHUB_INSTANCE_ID`, `AGENTHUB_LAUNCH_ID`); a new variable a native session exports and a fixture child reads joins that `env -u` list in `scripts/check.sh`. Never weaken production marker detection to make them pass.
9
9
  - `bunfig.toml` preloads `test/hub-home.ts`, which first clears inherited `AMEM_HOME` and gives every test run (the gate and `bun test <file>` alike) a temp `AGENTHUB_HOME` unless one is set; scoped tests may set their own `AMEM_HOME` after preload. Hub memory (#258) is on with any project config, and no test may write the person's `~/.agenthub`. Do not remove it; a test that needs its own home sets and restores the variable.
10
- - `test/registry-guard.test.ts` statically scans `src/` against the committed `test/registry-guard.baseline.json` (#451); a test never writes, regenerates or repairs that baseline, and a failing guard is fixed in the source or by the reviewed shrink the adapters note (`docs/agent-notes/adapters.md`) describes. Scanner behavior is pinned on inline sources in `test/registry-guard-fixtures.test.ts`, never on edits to real files.
10
+ - `test/registry-guard.test.ts` statically scans `src/` against the committed `test/registry-guard.baseline.json` (#451); a test never writes, regenerates or repairs that baseline, and a failing guard is fixed in the source by moving the policy into a definition (rule: `docs/agent-notes/adapters.md`); the baseline stays `[]`. Scanner behavior is pinned on inline sources in `test/registry-guard-fixtures.test.ts`, never on edits to real files.
11
11
  - A free-port probe binds `hostname: "127.0.0.1"`, matching the loopback server that takes the port (#232).
12
12
  - `ahub upgrade` without `--to` asks the registry (`npm view`) and hands a target newer than the CLI to `bun x` (#272): a test that reaches either puts a fake `npm` first on `PATH` and a recording stand-in for `process.execPath`, as `test/upgrade-cli.test.ts` does. The interactive screens are tested through a scripted `ScreenIO`; a CLI test that needs them marks `process.stdin` and `process.stdout` as terminals in a wrapper script.
13
13
  - Cross-process fixtures wait for the consuming client to process its handshake (the channel connected diagnostic), not merely the daemon's attached/idle state. Deadline-routing fixtures hold and release responses at the fallback decision; do not place a fixed sleep between deadline fractions and assume scheduler headroom (#287). Receipt-order fixtures use the normal watchdog for ordinary I/O, trigger expiry after observed native acceptance, and await a consumed transport barrier before asserting on late results.
@@ -21,3 +21,4 @@ Scope: tests, fakes and the test gate. Read before adding or changing a test or
21
21
 
22
22
  - Keep CI's Bun version sourced from `.bun-version` through the pinned setup-bun action; update the version file rather than adding per-job version literals.
23
23
  - Release verification requires both OS checks and the aggregate `seeded guards` success for an identical tree, a tag matching `package.json` on `origin/main`, and fresh ancestor-bound documentation stamps. Preserve the separate npm OIDC publish job and GitHub Release write job; neither job gets the other's credential authority.
24
+ - `test/conformance/` runs real adapters against fake transports. Record a case that does not apply as an explicit unsupported outcome, never synthetic success, and never change native production behavior to satisfy a generic case.
@@ -1,4 +1,8 @@
1
- # GSD-shaped collaboration in agent-hub (#252)
1
+ # Retired: GSD-shaped collaboration in agent-hub (#252)
2
+
3
+ Retired on 2026-10-18 by #481. The AI-Native SDLC Playbook is the lifecycle standard. Only the head-bound acceptance-criteria checklist remains, under the Compliance pass; phase task import is removed. The text below preserves the historical analysis and unrun measurement plan, not current configuration instructions. Current behavior and the migration window are in [SDLC](sdlc.md#acceptance-criteria-checklist).
4
+
5
+ ## Historical analysis
2
6
 
3
7
  Source review: 2026-10-11, agent-hub 5647038. Recommendation: preserve the default flow; offer a bounded gsd-lite checklist/dependency presentation through #257's opt-in lifecycle. Defer a bidirectional GSD artifact bridge until it has a separately reviewed import contract and measured value.
4
8
 
@@ -1,6 +1,6 @@
1
1
  # Operations guide
2
2
 
3
- This guide describes ahub 0.12.33 and control protocol 16. Live verification
3
+ This guide describes ahub 0.12.35 and control protocol 16. Live verification
4
4
  results and remaining prerequisites are recorded separately in [the smoke ledger](smoke.md).
5
5
 
6
6
  ## Agent-operated live verification
@@ -792,8 +792,9 @@ the task in progress (`check interrupted`); the owner marks it done again.
792
792
 
793
793
  A task reaching `approved` is a board transition. It is not proof that an
794
794
  external side effect happened unless the task's refs and a live readback show
795
- that effect. The hub can reassign after repeated review changes according to
796
- the routing configuration.
795
+ that effect. After `tasks.escalate_after` consecutive `changes_requested` verdicts
796
+ (default 3, `.agenthub/config.json`), the hub moves the task to the next attached
797
+ peer in the class's `escalate_to`.
797
798
 
798
799
  A review request carries a checklist: map the changed signatures and call sites
799
800
  to the task's plan (without one, to its detail, which the request then
@@ -1280,7 +1281,7 @@ denylist (Pi and local), or native vendor boundaries (Claude and Codex).
1280
1281
  | --- | --- | --- | --- |
1281
1282
  | Kimi Code CLI | ACP yolo | ACP auto | ACP default |
1282
1283
  | Codex | on-request for every outgoing turn/start | never for every outgoing turn/start | Restore the known native policy once, then stop overriding |
1283
- | Claude | Allow resolving project file tools outside .agenthub/.git/.claude/.codex/.qwen/.kimi/.kimi-code/.pi/.mcp.json | Hook allow for every tool | No hook decision |
1284
+ | Claude | Allow resolving project file tools outside .agenthub/.git/.claude/.codex/.qwen/.kimi/.kimi-code/.jcode/.pi/.mcp.json/.agents/.agent/_agents/_agent/.gemini/.grok/.claude.json | Hook allow for every tool | No hook decision |
1284
1285
  | Pi and local | Grant write/edit outside native agent configuration paths; read never asks; shell/git writes ask | Grant each tool once within the existing sandbox/guard | Normal approval handling |
1285
1286
 
1286
1287
  Kimi's map applies only to the actual Kimi Code CLI identity, not another ACP
@@ -1473,6 +1474,7 @@ ahub ui --settings # dashboard with a 15-minute settings session
1473
1474
  | Switches | `coordination` | `config.local.json` | newly created tasks only |
1474
1475
  | Switches | `task_sweep.enabled`, `.auto_reassign`, `.interval_s`, `.unaccepted_min`, `.idle_min`, `.review_min`, `.ready_min`, `.ladder_min` | `config.local.json` | live; reschedules the sweep timer |
1475
1476
  | Routing | `tasks.wip_limits.<peer>` | `config.local.json` | live |
1477
+ | Routing | `tasks.escalate_after` (1-10, default 3: consecutive `changes_requested` before a task moves to the next `escalate_to` peer) | `config.local.json` | live; the next verdict uses it |
1476
1478
  | Roles | `planner` (the one peer that plans) | `config.local.json` | at once |
1477
1479
 
1478
1480
  - Writes go only to `.agenthub/config.local.json` and `.agenthub/routing.local.toml`,
@@ -1743,6 +1745,14 @@ the system marks it `needs_review`. Establish evidence before choosing
1743
1745
  disconnects, cancellation, and partial effects are uncertain; do not replay
1744
1746
  them automatically.
1745
1747
 
1748
+ After the inactivity watchdog cancels a Codex turn, a late acceptance, answer or
1749
+ completion from that turn is dropped and never reaches a later audience; its
1750
+ delivery is held as `needs_review`. When the cancelled turn has no verified
1751
+ native id, Codex goes offline and the hub logs `Codex cancelled turn has no
1752
+ verified native id; reattach the native session before further hub delivery`:
1753
+ end its TUI and relaunch it (`ahub codex resume <thread-id>`) before resolving
1754
+ the held delivery.
1755
+
1746
1756
  ### Claude sessions without channel pushes
1747
1757
 
1748
1758
  Only `ahub claude` starts Claude Code with the development-channel flag. A
@@ -1767,6 +1777,15 @@ but cannot show pushes, so it attaches tools-only (protocol 16, issue #205):
1767
1777
  started while an `ahub claude` session holds the peer stands by and attaches
1768
1778
  only after that session leaves.
1769
1779
 
1780
+ ### Claude channel sessions while busy (#493)
1781
+
1782
+ A Claude session started with `ahub claude` receives pushes when it is idle. While it is busy:
1783
+
1784
+ - Every hub tool result carries fresh counts, `(N queued hub message(s), M important hub message(s) waiting: call hub_inbox before starting long local work)`, when important work waits. A zero count clears an earlier hint.
1785
+ - `hub_inbox` reads the daemon queue (and any local failed-push buffer) in a channel session too. A read is one `completed` delivery, like a tools-only read; holds refuse it the same way.
1786
+ - An important message is pushed into the running turn (steered) instead of waiting for the turn to end. Mid-turn reading was verified on Claude Code 2.1.302; an older client that drops it leaves the delivery `accepted` until a person resolves it. Ordinary messages still wait for the idle push or `hub_inbox`.
1787
+ - Do not use `[FYI]` for PR ready, done, submitted, ruling-needed, blocker or changed-contract messages: they need action. `hub_task_done` and `hub_review` already notify their recipients.
1788
+
1770
1789
  ## Graceful shutdown
1771
1790
 
1772
1791
  Before stopping, let active turns and approvals settle when possible:
@@ -1952,21 +1971,21 @@ section is what they run.
1952
1971
 
1953
1972
  Upgrade running projects with the target release's own coordinator. It accepts
1954
1973
  a running source on control protocol 9 (0.6.x), 10 (0.7.0 through 0.12.0),
1955
- 11 (0.12.1 and 0.12.2), 12 (0.12.3), 13 (0.12.4 through 0.12.15), 14 (0.12.16), 15 (0.12.17 through 0.12.19) or 16 (0.12.20 through 0.12.33), and only
1974
+ 11 (0.12.1 and 0.12.2), 12 (0.12.3), 13 (0.12.4 through 0.12.15), 14 (0.12.16), 15 (0.12.17 through 0.12.19) or 16 (0.12.20 through 0.12.35), and only
1956
1975
  a target on its own protocol, so the target's coordinator fits every supported
1957
1976
  source and carries every recovery fix released up to it. Protocol 8 and older
1958
1977
  (0.5.x and earlier) are refused as `manual-bootstrap-required`. Run from the
1959
1978
  project directory, without replacing the global CLI first:
1960
1979
 
1961
1980
  ```bash
1962
- bunx --package @staix/agent-hub@0.12.33 ahub upgrade --to 0.12.33 --dry-run
1963
- bunx --package @staix/agent-hub@0.12.33 ahub upgrade --to 0.12.33 --yes
1981
+ bunx --package @staix/agent-hub@0.12.35 ahub upgrade --to 0.12.35 --dry-run
1982
+ bunx --package @staix/agent-hub@0.12.35 ahub upgrade --to 0.12.35 --yes
1964
1983
  ```
1965
1984
 
1966
1985
  | Running now | Coordinator to use |
1967
1986
  | --- | --- |
1968
1987
  | 0.6.x (protocol 9) | the target's, through `bunx` as above |
1969
- | 0.7.0 through 0.12.0 (protocol 10), 0.12.1 and 0.12.2 (protocol 11), 0.12.3 (protocol 12), 0.12.4 through 0.12.15 (protocol 13), 0.12.16 (protocol 14), 0.12.17 through 0.12.19 (protocol 15), 0.12.20 through 0.12.33 (protocol 16) | the target's, through `bunx` as above |
1988
+ | 0.7.0 through 0.12.0 (protocol 10), 0.12.1 and 0.12.2 (protocol 11), 0.12.3 (protocol 12), 0.12.4 through 0.12.15 (protocol 13), 0.12.16 (protocol 14), 0.12.17 through 0.12.19 (protocol 15), 0.12.20 through 0.12.35 (protocol 16) | the target's, through `bunx` as above |
1970
1989
  | any supported source, with the installed CLI already at the target | `ahub upgrade` below, which is the same coordinator |
1971
1990
  | 0.5.x or earlier (protocol 8 and older) | not supported: bootstrap by hand with the matching CLI |
1972
1991
 
@@ -1998,14 +2017,14 @@ projects first:
1998
2017
 
1999
2018
  ```bash
2000
2019
  ahub restart --dry-run
2001
- ahub upgrade --to 0.12.33 --dry-run
2020
+ ahub upgrade --to 0.12.35 --dry-run
2002
2021
  ```
2003
2022
 
2004
2023
  Apply only after reviewing the plan:
2005
2024
 
2006
2025
  ```bash
2007
2026
  ahub restart --yes
2008
- ahub upgrade --to 0.12.33 --yes
2027
+ ahub upgrade --to 0.12.35 --yes
2009
2028
  ahub recovery status [<operation-id>]
2010
2029
  ahub recovery resume [<operation-id>]
2011
2030
  ahub recovery abort [<operation-id>]
@@ -3037,7 +3056,7 @@ that the file is untracked; a dropped value is reported by the settings result.
3037
3056
 
3038
3057
  ## SDLC artifact receipts and gates
3039
3058
 
3040
- The initial #257 lifecycle is off without .agenthub/sdlc.toml. Advise shows blockers; enforce checks head-bound review and approval transitions. See [the SDLC contract](sdlc.md) for the fixed stage order, required check names, claimed local receipts, console-only readbacks and pending custom/release/live parts. Owners/reviewers record bounded identities through hub_sdlc_record; these grant no additional authority.
3059
+ The playbook lifecycle (#257, #481) is off without `.agenthub/sdlc.toml`. When enabled its stages are intent, spec, plan, build, test, review, merge, release and maintain, grouped as Plan, Design, Build, Test, Deploy and Maintain. Advise shows blockers; enforce checks implemented head-bound review and approval gates. Stored implement/verify receipts read as build/test; new enabled receipts use canonical ids. See [the SDLC contract](sdlc.md) for gates, required check names, local receipts, console-only readbacks and the S0-S8 delivery table. S1-S7 and a full live advise/release cycle remain pending. Owners/reviewers record bounded identities through `hub_sdlc_record`; these grant no additional authority. Off output, receipt and schema shapes stay unchanged.
3041
3060
  ## Optional Qwen Code peer
3042
3061
 
3043
3062
  Select Qwen per project with `ahub roster add qwen`; it defaults off and is absent
@@ -3065,7 +3084,7 @@ Qwen controlled recovery and automatic crash resume load the recorded conversati
3065
3084
 
3066
3085
  ## Optional Jcode peer
3067
3086
 
3068
- Use ahub roster add jcode, then ahub jcode --mode headless [--model <alias>] [--provider-profile <name>]. The person launcher configures only agent-hub-jcode in .jcode/mcp.json; run ahub init afterwards to ignore the untracked machine-path file. Conductor/dashboard starts read that entry without editing it. Preview starts nothing, and ahub stop jcode stops only the owned private daemon/ACP groups. jcode_cmd, jcode_home and jcode_provider_profile are machine-local and never editable from settings.
3087
+ Use ahub roster add jcode, then ahub jcode --mode headless [--model <alias>] [--provider-profile <name>]. The launcher reads roster membership from the hub's status reply; a hub older than 0.12.35 does not send it, so the launcher stops with an upgrade-or-restart remedy and starts nothing (#484). The person launcher configures only agent-hub-jcode in .jcode/mcp.json; run ahub init afterwards to ignore the untracked machine-path file. Conductor/dashboard starts read that entry without editing it. Preview starts nothing, and ahub stop jcode stops only the owned private daemon/ACP groups. jcode_cmd, jcode_home and jcode_provider_profile are machine-local and never editable from settings.
3069
3088
 
3070
3089
  After upgrading to the recipient-retry release, a Jcode project MCP entry written before `AGENTHUB_DELIVERY_ONLY` was added is intentionally stale. Once per project, the person runs `ahub jcode --mode headless` before using conductor/dashboard starts. The launcher rewrites only the owned entry with the delivery-only marker, preserving foreign entries and refusing collisions; recovery relaunches already use this CLI path. Readiness checks never rewrite the file, and an explicit false marker or any other drift remains refused. Until that rewrite, daemon starts keep their actionable stale-entry refusal rather than advertising a retry tool Jcode cannot use.
3071
3090
 
@@ -3080,11 +3099,12 @@ Native policy is unmanaged and has NO hub sandbox; no permission_modes.jcode or
3080
3099
  - Protocol stays 16; a same-protocol older daemon answers the new request as unknown, which the CLI reports. No automatic pane opening is included in this slice.
3081
3100
  - Native T0 limits: [ACP watch verification](verification/2026-10-11-acp-watch-t0.md).
3082
3101
 
3083
- # gsd-lite lifecycle guidance (#257)
3102
+ # Acceptance-criteria checklist (#481)
3084
3103
 
3085
- - Set `workflow = "gsd-lite"` in `.agenthub/sdlc.toml` for the next daemon start; default none preserves the existing flow. Mode advise displays checklist blockers without blocking, while enforce checks them at approval.
3086
- - The task reviewer/person records phase criteria via `ahub sdlc record <id> '<receipt JSON>'`. Existing tasks and predecessor ids must match the board; import does not move owners or edit dependencies. Use the existing task propose `--after` command to create edges.
3087
- - `ahub sdlc status <id>`, `explain <id>` and `export <id>` read the same current board/config projection. Export hashes are projection/config hashes, with no external text, extra authority or verification claim. Schema and example: [SDLC](sdlc.md#gsd-lite-checklist-guidance).
3104
+ - The task reviewer/person records a head-bound `criteria` receipt with criterion ids matching the issue's AC ids, required/result fields and hashed evidence refs. This checklist belongs to the Compliance review pass. Use `ahub sdlc record <id> '<receipt JSON>'`; [schema and example](sdlc.md#acceptance-criteria-checklist).
3105
+ - Advise shows incomplete/stale checklist blockers; enforce checks them at approval. The `criteria` gate does not need a workflow selector. Dependencies stay with `ahub task propose --after`; checklist task import is removed.
3106
+ - `status <id>`, `explain <id>` and `export <id>` use the same current board/config projection. Export keeps sanitized state and source hashes, with no external text, extra authority or verification claim.
3107
+ - Migration from the retired workflow and its compatibility window (until hub 0.13.0) are described in [Retired workflow compatibility](sdlc.md#retired-workflow-compatibility-252-481-s8). Stored checklist task lists are ignored with a visible note; they never change board dependencies.
3088
3108
 
3089
3109
  # Catalog session guidance (#295)
3090
3110
 
@@ -3481,3 +3501,47 @@ Native transparency/floating and the bridge were verified in the macOS spike.
3481
3501
  Real keystrokes, dragging and persistence, Spaces, message delivery and body
3482
3502
  click need the manual PR checklist before merge; automated fixtures do not
3483
3503
  establish those native results.
3504
+
3505
+ ## Additional headless peer instances
3506
+
3507
+ Declare extra instances only in an untracked `.agenthub/config.local.json`, then
3508
+ restart the hub. For example, a reviewer can use:
3509
+
3510
+ ```json
3511
+ {
3512
+ "instances": {
3513
+ "codex-review": { "kind": "codex", "roles": ["reviewer"] },
3514
+ "kimi-review": { "kind": "kimi", "roles": ["reviewer"] }
3515
+ }
3516
+ }
3517
+ ```
3518
+
3519
+ Start them with `ahub codex --as codex-review` and
3520
+ `ahub kimi --as kimi-review`. Codex keeps its native TUI and its own hub-owned
3521
+ app-server proxy; extra Kimi, Qwen, Pi and local instances use headless starts.
3522
+ Extra Claude/channel and Jcode instances are unavailable in I2. A missing
3523
+ `peer_instances` feature on an old daemon or upgrade target requires a supporting
3524
+ version before launching or restoring extras.
3525
+
3526
+ An additional implementer must have an existing registered Git worktree and
3527
+ attached branch at `.worktrees/<instance-id>`, named explicitly by `worktree` in
3528
+ its declaration. The hub validates it and does not create it in this phase.
3529
+ Reviewer instances may share the checkout, or name their own validated worktree.
3530
+ The execution directory is separate from the canonical hub project. An Orca
3531
+ terminal may belong to either that canonical project or the instance's execution
3532
+ worktree; recovery preserves its exact recorded grouping and live launcher.
3533
+ Changing or removing the declaration invalidates its saved extra-instance
3534
+ binding. Do not infer ownership from a native-kind label alone.
3535
+
3536
+ A routing preference for a kind chooses among its instances using current load
3537
+ and WIP. A manual instance id remains exact. Reviewers from the same kind share
3538
+ a family; the board records a same-family fallback when no other eligible family
3539
+ exists. Native context is per conversation, while quota windows and quota pauses
3540
+ are shared by kind/account. A quota reset does not lift a manual pause.
3541
+
3542
+ Facts and turn snapshots follow the instance's execution checkout. Use a CLI
3543
+ that supports instances to undo those turns: older readers see their snapshot
3544
+ fields as unknown and cannot restore them into the main checkout. The supporting
3545
+ CLI checks the saved workspace against the current declaration again before any
3546
+ context change or file restoration. Removing the declaration intentionally
3547
+ blocks that undo until the identity and workspace can be reviewed.
@@ -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.33
20
+ bun add -g github:STAIxBWLB/agent-hub#v0.12.35
21
21
  ahub setup
22
22
  ```
23
23
 
@@ -30,10 +30,18 @@ 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.33
33
+ ## Upgrading to 0.12.35
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).
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).
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).
34
40
 
35
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.
36
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).
44
+
37
45
  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:
38
46
 
39
47
  ```bash