@staix/agent-hub 0.15.0 → 0.16.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 (80) hide show
  1. package/CHANGELOG.md +25 -0
  2. package/README.md +12 -8
  3. package/docs/agent-notes/README.md +4 -0
  4. package/docs/agent-notes/adapters.md +6 -3
  5. package/docs/agent-notes/budget.md +4 -2
  6. package/docs/agent-notes/daemon.md +9 -3
  7. package/docs/agent-notes/local-worker.md +2 -2
  8. package/docs/agent-notes/tasks.md +13 -1
  9. package/docs/agent-notes/tests.md +3 -0
  10. package/docs/dogfood.md +13 -4
  11. package/docs/events.md +1 -1
  12. package/docs/memory.md +78 -10
  13. package/docs/operations.md +116 -30
  14. package/docs/quickstart.md +8 -4
  15. package/docs/security.md +80 -18
  16. package/docs/smoke.md +7 -3
  17. package/docs/specs/2026-09-19-agent-hub-design.md +84 -8
  18. package/docs/verification/2026-10-24-always-ask-adapters.md +52 -0
  19. package/docs/verification/2026-10-24-amem-runtime.md +69 -0
  20. package/docs/verification/2026-10-24-codex-0164-reviewer-isolation.md +55 -0
  21. package/docs/verification/2026-10-24-delivery-holds-ac8.md +264 -0
  22. package/docs/verification/2026-10-24-hardcoded-values.md +2108 -0
  23. package/docs/verification/2026-10-25-0.16.0.md +82 -0
  24. package/docs/verified.json +26 -17
  25. package/package.json +2 -2
  26. package/plugins/agent-hub/.claude-plugin/plugin.json +1 -1
  27. package/plugins/agent-hub/server.js +2085 -1702
  28. package/src/adapters/acp.ts +5 -2
  29. package/src/adapters/codex-appserver.ts +69 -1
  30. package/src/adapters/compat.ts +2 -2
  31. package/src/adapters/definitions/builtin-metadata.ts +3 -3
  32. package/src/adapters/local-worker.ts +5 -4
  33. package/src/adapters/registry-types.ts +1 -1
  34. package/src/cli/console-state.ts +20 -2
  35. package/src/cli/console-symbols.ts +3 -2
  36. package/src/cli/console-usage.ts +5 -2
  37. package/src/cli/console.ts +5 -1
  38. package/src/cli/help.ts +7 -5
  39. package/src/cli/identity.ts +3 -1
  40. package/src/cli/main.ts +44 -10
  41. package/src/cli/memory.ts +119 -7
  42. package/src/cli/output.ts +12 -4
  43. package/src/hub/action-class.ts +211 -17
  44. package/src/hub/action-enforce.ts +13 -5
  45. package/src/hub/action-policy.ts +19 -8
  46. package/src/hub/ask.ts +16 -4
  47. package/src/hub/board.ts +86 -31
  48. package/src/hub/budget-notes.ts +65 -0
  49. package/src/hub/budget.ts +42 -10
  50. package/src/hub/bus.ts +1 -1
  51. package/src/hub/code-facts.ts +56 -16
  52. package/src/hub/conductor.ts +5 -2
  53. package/src/hub/config-apply.ts +7 -0
  54. package/src/hub/control-client.ts +1 -1
  55. package/src/hub/daemon.ts +250 -49
  56. package/src/hub/envelope.ts +1 -1
  57. package/src/hub/events.ts +2 -2
  58. package/src/hub/hub-tools.ts +3 -3
  59. package/src/hub/kimi-usage.ts +12 -0
  60. package/src/hub/lifecycle.ts +10 -5
  61. package/src/hub/memory-code-head.ts +44 -0
  62. package/src/hub/memory-code-overlay.ts +119 -0
  63. package/src/hub/memory-plan-guard.ts +10 -1
  64. package/src/hub/memory-stale-anchors.ts +159 -0
  65. package/src/hub/memory-symbols.ts +74 -0
  66. package/src/hub/planning.ts +10 -4
  67. package/src/hub/read-tools.ts +21 -3
  68. package/src/hub/sdlc.ts +34 -0
  69. package/src/hub/settings.ts +17 -2
  70. package/src/hub/settled-roles.ts +15 -3
  71. package/src/hub/task-sweep.ts +25 -3
  72. package/src/hub/tasks.ts +353 -64
  73. package/src/hub/token-window.ts +15 -2
  74. package/src/hub/window-kind.ts +13 -0
  75. package/src/local/tools.ts +5 -5
  76. package/src/memory/brief.ts +22 -12
  77. package/src/memory/capture.ts +26 -40
  78. package/src/memory/client.ts +137 -65
  79. package/src/memory/recall.ts +13 -22
  80. package/src/ui/index.html +100 -18
package/docs/memory.md CHANGED
@@ -1,12 +1,12 @@
1
1
  # Hub memory integration
2
2
 
