@staix/agent-hub 0.20.0 → 0.22.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (183) hide show
  1. package/CHANGELOG.md +66 -3
  2. package/README.md +2 -2
  3. package/docs/agent-notes/README.md +11 -0
  4. package/docs/agent-notes/adapters.md +7 -3
  5. package/docs/agent-notes/benchmarks.md +27 -0
  6. package/docs/agent-notes/budget.md +2 -1
  7. package/docs/agent-notes/bus.md +3 -3
  8. package/docs/agent-notes/daemon.md +17 -8
  9. package/docs/agent-notes/local-worker.md +4 -0
  10. package/docs/agent-notes/models.md +2 -0
  11. package/docs/agent-notes/tasks.md +17 -9
  12. package/docs/agent-notes/tests.md +9 -2
  13. package/docs/bench.md +1 -1
  14. package/docs/data-policy.md +2 -0
  15. package/docs/dogfood.md +39 -4
  16. package/docs/events.md +2 -1
  17. package/docs/memory.md +9 -3
  18. package/docs/operations.md +610 -62
  19. package/docs/profiles.md +13 -1
  20. package/docs/quickstart.md +18 -9
  21. package/docs/research.md +7 -4
  22. package/docs/sdlc.md +54 -10
  23. package/docs/security.md +396 -34
  24. package/docs/smoke.md +133 -0
  25. package/docs/specs/2026-09-19-agent-hub-design.md +1101 -22
  26. package/docs/verification/2026-10-27-0.19.0.md +5 -5
  27. package/docs/verification/2026-10-28-claude-2.1.311-2.1.312-bash-permission.json +635 -0
  28. package/docs/verification/2026-10-28-claude-2.1.311-2.1.312-permission-transport.json +403 -0
  29. package/docs/verification/2026-10-28-claude-implementer-permission-triage.md +123 -0
  30. package/docs/verification/2026-10-28-jcode-0.99.1-usage.md +41 -0
  31. package/docs/verification/2026-10-28-realpath-name-audit.md +222 -0
  32. package/docs/verification/2026-10-28-routing-f0bb8ad0.md +1000 -0
  33. package/docs/verification/2026-10-29-0.21.0.md +61 -0
  34. package/docs/verification/2026-10-29-model-effort-capabilities.md +123 -0
  35. package/docs/verification/2026-10-29-modelrouting-b152ddb9.md +953 -0
  36. package/docs/verification/2026-10-30-0.22.0.md +49 -0
  37. package/docs/verification/2026-10-30-445-capability-probes.md +172 -0
  38. package/docs/verification/2026-10-30-bus-delivery-operational-settings.md +77 -0
  39. package/docs/verification/2026-10-30-jcode-ac4.md +24 -0
  40. package/docs/verification/2026-10-30-jcode-ac6.md +52 -0
  41. package/docs/verification/2026-10-30-recovery-wait-settings.md +141 -0
  42. package/docs/verification/claude-implementer-permission-triage.ts +160 -0
  43. package/docs/verification/claude-permission-transport-native.py +71 -14
  44. package/docs/verification/profiles/YYYY-MM-DD-research.md +1 -1
  45. package/docs/verified.json +15 -15
  46. package/package.json +2 -2
  47. package/plugins/agent-hub/.claude-plugin/plugin.json +1 -1
  48. package/plugins/agent-hub/server.js +1016 -280
  49. package/src/adapters/acp.ts +79 -3
  50. package/src/adapters/channel-peer.ts +4 -0
  51. package/src/adapters/claude-channel.ts +13 -6
  52. package/src/adapters/claude-implementer.ts +60 -7
  53. package/src/adapters/codex-appserver.ts +77 -4
  54. package/src/adapters/conformance/fakes/jcode-agent.ts +6 -2
  55. package/src/adapters/conformance/kit.ts +3 -9
  56. package/src/adapters/definitions/builtin-metadata.ts +3 -114
  57. package/src/adapters/definitions/native-config-defaults.ts +1 -1
  58. package/src/adapters/definitions/native-config-policy.ts +3 -2
  59. package/src/adapters/definitions/native-context.ts +12 -0
  60. package/src/adapters/external-metadata.ts +48 -0
  61. package/src/adapters/external-modules.ts +63 -3
  62. package/src/adapters/jcode.ts +6 -2
  63. package/src/adapters/local-worker.ts +40 -25
  64. package/src/adapters/native-proof-ports.ts +10 -0
  65. package/src/adapters/registry-types.ts +4 -0
  66. package/src/broker/protocol.ts +203 -0
  67. package/src/broker/server.ts +314 -0
  68. package/src/broker/store.ts +548 -0
  69. package/src/cli/bench.ts +3 -2
  70. package/src/cli/broker.ts +58 -0
  71. package/src/cli/conductor-recovery.ts +142 -0
  72. package/src/cli/configured-acp-entry.ts +26 -6
  73. package/src/cli/console-state.ts +59 -17
  74. package/src/cli/console.ts +17 -2
  75. package/src/cli/facts-hook.ts +3 -1
  76. package/src/cli/help.ts +29 -16
  77. package/src/cli/identity.ts +25 -10
  78. package/src/cli/init.ts +34 -7
  79. package/src/cli/intake-output.ts +33 -0
  80. package/src/cli/learn-output.ts +38 -0
  81. package/src/cli/main.ts +334 -73
  82. package/src/cli/output.ts +41 -1
  83. package/src/cli/recovery-package.ts +18 -1
  84. package/src/cli/recovery-waits.ts +26 -0
  85. package/src/cli/role-holders.ts +35 -0
  86. package/src/cli/room.ts +304 -0
  87. package/src/cli/say-input.ts +16 -0
  88. package/src/cli/schedule-output.ts +49 -0
  89. package/src/cli/sdlc-output.ts +44 -6
  90. package/src/cli/smoke-pty-bridge.ts +82 -0
  91. package/src/cli/smoke-pty.ts +153 -0
  92. package/src/cli/smoke-run.ts +139 -0
  93. package/src/cli/smoke-target.ts +130 -0
  94. package/src/cli/source-output.ts +29 -0
  95. package/src/cli/task-output.ts +3 -2
  96. package/src/cli/upgrade-runtime.ts +112 -28
  97. package/src/cli/upgrade.ts +68 -8
  98. package/src/hub/ask.ts +76 -12
  99. package/src/hub/board.ts +132 -22
  100. package/src/hub/bus.ts +416 -65
  101. package/src/hub/child-process.ts +2 -0
  102. package/src/hub/ci-watch.ts +42 -20
  103. package/src/hub/claude-failure-stage.ts +85 -0
  104. package/src/hub/claude-shell-wrapper.ts +11 -5
  105. package/src/hub/conductor.ts +17 -4
  106. package/src/hub/config-apply.ts +34 -6
  107. package/src/hub/config-reload.ts +7 -5
  108. package/src/hub/config-trust.ts +7 -1
  109. package/src/hub/context-window.ts +33 -3
  110. package/src/hub/control-client.ts +14 -2
  111. package/src/hub/corpus-config.ts +34 -0
  112. package/src/hub/corpus-evidence.ts +184 -0
  113. package/src/hub/corpus.ts +373 -0
  114. package/src/hub/cost-governance.ts +26 -0
  115. package/src/hub/daemon.ts +1209 -183
  116. package/src/hub/deliverable-files.ts +8 -2
  117. package/src/hub/delivery-journal.ts +200 -73
  118. package/src/hub/dogfood-profile.ts +32 -5
  119. package/src/hub/dogfood-read.ts +6 -2
  120. package/src/hub/envelope.ts +11 -0
  121. package/src/hub/eval-gate.ts +131 -0
  122. package/src/hub/events.ts +1 -1
  123. package/src/hub/external-instance.ts +69 -0
  124. package/src/hub/facts.ts +15 -5
  125. package/src/hub/folder-trigger.ts +223 -0
  126. package/src/hub/hub-tools.ts +2 -1
  127. package/src/hub/inference.ts +20 -5
  128. package/src/hub/instance-config.ts +69 -3
  129. package/src/hub/instance-execution.ts +6 -2
  130. package/src/hub/instances.ts +32 -4
  131. package/src/hub/intake.ts +125 -0
  132. package/src/hub/learning.ts +117 -0
  133. package/src/hub/lifecycle.ts +7 -3
  134. package/src/hub/limits.ts +12 -5
  135. package/src/hub/native-permission-request.ts +77 -0
  136. package/src/hub/operational-authority.ts +63 -5
  137. package/src/hub/operational-settings.ts +154 -0
  138. package/src/hub/operational-view.ts +1 -1
  139. package/src/hub/packs.ts +132 -0
  140. package/src/hub/path-policy.ts +7 -0
  141. package/src/hub/peer-doing.ts +130 -0
  142. package/src/hub/peers.ts +12 -0
  143. package/src/hub/queue-backpressure.ts +58 -0
  144. package/src/hub/read-tools.ts +2 -0
  145. package/src/hub/recovery-authority.ts +151 -0
  146. package/src/hub/recovery-policy.ts +220 -0
  147. package/src/hub/research.ts +24 -14
  148. package/src/hub/restart.ts +7 -0
  149. package/src/hub/routing.ts +54 -11
  150. package/src/hub/schedules.ts +266 -0
  151. package/src/hub/sdlc-merge.ts +38 -14
  152. package/src/hub/sdlc-metrics.ts +32 -10
  153. package/src/hub/sdlc-queue.ts +7 -1
  154. package/src/hub/sdlc-release.ts +1 -1
  155. package/src/hub/sdlc.ts +37 -7
  156. package/src/hub/security-baseline.ts +38 -0
  157. package/src/hub/settings.ts +27 -2
  158. package/src/hub/shipped-packs.ts +8 -0
  159. package/src/hub/snapshots.ts +72 -6
  160. package/src/hub/source-read.ts +160 -0
  161. package/src/hub/sources.ts +311 -0
  162. package/src/hub/tasks.ts +987 -110
  163. package/src/hub/workflow.ts +1 -1
  164. package/src/local/doc-read.ts +69 -0
  165. package/src/local/proxy.ts +4 -13
  166. package/src/local/tools.ts +37 -3
  167. package/src/local/web-fetch-config.ts +43 -0
  168. package/src/local/web-fetch.ts +183 -0
  169. package/src/memory/brief.ts +85 -13
  170. package/src/memory/capture.ts +1 -1
  171. package/src/rooms/application.ts +114 -0
  172. package/src/rooms/boundary.ts +103 -0
  173. package/src/rooms/commands.ts +294 -0
  174. package/src/rooms/crypto.ts +371 -0
  175. package/src/rooms/link.ts +584 -0
  176. package/src/rooms/policy.ts +82 -0
  177. package/src/rooms/remote-peer.ts +80 -0
  178. package/src/rooms/service.ts +426 -0
  179. package/src/rooms/status.ts +146 -0
  180. package/src/rooms/store.ts +1210 -0
  181. package/src/rooms/wire.ts +465 -0
  182. package/src/ui/index.html +91 -4
  183. package/templates/packs/example.pack.json +13 -0