3
- Agent-hub consumes the exact-pinned first-party [`@staix/agent-mem`](https://github.com/STAIxBWLB/agent-mem) library (current pin `0.4.0`, initial pin `0.1.1`). The package owns portable storage, tiers, graph, goals, sync, import and raw commands. Agent-hub owns caller authority, current project privacy policy, task lifecycle integration and presentation. Optional claude-mem remains a separate capture and session-recall source.
3
+ Agent-hub consumes the exact-pinned first-party [`@staix/agent-mem`](https://github.com/STAIxBWLB/agent-mem) library (current pin `0.7.0`, initial pin `0.1.1`). The package owns portable storage, tiers, graph, goals, sync, import and raw commands. Agent-hub owns caller authority, current project privacy policy, task lifecycle integration and presentation. Claude-mem is an optional, explicit import source only; runtime recall and capture make no worker HTTP requests.
4
4
 
5
5
  ## Portable core and data directory
6
6
 
7
7
  The [agent-mem repository](https://github.com/STAIxBWLB/agent-mem) is the reference for the data model, immutable entry IDs, append-only segments, derived SQLite index and rebuild equality, home selection, import cursors, dedupe and machine sync. Shared v1 remains the default and needs no migration: schema `agent-hub.memory/v1`, canonical hashes and log/machine/lock paths remain compatible. `memoryHome()` comes from the package and selects `AMEM_HOME`, then `$AGENTHUB_HOME/memory`, then `~/.agenthub/memory`; an explicit `StoreOptions.home` keeps its parent-directory meaning. The test preload (`test/hub-home.ts`) clears inherited `AMEM_HOME` and supplies an isolated `AGENTHUB_HOME` when none is provided.
8
8
 
9
- `src/memory/` contains hub integration only: briefs, recall, the claude-mem worker client and capture. `src/hub/memory-policy.ts` supplies `withoutPii`, `promotionScreen` and `mentionsPrivatePath`; `src/hub/read-tools.ts` supplies screened peer reads. Deny, hardened git, realPath and process identity come from the package's Node-safe `@staix/agent-mem/platform` entry and are re-exported through the existing local/hub/Pi modules. No core implementation is vendored in agent-hub. The `0.1.1` platform subpath is required because `0.1.0` exposes only a root entry that loads `bun:sqlite`, which Node-hosted Pi extensions cannot import. This is a packaging compatibility export; the platform algorithms are unchanged.
9
+ `src/memory/` contains hub integration only: briefs, recall, the in-process amem client and deterministic capture. `src/hub/memory-policy.ts` supplies `withoutPii`, `promotionScreen` and `mentionsPrivatePath`; `src/hub/read-tools.ts` supplies screened peer reads. Deny, hardened git, realPath and process identity come from the package's Node-safe `@staix/agent-mem/platform` entry and are re-exported through the existing local/hub/Pi modules. No core implementation is vendored in agent-hub. The `0.1.1` platform subpath is required because `0.1.0` exposes only a root entry that loads `bun:sqlite`, which Node-hosted Pi extensions cannot import. This is a packaging compatibility export; the platform algorithms are unchanged.
10
10
 
11
11
  ## Selected memory profile
12
12
 
@@ -20,7 +20,7 @@ Person-facing status and memory output show the profile and nonzero malformed-ro
20
20
 
21
21
  A running daemon keeps its original binding. Explicit checks at hub operation entry and before sidecar writes fence later operations when the selector changes or becomes unavailable; they do not switch stores in place. These checks are not atomic with an in-flight core operation: changing selection during such an operation is outside the supported cutover contract. Before migration or a selector change, quiesce every consumer of this memory home and close its handles; reopen through the supported session-preserving lifecycle afterward. A public core pre-write hook is the upgrade path for atomic in-flight cutover. Current policy screening, PII-turn refusal, capture exclusion and fail-open task delivery remain in force.
22
22
 
23
- Candidate/counter data stays at `M/candidates.db`, independently of the selected note profile, using the package's explicit sidecar home convention. It is not copied into `G`, migrated or synced. The hub fences sidecar producers with the note-profile binding. Standalone `amem review` still refuses a selected generation; this integration does not add generation candidate acceptance or dashboard acceptance.
23
+ Candidate/counter data stays at `M/candidates.db`, independently of the selected note profile, using the package's explicit sidecar home convention. It is not copied into `G`, migrated or synced. The hub fences sidecar producers with the note-profile binding. Since agent-mem 0.7.0, `amem review` lists and accepts candidates on a selected generation too, writing accepted notes into that generation; dashboard acceptance is still not added.
24
24
 
25
25
  Source and synthetic fixture checks do not authorize migration of the person's memory home. Before real migration, every hub sharing the memory home must run 0.12.30 or later. Real rollout still needs separately approved installation, matching live package/root/profile/selector bindings, synthetic cross-write and prune readbacks, and a supported quiesce/close/reopen sequence before separately confirmed real migration. Every running hub using this home needs an authenticated receipt: a matching ready binding, or a generation-capable unbound receipt (`disabled` when native memory is off, `unavailable` when opening failed) proving it holds no memory handle. An unbound daemon and zero running hubs are quiesced for memory; a stale bound handle, unreadable registry or unknown runtime is not ready. Real account/device sync, native capture and live memory receipts remain distinct acceptance legs. See [agent-mem migration and prune](https://github.com/STAIxBWLB/agent-mem#generation-migration-and-exact-entry-prune) for the person CLI and recovery limits.
26
26
 
@@ -95,12 +95,11 @@ A running hub syncs 5 seconds after start and every `memory.sync_interval_min` m
95
95
 
96
96
  - **Task briefs**: when a task is handed to an owner, its brief lists hub memory first (entries one or two hops from
97
97
  the task's paths, symbols, issue and task: a linked fail first, then nearer, then newer; superseded entries dropped;
98
- other projects' entries only when shared), then claude-mem's, `memory.brief_items` lines in all. A peer is never
98
+ other projects' entries only when shared), then related amem curated notes, `memory.brief_items` lines in all. A peer is never
99
99
  shown the same entry twice in one hub run.
100
- - **Session-start recall**: this project's long-term decisions, contracts and fails (and shared ones) lead the recall block; display-only filtering omits automatic approval notes, while changes-requested notes show their nonempty reasons. Lines preserve their headers and end at word boundaries with a visible marker when capped; each delivery deduplicates memory ids across prefaces and note lines. Stored notes and brief evidence are unchanged. Claude-mem's recent work follows; each gets half of `memory.inject_tokens` when both answer.
100
+ - **Session-start recall**: this project's long-term decisions, contracts and fails (and shared ones) lead the recall block; display-only filtering omits automatic approval notes, while changes-requested notes show their nonempty reasons. Lines preserve their headers and end at word boundaries with a visible marker when capped; each delivery deduplicates memory ids across prefaces and note lines. Stored notes and brief evidence are unchanged. Recent short-term amem curated notes follow; each gets half of `memory.inject_tokens` when both answer.
101
101
  - **`ahub ask`**: entries that match the question and the graph neighbourhood of what they are about are evidence, cited
102
- as `[mem:<8 hex>]`. A question that carries PII skips them. `ahub ask --remember` still saves only to claude-mem, so a
103
- model's answer never becomes hub memory.
102
+ as `[mem:<8 hex>]` for existing native projections or `[mem:<32 hex>]` for full entry references. A question that carries PII skips them. `ahub ask --remember` writes a pending candidate, never curated evidence; a person must review it.
104
103
  - **`ahub memory`** needs no running hub. The hub CLI identity gate allows screened list/search/show reads in agent shells; graph and unscreened views are a person's. Managed peers use the bounded `hub_memory_search`, `hub_memory_show` and `hub_memory_graph` tools through the daemon, with project scope, `memory_read`, PII-turn refusal and capture exclusion:
105
104
 
106
105
  ```bash
@@ -146,8 +145,8 @@ The package owns deterministic import identity, text-only graph extraction, curs
146
145
 
147
146
  Whether hub memory helps is measured, not assumed (#258 AC11, with #248):
148
147
 
149
- - Each task brief writes a `memory_brief` event with counts only (hub-memory items, fails among them, claude-mem
150
- items); research records keep `briefs` (`delivered`, `withFail`), and `ahub research` splits approved tasks into
148
+ - Each task brief writes a `memory_brief` event with counts only (hub-memory items, fails among them, supplemental
149
+ items in the legacy `claudeMem` count field; new events identify `source: "amem"`); research records keep `briefs` (`delivered`, `withFail`), and `ahub research` splits approved tasks into
151
150
  "a fail note in its brief", "brief without a fail note" and "no brief recorded" (first pass, rework, check failures).
152
151
  - `ahub bench run --memory off|claude-mem|native|both` declares an arm's memory and refuses a hub that runs with other
153
152
  settings; compare the arms with `ahub bench compare`.
@@ -183,6 +182,75 @@ Agents propose a correction with `hub_memory_correct {id, text, reason}`. The ta
183
182
 
184
183
  A `changes_requested` review with unmet items can create one fail candidate. Tasks screens the public task title, unmet items and review evidence before the sidecar write; private tasks, private turns, withheld text and failed or unknown screens produce no candidate. Cause and retry conditions are explicitly unknown unless a person supplies them when reviewing.
185
184
 
186
- Candidates and rendered/delivered/cited counters live only in agent-mem's machine-local `M/candidates.db` (package retention: 500 candidates/project for 30 days, 10,000 counters/project for 90 days). They are never imported or synced and are not evidence for recall, briefs, ask or graph. For shared v1, a person reviews them with `amem review` and accepts or rejects through that CLI's TTY and policy checks; generation review remains unsupported and refused. Dashboard acceptance is a later step. Accepted results are ordinary v1 notes under the package's acceptance contract; the hub tool has no acceptance authority.
185
+ Candidates and rendered/delivered/cited counters live only in agent-mem's machine-local `M/candidates.db` (package retention: 500 candidates/project for 30 days, 10,000 counters/project for 90 days). They are never imported or synced and are not evidence for recall, briefs, ask or graph. A person reviews them with `amem review` and accepts or rejects through that CLI's TTY and policy checks, on shared v1 and, since agent-mem 0.7.0, on a selected generation. Dashboard acceptance is a later step. Accepted results are ordinary v1 notes under the package's acceptance contract; the hub tool has no acceptance authority.
187
186
 
188
187
  Rendered observations use the final journal payload, including memory lines in prefaces and briefs, with the real delivery id. Delivered observations require the journal's first positive transport acknowledgement (or the pull path's completed read receipt). Failed or uncertain handoffs do not establish delivery; journal-less transports leave it unknown. Citation observations resolve `mem:<id8+>` in a peer message or task completion summary against screened project-visible entries. Citations use explicit `message:<envelope-id>` or `summary:<task>:<history-index>` observation keys when the source delivery is not proven; their rendered/delivered values remain unknown. They do not establish a per-delivery causal ratio. Duplicate observations are idempotent per entry/context/observation, not cumulative usage claims. Unavailable task, turn or session identity and unobserved metrics remain unknown, never zero. No observation is written for a private envelope, PII task or PII turn. These are delivery and citation proxies, not proof that an agent understood or used a note correctly.
188
+
189
+ ## In-process recall and capture (agent-mem#13 P4)
190
+
191
+ The exact 0.6.0 pin is the first published package with P3. It was checked directly against the cache-busting npm registry read: gitHead `2a182f2f23b7744a746b6854f9fb920df627a99f`, published 2026-10-23. The hub uses the canonical, alias-resolved project key and the selected profile; caller-written legacy project-chain labels never select a different store. `memory.worker_url` is retained only as ignored legacy configuration, not a network destination. `ahub doctor` reports the library rather than probing a worker.
192
+
193
+ Changes from the worker integration are deliberate:
194
+
195
+ - Task briefs and ask use curated amem entries with authentic `mem:` identifiers. Raw capture is not evidence. The existing native graph-neighbourhood/fails-first path is retained.
196
+ - Recent recall supplements long-term notes with short-term curated notes. It does not recreate claude-mem's model-produced observation summaries. Those historical rows require the explicit person-run import.
197
+ - Local capture deterministically appends SessionStart, tool events, Stop excerpts and SessionEnd. Host session/turn/step/invocation identity distinguishes actual executions even when a provider reuses its tool-call id; a replay of the same observation dedupes. There is no model summarizer, worker, watcher or network call. Published CaptureStore owns bounded excerpts, cap/retention, contention spool and replay. The bound handle is reused; effective R6 config is checked before each append and a change reopens it before writing. Refusal and daemon shutdown close it.
198
+ - The host excludes private turns, PII tasks, private paths, memory/tool bookkeeping and secrets before capture. It screens complete input/output before truncation. Whole-row privacy screening also precedes read selection and rendering.
199
+ - Native task notes are written once. Auxiliary checkpoint and model-answer writes are candidates, not another curated copy; their submitted metadata is retained in the candidate text and their note kind is preserved. The complete candidate must fit the package's 4000-character limit, or the save is unavailable. Model answers are excluded from every curated read projection even if an old import contains one.
200
+ - Supplemental reads use bounded exact-project pages with explicit coverage; see "P4 supplemental read and capture bounds" below. Published SearchIndex supplies ranking after screening, with a term-wise labelled fallback when FTS5 is absent.
201
+
202
+ The integration test uses actual temporary stores with fetch denied and verifies recall, brief and ask without a worker, local-only capture, replay dedupe, privacy before excerpts, project binding and candidate/evidence separation. Older fixture transport tests use a host-only injected FakeMemoryClient; no URL or fixture registry is accepted from production configuration.
203
+
204
+ Historical benchmark memory-arm labels are retained for old records. Their `claude-mem`/`both` names are not evidence of a live worker on this runtime; new comparisons require a new declared condition and an amem-aware benchmark plan. No benchmark collection was run for this integration.
205
+
206
+
207
+ Write screening examines both the JSON representation and literal structured keys/values before storage escaping. Authoritative tool input/output JSON is decoded once, from the complete original output before excerpt bounding; arbitrary nested JSON-as-text or other encodings are not interpreted. Host locators such as cwd are not capture content and are not persisted: treating the project root as content would falsely classify every event as mentioning every private subtree. Persisted capture fields and complete tool data remain screened, and complete custom deny paths are checked before token fallback. The test-only old-HTTP projection retains its old 4000-character response cap; production capture delegates its bound to CaptureStore after policy.
208
+
209
+
210
+ ## P4 supplemental read and capture bounds
211
+
212
+ Session-start supplemental recall is one screened short-term curated block from every author, including the receiving peer. No adapter platform whitelist or "agent-hub" pseudo-source remains. The supplemental view uses the published `readPage` keyset API: at most 16 pages of 256 exact-project candidate bodies, plus one lookahead. It never calls the materializing `list()` path. Newest project candidates are considered first; filtering private or non-short rows may leave fewer displayed rows. A remaining page produces `memory view truncated: newest 4096 project candidates considered` in recall, search and task briefs, including when all considered rows are withheld. The control status `memoryRead` reports the last read's `truncated`, `scanned` live candidate bodies and `limit`; it is not a total corpus count.
213
+
214
+ This supplemental path deliberately excludes other projects' shared rows, which could previously crowd out the project's own short-term notes. Native shared long-term recall, graph evidence and peer read tools retain their existing scope. The package still folds relation metadata during a body page; tier/scope-aware core pages and incremental metadata snapshots are the upgrade path for that remaining cost. No total-cost bound on the core refresh is claimed.
215
+
216
+ Capture reuses one store handle per bound project/profile home and closes it on a binding error and daemon shutdown. Effective published R6 config is checked before each append; changed cap or retention reopens the handle before that append, so person settings retain next-invocation behavior. Every effect still rechecks the current binding and screens complete input/output first. Persisted path metadata contains only project-relative read/write/edit paths; bash commands, git argv and outside-root paths are omitted from that column. Legacy library callers without a host invocation id still deduplicate a native id within a summary interval; live LocalPeer supplies a separate session/turn/step/invocation id. Other hosts must supply that port before claiming cross-turn identity.
217
+
218
+ ## Canonical symbol anchors (#684 phase 3)
219
+
220
+ The hub normalizes function signatures with its shared symbol normalizer and uses the existing amem `symbol:<qualified>@<path>` node format. Code-backed completion anchors require explicit file identity; an unknown or name-only resolution never invents a file. Existing non-completion notes retain normalized plan-intent associations, which are not evidence that a symbol exists or changed. Completion notes replace those guesses with verified actual symbols, or none when observations are unknown.
221
+
222
+ At an admitted completion, only the current head's screened host observations are candidates. The optional code-facts `exists` check must positively confirm an unambiguous existing symbol before its node enters the new completion note. Private tasks make no symbol queries and write no memory. Unknown observations write no symbol anchors and the completion note labels their coverage unknown. At most 16 candidates are considered; new checks stop after a shared two-second scheduling budget. An already-started call retains the code-facts boundary's own bounded execution and cleanup, so this is not a strict two-second wall-clock guarantee. Missing evidence never refuses completion.
223
+
224
+ Earlier notes stay immutable. The completion note and earlier notes share the task's reset-safe node. `related(project, [symbol], limit)` reaches the earlier notes at memory hop two; literal graph edges are symbol -> completion note -> task -> earlier note. The pinned package's `graph(..., 2)` stops at the task node. No overlay, code structure, copied note, supersede operation or new storage schema is introduced.
225
+
226
+ ponytail: the symbol -> task bridge expires with the existing short-term completion finding; afterward a long-term note is found by symbol only through its own `about`. The upgrade path is phase 4 (#367), deriving edges from recorded changed symbols at read time without storing them.
227
+
228
+ Qualified-name misses from the current ripwire CLI remain unknown: version 0.6.7 can report `Parser.run` absent while observing `run` in `Parser`. Plain-name negative evidence retains its existing meaning. The shared code-facts boundary can lift this ceiling once a CLI/schema attests qualified lookup.
229
+
230
+ `ahub doctor --stale-anchors [--json]` is an optional read-only inspection of symbol anchors against the current head. It reports bounded coverage and unknown evidence explicitly. A positive absence establishes stale status; unavailable, private or ambiguous evidence does not. It never deletes, edits, promotes or repairs a note. The default doctor checks are unchanged.
231
+
232
+ Inspection considers at most 256 exact-project notes and starts at most 16 code-facts checks, with explicit unknown/truncated coverage. A task-linked anchor also requires a current, public, reset-safe task record in this repository; historical public text cannot override a now-private, foreign or missing task record. A missing/uninitialized selected profile or project shard remains unknown and is not created. Existing core reads may maintain derived indexes and reader leases, but the report changes no authoritative note or candidate record.
233
+
234
+ ## Symbol preparation and historical query compatibility (#684 follow-ups)
235
+
236
+ A filled symbol-preparation key (execution root, head and merge base) is reused between the prepare-only and completion passes. Within one pass, identical name lookups share known or unknown results even when several candidate file paths use that name. Completion still rechecks current privacy, repository and head provenance; cached advice never grants authority.
237
+
238
+ Plan-guard and related-memory queries include both an explicit raw symbol input and its normalized form, so an older `symbol:foo(a: string)@x.ts` association remains reachable alongside `symbol:foo@x.ts`. This is query compatibility only; existing notes and their IDs are never rewritten.
239
+
240
+ The stale report leaves unsupported `#`, `->` and `:` lookup spellings unknown. A plain name not found by the index is labelled "absent from indexed definitions", not proof that an imported package API or type was removed. The JSON state/reason fields retain their existing meaning, with an additive label only on final validated absence rows.
241
+
242
+ Code-facts queries can register transient detached Git worktrees. Interrupted cleanup, including Ctrl-C, can leave a prunable registration. Inspection does not auto-prune: inspect `git worktree list --porcelain` and clean only a confirmed abandoned entry. A repeated owned cleanup failure returns neutral unknown coverage; borrowed handles remain caller-owned. Note and candidate bytes are unchanged by inspection.
243
+
244
+ ## Read-time code neighbours (#684 phase 4)
245
+
246
+ Select a symbol in the dashboard Memory view and enable Code neighbours to inspect one hop of callers and callees. Dashed `calls` edges are computed through the optional code-facts boundary at the displayed full commit SHA; they are never stored in amem. Attached notes are highlighted, fails first. Turning the toggle off returns the stored graph. An ambiguous seed or name-only relation is withheld; unavailable or partial evidence is explicitly unknown.
247
+
248
+ `hub_memory_graph` also prepares this overlay for symbol starts. Stored and derived nodes share its existing maximum 120 nodes, 240 edges and 6144-byte serialized response budget (default 60 nodes). Omitted nodes/edges contribute to truncation. Authored note associations remain ordinary `about` or `rules_out` edges; only code calls carry the derived SHA stamp.
249
+
250
+ A preparation starts at most four canonical, path-qualified seeds and no new query after two seconds. It retains at most 32 distinct neighbours, 64 call edges and 16 screened attached notes. In-flight work retains the existing code-facts timeout and cleanup bounds. The file:name selector must attest exactly one definition; ambiguous/name-only seeds are unknown even when their relation lists are empty. Queries run outside the synchronous dashboard snapshot Git budget, and current HEAD, profile, policy and caller/task privacy are rechecked across awaits. PII tasks and turns do not query code facts.
251
+
252
+ Task briefs merge direct and neighbour-attached fail/decision notes first, deduplicate IDs and retain the existing combined `brief_items` cap and line excerpts. The cap is snapshotted once before selection; a reload applies to the next brief. The console task detail's related-note line counts this bounded brief selection, not all notes in the store; unknown code coverage remains unknown. This does not persist a code index, extend completion-note TTLs, or change amem's graph API.
253
+
254
+ Code-neighbour reads are optional work: review and completion queries take priority, interrupt an in-flight optional CLI and wait for its owned cleanup before starting. Optional unknown results are retained for five seconds; known topology is cached for thirty seconds, with a shared five-second minimum between derive starts. Current attached notes are refreshed and screened at publication, so this cache does not retain a stale note selection. Direct path/title/symbol advice is independent of code eligibility or availability and keeps its existing fail-open behaviour.
255
+
256
+ Only an open, visible Memory view requests dashboard code work. Console task detail uses a cache-only overlay lookup and returns unknown on a cold cache, without starting a code query. A derive makes one hardened Git HEAD probe; cached publication checks bounded Git-owned HEAD/ref files instead of spawning Git on every poll. Unsupported ref layouts or packed-ref inventories above 1 MiB remain unknown. Dashboard note filters apply to neighbour notes as well, and selecting a non-symbol resets the toggle. Stored symbol nodes retain their authored identity when derived edges are added.
@@ -1285,7 +1285,7 @@ ahub task label 12 reverted # a later verdict (ok, regressed, reverted, i
1285
1285
 
1286
1286
  ## Hub memory
1287
1287
 
1288
- The hub opens the selected memory profile under `memoryHome()` (default `~/.agenthub/memory/`) beside claude-mem ([how it works](memory.md)): notes saved
1288
+ The hub opens the selected amem memory profile under `memoryHome()` (default `~/.agenthub/memory/`), with no claude-mem worker dependency ([how it works](memory.md)): notes saved
1289
1289
  against an open task are short-term; when the task is approved its decisions, contracts and fails become long-term.
1290
1290
  Task briefs, session-start recall and `ahub ask` read it first, and the dashboard has a Memory section.
1291
1291
 
@@ -1298,9 +1298,9 @@ ahub memory forget 1a2b3c4d # a person's; also promote, demote, sup
1298
1298
 
1299
1299
  `"memory": { "native": false }` in `.agenthub/config.json` turns it off for a project.
1300
1300
 
1301
- The exact-pinned `@staix/agent-mem` 0.4.0 resolves `AMEM_HOME`, then `$AGENTHUB_HOME/memory`, then `~/.agenthub/memory`. An absent `M/profile.json` selects shared v1; an explicit generation selects `M/generations/G` with the package's readonly legacy bridge. All daemon memory consumers and the hub memory/goal CLI use that selected profile. Invalid metadata or a missing selected generation makes native memory unavailable while the bus and board continue; there is no fallback to legacy writes. Inspect the daemon's memory binding/availability before interpreting missing memory as an empty store.
1301
+ The exact-pinned `@staix/agent-mem` 0.7.0 resolves `AMEM_HOME`, then `$AGENTHUB_HOME/memory`, then `~/.agenthub/memory`. An absent `M/profile.json` selects shared v1; an explicit generation selects `M/generations/G` with the package's readonly legacy bridge. All daemon memory consumers and the hub memory/goal CLI use that selected profile. Invalid metadata or a missing selected generation makes native memory unavailable while the bus and board continue; there is no fallback to legacy writes. Inspect the daemon's memory binding/availability before interpreting missing memory as an empty store.
1302
1302
 
1303
- The daemon checks its selector at hub operation entry and before candidate/counter writes. It does not hot-switch handles or provide an atomic fence against selection changes during an in-flight core call. Quiesce consumers and close their handles before migration or selection changes; reopen through the supported session-preserving lifecycle afterward. Candidate/counter storage remains `M/candidates.db`, independent of `G`; `amem review` on a generation is still refused. Every running hub using this home needs an authenticated receipt: a matching ready binding, or a generation-capable unbound receipt (`disabled` when native memory is off, `unavailable` when opening failed) proving it holds no memory handle. An unbound daemon and zero running hubs are quiesced for memory; a stale bound handle, unreadable registry or unknown runtime is not ready. Existing JSON memory/goal results remain unchanged; the additive binding receipt belongs to daemon status, profile-check and the decay object. This source integration does not authorize installing a new runtime, migrating real memory, restarting sessions or claiming real-machine sync/capture receipts. Migration/deactivation and their limits belong to [agent-mem](https://github.com/STAIxBWLB/agent-mem); see [the hub profile contract](memory.md#selected-memory-profile).
1303
+ The daemon checks its selector at hub operation entry and before candidate/counter writes. It does not hot-switch handles or provide an atomic fence against selection changes during an in-flight core call. Quiesce consumers and close their handles before migration or selection changes; reopen through the supported session-preserving lifecycle afterward. Candidate/counter storage remains `M/candidates.db`, independent of `G`; since agent-mem 0.7.0 `amem review` also lists and accepts candidates on a selected generation. Every running hub using this home needs an authenticated receipt: a matching ready binding, or a generation-capable unbound receipt (`disabled` when native memory is off, `unavailable` when opening failed) proving it holds no memory handle. An unbound daemon and zero running hubs are quiesced for memory; a stale bound handle, unreadable registry or unknown runtime is not ready. Existing JSON memory/goal results remain unchanged; the additive binding receipt belongs to daemon status, profile-check and the decay object. This source integration does not authorize installing a new runtime, migrating real memory, restarting sessions or claiming real-machine sync/capture receipts. Migration/deactivation and their limits belong to [agent-mem](https://github.com/STAIxBWLB/agent-mem); see [the hub profile contract](memory.md#selected-memory-profile).
1304
1304
 
1305
1305
  Run `ahub memory profile-check [--json]` from the person's terminal for the read-only preflight. It identifies live registry owners by signed process identity and authenticated daemon replies, then reports their loaded memory package and bound home/profile. The receipt names the current `AGENTHUB_HOME` registry scope; zero results establish no live daemon in that inventory, not system-wide process absence. Inventory and check separate custom registries too when they share `AMEM_HOME`. A missing or unreadable runtime receipt never counts as a different home or permission to migrate.
1306
1306
 
@@ -1310,7 +1310,7 @@ A curated split (`amem migrate --shards`) writes `M/shards.json`; from 0.12.37 t
1310
1310
 
1311
1311
  Managed agents read through `hub_memory_search`, `hub_memory_show` and `hub_memory_graph`, with the server's project identity, the `memory_read` capability and current privacy screening. They cannot supply another project; private turns are refused. The tools bound UTF-8 input/output, screen before counts and truncation, and exclude their arguments/results from hub-managed capture. `hub_memory_correct` has its own `memory_correct` capability and records a screened correction candidate, not a note change. See [the complete memory contract](memory.md) for caps and refusal semantics.
1312
1312
 
1313
- Review candidates and rendered/delivered/cited counters stay in machine-local `M/candidates.db`, outside sync, import, recall, briefs, ask and graph evidence. Public changes-requested reviews with unmet items may propose a fail candidate; missing cause/retry details stay unknown. Shared-v1 acceptance is a person's `amem review` action with its TTY/policy checks; selected generations still refuse that command. Read-time `(stale?)` hints and plan-fail advice never change stored rows or task authority. Structured fail notes are validated against 4000 UTF-8 bytes before screening or writes, with no silent truncation.
1313
+ Review candidates and rendered/delivered/cited counters stay in machine-local `M/candidates.db`, outside sync, import, recall, briefs, ask and graph evidence. Public changes-requested reviews with unmet items may propose a fail candidate; missing cause/retry details stay unknown. Acceptance is a person's `amem review` action with its TTY/policy checks, on shared v1 and, since agent-mem 0.7.0, on a selected generation. Read-time `(stale?)` hints and plan-fail advice never change stored rows or task authority. Structured fail notes are validated against 4000 UTF-8 bytes before screening or writes, with no silent truncation.
1314
1314
 
1315
1315
  Recall presentation omits generated task-approval outcome notes and shows only nonempty reasons from generated changes-requested notes; an ordinary decision that merely resembles a header stays visible. Contracts and fails remain eligible. Capped recall lines preserve memory headers and end at word boundaries, and one delivery deduplicates ids across restored/current prefaces and shared note lines. Stored notes and brief evidence are unchanged; platform recall retains its budget split.
1316
1316
 
@@ -1606,20 +1606,24 @@ stays the default until an evaluation says otherwise (`docs/cooperbench.md`).
1606
1606
 
1607
1607
  ## Always-ask action policy foundation (#636)
1608
1608
 
1609
- The classifier, local configuration and session budget counter decide; since slice 2 the Claude PreToolUse hook and ACP `session/request_permission` enforce them. Codex, Jcode, Pi and the local worker are not enforced yet (slice 3, #313): their `ahub status` rows still say `always-ask not enforceable: adapter integration pending`. A headless Claude implementer (#525-4) runs the same hook check (its facts hook returns `ask` for a classed call), but its status row also still says `adapter integration pending`. A default-on policy adds a pre-tool check only where the hub sees the call before it runs, and each peer's status row says whether it does.
1609
+ The classifier, local configuration and session budget counter decide. The managed Claude PreToolUse hook (including the headless Claude implementer, #525-4), ACP permission requests with a host gate, received Codex command approval requests, and the hub-owned local/Pi tool boundary consume the same policy. Jcode still has no hub pre-tool approval path and remains listed as not enforceable. A default-on policy adds a pre-tool check only where the hub sees the call before it runs, and each peer's status row says whether it does.
1610
1610
 
1611
1611
  What is enforced, and where it is not:
1612
1612
 
1613
1613
  | Peer | Enforced | Not enforceable (the status row says why) |
1614
1614
  | --- | --- | --- |
1615
1615
  | Claude (`ahub claude`) | the managed PreToolUse hook returns `ask` for a classed call in `ask`, `ask-when-needed` and `never-ask` alike, and `ask` beats every allow the hook would add | a session with no hub permission hook, or one whose attached session has not yet run it (the launch record outlives the process, so it proves nothing alone: the status row says `no live Claude session has run the hub permission hook yet`); an `--unattended` launch (`--dangerously-skip-permissions` skips every permission check, hooks included); a hub that cannot answer the hook leaves Claude's own policy |
1616
- | Kimi and other hub-owned ACP peers (headless) | `session/request_permission` for a classed call is never auto-approved (not even for a hub-owned tool title), carries `Action approval (<classes>): <command>`, withholds `allow_always`, and under `ahub --unattended` still waits for a person and expires as a denial | a peer in its native `ask-when-needed` (`yolo`) or `never-ask` (`auto`) mode: Kimi sends no request for a call that mode allows, so the hub cannot see it; the status row says `its native mode never-ask sends no request for a call it allows`. Switch the peer to `ask` to enforce. A request whose payload the agent did not report is not classed (unknown follows the mode) |
1616
+ | Kimi and other hub-owned ACP peers with a host approval gate (headless) | `session/request_permission` for a classed call is never auto-approved (not even for a hub-owned tool title), carries `Action approval (<classes>): <command>`, withholds `allow_always`, and under `ahub --unattended` still waits for a person and expires as a denial | a peer in its native `ask-when-needed` (`yolo`) or `never-ask` (`auto`) mode: Kimi sends no request for a call that mode allows, so the hub cannot see it; the status row says `its native mode never-ask sends no request for a call it allows`. Switch the peer to `ask` to enforce. A request whose payload the agent did not report is not classed (unknown follows the mode) |
1617
+ | Claude headless implementer | the managed hook asks for classed calls, and the current-child-bound prompt tool accepts only a person's allow-once or denial; ask and ask-when-needed retain their native requirements | an idle/offline implementer without a current child; never-ask and unattended are refused at startup |
1618
+ | Codex app-server | received command approval RPCs (v2 command execution or legacy exec) are classed before reaching the TUI; a classed request offers only allow-once/deny and an explicit bounded class-budget choice | status conservatively says not enforceable: native policy can execute without emitting an approval request; never-ask sends approvalPolicy never. writeStdin, absent/unknown payloads, file-change and permission-profile requests retain native handling |
1619
+ | Local worker and Pi | every model tool call is classed at the host permit boundary before never-ask, ask-when-needed or Pi's tool-wide grant; unattended never answers a classed request. Pi replayed call ids return the stored result without spending again | an offline local worker or Pi without a verified live bridge; Pi's verified person-authored idle shell remains the person's command, not a model tool call |
1620
+ | Jcode | no hub pre-tool approval path | always-ask not enforceable; its unmanaged native policy remains authoritative |
1617
1621
 
1618
- Class budgets: the approval of a classed ACP call offers one extra choice, `Allow <class> xN for this session`, when the class budget is above one. Only that choice (never an ordinary allow) spends the ledger: it allows this call and the next N-1 of the same classes in the same native session, then asks again; several requests open at once share it, so a budget of N never lets more than N approved calls through. A new native session, a changed policy or a stopped, crashed or restarted peer starts from zero, even when it resumes the same session id. A budget of one is the single call the ordinary allow already covers. Claude's own permission dialog cannot carry that choice, so every classed Claude call asks.
1622
+ Class budgets: the approval of a classed ACP, received Codex command, local or Pi call offers one extra choice, `Allow <class> xN for this session`, when the class budget is above one. Only that choice (never an ordinary allow) spends the ledger: it allows this call and the next N-1 of the same classes in the same native session, then asks again; several requests open at once share it, so a budget of N never lets more than N approved calls through. A new native session, a changed policy or a stopped, crashed or restarted peer starts from zero, even when it resumes the same session id. A budget of one is the single call the ordinary allow already covers. Claude's own permission dialog cannot carry that choice, so every classed Claude call asks. Local/Pi show the complete tool title plus its class on the person's console, including private turns. Other surfaces retain the screened public view. Any view that remains cut (`[cut, N chars]`) or withheld offers denial only, with no allow-once or class-budget choice.
1619
1623
 
1620
- Only ignored, untracked `.agenthub/config.local.json` can hold `action_policy`. Its optional keys are `enabled` (default true), `always_ask` (a distinct subset of publish, external-write, destroy, install; default all), and `budgets` (per-class integers 0..1000; defaults 1, 5, 3, 2 respectively). Shared `config.json` cannot set this policy and gets no confirmation offer. A tracked or unknown-trust local file is ignored. Use `ahub settings set action_policy.enabled <true|false>` or `ahub settings set action_policy.budgets.<class> <n>` for registered settings; change `always_ask` in the local file and use the existing confirmed terminal reload. Policy publication invalidates cached grants and pending approval challenges. Startup still reads the untracked local policy without a retained confirmation, although the Claude and ACP gates now enforce it; this ceiling stays open (#312/#313).
1624
+ Only ignored, untracked `.agenthub/config.local.json` can hold `action_policy`. Its optional keys are `enabled` (default true), `always_ask` (a distinct subset of publish, external-write, destroy, install; default all), and `budgets` (per-class integers 0..1000; defaults 1, 5, 3, 2 respectively). Shared `config.json` cannot set this policy and gets no confirmation offer. A tracked or unknown-trust local file is ignored. Use `ahub settings set action_policy.enabled <true|false>` or `ahub settings set action_policy.budgets.<class> <n>` for registered settings; change `always_ask` in the local file and use the existing confirmed terminal reload. Policy publication invalidates cached grants and pending approval challenges. Startup reads the untracked local policy without a retained confirmation. A policy that disables enforcement, omits a class or increases a class budget appends an attention notice to every status policy row. `ponytail:` this makes weakened startup policy visible, but the startup confirmation ceiling remains open; the upgrade is retained person-bound startup confirmation.
1621
1625
 
1622
- The ledger asks first and after the approved class budget is spent. Adapter integrations must request that class allowance explicitly; an ordinary one-time answer does not seed the cache, and a cached budget never suppresses a prompt required by the native mode. One approval includes its own invocation; budget zero asks for every invocation. A fresh native session resets counters, and status polls do not bind sessions. Unknown command syntax retains native permission handling. This foundation never changes a sandbox or bypasses a denied permission; per-adapter enforcement tests land with their integrations.
1626
+ The ledger asks first and after the approved class budget is spent. Adapter integrations must request that class allowance explicitly; an ordinary one-time answer does not seed the cache, and a cached budget never suppresses a prompt required by the native mode. One approval includes its own invocation; budget zero asks for every invocation. A fresh native session resets counters, and status polls do not bind sessions. A command with no classed literal prefix retains native permission handling; around a shell expansion the literal words are classed, and where the opaque part could change the class the strictest reachable class applies (#756, #762; [security](security.md)). This foundation never changes a sandbox or bypasses a denied permission; per-adapter enforcement tests land with their integrations.
1623
1627
 
1624
1628
  ## Permission modes for running peers
1625
1629
 
@@ -1839,7 +1843,7 @@ A guarded file edit that the daemon keeps at its accepted value remains visible
1839
1843
  | Switches | `task_sweep.enabled`, `.auto_reassign`, `.interval_s`, `.unaccepted_min`, `.idle_min`, `.review_min`, `.ready_min`, `.ladder_min`, `.no_writes_turns` | `config.local.json` | live; `.no_writes_turns` sets the turn threshold independently; other keys reschedule the sweep timer |
1840
1844
  | Routing | `tasks.wip_limits.<peer>` | `config.local.json` | live |
1841
1845
  | Routing | `tasks.escalate_after` (1-10, default 3: consecutive `changes_requested` before a task moves to the next `escalate_to` peer) | `config.local.json` | live; the next verdict uses it |
1842
- | Roles | `planner` (the one peer that plans) | `config.local.json` | at once |
1846
+ | Roles | `planner` (one or up to eight planner holders) | `config.local.json` | at once |
1843
1847
 
1844
1848
  - Writes go only to `.agenthub/config.local.json` and `.agenthub/routing.local.toml`,
1845
1849
  the machine-local files (`ahub init` ignores both in git). The shared
@@ -2101,6 +2105,24 @@ A budget pause remains authoritative until the budget command explicitly
2101
2105
  overrides it or the window resets. Check `ahub status` and `ahub board` after
2102
2106
  a pause or handoff.
2103
2107
 
2108
+ `ahub budget` (#285 phase 4) prints one row per window and a line for every attached peer that has no number. The KIND column says what the
2109
+ number is, and SOURCE where it came from; MEASURED is its age.
2110
+
2111
+ | Kind | Meaning | Source examples |
2112
+ | --- | --- | --- |
2113
+ | `native` | the provider's own ratio and reset | `claude status line`, `codex rateLimits`, `kimi ACP usage-limit refusal` |
2114
+ | `count` | tokens the hub counted, against a ceiling a person set | `hub count, kimi wire log, of kimi_tokens_5h 800000` (window `tokens`) |
2115
+ | `manual` | a person fed it with `ahub budget set <peer> <0..1> [--window 5h\|week] [--resets-in 2h]` | `ahub budget set` |
2116
+
2117
+ A count never claims to be the provider's quota. For one window and peer a manual value replaces the provider's at the gate, and while any native
2118
+ or manual reading is fresh the count is shown as `info only` and does not gate or feed routing headroom; when those go stale, or reset, the
2119
+ count gates again. A provider's hard refusal clears the person's number for that window, so a pause is never outvoted. A peer without a number says
2120
+ why instead: Pi and local print `no quota: on-prem ... has no provider quota` with the hub's own 5 h token count (information, never a meter);
2121
+ Kimi without `budget.kimi_tokens_5h` says it reports context occupancy only; an unreadable Kimi wire log says so with a reason code; a peer whose
2122
+ source has not reported yet says which. `--json` carries `kind`, `source`, `superseded` and a `peers` list of those notes; an older hub answers
2123
+ without them and the CLI classifies a window by its source. `ahub doctor` adds the row `kimi usage source` (counting, no ceiling, or unreadable).
2124
+ The console's peer detail (Enter on Peers) and the Usage detail, and the dashboard's Usage budgets row, show the same kind and source per window.
2125
+
2104
2126
  Kimi Code CLI 2.1.1's observed 5-hour usage-limit refusal creates a hard budget reading (`kimi ACP usage-limit refusal`). `ahub status` shows a budget pause and `ahub budget` shows its source and reset estimate. The refusal gives no reset timestamp, so the hub estimates the reset at five hours after it was observed and resumes on the next budget tick after the existing one-minute grace. With `budget.wait_max_min` below that estimate, the existing budget relay automatically hands open tasks to an attached eligible peer; routing skips Kimi while paused. No checkpoint is requested from an exhausted agent.
2105
2127
  `ahub budget resume kimi` overrides the reading so the provider can be tried early. A new provider refusal ends that override and pauses it again; a pending soft checkpoint cannot postpone this hard pause.
2106
2128
  During one hub run, an early override followed by the same window's refusal keeps its earlier future reset bound; a provider's explicit reset remains authoritative. A restored pause without a per-window reading cannot identify that earlier window, so its next refusal starts a new estimate.
@@ -2259,14 +2281,16 @@ dashboard manager does the same when its home directory vanishes.
2259
2281
 
2260
2282
  ## The planner (#455)
2261
2283
 
2262
- One peer plans: it proposes and splits tasks, grooms the backlog and gets the
2263
- backlog and sweep notices. `ahub status` prints it with its source, the
2264
- dashboard's Agents view shows a Planner badge, and the console marks its row.
2284
+ Planner holders propose and split tasks and read project-wide grooming. Each
2285
+ task's effective planner gets its backlog and sweep notices. `ahub status`
2286
+ prints the holders with their source, the dashboard's Agents view shows a
2287
+ Planner badge, and the console marks their rows.
2265
2288
 
2266
2289
  - `setting`: `planner` in `.agenthub/config.json` or `config.local.json` (the
2267
- machine's file wins). That peer is the only planner; every other peer loses
2268
- the role. It is one of `claude`, `codex`, `kimi`, `qwen` or `jcode`: the
2269
- hub-sandboxed `pi` and `local` never plan.
2290
+ machine's file wins), either one id or a list of up to eight distinct ids.
2291
+ Only those peers hold the role. Builtins are `claude`, `codex`, `kimi`, `qwen`
2292
+ and `jcode`; a declared planner-capable instance is also eligible. A singleton
2293
+ settings write stores a string for older configuration readers.
2270
2294
  - `roles`: with no `planner` key, the `roles` planners stand. That is `claude`
2271
2295
  by default.
2272
2296
  - `automatic`: when no rostered peer holds the role, the hub gives it to the
@@ -2275,9 +2299,39 @@ dashboard's Agents view shows a Planner badge, and the console marks its row.
2275
2299
 
2276
2300
  Change it with `ahub settings set planner codex`, the dashboard Settings row
2277
2301
  (a settings session, `ahub ui --settings`) or `l` in the console. Planning
2278
- authority and the backlog notices move at once. The new planner's role text
2279
- updates at its next start. `ahub settings undo` puts the previous planner
2280
- back. Agents cannot change it.
2302
+ holders and their backlog notices update at once. Use a comma list, for example
2303
+ `ahub settings set planner claude,codex`, to select several holders and follow
2304
+ any displayed holder-change confirmation. Role text updates at the next peer
2305
+ start. `ahub settings undo` restores the previous designation. Agents cannot
2306
+ change it.
2307
+
2308
+ New tasks record their planner through the same pure assignment walk shown by
2309
+ `ahub route explain`. Only that effective planner or the person changes their
2310
+ planning. A bound holder keeps authority while busy, offline or paused. If it
2311
+ is removed or leaves the roster, authority falls back to the current holders;
2312
+ the stored binding remains available for audit. Old tasks and releases with a
2313
+ null planner retain the legacy holder-wide authority. PII tasks keep null.
2314
+ The person names a different holder with
2315
+ `ahub task assign <id> <peer> --role planner`; this leaves the owner, reviewer,
2316
+ state and delivery offer intact. A planner-created release belongs to that
2317
+ planner; a person-created release keeps null.
2318
+
2319
+ A plan review is the exception: another settled holder may review a plan even
2320
+ when it is not that task's planner, without gaining its other planning powers.
2321
+ An agent author cannot self-review while another rostered non-offline planner
2322
+ exists. With no independent holder, the receipt says `selfReview: true`; it
2323
+ also records `planAuthor` (null when unknown). A person's own-plan override is
2324
+ marked self-review too. Independent requests contain ids only. Controlled
2325
+ recovery requires `task_planners` once a list/instance planner is configured or
2326
+ any task/release binding or plan-review authorship marker is stored, even after
2327
+ that configuration is removed.
2328
+
2329
+ With the lifecycle on (an `.agenthub/sdlc.toml` mode other than off), a card's planners get a "The board
2330
+ changed" notice (#758). The planner whose own write changed the board is skipped.
2331
+ A planning revision, a dependency change or a new card is a task notice; state
2332
+ moves, reviews and assignments are recorded at `fyi` weight (no turn), at most
2333
+ once per planner per ten minutes. A newer notice replaces an unsettled copy, and
2334
+ the hub's own bookkeeping tells nobody.
2281
2335
 
2282
2336
  ## Resetting a project's hub
2283
2337
 
@@ -3336,8 +3390,11 @@ unreadable result runs the main gate again and refuses release. A manual
3336
3390
  it is never accepted as full-gate evidence. A `timing_runs=20` dispatch runs only the daemon, PII screen and ACP
3337
3391
  test files twenty times on macOS, in its own concurrency group; it does not replace the full gate. Each PR has its own CI concurrency group, so PRs run side by side; within a PR a newer pending run replaces the older pending one and an admitted run is never cancelled; main pushes and dispatches keep queueing every run. Two groups admitting the same tree at once may run it twice, never a false green (#609). `scripts/check.sh --changed [<base>]` runs every non-test step but only the affected test files, and the full suite when it cannot tell; the full gate stays CI's (#610).
3338
3392
  `scripts/check.sh` stops a suite still running after `AHUB_CHECK_HANG_S` seconds
3339
- (960 by default); hosted CI sets 1080 on Linux and 1260 on macOS inside a 25-minute
3340
- job limit (#378, #593).
3393
+ (960 by default); hosted CI sets 1080 on Linux inside a 25-minute job limit and 1440
3394
+ on macOS inside a 30-minute one (#378, #593, #752).
3395
+ The `closing-refs` workflow fails a PR whose title, body or commit messages would make
3396
+ GitHub close an issue the text only mentions (`Refs #N`, a negated or qualified keyword);
3397
+ an issue linked only in the sidebar passes (#760).
3341
3398
  The gate holds `<git common dir>/ahub-heavy.lock` across every worktree of a
3342
3399
  repository. A second gate waits up to `AHUB_CHECK_LOCK_WAIT_S` (1800 seconds by
3343
3400
  default; 0 fails immediately) and names the holder. A holder whose PID is gone
@@ -3690,7 +3747,7 @@ A bundled adapter module is authorized by the person with `adapter_modules` (at
3690
3747
  # Acceptance-criteria checklist (#481)
3691
3748
 
3692
3749
  - The task reviewer/person records a head-bound `criteria` receipt with criterion ids matching the issue's AC ids, required/result fields and hashed evidence refs. This checklist belongs to the Compliance review pass. Use `ahub sdlc record <id> '<receipt JSON>'`; [schema and example](sdlc.md#acceptance-criteria-checklist).
3693
- - Deliverables (#509a, docs/sdlc.md, Deliverable records): `hub_task_done` and `hub_sdlc_record` take `deliverables: [{path, format, version?}]`; the hub hashes the files itself (a claimed hash is refused), in a plain folder with no git or PR, under the owner's execution root (its own worktree for a declared worktree instance; an unproven root refuses and reads `unknown`), and a reviewer's approval is refused with `deliverable changed since it was delivered` when a file changed since delivery (the owner calls `hub_task_done` again, or the person approves the current bytes). `ahub task show <id>` shows each file's state (`unchanged`, `changed`, `missing`, `unknown`) and `changedSinceReview`; a PII task or `pii.paths` file shows id, hash and size only.
3750
+ - Deliverables (#509a, docs/sdlc.md, Deliverable records): `hub_task_done` and `hub_sdlc_record` take `deliverables: [{path, format, version?}]`; the hub hashes the files itself (a claimed hash is refused), in a plain folder with no git or PR, under the owner's execution root (its own worktree for a declared worktree instance; an unproven root refuses and reads `unknown`), and a reviewer's approval is refused with `deliverable changed since it was delivered` when a file changed since delivery (the owner calls `hub_task_done` again, or the person approves the current bytes). `ahub task show <id>` shows each file's state (`unchanged`, `changed`, `missing`, `unknown`) and `changedSinceReview`; a PII task or `pii.paths` file shows id, hash and size only. The person lists every task's delivered and approved files with `ahub deliverables [--task <id>] [--approved] [--check] [--json]`: without `--check` it reads the board's records and opens no file; `--check` measures them now and marks a row `changed since review`, `a file is missing` or `could not be measured`. The dashboard card shows a `Deliverables n` chip and the task sheet a Deliverables group from the same records; it shows a change only after a live check. An agent's shell is refused (#317).
3694
3751
  - The review request lists the recorded criteria with their ids and results, and a `hub_review` unmet item that starts with a recorded id (`AC2: ...`) is recorded on the verdict as `unmetCriteria` (#507b); `ahub task show` lists those ids in the history. A task with no checklist, and a PII task, are unchanged.
3695
3752
  - Advise shows incomplete/stale checklist blockers; enforce checks them at approval. The `criteria` gate does not need a workflow selector. Dependencies stay with `ahub task propose --after`; checklist task import is removed.
3696
3753
  - `status <id>`, `explain <id>` and `export <id>` use the same current board/config projection. Export keeps sanitized state and source hashes, with no external text, extra authority or verification claim.
@@ -3812,8 +3869,8 @@ only after every member is approved, cancelled or moved out; it runs no tag or p
3812
3869
  The dashboard shows phase progress, blocking dependencies, dates and remaining
3813
3870
  work. Actual task dates come from execution/review/approval history, never input.
3814
3871
 
3815
- Configured planners read `hub_backlog_list` and `hub_release_list`, edit with
3816
- `hub_backlog_plan`, and undo their own latest eligible change with
3872
+ Configured planners read `hub_backlog_list` and `hub_release_list`, edit the
3873
+ cards they plan (see "The planner") with `hub_backlog_plan`, and undo their own latest eligible change with
3817
3874
  `hub_backlog_undo`. Catalog guidance or a caller-supplied role grants no authority.
3818
3875
  PII cards stay stubs, reasons are screened/withheld in public history, and planning
3819
3876
  telemetry records ids/changed fields without prose. #247 task records carry
@@ -4230,7 +4287,7 @@ From 0.12.37 reviews can rotate across fresh headless Claude and Codex reviewers
4230
4287
 
4231
4288
  Start them with `ahub claude --as claude-review`, `ahub codex --as codex-review` and `ahub kimi --as kimi-review`; stop with `ahub stop <id>`. For the Claude and Codex reviewers the command asks the running hub to start the reviewer and returns: nothing runs in that terminal, it takes no launch arguments, and `--print-command` or `--dry-run` prints a hub-start note instead of a launch (#720). Fresh Claude and Codex variants share the project checkout; the Kimi variant runs in a clean temporary working directory and reviews only the screened brief and host facts. All fresh variants refuse a custom worktree or permission mode. Codex and Kimi require `review_mode: "fresh"`; without it, an existing headless instance with the reviewer role keeps its native app-server/ACP behavior, worktree and permission options and does not join this pool. Claude retains its #223 automatic headless-reviewer declaration. The agy reviewer slot remains automatically unavailable (`agy is an implementer peer (#485)`). A failed or unsupported fresh start never downgrades to a native continuing session. Ordinary implementer instances retain their own adapters. The default logical pool order is Claude, Codex, Kimi. Only configured kinds with at least one declared reviewer count as slots, including declared ones that are offline, paused, quota-limited or failing, so availability never renumbers them; configured kinds with no declaration are listed after the walk as `no declared reviewer`. `review.rerotate` defaults to false. Pool selection starts at PR number modulo the number of declared kind slots, or task id when there is no PR, then skips the implementer's family and unavailable candidates. Delta review prefers the preceding eligible independent family in a fresh child. A preceding same-family fallback does not outrank a newly available independent family. `ahub route explain` shows the same pure walk.
4232
4289
 
4233
- Codex requires a verified `codex-cli` (0.146.0 or 0.163.0) or one the person trusts on this machine, and a passing bounded runtime receipt before its model child starts. Both verified versions passed the [2026-10-22 model-free production admission](verification/2026-10-22-codex-reviewer-isolation.md). A refusal names the installed and verified versions. Either set `codex_bin` to a verified executable, or run `ahub agents trust codex-review <version>`: trust lifts only the version pin, and every proof below still runs before each child (#691). The producer checks native binary/config stability, configured MCP inventory with only the two hub tools, effective Apps/plugins/hooks disablement, an explicit empty legacy `notify` command and a named filesystem profile. Apps needs separate feature readback because configured inventory omits runtime connectors. Web search is requested disabled; its effective turn mode is not read back and managed requirements can select Cached (see the verification ceiling). Each launch proves public-read, exact/glob/symlink private-denial, standard host credential denial and write-denial in temporary fixtures. Standard credential and configured tool-home/XDG subtrees are denied independently of project `local.deny`. Its sandboxed shell is filesystem read-only with private paths denied and network configured disabled; it is not Claude's Read/Grep/Glob-only tool whitelist. No credential home is copied.
4290
+ Codex requires a verified `codex-cli` (0.146.0, 0.163.0 or 0.164.0) or one the person trusts on this machine, and a passing bounded runtime receipt before its model child starts. 0.146.0 and 0.163.0 passed the [2026-10-22 model-free production admission](verification/2026-10-22-codex-reviewer-isolation.md) and 0.164.0 the [2026-10-24 one](verification/2026-10-24-codex-0164-reviewer-isolation.md). A refusal names the installed and verified versions. Either set `codex_bin` to a verified executable, or run `ahub agents trust codex-review <version>`: trust lifts only the version pin, and every proof below still runs before each child (#691). The producer checks native binary/config stability, configured MCP inventory with only the two hub tools, effective Apps/plugins/hooks disablement, an explicit empty legacy `notify` command and a named filesystem profile. Apps needs separate feature readback because configured inventory omits runtime connectors. Web search is requested disabled; its effective turn mode is not read back and managed requirements can select Cached (see the verification ceiling). Each launch proves public-read, exact/glob/symlink private-denial, standard host credential denial and write-denial in temporary fixtures. Standard credential and configured tool-home/XDG subtrees are denied independently of project `local.deny`. Its sandboxed shell is filesystem read-only with private paths denied and network configured disabled; it is not Claude's Read/Grep/Glob-only tool whitelist. No credential home is copied.
4234
4291
 
4235
4292
  Kimi requires a compat-admitted Kimi Code on the hub's PATH (receipt-verified 2.2.0 and 2.3.0), signed in through its managed OAuth provider, and a passing runtime preparation before each child; an unverified/untrusted version or a failed preparation is unavailable with its reason. The [2.3.0 receipt](verification/2026-10-22-kimi-qwen-compat.md) is model-free and does not verify real login/provider readiness. Each review runs a new `kimi -p` child without Seatbelt, in a private `KIMI_CODE_HOME` with managed-provider-only config, a clean temporary working directory and an empty skills directory. Its agent may call only `hub_review` and `hub_task_show`, through a task-bound loopback MCP session that never carries the hub's control token, and has no native read, file or shell tool: it reviews the screened brief and host facts only. Its `credentials/`, `oauth/` and `device_id` link to the person's native Kimi files, so it shares the native login, refresh lock and the Kimi account's budget; no token is copied. A stop revokes its verdict authority first, then waits briefly while an OAuth refresh holds the lock; a forced stop mid-refresh may require a native re-login. The Kimi backend records no measured token counts here, so its usage stays unknown; the 2.3.0 receipt makes no usage claim. This is a native tool-authority boundary, not filesystem or network isolation.
4236
4293
 
@@ -4299,17 +4356,46 @@ reattachment or TUI detachment clears it. The notice contains fixed guidance, wi
4299
4356
 
4300
4357
  The design for planner, implementer and reviewer instances per stage is in the spec section "Amendment: roles per SDLC stage (#525)". Slice 525-1 is available: role capability per kind and form, Claude channel instances as implementer or verifier in their own worktree, and `planner` refused on builtin kinds that cannot plan (pi, local and agy): an instance declaring it is refused with `this kind cannot plan`, and a `roles` entry keeps its other roles with the notice `roles.<peer>: ignored roles outside the adapter ceiling`. A configured ACP agent (`adapters`) is different: it may hold `planner` in its `roles` or in a `roles` entry and then plans like any peer, but it can never be pinned with the `planner` setting or chosen as the automatic planner, because it has no host projection (#735). Slices 525-3, 525-4 and 525-5 are available too:
4301
4358
 
4302
- - 525-3: `assign()` walks the planner holders (rotation by task id over every holder, never renumbered by availability; a named planner exact; a recorded planner kept; none for a PII task) and `ahub route explain <id>` prints that walk. Nothing records the planner yet, so the walk is information only. A peer that accepted or finished a task never reviews it, a named reviewer and the declared reviewer pool included; when no other candidate is left the person reviews.
4359
+ - 525-3: `assign()` walks the planner holders (rotation by task id over every holder, never renumbered by availability; a named planner exact; a recorded planner kept; none for a PII task) and `ahub route explain <id>` prints that walk. Task planner bindings are recorded by 525-2. A peer that accepted or finished a task never reviews it, a named reviewer and the declared reviewer pool included; when no other candidate is left the person reviews.
4303
4360
  - 525-4: a headless Claude implementer runs one owned child per turn in its declared worktree, with a separate persisted session per task (`start_mode: "headless"`, permission `ask` or `ask-when-needed`; `never-ask` refused).
4304
4361
  - 525-5: terminal Claude implementer instances own write-class tasks once the daemon has verified their execution root.
4305
4362
 
4306
- The operator steps for 525-1, 525-4 and 525-5 are in "Additional headless peer instances" above; each later slice (525-2, 525-6) adds its own here when it ships, and until then those instance rules are current. The planned changes an operator will see:
4307
-
4308
- - The `planner` setting accepts a list of peers or instance ids; each task records its own planner, and only that planner or the person runs planner tools on it.
4309
- - A plan's author does not record its own plan review while another planner is available.
4363
+ The operator steps for 525-1, 525-4 and 525-5 are in "Additional headless peer instances" above. Slice 525-2 adds the planner settings, task/release bindings, independent plan review and recovery floor described in "The planner" above. Slice 525-6 (Codex planner-only visibility) remains separate.
4310
4364
 
4311
4365
  ## Daemon responsiveness and launch connects (#719)
4312
4366
 
4313
4367
  Periodic peer-roster and delivery-supervision reads select pending journal metadata/rows, excluding completed payload history. Queue revision reads do not construct a recovery snapshot. Explicit queue/history commands retain terminal history. The pending-state index supports these reads without sorting historical payloads.
4314
4368
 
4315
4369
  Native launcher connections retry transport timeouts/unclean closes with bounded backoff for at most 15 seconds, printing `hub busy, retrying` once. The ordinary control-connect timeout remains 3 seconds. An immediately unreachable listener, authentication refusals, wire mismatch and project/instance mismatch are terminal. Every retry rereads the descriptor and preserves the invocation's delegated-audit instance pin. Usage queries retain their existing catch-up budgets and report `behind` until complete; time slicing never advances beyond the last atomically folded record.
4370
+
4371
+ ## amem runtime (agent-mem#13 P4)
4372
+
4373
+ The hub exact-pins @staix/agent-mem 0.7.0 for recall, curated evidence and local deterministic capture. No runtime call reaches a claude-mem worker; `memory.worker_url` is ignored. The person's `ahub memory import claude-mem` remains explicit and off by default. Existing profile, alias, PII and private-turn fences still apply. `ask --remember` and context checkpoints propose machine-local candidates, never automatic curated evidence. See [memory integration](memory.md#in-process-recall-and-capture-agent-mem13-p4) for changed behavior and bounded-view ceilings.
4374
+
4375
+ ## Backlog issue identity and notes
4376
+
4377
+ The card's effective planner or the person can relink an unstarted backlog card through `hub_backlog_plan`, using `fields: {sdlc: {repo: "owner/repo", issue: 525}}`. The change is one planning revision and clears the old spec acceptance. Stage, reviewed heads, checklists and other lifecycle evidence cannot be supplied through this path; `hub_sdlc_record` still refuses backlog work.
4378
+
4379
+ Omit tool `fields`, or use `{}`, to record only the bounded reason as a new revision. The person CLI retains its JSON argument: `ahub backlog plan <id> <revision> '{}' '<reason>'`. Existing planner scope, pins, frozen releases and stale-revision checks still apply. Undo restores identity members without restoring old consent; null members represent an absent previous repo or issue.
4380
+
4381
+ The grooming digest no longer calls ordered dependency phases duplicate merely because they share a design issue. It still reports path/symbol overlap, unrelated same-issue cards and references requiring attention. It uses dependency edges only, including bounded public context for approved intermediate phases.
4382
+
4383
+ ## Inspecting memory symbol anchors
4384
+
4385
+ `ahub doctor --stale-anchors [--json]` (#684 phase 3) checks a bounded set of this project's screened symbol anchors through the optional code-facts boundary. Stale means positively absent at the reported head; unavailable or ambiguous evidence stays unknown. Read the coverage/truncation fields before treating the report as complete. This command performs no note mutation or automatic repair.
4386
+
4387
+ A task completion note stores actually observed, existing symbol nodes instead of plan guesses. Earlier notes remain connected through the same reset-safe task node. Related-memory retrieval reaches those earlier notes at memory hop two; the graph's literal two-edge view shows the completion note and task node. Unknown observations are labelled and never substituted with the plan. See [canonical symbol anchors](memory.md#canonical-symbol-anchors-684-phase-3) for bounds.
4388
+
4389
+ ## Code-facts inspection cleanup and lookup coverage
4390
+
4391
+ `doctor --stale-anchors` may create transient detached Git worktrees for exact-head analysis. A Ctrl-C or interrupted cleanup can leave a prunable registration. Inspect `git worktree list --porcelain`; remove or prune only an entry confirmed abandoned, through the person's normal Git maintenance. The report performs no automatic prune. Repeated optional cleanup failure is reported as unknown rather than escaping as a second error.
4392
+
4393
+ An absence row says "absent from indexed definitions". Imported package APIs/types and unsupported host qualification are not proof of removed source. Names containing `#`, `->` or `:` remain unknown. Existing raw-signature and normalized note associations are queried together without changing stored entries. See [symbol preparation and query compatibility](memory.md#symbol-preparation-and-historical-query-compatibility-684-follow-ups).
4394
+
4395
+ ## Inspecting memory code neighbours
4396
+
4397
+ In the dashboard Memory view (#684 phase 4), select a symbol and enable Code neighbours. Dashed calls are read-time caller/callee evidence at the displayed commit SHA; solid note associations still come from authored metadata. Unknown, lower-bound and truncated coverage are advisory, never a proof of no callers. The accessible edge list carries the same SHA and direction. Toggle off to view the stored graph alone.
4398
+
4399
+ `hub_memory_graph` includes the same bounded derived context for symbol starts within its existing 6 KB output budget. Task briefs prioritize attached fails and decisions within `brief_items`; the console related-note count refers to that bounded selection. Code facts remain optional: no index is copied into memory, no graph edges are persisted and no PII task/turn starts a query.
4400
+
4401
+ Optional code work runs only while Memory is open and visible. A cold console count reports unknown immediately; it does not prepare code facts. Direct task memory remains available when optional code facts are ineligible, interrupted or unavailable. Review/completion queries pre-empt optional native work after its owned process/tree cleanup. Unknown lookups have a five-second retry floor; topology caches are commit-bound and note bodies are refreshed under current policy. These are advisory coverage bounds, not empty-result proofs.
@@ -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.15.0
20
+ bun add -g github:STAIxBWLB/agent-hub#v0.16.0
21
21
  ahub setup
22
22
  ```
23
23
 
@@ -30,6 +30,10 @@ 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.16.0
34
+
35
+ Always-ask now covers the local worker, Pi and Codex too (#636 slice 3): a local or Pi model tool call is classed before its permission mode, a `never-ask` grant, Pi's `Always allow` or `--unattended`, and a classed command approval request that Codex sends offers only allow-once, deny and the class budget. Unattended local or Pi work that publishes, installs or deletes now waits for you: answer it in the console, or change `action_policy` as in the 0.15.0 notes below. `ahub status` still lists Codex as not enforceable, because its native policy can run a command without sending a request, and Jcode as having no hub approval path. An approval whose command is cut or withheld (an ACP peer's private turn, for one) can only be denied, and a local `action_policy` that weakens the defaults is flagged on every status policy row. The classifier now reads the literal words around a shell expansion (`git push origin $B` asks) and takes the strictest class where the expansion can change it, while a `gh api graphql` call with one literal query no longer asks (#762, #756; see [security notes](security.md)). The hub no longer calls a claude-mem worker (agent-mem#13 P4): recall, briefs, `ahub ask` and the local worker's capture run in process on the exact-pinned `@staix/agent-mem` 0.7.0, `memory.worker_url` is ignored, `ahub doctor` reports the library, and `ahub ask --remember` and context checkpoints save pending candidates that only a person accepts; `ahub memory import claude-mem` stays the explicit way to copy old notes. The planner setting takes up to eight peers (`ahub settings set planner claude,codex`): each new task records one of them, only that planner or you change its planning, an agent's own plan is reviewed by another holder whenever one is online, and `ahub task assign <id> <peer> --role planner` moves it (#525-2; see [the planner](operations.md#the-planner-455)). Once a planner list is set or any task records a planner, `ahub upgrade` and recovery refuse a target release without `task_planners`, so the project cannot be moved back to 0.15.0 that way. `ahub deliverables [--task <id>] [--approved] [--check]` lists what each task delivered and a reviewer approved, and a dashboard card shows a `Deliverables n` chip (#509b). `ahub budget` prints each window's kind and source and says why a peer has no number (#285). The dashboard Memory view gains a Code neighbours toggle for a selected symbol, and `ahub doctor --stale-anchors` is a read-only report of memory symbol anchors at the current head (#684). The `codex-review` reviewer on Codex 0.164.0 no longer needs `ahub agents trust` (#757). Upgrade running hubs with `ahub upgrade` and restart Claude sessions so they load the 0.16.0 plugin. Coming from 0.14.0 or older, the notes below apply too.
36
+
33
37
  ## Upgrading to 0.15.0
34
38
 
35
39
  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.
@@ -98,7 +102,7 @@ The restarted Claude planner gets the release-train tools (`hub_release_new`, `h
98
102
 
99
103
  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`.
100
104
 
101
- Since 0.12.37 the hub pins `@staix/agent-mem` 0.4.0 and opens a split memory home through its shards (#534). Before anyone runs `amem migrate --shards` on a memory home, every hub using that home must run 0.12.37 or later: an older hub does not see the split and keeps writing the old layout. `ahub memory profile-check` is split-aware and compares each hub with its own project's shard (#536). See [memory profiles and rollout](memory.md#selected-memory-profile).
105
+ Since 0.12.37 the hub opens a split memory home through its shards (#534); the current exact `@staix/agent-mem` pin is 0.7.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).
102
106
 
103
107
  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:
104
108
 
@@ -150,7 +154,7 @@ or set `OMNIROUTE_API_KEY`. The `.agenthub/routing.toml` that `ahub init` writes
150
154
  | `ahub task propose [--class <c>] <title>` | put work on the board; the hub picks an owner and a reviewer, and the class if you name none |
151
155
  | `ahub board` | who has what, one line per task at a terminal width; `--full` for whole titles and SDLC advice, `ahub task show <id>` for one task |
152
156
  | `ahub task cancel <id> --reason <text>` | close obsolete work without approving it or stopping a native turn |
153
- | `ahub settings set planner <peer>` | pin the planner; status reports whether it came from settings, roles or the automatic choice |
157
+ | `ahub settings set planner <peer>[,<peer>...]` | pin up to eight planners; status reports whether they came from settings, roles or the automatic choice |
154
158
  | `ahub settings reload` | apply edited config files without a restart; guarded values wait for your `y` in this terminal, and restart-only keys stay pending |
155
159
  | `ahub settings edit <file> set <path> <json>` | change an instance, the review block or the `sdlc.toml` lifecycle with a validated diff and a backup; `ahub settings backups` lists the backups |
156
160
  | `ahub status` | peers, queues, pauses; `ahub budget` for quota windows |
@@ -165,7 +169,7 @@ More: `ahub help`. How it is built and why: [`docs/specs/2026-09-19-agent-hub-de
165
169
 
166
170
  ## Shared memory
167
171
 
168
- The hub exact-pins the first-party `@staix/agent-mem` 0.4.0 core. Existing shared-v1 memory remains the default; upgrading does not migrate it. Managed agents use screened `hub_memory_search`, `hub_memory_show` and `hub_memory_graph` tools, or propose a correction with `hub_memory_correct`; only a person accepts candidates through the supported `amem review` path. Generation candidate acceptance is still refused.
172
+ The hub exact-pins the first-party `@staix/agent-mem` 0.7.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.
169
173
 
170
174
  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.
171
175