package/docs/profiles.md CHANGED
@@ -46,10 +46,12 @@
46
46
  - `.agenthub/ignore` contains owner-managed patterns for large/raw data (`data/raw/` initially). Hub snapshots and facts honor it, while runtime/local config and shadow metadata remain excluded. Models gain no permission to write hub metadata.
47
47
  - Research/writing/general projects without Git or shadow run the hub with facts/snapshots/undo off and one notice. Legacy dev named-file facts remain supported; Git directory facts, snapshots and undo require a repository.
48
48
  - Shadow HEAD/index are not changed by hub snapshots or raw undo. Later-edit and concurrent/unknown-turn guards still apply. Linked/shared/malformed shadow metadata is refused. Each shadow Git call revalidates descendants without following links (at most 20000 entries and 32 levels), including cached repository handles. Native model/account research runs remain unverified.
49
+ - Git submodules (#798): a submodule's own edits never move the superproject's gitlink, so snapshots, turn facts, overlap and undo cannot observe inside one (observing contents stays out of scope). The hub says so instead of reporting nothing. A project without `.gitmodules` pays no extra git call; with one, a turn end spends one bounded `git status` probe inside the existing snapshot budget. (1) A turn that leaves a submodule dirty (modified, untracked or new commits) lists its path as `changed (contents unobserved)` in `ahub turns` (and `unobserved` in `--json`), apart from the changed files. A turn whose probe failed, or an older turn, has no list at all: unknown, never none. (2) A task that declares a path inside a submodule (refs, plan paths, the file part of a section anchor) gets `inside submodule <path>: facts, overlap and undo do not observe it` at `hub_task_propose` and `hub_task_accept`. It is a notice, never a refusal; a PII task gets none, and a task that names another repository is not judged against this project's `.gitmodules`. (3) `ahub undo` for such a turn says its contents cannot be restored and names them; it restores the observed files as before and never reports only "changed no files" or "already undone".
49
50
 
50
51
  # Project profile overrides
51
52
 
52
53
  - Acceptance criteria are profile-independent (#507b): a task's recorded criteria checklist (docs/sdlc.md, Acceptance-criteria checklist) is listed in every profile's review request, ahead of the profile's `review_checklist` lines, and an unmet item that starts with a criterion id names it; a profile only changes the wording of its checklist, never the criteria or their ids.
54
+ - Recurring unmet criteria become a candidate, never a change (#514a): when reviews name the same criterion id unmet on at least 3 tasks of one profile and class within 30 days, and those tasks make up at least 2 separate pieces of work (tasks sharing a PR or a branch count once), the hub files one backlog card with the `profile_overrides.review_checklist` line to consider. The line is text, and nothing applies it on its own. By hand: `profile_overrides` is not machine-local, so you choose the shared `.agenthub/config.json` (every checkout) or `config.local.json` (this machine), and add the line to your current checklist because `review_checklist` replaces as a unit. With the command (#514b): `ahub learn list` shows the open cards, `ahub learn accept <id>` adds that one line to your current checklist in `.agenthub/config.local.json` (this machine only, never the shared or any tracked file) through the settings writer, closes the card, and says it applies after a restart; `ahub settings undo` puts the previous file back; `ahub learn reject <id>` only closes the card. The line is rebuilt from the key line of the card and the board's own evidence, never parsed from the card text. Person only: an agent shell and the configured conductor are refused. PII tasks and evidence-only verdicts never count.
53
55
  - `profile_overrides` in shared/local config selects project data over the chosen shipped profile. `classes`, `role_text` and `catalog_roles` maps merge by key across layers; `plan_keys`, `agents`, `example_checks` and `review_checklist` replace as units. A local `profile` selects the active base for both init and native contracts.
54
56
  - Classes may add or remap profile aliases to existing base classes. Existing base names keep their meanings; class maps are bounded to 32 override entries. Task storage and route explanations preserve the alias, including a dev custom alias, while routing/budgets/checks use its base. Other tasks under custom dev data retain the selected profile semantics too, so document sections do not become whole-path overlaps. Unmodified dev rows retain their old shape.
55
57
  - `plan_keys` selects distinct existing plan fields, not new board fields. Task proposals and their schemas use the selection. Accept schemas retain legacy fields for already existing tasks/profile changes.
@@ -57,6 +59,16 @@
57
59
  - `agents` replaces only the managed AGENTS block (16000 characters maximum); surrounding instructions remain. Init previews contain paths/actions only. `example_checks` is at most 16 display strings of 2000 characters, shown as advisory examples by init and never selected as executable checks. `review_checklist` is at most 16 non-empty strings of 500 characters, customizing the checklist items in review requests with `{target}` and `{check}` interpolations. Existing `checks` trust remains authoritative.
58
60
  - No override mutates shipped profile data or another project. Unknown fields, base remapping and invalid/oversized data are refused. Headers are quoted. Daemon/MCP restart is required for config changes; no native model consumption or research measurement is claimed.
59
61
 
62
+ # Domain packs (#513a)
63
+
64
+ A pack bundles a profile with the roles it expects, a review rubric and example checks in one file, so a project can adopt a way of working with one command. The format and the loader are here; the shipped writing, analysis and ops packs are a later slice (#513b).
65
+
66
+ - A pack comes from two layers: the shipped one (`templates/packs/`, bundled; only a format example, `example`, until the writing, analysis and ops packs of #513b), and the project one, `.agenthub/roles/<name>.pack.json`, which replaces a shipped pack of the same name. The project layer is in the directory the role catalog already reads (the catalog takes only `.md`, so the two never collide). At most 16 packs of 8192 bytes each, plain regular files that are not links, in a directory that is not a link. A pack that is oversize, malformed or names a role that is not in the catalog (shipped or project) is refused with the reason, never loaded half usable.
67
+ - The file holds exactly `name` (equal to the file name, and never a profile's name), `profile` (one of dev, research, writing, general), `summary` (160 characters), `roles` (at most 8 distinct role ids), `rubric` (1 to 12 criteria, each `id`, `text` of at most 300 characters and `required`) and `checks` (at most 8, each `id` and a `note` of at most 500 characters). Any other field is refused. Text is plain ASCII punctuation with no control characters.
68
+ - Rubric ids are #507's criterion ids (a letter, then letters, digits or `-`, at most 48 characters), the same shape a task's recorded criteria use, so a reviewer names an unmet item with the id at the start of the line. The rubric only changes the wording of the review checklist; it never adds or removes a task's recorded criteria.
69
+ - A pack is data, never authority. A check is display text exactly like `example_checks`: nothing selects, runs or trusts a command from a pack, and there is no field that could. Roles are named, not created: the pack asks for catalog roles by id and the project assigns them with `catalog_roles` as before.
70
+ - `ahub init --profile <profile|pack>` takes a shipped profile or a project pack. `--dry-run` (add `--json` for rows) previews the config row with the pack's id, base profile, rubric criterion ids, check ids and expected role ids, and never their text. The apply writes the pack's rubric as `profile_overrides.review_checklist` (each line `<id>: <text>`, optional criteria marked, then the `{check}` line) and its checks as `profile_overrides.example_checks`, into a new `.agenthub/config.json` or the existing one, keeping every other key. Applying the same pack again changes nothing. A project whose `profile` differs from the pack's, or whose `profile_overrides` already hold different data (compared with sorted keys, so key order never matters), is refused with the reason rather than overwritten. Init does not copy role files and a check is a note, not a command: the spec amendment "domain packs (#513a)" records why.
71
+
60
72
  # Profile scorecard (#799)
61
73
 
62
74
  The existing ahub report --dogfood command adds a profile section when the selected profile is non-dev, or bound non-dev tasks have evidence in the requested window. Dev-only JSON and readable output retain their prior bytes. Existing flow, quality, cost and other fields keep their meanings; the profile section does not require merged PRs.
@@ -79,7 +91,7 @@ A missing, incomplete or unsupported source produces JSON null, rendered as unkn
79
91
 
80
92
  Research rows also expose bounded current-plan source and claim counts per task. Claim slots are anonymous ordinal numbers; declaration text and identifiers are never exported. In a stored board plan a key the plan omits counts as zero declarations, as an explicit empty list does; a cut or malformed plan leaves that one task's declarations unknown without affecting the rest of the board. These are current-plan counts, not a reconstruction of past plans.
81
93
 
82
- Each research class alias retains its own recorded task-check pass/fail counts under `observedCheckOutcomes`. Those history events do not record which check key ran. Consequently `factCheck` pass/fail, check selection and missing-key counts per run remain unknown until that provenance is recorded. No new telemetry is collected. Model labels, person verdicts, claims reaching a person verdict and per-label agreement also remain unknown because no claim-level verdict source exists. Task approval never supplies a claim verdict. Research uses the same recorded section-overlap rows as writing.
94
+ Each research class alias retains its own recorded task-check pass/fail counts under `observedCheckOutcomes`. Since #901 the hub also stamps `checkKey` (the `checks` entry whose command ran: the profile class alias, else the class) on its own `check passed` and `check failed` entries, taken from the same lookup that chose the command and never from output or an owner's words, and `missingKeys` when the check's last line is exactly the example check's `citations: N declared keys, M missing`. `factCheck` selection, pass/fail and `missingKeysPerRun` are values only when the group has at least one `fact_check` check entry and every check entry of the group carries a key (an older entry has none, which makes them unknown, not a quiet subset); `missingKeysPerRun` is a list only when every such run printed a count. Verdicts are the person's, entered with `ahub task claim <id> <slot> <supported|unsupported|unclear> [--model <label>] [--model-said <supported|unsupported|unclear>]` (console only; the claim is named by its 1-based slot in the current plan and its text is never typed, stored or printed). Each verdict is a `claim verdict` history entry holding the slot, the verdict, an optional bounded model label and the verdict the model gave; a later verdict for the slot replaces the earlier one. The hub binds each verdict to the claim list it evaluated, as an opaque 16-hex id of the plan's ordered claims (never their text): after the owner replaces or reorders the claims the earlier verdict names no claim of the new list and the slot reads unknown again (restoring the exact list brings it back), and a verdict recorded before binding existed counts for nothing. Recording a verdict is quiet task history, so it never turns a running completion check into `check finished late`. `reachedPersonVerdict`, and a slot's `modelLabel` and `personVerdict` come from those entries; a slot's `agreement` is true or false only when the model's own verdict was entered and is null otherwise, and `factCheck.agreement` counts per label only the slots where the model's verdict was entered and equals the person's (a slot without the model's verdict counts for nothing, a disagreement counts for no label, and with no comparable slot every label is null). A PII task counts its verdicts but names no model label. With no recorded verdict or no keyed check every row stays null, never zero, and no new telemetry is collected. Task approval never supplies a claim verdict. Research uses the same recorded section-overlap rows as writing.
83
95
 
84
96
  The weekly writing, general and research receipt templates are in the verification/profiles directory. Copy only the emitted profile subsection, preserve null values, and include no project/person names or paths. A template is not a completed measurement or a live acceptance claim.
85
97
 
@@ -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.20.0
20
+ bun add -g github:STAIxBWLB/agent-hub#v0.22.0
21
21
  ahub setup
22
22
  ```
23
23
 
@@ -30,13 +30,21 @@ 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.22.0
34
+
35
+ Nothing changes until you set something. Ten operational numbers became settings: five bus and delivery limits (`bus.*`, #975) and five upgrade-recovery waits (`recovery.*`, #979). Unset, each keeps the value it had. `ahub learn list|accept|reject` (#514b, person only) is your verdict on the recurring-unmet-criterion cards. The macOS PTY bridge no longer hangs under load or outlives its caller (#560). The `@staix/agent-mem` pin stays 0.13.0. Upgrade running hubs with `ahub upgrade` and restart Claude sessions so they load the 0.22.0 plugin.
36
+
37
+ ## Upgrading to 0.21.0
38
+
39
+ The configured conductor may now run more operational commands from its own shell (#933): `board import`, `intake approve`, `schedule run|pause|resume`, `research backfill`, `bench run`, `source add`, `goal link`, `task unassign`, starting Jcode, and `settings set tasks.escalate_after`; the hub rechecks the role and records each run. Trust, money, attribution and interactive terminals stay the person's: `permission`, `agents trust`, `role assign`, budget changes, `--as-user`, `pet --window`, `watch`, `recovery reattach`, memory changes and every goal change except `link`. Any agent may `task cancel`, `task label` and `task role` a task it owns and list `ahub deliverables` for the tasks it can see. A conductor's `restart --yes`, `upgrade --yes --to <exact-version>` and `recovery resume|abort` need a source-issued continuation bound to that project, and a handoff to a newer release's coordinator stays the person's (#900; see [conductor operations](operations.md#conductor-operations-812-t2-933-900)). `ahub sdlc merge` and the conductor's `ahub doctor --orphans --kill` now print readable output: a script that parses them passes `--json`, which prints what they printed before (#899). `ahub down` stops the hub like `ahub kill`. New and opt-in: `ahub source add|list` (#508), machine-local `schedules` and `folder_triggers` with `ahub schedule` (#512), `ahub intake` draft backlog cards (#511), domain packs with `ahub init --profile <pack>` (#513a), `local.web_fetch` and `local.doc_read` for the on-prem worker (#515), queue backpressure and context headroom admission for new work (#800, #802), and person-only rooms between machines through `ahub broker` and `ahub room` (#446; see [outbound room membership](operations.md#outbound-room-membership-446-x3)). `ahub turns` and `ahub undo` name a dirty git submodule instead of hiding it (#798). A `hub_send` whose `to` is one peer id given as a string now reaches only that peer instead of every peer, and any other malformed `to` is refused before anything is sent (#963). The hub now exact-pins `@staix/agent-mem` 0.13.0, with no storage change for the hub. Upgrade running hubs with `ahub upgrade` and restart Claude sessions so they load the 0.21.0 plugin. Coming from 0.19.0 or older, the notes below apply too.
40
+
33
41
  ## Upgrading to 0.20.0
34
42
 
35
43
  The hub now exact-pins `@staix/agent-mem` 0.11.0. Every `amem` that shares a memory home must run 0.11.0 or later to keep consolidation review receipts, so upgrade a global `amem` CLI too. When `hub_task_done` leaves a review whose exact-head CI is not yet green, the hub watches that PR head once a minute for up to 45 minutes and tells the owner and the reviewer once with a `ci_completed:` task notice; no peer turn waits for CI (#785). The new `tasks.auto_pull` setting is opt-in and off by default, and a committed config cannot turn it on: set it in `.agenthub/config.local.json` or a settings session, and when a peer's planner slot frees the hub pulls the top backlog card explicitly reserved for that peer (#785; see [the planner](operations.md#the-planner-455)). The local worker and Pi sandbox now lets a command add only new loose objects to the shared git object store (#864): `git gc`, `repack`, `fetch` and `maintenance` no longer run inside it, so run them yourself in a terminal. A task's host-observed branch binding no longer turns a running check result into "check finished late" (#880). Upgrade running hubs with `ahub upgrade` and restart Claude sessions so they load the 0.20.0 plugin. Coming from 0.18.0 or older, the notes below apply too.
36
44
 
37
45
  ## Upgrading to 0.19.0
38
46
 
39
- Pi is now a gated CLI (#843): `ahub agents` lists it, 1.4.0 is the verified version, and any other version is refused at every launch path with the exact `ahub agents trust pi <version>` command. If your Pi is not 1.4.0, trust that version (person only) or install 1.4.0 before Pi work resumes. At hub start and once a day the hub reads each gated CLI's installed version without network, tells the planner once per id and version that is not verified, and `ahub status` shows an `agent CLI drift` line until it is (#843). Newly verified: agy 1.4.7, Jcode 0.99.1, headless Qwen 0.26.1, the Kimi fresh reviewer 2.3.2 and the headless Claude implementer 2.1.310. In `ask-when-needed`, a headless Claude implementer now runs proven read-only shell commands and a closed set of task commands (`scripts/check.sh --changed`, `bun run build`, `bun test <test files>`, `git add|commit`, `git push` and `gh pr view|checks|create` on the task's bound branch) through a host-owned wrapper with single-use tickets; check, test and build run in the local worker's sandbox, any other command and any call in an enabled always-ask class still ask, and an older plugin keeps asking (#845; see [additional headless peer instances](operations.md#additional-headless-peer-instances)). It may also name its model with `"model"` on its declaration in untracked `config.local.json`, applied at the next hub daemon restart (#844). The fresh Codex and Kimi reviewers now declare review scope `code`: they review code only, a `changes_requested` with no open structured finding is recorded as evidence-only and does not count toward escalation, `ahub status` shows `review scope` on each reviewer row, and `"review_scope"` on the instance in `config.local.json` overrides it (#852). Planners get one `Planner work ready:` notice when a write frees WIP capacity or makes backlog work ready; nothing is pulled for them (#785). The remote check runner installs dependencies once per lockfile key into a read-only volume and refuses a commit whose `package.json` declares an install lifecycle script (#793). A conductor operation whose hub connection or authorization handshake is unavailable now says so instead of reporting a role refusal (#812). The hub sandbox also protects a linked worktree's submodule git dirs and `config.worktree`, and refuses writes under a modules root outside a discovered git dir (#518, #865). A Qwen TUI background-task notice no longer ends a hub turn as uncertain (#517). `@staix/agent-mem` is pinned at 0.10.0. Upgrade running hubs with `ahub upgrade` and restart Claude sessions so they load the 0.19.0 plugin. Coming from 0.17.0 or older, the notes below apply too.
47
+ Pi is now a gated CLI (#843): `ahub agents` lists it, 1.4.0 is the verified version, and any other version is refused at every launch path with the exact `ahub agents trust pi <version>` command. If your Pi is not 1.4.0, trust that version (person only) or install 1.4.0 before Pi work resumes. At hub start and once a day the hub reads each gated CLI's installed version without network, tells the planner once per id and version that is not verified, and `ahub status` shows an `agent CLI drift` line until it is (#843). Newly verified: agy 1.4.7, Jcode 0.99.1, headless Qwen 0.26.1, the Kimi fresh reviewer 2.3.2 and the headless Claude implementer 2.1.310. In `ask-when-needed`, a headless Claude implementer now runs proven read-only shell commands and a closed set of task commands (`scripts/check.sh --changed`, `bun run build`, `bun test <test files>`, `git add|commit`, `git push` and `gh pr view|checks|create` on the task's bound branch) through a host-owned wrapper with single-use tickets; check, test and build run in the local worker's sandbox, any other command and any call in an enabled always-ask class still ask, and an older plugin keeps asking (#845; see [additional headless peer instances](operations.md#additional-headless-peer-instances)). It may also name its model with `"model"` on its declaration in untracked `config.local.json`, applied at the next hub daemon restart (#844). The fresh Codex and Kimi reviewers now declare review scope `code`: they review code only, a `changes_requested` with no open structured finding is recorded as evidence-only and does not count toward escalation, `ahub status` shows `review scope` on each reviewer row, and `"review_scope"` on the instance in `config.local.json` overrides it (#852). Planners get one `Planner work ready:` notice when a write frees WIP capacity or makes backlog work ready; nothing is pulled for them (#785). The remote check runner installs dependencies once per lockfile key into a read-only volume and refuses a commit whose `package.json` declares an install lifecycle script (#793). A conductor operation whose hub connection or authorization handshake is unavailable now says so instead of reporting a role refusal (#812). The hub sandbox also protects a linked worktree's submodule git dirs and `config.worktree`, and refuses writes under a modules root outside a discovered git dir (task 518, #865). A Qwen TUI background-task notice no longer ends a hub turn as uncertain (task 517). `@staix/agent-mem` is pinned at 0.10.0. Upgrade running hubs with `ahub upgrade` and restart Claude sessions so they load the 0.19.0 plugin. Coming from 0.17.0 or older, the notes below apply too.
40
48
 
41
49
  ## Upgrading to 0.18.0
42
50
 
@@ -46,7 +54,7 @@ Agents can now run the read-only `ahub` forms themselves (`doctor`, `projects`,
46
54
 
47
55
  ## Upgrading to 0.17.0
48
56
 
49
- `ahub qwen --mode tui` runs Qwen Code's own TUI in your terminal, driven by the hub through its dual-output files (#286). It is opt-in: the hub never opens it by itself, `roster.qwen` stays off by default, and headless Qwen works as before. It needs Qwen Code 0.26.1 (a new `qwen-dual` compat entry; the headless entry is not widened), refuses an extra instance (`--as`) and refuses while a headless Qwen runs (`ahub stop qwen` first). It has two limits: a hub deny ends the turn without an answer (the delivery completes, so later messages still reach Qwen), and a stalled turn is never cancelled; the hub reports it and you press Esc in the Qwen terminal. `ahub stop qwen` does not end the TUI, and `ahub upgrade` and recovery do not restore it: end it in its terminal before you upgrade, then run `ahub qwen --mode tui` again, which resumes its session. The `codex-review` reviewer now approves exactly `hub_review` and `hub_task_show`, per tool: on Codex 0.164.0 `exec` refused every MCP tool call, so its reviews ended without a verdict (#783). A reviewer turn that still ends without a verdict leaves one screened `hub.log` line (exit code and the hub tools it called) and is offered on, or to you when you named that reviewer (#453). A stale review request that the hub refuses at admission no longer marks the reviewer unavailable: a request whose binding changed is skipped, and one whose policy changed is rerouted as no verdict (#781). An unavailable reviewer slot no longer stops the agy TUI from reading its own task (#794). An `ahub recovery dispose --stop-and-archive` audit entry now keeps the error that blocked the upgrade as `cause` (#776). A task counts toward a recurring finding only where that finding was recorded open or accepted, so a fix-only record no longer files a "Review policy amendment" card (#328), and a missing generation sync remote no longer logs a failure every sync interval (#401). Upgrade running hubs with `ahub upgrade` and restart Claude sessions so they load the 0.17.0 plugin. Coming from 0.15.0 or older, the notes below apply too.
57
+ `ahub qwen --mode tui` runs Qwen Code's own TUI in your terminal, driven by the hub through its dual-output files (#286). It is opt-in: the hub never opens it by itself, `roster.qwen` stays off by default, and headless Qwen works as before. It needs Qwen Code 0.26.1 (a new `qwen-dual` compat entry; the headless entry is not widened), refuses an extra instance (`--as`) and refuses while a headless Qwen runs (`ahub stop qwen` first). It has two limits: a hub deny ends the turn without an answer (the delivery completes, so later messages still reach Qwen), and a stalled turn is never cancelled; the hub reports it and you press Esc in the Qwen terminal. `ahub stop qwen` does not end the TUI, and `ahub upgrade` and recovery do not restore it: end it in its terminal before you upgrade, then run `ahub qwen --mode tui` again, which resumes its session. The `codex-review` reviewer now approves exactly `hub_review` and `hub_task_show`, per tool: on Codex 0.164.0 `exec` refused every MCP tool call, so its reviews ended without a verdict (#783). A reviewer turn that still ends without a verdict leaves one screened `hub.log` line (exit code and the hub tools it called) and is offered on, or to you when you named that reviewer (task 453). A stale review request that the hub refuses at admission no longer marks the reviewer unavailable: a request whose binding changed is skipped, and one whose policy changed is rerouted as no verdict (#781). An unavailable reviewer slot no longer stops the agy TUI from reading its own task (#794). An `ahub recovery dispose --stop-and-archive` audit entry now keeps the error that blocked the upgrade as `cause` (#776). A task counts toward a recurring finding only where that finding was recorded open or accepted, so a fix-only record no longer files a "Review policy amendment" card (task 328), and a missing generation sync remote no longer logs a failure every sync interval (task 401). Upgrade running hubs with `ahub upgrade` and restart Claude sessions so they load the 0.17.0 plugin. Coming from 0.15.0 or older, the notes below apply too.
50
58
 
51
59
  ## Upgrading to 0.16.0
52
60
 
@@ -54,7 +62,7 @@ Always-ask now covers the local worker, Pi and Codex too (#636 slice 3): a local
54
62
 
55
63
  ## Upgrading to 0.15.0
56
64
 
57
- Publish, external-write, destroy and install calls now ask a person (#636 slice 2): the Claude PreToolUse hook returns `ask` for such a call in every permission mode, `never-ask` included, and a hub-owned ACP peer such as Kimi in native `ask` mode never auto-approves one, not under `--unattended` either; it waits in the console and the dashboard as `Action approval (<classes>): <command>` and is denied when `approvals.timeout_s` passes. When a class budget is above one, the approval of a classed ACP call also offers `Allow <class> xN for this session`. Unattended work that publishes, installs or deletes now stops for you: answer it, or set `ahub settings set action_policy.enabled false` or `action_policy.budgets.<class> <n>` (the policy and its budgets live only in untracked `.agenthub/config.local.json`). `ahub status` names each peer where the hub cannot see the call before it runs: a Kimi in `yolo` or `auto`, a Claude session that has not run the hub's permission hook, an `--unattended` Claude launch, and Codex, Pi and the local worker until #313 (see [always-ask action policy](operations.md#always-ask-action-policy-foundation-636)). A terminal Claude instance declared with `implementer` or `verifier` and its own `worktree` now owns write-class tasks once the daemon verifies its launch record and channel cwd; start it again with `ahub claude --as <id>` after the upgrade, because a launch recorded without its execution root attaches without isolation and gets no write task (#525-5). A headless Claude implementer can be declared in `.agenthub/config.local.json` (`"start_mode": "headless"`, roles `["implementer"]`, its own registered `worktree`, `permission_mode` `ask` or `ask-when-needed`; `never-ask` is refused): create and register the worktree first, then `ahub claude --as <id>` asks the hub to start it and returns. It keeps one session per task, and its writes and shell calls reach you as allow-once or deny requests in the console; no answer is a deny (#525-4; see [additional headless peer instances](operations.md#additional-headless-peer-instances)). `hub_task_done` and `hub_sdlc_record` take `deliverables: [{path, format, version?}]`: the hub hashes the files itself, a reviewer's approval is refused when a file changed since delivery, and `ahub task show` reports each file as `unchanged`, `changed`, `missing` or `unknown` (#509; see [deliverable records](sdlc.md#deliverable-records-509a)). A review request lists the task's recorded acceptance criteria by id, and an unmet item that starts with one (`AC2: what is missing`) is recorded on the verdict (#507). A peer that accepted or finished a task no longer reviews it; when no other reviewer is left, you review it, and `ahub route explain <id>` also prints the planner walk (#525-3). Instances, `review`, `snapshots.keep`, `approvals.timeout_s`, `context.gate` and the `sdlc.toml` `mode` and `required_checks` can be edited with `ahub settings edit`, the console Settings key `c` or the Structure group of an `ahub ui --settings` session: the hub validates, shows a diff and keeps a backup; a changed instance or `sdlc.toml` waits for a hub restart (console `R`), and keys that run code or locate secrets stay console only with their name typed (#689; see [structural configuration](operations.md#structural-configuration-from-the-console-and-the-dashboard-issue-689)). Kimi's token use is now counted from its session wire log, so a `budget.kimi_tokens_5h` you set pauses Kimi when it is reached (#285). `ahub backlog list` prints a table by rank; a script that read its output uses `--json` (#399). A launcher that meets a busy hub retries for up to 15 s and prints `hub busy, retrying` once (#719). Upgrade running hubs with `ahub upgrade` before relying on deliverables: an older daemon drops them from a newer plugin's `hub_task_done` without a word. Restart Claude sessions so they load the 0.15.0 plugin, and if `ahub status` shows a fresh reviewer instance such as `codex-review` stopped after the upgrade, start it again with `ahub codex --as codex-review`. Coming from 0.13.0 or older, the notes below apply too.
65
+ Publish, external-write, destroy and install calls now ask a person (#636 slice 2): the Claude PreToolUse hook returns `ask` for such a call in every permission mode, `never-ask` included, and a hub-owned ACP peer such as Kimi in native `ask` mode never auto-approves one, not under `--unattended` either; it waits in the console and the dashboard as `Action approval (<classes>): <command>` and is denied when `approvals.timeout_s` passes. When a class budget is above one, the approval of a classed ACP call also offers `Allow <class> xN for this session`. Unattended work that publishes, installs or deletes now stops for you: answer it, or set `ahub settings set action_policy.enabled false` or `action_policy.budgets.<class> <n>` (the policy and its budgets live only in untracked `.agenthub/config.local.json`). `ahub status` names each peer where the hub cannot see the call before it runs: a Kimi in `yolo` or `auto`, a Claude session that has not run the hub's permission hook, an `--unattended` Claude launch, and Codex, Pi and the local worker until task 313 (see [always-ask action policy](operations.md#always-ask-action-policy-foundation-636)). A terminal Claude instance declared with `implementer` or `verifier` and its own `worktree` now owns write-class tasks once the daemon verifies its launch record and channel cwd; start it again with `ahub claude --as <id>` after the upgrade, because a launch recorded without its execution root attaches without isolation and gets no write task (#525-5). A headless Claude implementer can be declared in `.agenthub/config.local.json` (`"start_mode": "headless"`, roles `["implementer"]`, its own registered `worktree`, `permission_mode` `ask` or `ask-when-needed`; `never-ask` is refused): create and register the worktree first, then `ahub claude --as <id>` asks the hub to start it and returns. It keeps one session per task, and its writes and shell calls reach you as allow-once or deny requests in the console; no answer is a deny (#525-4; see [additional headless peer instances](operations.md#additional-headless-peer-instances)). `hub_task_done` and `hub_sdlc_record` take `deliverables: [{path, format, version?}]`: the hub hashes the files itself, a reviewer's approval is refused when a file changed since delivery, and `ahub task show` reports each file as `unchanged`, `changed`, `missing` or `unknown` (#509; see [deliverable records](sdlc.md#deliverable-records-509a)). A review request lists the task's recorded acceptance criteria by id, and an unmet item that starts with one (`AC2: what is missing`) is recorded on the verdict (#507). A peer that accepted or finished a task no longer reviews it; when no other reviewer is left, you review it, and `ahub route explain <id>` also prints the planner walk (#525-3). Instances, `review`, `snapshots.keep`, `approvals.timeout_s`, `context.gate` and the `sdlc.toml` `mode` and `required_checks` can be edited with `ahub settings edit`, the console Settings key `c` or the Structure group of an `ahub ui --settings` session: the hub validates, shows a diff and keeps a backup; a changed instance or `sdlc.toml` waits for a hub restart (console `R`), and keys that run code or locate secrets stay console only with their name typed (#689; see [structural configuration](operations.md#structural-configuration-from-the-console-and-the-dashboard-issue-689)). Kimi's token use is now counted from its session wire log, so a `budget.kimi_tokens_5h` you set pauses Kimi when it is reached (#285). `ahub backlog list` prints a table by rank; a script that read its output uses `--json` (task 399). A launcher that meets a busy hub retries for up to 15 s and prints `hub busy, retrying` once (#719). Upgrade running hubs with `ahub upgrade` before relying on deliverables: an older daemon drops them from a newer plugin's `hub_task_done` without a word. Restart Claude sessions so they load the 0.15.0 plugin, and if `ahub status` shows a fresh reviewer instance such as `codex-review` stopped after the upgrade, start it again with `ahub codex --as codex-review`. Coming from 0.13.0 or older, the notes below apply too.
58
66
 
59
67
  ## Upgrading to 0.14.0
60
68
 
@@ -62,11 +70,11 @@ At a terminal width `ahub board` now prints one line per task: the title is cut
62
70
 
63
71
  ## Upgrading to 0.13.0
64
72
 
65
- One version policy now gates the Codex and Kimi fresh reviewers, Jcode, agy and Qwen (#691): `ahub agents` lists each installed CLI with its version and verdict, a version outside the verified ones (or, for Jcode and agy, outside their compatible line) is refused with the trust command, and `ahub agents trust <id> <version>` accepts that exact version on this machine; only the person can run it, and the reviewers' isolation proofs still run. See [agent CLI versions](operations.md#agent-cli-versions). If you use Jcode, run `ahub jcode` once per project: the hub-owned `.jcode/mcp.json` entry no longer names a peer, `ahub status` and `ahub doctor` show the old entry as not ready until it is rewritten, and the hub admits Jcode's hub tools only from an owned launch (#368). Extra Jcode instances, each with its own `jcode_home`, can be declared in `.agenthub/config.local.json` and started with `ahub jcode --as <id>` (#685; see [Jcode operations](operations.md#optional-jcode-peer)). Run `ahub init` once per project to refresh the hub block in `AGENTS.md`: it tells pull-only agents never to poll `hub_inbox` or CI on a timer and to run one heavy job per repository at a time (#681). `ahub usage` (with `limits` and `total`) and the console's Usage panel (`7`) show tokens per peer, task, issue, PR, release, day or model and the quota pause and reset history (#683); `ahub report --dogfood` prints a read-only scorecard (#687). In the console, `r` on a task in review now asks `a` (approve) or `c` (request changes, which needs a note) (#711), and a task's detail shows an overlaps block and, when ripwire 0.6.7 is on the daemon's PATH, its changed symbols, which the review brief lists too (#684). A pull-only peer now drains its queue up to 64 KiB per `hub_inbox` read, a queued delivery can be discarded while its peer is busy, and the person clears one peer's queue with `ahub queue discard --peer <id> --reason <text> --yes` (#713). `ahub sdlc authorize-release <version>` records the person's release approval, and `ahub init --release-guard` installs an optional tag pre-push guard (#481 S6; see [release approval](operations.md#release-approval-and-optional-tag-guard)). As announced since 0.12.x, `sdlc.workflow = "gsd-lite"` no longer loads and the `phase` receipt name is refused (#481): remove the setting and record a `criteria` checklist with `hub_sdlc_record`. Restart Claude sessions so they load the 0.13.0 plugin. Coming from 0.12.44 or older, the notes below apply too.
73
+ One version policy now gates the Codex and Kimi fresh reviewers, Jcode, agy and Qwen (#691): `ahub agents` lists each installed CLI with its version and verdict, a version outside the verified ones (or, for Jcode and agy, outside their compatible line) is refused with the trust command, and `ahub agents trust <id> <version>` accepts that exact version on this machine; only the person can run it, and the reviewers' isolation proofs still run. See [agent CLI versions](operations.md#agent-cli-versions). If you use Jcode, run `ahub jcode` once per project: the hub-owned `.jcode/mcp.json` entry no longer names a peer, `ahub status` and `ahub doctor` show the old entry as not ready until it is rewritten, and the hub admits Jcode's hub tools only from an owned launch (task 368). Extra Jcode instances, each with its own `jcode_home`, can be declared in `.agenthub/config.local.json` and started with `ahub jcode --as <id>` (#685; see [Jcode operations](operations.md#optional-jcode-peer)). Run `ahub init` once per project to refresh the hub block in `AGENTS.md`: it tells pull-only agents never to poll `hub_inbox` or CI on a timer and to run one heavy job per repository at a time (#681). `ahub usage` (with `limits` and `total`) and the console's Usage panel (`7`) show tokens per peer, task, issue, PR, release, day or model and the quota pause and reset history (#683); `ahub report --dogfood` prints a read-only scorecard (#687). In the console, `r` on a task in review now asks `a` (approve) or `c` (request changes, which needs a note) (#711), and a task's detail shows an overlaps block and, when ripwire 0.6.7 is on the daemon's PATH, its changed symbols, which the review brief lists too (#684). A pull-only peer now drains its queue up to 64 KiB per `hub_inbox` read, a queued delivery can be discarded while its peer is busy, and the person clears one peer's queue with `ahub queue discard --peer <id> --reason <text> --yes` (#713). `ahub sdlc authorize-release <version>` records the person's release approval, and `ahub init --release-guard` installs an optional tag pre-push guard (#481 S6; see [release approval](operations.md#release-approval-and-optional-tag-guard)). As announced since 0.12.x, `sdlc.workflow = "gsd-lite"` no longer loads and the `phase` receipt name is refused (#481): remove the setting and record a `criteria` checklist with `hub_sdlc_record`. Restart Claude sessions so they load the 0.13.0 plugin. Coming from 0.12.44 or older, the notes below apply too.
66
74
 
67
75
  ## Upgrading to 0.12.45
68
76
 
69
- The dashboard is redesigned (#469): from 1024px a top bar and a left sidebar (a row of tabs below that), keys `1`-`7` switch views, `?` lists the keys and `Esc` closes a sheet; a task opens as a side sheet, and the plan, split, releases, delivery evidence and propose forms sit in a Plan sheet opened from the board toolbar. View names, ids and the view cookie are unchanged. A person can load a bundled ACP adapter module declared under `adapter_modules` (a `path` and its `sha256`) in untracked `.agenthub/config.local.json`: only the daemon imports it, at startup, so a changed declaration needs a restart; Settings and hub tools cannot write it, and a declaration in the shared `.agenthub/config.json` is ignored. A project that declares modules can be upgraded or recovered only between releases that advertise `external_adapter_modules`. See [Adding an AI tool](adding-an-ai-tool.md#5-external-adapter-modules-451-a4) (#451). `hub_review` and `ahub review ... --findings '<json>'` take structured findings, `max_nits` in `.agenthub/sdlc.toml` caps the nits per report (default 5), and in enforce an open Important finding refuses approval (#481 S3). Plans take optional `proof` and `risks`. In enforce, approval also needs every changed PR file covered by a literal `plan.paths` entry; otherwise amend the plan through `hub_task_accept` and call done again, or the person runs `ahub sdlc accept-departure <task> <head> <reason...>`, which lapses when the hub restarts (#481 S2). An unresolved review thread now settles only by resolution or a later reply from a GitHub user; any other author kind counts as a pending bot comment (#481). `ahub roster remove <peer>` also deletes the roster key of an instance no longer declared (#225). When `ahub kimi` refuses a `kimi_cmd` with no TUI form, it now says the value is the running hub's, retained until `ahub settings reload` (#654). A Claude session no longer gets `hub_inbox` advice while a delivery hold refuses its pulls (#221). A same-size rewrite of a large file within one filesystem timestamp tick is now told apart by its bytes (#428). Restart Claude sessions so they load the 0.12.45 plugin. Coming from 0.12.43 or older, the notes below apply too.
77
+ The dashboard is redesigned (#469): from 1024px a top bar and a left sidebar (a row of tabs below that), keys `1`-`7` switch views, `?` lists the keys and `Esc` closes a sheet; a task opens as a side sheet, and the plan, split, releases, delivery evidence and propose forms sit in a Plan sheet opened from the board toolbar. View names, ids and the view cookie are unchanged. A person can load a bundled ACP adapter module declared under `adapter_modules` (a `path` and its `sha256`) in untracked `.agenthub/config.local.json`: only the daemon imports it, at startup, so a changed declaration needs a restart; Settings and hub tools cannot write it, and a declaration in the shared `.agenthub/config.json` is ignored. A project that declares modules can be upgraded or recovered only between releases that advertise `external_adapter_modules`. See [Adding an AI tool](adding-an-ai-tool.md#5-external-adapter-modules-451-a4) (#451). `hub_review` and `ahub review ... --findings '<json>'` take structured findings, `max_nits` in `.agenthub/sdlc.toml` caps the nits per report (default 5), and in enforce an open Important finding refuses approval (#481 S3). Plans take optional `proof` and `risks`. In enforce, approval also needs every changed PR file covered by a literal `plan.paths` entry; otherwise amend the plan through `hub_task_accept` and call done again, or the person runs `ahub sdlc accept-departure <task> <head> <reason...>`, which lapses when the hub restarts (#481 S2). An unresolved review thread now settles only by resolution or a later reply from a GitHub user; any other author kind counts as a pending bot comment (#481). `ahub roster remove <peer>` also deletes the roster key of an instance no longer declared (task 225). When `ahub kimi` refuses a `kimi_cmd` with no TUI form, it now says the value is the running hub's, retained until `ahub settings reload` (#654). A Claude session no longer gets `hub_inbox` advice while a delivery hold refuses its pulls (task 221). A same-size rewrite of a large file within one filesystem timestamp tick is now told apart by its bytes (#428). Restart Claude sessions so they load the 0.12.45 plugin. Coming from 0.12.43 or older, the notes below apply too.
70
78
 
71
79
  ## Upgrading to 0.12.44
72
80
 
@@ -74,7 +82,7 @@ Run `ahub init` once per project to refresh the hub block in `AGENTS.md`: it tel
74
82
 
75
83
  ## Upgrading to 0.12.43
76
84
 
77
- A busy hub no longer stalls when the checkout's HEAD moves under open turns: snapshot Git work in one burst shares a 250 ms budget, and incoming checkouts, merges and fast-forwards are told apart from a peer's own commits through the HEAD reflog, so they no longer raise false concurrent-edit warnings (#576). `ahub task assign` and `hub_task_assign` now refuse with the reason when the named peer cannot take the task (#601). `ahub completion zsh|bash|fish` prints a shell completion script (#603). The console's Peers tab shows each peer's planner WIP, and `w` edits its limit (#532). agy native file reads are recorded as peer facts on 1.4.2 and 1.4.3 (#555); 1.4.3 stays unverified, and in its TUI the hub tools appear from the second turn (#267, #607). Coming from 0.12.41 or older, the notes below apply too.
85
+ A busy hub no longer stalls when the checkout's HEAD moves under open turns: snapshot Git work in one burst shares a 250 ms budget, and incoming checkouts, merges and fast-forwards are told apart from a peer's own commits through the HEAD reflog, so they no longer raise false concurrent-edit warnings (#576). `ahub task assign` and `hub_task_assign` now refuse with the reason when the named peer cannot take the task (#601). `ahub completion zsh|bash|fish` prints a shell completion script (#603). The console's Peers tab shows each peer's planner WIP, and `w` edits its limit (#532). agy native file reads are recorded as peer facts on 1.4.2 and 1.4.3 (#555); 1.4.3 stays unverified, and in its TUI the hub tools appear from the second turn (task 267, #607). Coming from 0.12.41 or older, the notes below apply too.
78
86
 
79
87
  ## Upgrading to 0.12.42
80
88
 
@@ -120,7 +128,7 @@ The restarted Claude planner gets the release-train tools (`hub_release_new`, `h
120
128
 
121
129
  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`.
122
130
 
123
- Since 0.12.37 the hub opens a split memory home through its shards (#534); the current exact `@staix/agent-mem` pin is 0.11.0. Before anyone runs `amem migrate --shards` on a memory home, every hub using that home must run 0.12.37 or later: an older hub does not see the split and keeps writing the old layout. `ahub memory profile-check` is split-aware and compares each hub with its own project's shard (#536). See [memory profiles and rollout](memory.md#selected-memory-profile).
131
+ Since 0.12.37 the hub opens a split memory home through its shards (#534); the current exact `@staix/agent-mem` pin is 0.13.0. Before anyone runs `amem migrate --shards` on a memory home, every hub using that home must run 0.12.37 or later: an older hub does not see the split and keeps writing the old layout. `ahub memory profile-check` is split-aware and compares each hub with its own project's shard (#536). See [memory profiles and rollout](memory.md#selected-memory-profile).
124
132
 
125
133
  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:
126
134
 
@@ -182,12 +190,13 @@ or set `OMNIROUTE_API_KEY`. The `.agenthub/routing.toml` that `ahub init` writes
182
190
  | `ahub ask <question>` | what the board, shared memory and the log say, with the ids the answer rests on |
183
191
  | `ahub stop <peer>` | stop one headless peer (Kimi, headless Pi, `local`); the hub and the other peers keep running |
184
192
  | `ahub kill` | stop the daemon and everything it started |
193
+ | `ahub down` | the same as `ahub kill`, named as the counterpart of `ahub up` |
185
194
 
186
195
  More: `ahub help`. How it is built and why: [`docs/specs/2026-09-19-agent-hub-design.md`](specs/2026-09-19-agent-hub-design.md). What it protects and what it does not: [`docs/security.md`](security.md).
187
196
 
188
197
  ## Shared memory
189
198
 
190
- The hub exact-pins the first-party `@staix/agent-mem` 0.11.0 core, including deterministic capture; no claude-mem worker is needed. Existing shared-v1 memory remains the default; upgrading does not migrate it. Managed agents use screened `hub_memory_search`, `hub_memory_show` and `hub_memory_graph` tools, or propose a correction with `hub_memory_correct`; only a person accepts candidates through the supported `amem review` path. Since agent-mem 0.7.0, `amem review` also accepts candidates on a selected generation.
199
+ The hub exact-pins the first-party `@staix/agent-mem` 0.13.0 core, including deterministic capture; no claude-mem worker is needed. Existing shared-v1 memory remains the default; upgrading does not migrate it. Managed agents use screened `hub_memory_search`, `hub_memory_show` and `hub_memory_graph` tools, or propose a correction with `hub_memory_correct`; only a person accepts candidates through the supported `amem review` path. Since agent-mem 0.7.0, `amem review` also accepts candidates on a selected generation.
191
200
 
192
201
  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.
193
202
 
package/docs/research.md CHANGED
@@ -73,8 +73,11 @@ given up). It labels the current board's task with that id (its proposal in `eve
73
73
  later task with the same id after a reset does not inherit it. A task from before `ahub reset --all` is not on the board
74
74
  any more: it is the store's only record with that id, or, when there are several or the id is in use again, the one
75
75
  `--created` names (the `createdAt` that `ahub research export` shows; a record without one takes no label). A task with no record yet (not approved, its record still waiting for
76
- the turns that approved it, or approved while research was off) is refused; labels mark approved tasks only. Only a
77
- person can run it (agent shells are refused), and only while research is on. The latest label wins.
76
+ the turns that approved it, or approved while research was off) is refused; labels mark approved tasks only. A person
77
+ labels any task; an agent labels only a task it owns, the hub checks that by role, and its record carries `by: <peer>`
78
+ (#933). Only while research is on. A person's latest label for a record always wins, whatever an agent labelled
79
+ after it; an agent's latest label counts only where the person never labelled that record. The CSV's `labelBy` column
80
+ names the agent (empty for the person).
78
81
 
79
82
  ## Measures
80
83
 
@@ -92,7 +95,7 @@ They are computed each time from the records; nothing derived is stored. `ahub r
92
95
  [--since] [--all]` writes the records for outside analysis; the CSV columns are, in order: project, task, class, pii,
93
96
  outcome, createdAt, startedAt, approvedAt, wallMs, activeMs, owners, reviewer, reviewRounds, changesRequested, checkPassed,
94
97
  checkFailed, dones, reassignments, firstPass, stuck, overlaps, conflicts, testsPass, testsFail, tokens, turns,
95
- filesChanged, models, label, writerVersion, writerSource. The CSV is a flat subset; JSONL carries every field.
98
+ filesChanged, models, label, labelBy, writerVersion, writerSource. The CSV is a flat subset; JSONL carries every field.
96
99
  Another project's measures: `ahub --project <dir> research`.
97
100
 
98
101
  ## Add-ons (#248)
@@ -119,4 +122,4 @@ either side a difference is inconclusive. This is field data: the sides differ i
119
122
  association, not cause. Controlled arms are `ahub bench run --addon <name>=on|off` (docs/bench.md), and the decisions
120
123
  taken from both are in `docs/addons/`.
121
124
 
122
- Agents may read the measures and export (ids and counts only); `backfill` and `task label` are a person's.
125
+ Agents may read the measures and export (ids and counts only); `backfill` is the person's or the configured conductor's (#933), and an agent labels only its own task.
package/docs/sdlc.md CHANGED
@@ -16,7 +16,7 @@ The built-in lifecycle follows the [AI-Native SDLC Playbook](https://claude.com/
16
16
  | spec | Design | person/planner | issue identity and scope | spec acceptance/body drift checked at build entry | person-only (acceptance) |
17
17
  | plan | Build | planner/owner | plan, branch, existing predecessor gates | current planner review required at build admission; literal-path conformance gates approval in enforce | agent-executable |
18
18
  | build | Build | owner | PR identity/current head and local check on that head | entering review, when enabled | agent-executable |
19
- | test | Test | verifier/reviewer | required checks and local gate on the reviewed head | approval, when enabled; bug test integrity below, evals pending S5 | agent-executable |
19
+ | test | Test | verifier/reviewer | required checks and local gate on the reviewed head | approval, when enabled; bug test integrity and the configuration-PR eval gate below | agent-executable |
20
20
  | review | Deploy | assigned reviewer | current head-bound verdict, no pending change request/open thread; AC checklist under Compliance | approval, when enabled; open Important findings block in enforce | agent-executable |
21
21
  | merge | Deploy | person | current-head readiness, ordered touching PRs | shown; the guarded merge below runs only when `auto_merge` is `on` (default off) | agent-executable where the project's AGENTS.md delegates it to the approving reviewer (this repository does, #784); the hub still shows the person as actor |
22
22
  | release | Deploy | person | merged commit, version/tag, docs freshness/live receipt checklist | person authorization and optional tag hook below | agent-evidence / person-verdict (consent) |
@@ -33,9 +33,9 @@ Stored `implement` and `verify` stage ids read as `build` and `test`. A write th
33
33
  | S2 | Accepted plan and diff conformance | Bounded proof/risks, metadata-only plan conformance and person-bound departure acceptance |
34
34
  | S3 | Bugs, Security and Compliance review passes | Head-bound findings, configured nit cap, Important approval gate and recurring-finding amendment proposals |
35
35
  | S4 | Red-first tests and test integrity | Bug-label metadata, source-labelled red-first proof and changed pre-existing test blockers |
36
- | S5 | Continuous evals for agent configuration | Pending gate, dependent on #251/#266; no eval pass claimed |
36
+ | S5 | Continuous evals for agent configuration | Gate mechanism only: `config_paths` mark a configuration PR and the configured `eval_check` is then required, with its pass rate and suite version recorded. No suite is chosen, created or defaulted: suite choice pending the person (#251/#266) |
37
37
  | S6 | Production approval gate | Person-only commit/version/docs receipt and opt-in Git tag pre-push guard; report stays read-only |
38
- | S7 | Metrics, deterministic control bands and follow-up intent | `ahub sdlc metrics`, optional `[bands]` with log, diagnose and propose tiers, one card per breach episode; no PR readback and no security baseline (design spec amendment) |
38
+ | S7 | Metrics, deterministic control bands and follow-up intent | `ahub sdlc metrics`, optional `[bands]` with log, diagnose and propose tiers, one card per breach episode; merge-time review and CI figures recorded from the merged PR; release metrics (interval, lead time, hotfix rate) and a `hotfix_rate` band; an opt-in scheduled security-scan baseline (`[security_scan]`, `security_open_alerts` band). Only the per-release gates-run record remains unbuilt (design spec amendment) |
39
39
  | S8 | Compliance acceptance-criteria checklist | Head-bound checklist retained; legacy workflow and phase compatibility deprecated; task import removed |
40
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
41
  | S10 | Planner cost governance | Typed review/CI scope, premature-review admission, identical-tree CI reuse and read-only cost report |
@@ -64,11 +64,11 @@ The acceptance-criteria checklist belongs to the Compliance pass over the same b
64
64
 
65
65
  ## Delivery and verification state
66
66
 
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. S5, S7 and the native advise-through-merge/release cycle remain pending. S6 authorization and its optional tag hook are implemented in source. S4 test integrity is implemented in source. S2 plan conformance and S3 findings are implemented in source. Installation and native acceptance require separate receipts; a person authorization is consent, not native verification.
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. S5 and S7 are implemented in source (S5 as a gate mechanism with no suite behind it, S7 with merge-time readback, release metrics and an opt-in security baseline). Only the native advise-through-merge/release cycle and the per-release gates-run record remain pending. S6 authorization and its optional tag hook are implemented in source. S4 test integrity is implemented in source. S2 plan conformance and S3 findings are implemented in source. Installation and native acceptance require separate receipts; a person authorization is consent, not native verification.
68
68
 
69
69
  ## Initial commands
70
70
 
71
- A per-project `.agenthub/sdlc.toml` may set `mode = "advise"` and `required_checks = ["check (ubuntu-latest)", "check (macos-latest)", "seeded guards"]`. Omit the file for off. A missing/empty check set is unknown, never a green approval gate. The built-in stage order follows the table above; changing the file applies at the next hub start.
71
+ A per-project `.agenthub/sdlc.toml` may set `mode = "advise"` and `required_checks = ["check (ubuntu-latest)", "check (macos-latest)", "seeded guards"]`; the optional `eval_check` and `config_paths` are described under the eval gate below. Omit the file for off. A missing/empty check set is unknown, never a green approval gate. The built-in stage order follows the table above; changing the file applies at the next hub start.
72
72
 
73
73
  A person records ids and a claimed local receipt with `ahub sdlc record 12 '{"repo":"owner/repo","issue":109,"pr":325,"localHead":"<full SHA>","localResult":"passed","stage":"build"}'`. Owners/reviewers have the bounded `hub_sdlc_record` tool; it accepts no remote bodies or external-check results. Only the assigned reviewer or person records a reviewed head. `ahub review 12 approved --head <full SHA>` rechecks current evidence before approval. Changes requested does not require green CI.
74
74
 
@@ -168,7 +168,7 @@ The release reader additionally requests --committed: manifest, README and packa
168
168
 
169
169
  `.agenthub/sdlc.toml` takes `auto_merge = "off" | "dry-run" | "on"` (default `off`) and optional `[repos."owner/name"]` tables with `required_checks` and `auto_merge`. A table's mode overrides the top-level one for that repository; its `required_checks` come first, then the top-level list, so a repository without branch protection is gated by what this file names (an empty set refuses: `required checks unknown`). Unknown keys are refused with a field path. The file is read once at start.
170
170
 
171
- Two triggers, neither blocking: an approving `hub_review` at the PR's current head (the check runs after the verdict is saved and reports through a notice; a failing or throwing check never changes the verdict), and `ahub sdlc merge <task> [--dry-run]` (the person or the one configured conductor; a daemon control op that always replies). Right before acting the hub re-reads the PR (`readSdlcPr`) and the queue (`readMergeQueue`) and refuses, naming every failing gate: task not approved, head changed since the approved head, a required check failing, pending or missing, a pending change request, pending or unknown review requests, an unresolved or unknown thread, an open Important finding, a draft, a PR not first among its touching PRs or a dependency that is not approved, a release PR (`release PR: person consent required`), a private task, or unreadable evidence. Unknown is never a pass. The decision judges every gate on three readings (the PR, the queue's own read, then the PR once more, last) and refuses what any of them fails, so a check, review request, thread or draft that changes between reads at the same head is caught; `--match-head-commit` pins only the head. The board-side inputs are read again, synchronously, in the same stretch as the `gh pr merge` call (nothing is awaited between the read and the call) and compared with what the decision judged: the task's state, repo, PR and reviewed head, its privacy, every dependency's state and the dependency edges, open Important findings on the head, the release stage, the merge mode and required checks in force, and the asker's authority and the hub's availability. Any difference refuses and runs nothing. The complete local queue inputs (all task links, dependency edges, receipts, plans, findings, privacy and SDLC configuration) must also match their pre-read fingerprint in a synchronous check after the fresh gates and argv validation, immediately before dispatch. The caller must still be the person, the hub approval trigger or the currently configured conductor at that final check. Any changed projection refuses, conservatively including an unrelated task projection change; a new invocation reads the queue again.
171
+ Two triggers, neither blocking: an approving `hub_review` at the PR's current head (the check runs after the verdict is saved and reports through a notice; a failing or throwing check never changes the verdict), and `ahub sdlc merge <task> [--dry-run]` (the person or the one configured conductor; a daemon control op that always replies; the decision prints for a person, and `--json` prints it as a document). Right before acting the hub re-reads the PR (`readSdlcPr`) and the queue (`readMergeQueue`) and refuses, naming every failing gate: task not approved, head changed since the approved head, a required check failing, pending or missing, a pending change request, pending or unknown review requests, an unresolved or unknown thread, an open Important finding, a draft, a PR not first among its touching PRs or a dependency that is not approved, a release PR (`release PR: person consent required`), a private task, or unreadable evidence. Unknown is never a pass. The decision judges every gate on three readings (the PR, the queue's own read, then the PR once more, last) and refuses what any of them fails, so a check, review request, thread or draft that changes between reads at the same head is caught; `--match-head-commit` pins only the head. The board-side inputs are read again, synchronously, in the same stretch as the `gh pr merge` call (nothing is awaited between the read and the call) and compared with what the decision judged: the task's state, repo, PR and reviewed head, its privacy, every dependency's state and the dependency edges, open Important findings on the head, the release stage, the merge mode and required checks in force, and the asker's authority and the hub's availability. Any difference refuses and runs nothing. The complete local queue inputs (all task links, dependency edges, receipts, plans, findings, privacy and SDLC configuration) must also match their pre-read fingerprint in a synchronous check after the fresh gates and argv validation, immediately before dispatch. The caller must still be the person, the hub approval trigger or the currently configured conductor at that final check. Any changed projection refuses, conservatively including an unrelated task projection change; a new invocation reads the queue again.
172
172
 
173
173
  `on` runs exactly `gh pr merge <pr> --repo <repo> --squash --match-head-commit <head>` (a closed argv shape checked right before the call) and reads the PR back: only GitHub reporting `MERGED` is a merge, whatever the exit code. Never `--admin`, never a ref update used to perform the merge, and no `--auto` while the repository does not allow auto-merge (a person's repository setting). `dry-run` reports the same decision and the argv it would run and runs nothing; `off` reads nothing. Logs and notices name the task number and gates only. The verdict mirror is described in the next section.
174
174
 
@@ -254,18 +254,39 @@ What each metric reads, and what it does not:
254
254
 
255
255
  - `first_pass_check` is the hub's own completion check (`check passed` or `check failed` at `hub_task_done`), not PR CI. It is `unknown` for every task where completion checks are off, which is the default.
256
256
  - `stale_head_re_reviews` counts review rounds whose verdict was bound to a different head than the review was requested for (the `done`'s recorded head against the verdict's findings head), so it needs both heads on the round. A task with no such round is `unknown`, not 0. (A `done` after an `approved` is not a signal: the board cannot produce one.)
257
- - `acceptance_to_merge_ms` and `same_head_ci_reruns` need a PR readback. This report makes none, so they read `unknown` here; `ahub sdlc costs` carries the PR-based review rounds, CI runs and reruns.
257
+ - `acceptance_to_merge_ms`, `same_head_ci_reruns`, `ci_runs_at_merge`, `github_reviews_at_merge` and `review_rounds_at_merge` come from the merge-time readback. The guarded merge (`ahub sdlc merge`, or the auto-merge after an approving verdict) reads the merged PR once, after the merge is observed, and records on the task's merge record `cost: { mergedAt, ciRuns, ciReruns, reviews }`: CI runs linked to any head of the PR (an attempt after the first of one run id is a rerun) and distinct GitHub reviews. The read is bounded (8 s, the same fixed queries as `ahub sdlc costs`), host-written and never a tool input, and it never decides the merge result. A CI figure is known only when the run list was complete, and a failed or partial read leaves the field `null`; a task whose merge was not read, merged by hand or merged before this record existed, is `unknown` for all of them, never 0. `review_rounds_at_merge` counts the task's review handoffs (`done`) up to the merge. `merged` in the report says how many counted tasks have a read merge and how many do not. `ahub sdlc costs` still carries the per-PR cost table with its own scope.
258
258
  - `stalls` counts open stalls on public tasks by kind and the dones recorded without writes.
259
259
 
260
- `.agenthub/sdlc.toml` may set `[bands.<metric>]` for `acceptance_to_plan_ms`, `acceptance_to_first_review_ms`, `first_pass_approval`, `rework_rounds`, `first_pass_check` or `stale_head_re_reviews`. Every field is optional and bounded: `window` (4 to 200, default 20), `min_samples` (3 to 200, default 8, at most `window`), `log_sigma` 2, `diagnose_sigma` 3, `propose_sigma` 4 (ordered, 0.5 to 20), `drift_run` (3 to 50, default 6) and `min_delta` (the smallest step worth any tier: an hour for the times, one round, half a rate). An unknown metric or field is refused with its path. The newest value is compared with the mean and population standard deviation of the `window` values before it, on the bad side only (a longer time, more rework, a lower rate). The deviation is floored per metric (half an hour, or half a round or a rate) so a flat baseline does not turn one step into thousands of deviations. Fewer than `min_samples` before it fires nothing, and so does a step under `min_delta`, drift included. A run of `drift_run` newest values all more than one deviation on the bad side is at least `diagnose` even when none reaches a sigma tier, provided the newest step clears `min_delta`. Tiers:
260
+ `.agenthub/sdlc.toml` may set `[bands.<metric>]` for `acceptance_to_plan_ms`, `acceptance_to_first_review_ms`, `first_pass_approval`, `rework_rounds`, `first_pass_check`, `stale_head_re_reviews`, `hotfix_rate` or `security_open_alerts`. Every field is optional and bounded: `window` (4 to 200, default 20), `min_samples` (3 to 200, default 8, at most `window`), `log_sigma` 2, `diagnose_sigma` 3, `propose_sigma` 4 (ordered, 0.5 to 20), `drift_run` (3 to 50, default 6) and `min_delta` (the smallest step worth any tier: an hour for the times, one round, half a rate). An unknown metric or field is refused with its path. The newest value is compared with the mean and population standard deviation of the `window` values before it, on the bad side only (a longer time, more rework, a lower rate). The deviation is floored per metric (half an hour, or half a round or a rate) so a flat baseline does not turn one step into thousands of deviations. Fewer than `min_samples` before it fires nothing, and so does a step under `min_delta`, drift included. A run of `drift_run` newest values all more than one deviation on the bad side is at least `diagnose` even when none reaches a sigma tier, provided the newest step clears `min_delta`. Tiers:
261
261
 
262
262
  - `log`: one hub notice per breach episode, remembered in the board database so a restart does not repeat it.
263
263
  - `diagnose`: one read-only `triage` backlog task for the planner to inspect the histories behind the metric. It asks for no edit of files, branches or the board.
264
264
  - `propose`: one `plan` backlog card that names the metric and its evidence (the newest value, the task that produced it, where the episode began, the baseline and the deviations).
265
265
 
266
- `rework_rounds` and `stale_head_re_reviews` are final only when a task is approved: a task still in flight is `unknown` for both and the series places them at the approval, so a provisional value cannot move and start a second episode inside one shift. The stale-head count compares the head a `done` requested with the head the verdict is bound to; the verdict head prefers the lifecycle receipt's `head` or `localHead` over the commit, so a task whose receipt head and commit disagree can read one spurious stale round. Release metrics (interval, lead time, hotfix rate), release bands and the per-release gates-run record are not built; they need release history and S6, and are the next slice. A breach is an episode: the run of non-ok values of one metric, keyed `<metric>:#<task that started it>:<tier>` in the card (`Band breach key:`). The key comes from history alone, so a sustained shift keeps one key however many tasks finish inside it, and a restart finds the same one. A metric with an open band card (a backlog or open task, not approved or cancelled) writes nothing more, so a value that grows from `diagnose` to `propose` inside one episode adds no second card. When the planner closes the card and the metric returns to ok, a later breach is a new episode and a new card. The daemon evaluates every five minutes, only when the board or the band configuration changed since the last evaluation that ran, and not while it is stopping or recovering or has a storage error; a held pass, mode off or no bands consumes nothing, so the breach is written when the hold clears. A failed evaluation is one `hub.log` line. Detection never calls a model, the cards hold fixed words, numbers and task ids and no task text, and the hub opens no GitHub issue: external registration stays a person's decision.
266
+ `rework_rounds` and `stale_head_re_reviews` are final only when a task is approved: a task still in flight is `unknown` for both and the series places them at the approval, so a provisional value cannot move and start a second episode inside one shift. The stale-head count compares the head a `done` requested with the head the verdict is bound to; the verdict head prefers the lifecycle receipt's `head` or `localHead` over the commit, so a task whose receipt head and commit disagree can read one spurious stale round. Release metrics (interval, lead time, hotfix rate) and the `hotfix_rate` band are built (see Release metrics below); only the per-release gates-run record is not, because it needs gate-execution evidence the S6 authorization path would supply. A breach is an episode: the run of non-ok values of one metric, keyed `<metric>:#<task, release or scan that started it>:<tier>` in the card (`Band breach key:`). The key comes from history alone, so a sustained shift keeps one key however many tasks finish inside it, and a restart finds the same one. A metric with an open band card (a backlog or open task, not approved or cancelled) writes nothing more, so a value that grows from `diagnose` to `propose` inside one episode adds no second card. When the planner closes the card and the metric returns to ok, a later breach is a new episode and a new card. The daemon evaluates every five minutes, only when the board or the band configuration changed since the last evaluation that ran, and not while it is stopping or recovering or has a storage error; a held pass, mode off or no bands consumes nothing, so the breach is written when the hold clears. A failed evaluation is one `hub.log` line. Detection never calls a model, the cards hold fixed words, numbers and task, release or scan ids and no task text, and the hub opens no GitHub issue: external registration stays a person's decision.
267
267
 
268
- Bands act only when the lifecycle `mode` is `advise` or `enforce`: in `off` the writer is idle, though `ahub sdlc metrics` still reports each configured band's tier. A scheduled security scan baseline is not part of this slice.
268
+ Bands act only when the lifecycle `mode` is `advise` or `enforce`: in `off` the writer is idle, though `ahub sdlc metrics` still reports each configured band's tier.
269
+
270
+ Two bands are not made of tasks. `hotfix_rate` has one point per shipped release (1 for a hotfix train, 0 otherwise); a shipped release with no recorded train history is left out, never a 0, and a release whose scope holds private work is left out too. `security_open_alerts` has one point per recorded security scan. Their breach cards name the subject (`release #N`, `security scan #N`) instead of a task, with the same key format, one card per episode and the same open-card rule.
271
+
272
+ ## Release metrics (#481 AC12)
273
+
274
+ `ahub sdlc release` (the readiness report) and `ahub sdlc train report <release-id>` show release interval (the time between consecutive shipments), lead time from a task's final approval to the release that shipped it, and the hotfix rate (hotfix trains over shipped trains with recorded history). They are computed from the shipped releases' recorded train history and the board's task history by `releaseMetrics`, with unknown kept `null` and named (`1 release without complete history: #2`): a release shipped before train history was recorded, or a task with no approval before the ship, is unknown, never 0. Releases whose scope holds private work are left out. A band on `hotfix_rate` proposes a planning card (see Control bands). The per-release gates-run record is not built: it needs gate-execution evidence the S6 authorization path would supply, and a cut record is not that evidence.
275
+
276
+ ## Security-scan baseline (#481 S7)
277
+
278
+ The hub runs no scanner and invents no control. `[security_scan]` in `.agenthub/sdlc.toml` makes it read the open-alert counts GitHub already holds for the repository and record them as a baseline:
279
+
280
+ ```toml
281
+ [security_scan]
282
+ sources = ["dependabot", "secret_scanning"] # nonempty, from dependabot, secret_scanning, code_scanning; there is no default
283
+ every_h = 24 # 1 to 720, default 24
284
+ # repo = "owner/name" # default: the project repository
285
+ ```
286
+
287
+ Nothing is scheduled unless this table exists, and a lifecycle `mode` of `off` schedules nothing. The daemon checks hourly whether a scan is due and, when it is, reads each named source with a fixed `gh api repos/<repo>/<source>/alerts?state=open&per_page=100 --jq length` (paged to at most 1000 alerts, 20 s in all) and keeps only the counts: one `security_scans` row of counts per scan, never an alert's text, package, path or location. A source GitHub will not read (not enabled, not permitted, over the cap, a failed call) makes the row's total unknown, and an unknown total is not a point in the series: a repository with nothing readable records nothing useful and never reads as zero alerts. The scan is held like every host writer (stopping, recovery, storage error) and judged again after its read, so a configuration or hold that changed meanwhile writes nothing. A repository identity that matches a privacy pattern is never scanned.
288
+
289
+ The `security_open_alerts` band judges the total over the preceding scans like every other band, and a breach is one `log` notice, one read-only `triage` task or one `plan` card keyed `security_open_alerts:#<scan>:<tier>`, naming the scan and the counts and no alert. Without a `[bands.security_open_alerts]` table the scans are recorded and shown, and nothing is judged.
269
290
 
270
291
  # Planner review and CI cost governance (#481 S10)
271
292
 
@@ -343,6 +364,29 @@ Status/explain and merge-queue rows expose `testIntegrity` with applicability, r
343
364
 
344
365
  Reads share the existing 20 s lifecycle/queue allowance and fixed argument/output caps. Labels and check contexts are bounded to 100 each, PR commits to 100, and the red commit to fewer than 100 changed files; larger proof is unknown rather than silently accepted. The queue revalidates every linked issue's applicability with its final PR snapshot (up to 32 distinct issues). Metadata cannot detect a later commit gutting assertions in a test added by the PR; content-aware review remains required. These metadata/claim receipts do not prove a native/container run by themselves; no live advise-through-release receipt is claimed.
345
366
 
367
+ ## Eval gate for agent configuration (#481 S5)
368
+
369
+ Suite choice pending the person. This slice wires the gate only. It chooses, creates and defaults to no eval suite: `eval_check` has no default, and until the person names one a configuration PR shows `eval check not configured` in its status and advice (never a silent pass) and nothing is required of it, in any mode (#251 and #266 own the suite and its smoke subset).
370
+
371
+ `.agenthub/sdlc.toml` takes two optional keys, read at hub start:
372
+
373
+ - `eval_check = "<check name>"`: the one check a configuration PR must have passed on its current head. One nonempty name of at most 120 characters.
374
+ - `config_paths = ["AGENTS.md", "docs/agent-notes/**", ...]`: the patterns that mark a PR as a configuration change. At most 32 unique repository-relative patterns of literal names, `*` and `?` inside one segment, and a whole-segment `**` (`dir/**` matches everything under `dir`, never `dir` itself). Absent means the defaults: `AGENTS.md`, `docs/agent-notes/**`, `templates/**` (role texts and profiles), `.agenthub/roles/**`, `.agenthub/routing.toml`, `routing.toml` and `src/hub/hub-tools.ts` (the hub tool definitions). A list replaces the defaults, and an empty list turns the gate off. Bad values refuse at load with a field path.
375
+
376
+ A PR is a configuration change when any of its changed files matches. The decision uses the PR's complete file inventory (metadata, never patches): an incomplete or truncated inventory is `eval gate: PR file coverage incomplete or unknown`, never a clean code-only PR. A code-only PR is unaffected: no extra required check, no eval read, no `evalGate` in its status or queue row, and off output stays byte for byte.
377
+
378
+ For a configuration PR with an `eval_check`:
379
+
380
+ - The check joins the required checks on every reading: `ahub sdlc status`, the review and approval gates, the merge queue and the guarded merge decision (`required check evals: ...`).
381
+ - The hub reads that check run once at the exact head (fixed argv, at most 32 KiB of output and 10 s, one completed run of that exact name at that head, else unknown) and keeps only two values from its title and summary, `eval_pass_rate=<0 to 1>` and `eval_suite=<version>`. No other text is stored or shown.
382
+ - `evalGate` in status and queue rows shows the check, its state, how many changed files matched, the pass rate and the suite version. A verdict that approves a passing check appends `Eval gate: <check> passed at <head>, pass rate <rate>, suite <version>` to the approval note, which is the recorded pass rate and suite version.
383
+ - With an `eval_check`, a passing check that reports no pass rate or suite version and unknown file coverage are gaps. Advise shows each in `evalGate.gaps` and in the review advice notice and never blocks on it. Enforce adds each to the blockers, so the review, approval and merge gates refuse.
384
+ - Without an `eval_check`, the gate is a note (`evalGate.notes`, `state: not_configured`) in every mode and never a blocker: an existing enforce setup gains no refusal until the person names a suite. File coverage is not judged then.
385
+
386
+ The guarded merge reads the eval check run before its final PR read and judges that last reading with no remote await in between, so a change request, thread, draft or check that moves during the eval read refuses. A passed eval check on the last reading with no result read (the PR entered configuration scope after the read) counts as unreported.
387
+
388
+ A private (PII) task reads no eval metadata and records none.
389
+
346
390
  ## Person release authorization (#481 S6)
347
391
 
348
392
  From the running daemon's candidate checkout, the person runs `ahub sdlc authorize-release <version>`. The console-only operation binds approval to the exact current commit, its committed package version, the origin fetch/push identity and fresh committed documentation evidence. It rereads the head and remote after the documentation check. Agent shells, including `--live-test`, cannot grant it. An append-only metadata event records the approval id, commit, version and documentation digest/count. The authoritative ledger is daemon-owned memory: restart revokes every approval; audit history cannot restore one.