@staix/agent-hub 0.30.0 → 0.32.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 (130) hide show
  1. package/CHANGELOG.md +52 -0
  2. package/README.md +13 -7
  3. package/docs/agent-notes/README.md +1 -0
  4. package/docs/agent-notes/adapters.md +5 -2
  5. package/docs/agent-notes/budget.md +1 -0
  6. package/docs/agent-notes/bus.md +11 -4
  7. package/docs/agent-notes/daemon.md +16 -8
  8. package/docs/agent-notes/local-worker.md +1 -0
  9. package/docs/agent-notes/tasks.md +7 -3
  10. package/docs/agent-notes/tests.md +6 -4
  11. package/docs/design/adapters.md +52 -0
  12. package/docs/design/agent-hub-architecture.md +46 -0
  13. package/docs/design/bus.md +178 -1
  14. package/docs/design/task-board.md +82 -0
  15. package/docs/dogfood.md +23 -4
  16. package/docs/events.md +3 -0
  17. package/docs/memory.md +17 -1
  18. package/docs/operations.md +355 -78
  19. package/docs/profiles.md +35 -0
  20. package/docs/quickstart.md +24 -5
  21. package/docs/security.md +178 -60
  22. package/docs/specs/2026-09-19-agent-hub-design.md +127 -14
  23. package/docs/specs/2026-09-19-multi-project-design.md +7 -2
  24. package/docs/verification/2026-11-04-1171-channel-client-evidence.md +31 -0
  25. package/docs/verification/2026-11-05-0.31.0.md +182 -0
  26. package/docs/verification/2026-11-05-claude-implementer-2.1.318.md +70 -0
  27. package/docs/verification/2026-11-05-hardcoded-values-classification-rules.md +311 -0
  28. package/docs/verification/2026-11-05-hardcoded-values-ratchet.md +55 -0
  29. package/docs/verification/2026-11-06-0.32.0.md +149 -0
  30. package/docs/verification/2026-11-06-rooms-presence-cost.md +152 -0
  31. package/docs/verification/claude-shell-wrapper-native.py +1 -1
  32. package/docs/verified.json +14 -14
  33. package/package.json +3 -4
  34. package/plugins/agent-hub/.claude-plugin/plugin.json +1 -1
  35. package/plugins/agent-hub/server.js +4393 -3609
  36. package/src/adapters/acp.ts +5 -4
  37. package/src/adapters/channel-peer.ts +72 -3
  38. package/src/adapters/claude-channel-evidence.ts +52 -0
  39. package/src/adapters/claude-channel.ts +61 -18
  40. package/src/adapters/claude-implementer.ts +29 -7
  41. package/src/adapters/claude-print-child.ts +12 -2
  42. package/src/adapters/codex-appserver.ts +13 -12
  43. package/src/adapters/compat.ts +3 -3
  44. package/src/adapters/definitions/builtin-metadata.ts +13 -13
  45. package/src/adapters/definitions/qwen.ts +1 -0
  46. package/src/adapters/local-worker.ts +61 -18
  47. package/src/adapters/pi.ts +48 -6
  48. package/src/adapters/registry-types.ts +12 -0
  49. package/src/adapters/registry.ts +2 -0
  50. package/src/adapters/reviewer-print.ts +50 -11
  51. package/src/broker/server.ts +44 -13
  52. package/src/broker/store.ts +38 -8
  53. package/src/cli/answer-args.ts +50 -0
  54. package/src/cli/attention.ts +17 -2
  55. package/src/cli/backlog-return-args.ts +37 -0
  56. package/src/cli/console-question.ts +110 -0
  57. package/src/cli/console-state.ts +78 -17
  58. package/src/cli/console.ts +54 -1
  59. package/src/cli/handoff-args.ts +15 -5
  60. package/src/cli/handoff-output.ts +15 -2
  61. package/src/cli/help.ts +8 -5
  62. package/src/cli/identity.ts +13 -3
  63. package/src/cli/main.ts +128 -16
  64. package/src/cli/memory.ts +107 -3
  65. package/src/cli/output.ts +29 -6
  66. package/src/cli/pet.ts +4 -4
  67. package/src/cli/results-args.ts +30 -0
  68. package/src/cli/room.ts +20 -10
  69. package/src/cli/runtime-version-warning.ts +50 -0
  70. package/src/cli/status-lines.ts +41 -1
  71. package/src/cli/tail-render.ts +1 -0
  72. package/src/cli/upgrade-interactive.ts +14 -2
  73. package/src/hub/action-class.ts +9 -1
  74. package/src/hub/board.ts +369 -18
  75. package/src/hub/bus.ts +511 -53
  76. package/src/hub/completion-detail.ts +50 -0
  77. package/src/hub/completions-store.ts +168 -0
  78. package/src/hub/completions.ts +120 -0
  79. package/src/hub/config-apply.ts +7 -5
  80. package/src/hub/config-reload.ts +10 -1
  81. package/src/hub/control-client.ts +40 -4
  82. package/src/hub/daemon.ts +1576 -139
  83. package/src/hub/delivery-failure.ts +14 -0
  84. package/src/hub/delivery-hints.ts +14 -4
  85. package/src/hub/delivery-journal.ts +751 -42
  86. package/src/hub/delivery-superseded.ts +4 -1
  87. package/src/hub/envelope.ts +3 -3
  88. package/src/hub/events.ts +6 -0
  89. package/src/hub/handoff.ts +145 -6
  90. package/src/hub/hub-tools.ts +56 -7
  91. package/src/hub/manager.ts +59 -3
  92. package/src/hub/memory-policy.ts +44 -1
  93. package/src/hub/memory-sync-policy.ts +109 -0
  94. package/src/hub/memory-sync-state.ts +145 -0
  95. package/src/hub/no-writes.ts +1 -1
  96. package/src/hub/operational-settings.ts +11 -2
  97. package/src/hub/operational-view.ts +12 -1
  98. package/src/hub/peers.ts +25 -2
  99. package/src/hub/planning.ts +6 -1
  100. package/src/hub/question-desk.ts +201 -0
  101. package/src/hub/question-detach.ts +6 -0
  102. package/src/hub/questions.ts +260 -0
  103. package/src/hub/research-evidence.ts +106 -0
  104. package/src/hub/restart.ts +14 -0
  105. package/src/hub/review-anchor.ts +181 -0
  106. package/src/hub/review-brief.ts +13 -0
  107. package/src/hub/review-comments.ts +115 -0
  108. package/src/hub/review-findings.ts +7 -2
  109. package/src/hub/review-material.ts +76 -0
  110. package/src/hub/routing.ts +3 -1
  111. package/src/hub/task-sweep.ts +2 -0
  112. package/src/hub/tasks.ts +577 -63
  113. package/src/memory/capture.ts +5 -2
  114. package/src/pet/config.ts +25 -13
  115. package/src/rooms/commands.ts +61 -16
  116. package/src/rooms/link.ts +338 -36
  117. package/src/rooms/liveness.ts +11 -0
  118. package/src/rooms/remote-peer.ts +186 -19
  119. package/src/rooms/service.ts +475 -35
  120. package/src/rooms/status.ts +8 -1
  121. package/src/rooms/store.ts +447 -65
  122. package/src/rooms/wire.ts +24 -6
  123. package/src/ui/index.html +142 -9
  124. package/src/ui/needs.ts +10 -4
  125. package/src/ui/plugin-questions.ts +56 -0
  126. package/src/ui/plugin-results.ts +81 -0
  127. package/src/ui/plugin.ts +207 -27
  128. package/src/pet/window.html +0 -114
  129. package/src/pet/window.jxa.js +0 -122
  130. package/src/pet/window.ts +0 -154
@@ -33,6 +33,11 @@ The bus applies limits to a sender, sender-recipient pair, important messages, a
33
33
 
34
34
  The in-memory bus schedules messages; `DeliveryJournal` durably records an individual transport attempt and the original envelopes behind any rendered output. A peer receipt such as `accepted` or `completed` describes transport, not the corresponding task's acceptance, completion, or approval. That separation lets a timeout or restart remain explicitly uncertain without inventing a task outcome ([#37](https://github.com/STAIxBWLB/agent-hub/issues/37)).
35
35
 
36
+ A closed bus returns an empty publication outcome before provenance, privacy,
37
+ journal preflight, observers or queue mutation. Deferred publication has the same
38
+ fence. A storage failure while the bus is open still fails rather than becoming
39
+ an apparently successful empty send.
40
+
36
41
  For #1055, `Bus.onDelivered(peer, originals, deliveryId?)` exposes the existing
37
42
  journal delivery ID to the observer at the first positive transport ACK. The
38
43
  optional argument accompanies the original envelopes, including when the
@@ -41,16 +46,188 @@ and an observer exception cannot alter delivery settlement. This supports the
41
46
  [ID-only session-title restart binding](adapters.md#session-title-slots) without
42
47
  changing dispatch, retry, task acceptance or task-completion authority.
43
48
 
49
+ A successful nonempty inbox handout records current-input and bounded named-parent review provenance through `Bus.onPulled(peer, originals)`. This metadata hook does not invoke `onDelivered`, certify native consumption, start a session-title receipt or change completion authority. Presence-only handouts do not release review exclusion, and a mixed quote-bearing batch cannot seed a title binding from its ordinary member. A previously saved binding to an ordinary member of such a batch is also rejected on status and delivery recovery; a fresh ordinary input can establish a safe source. It follows the existing pull handout ceiling: the transport result may still be lost before the client reads it. The separate metadata-only `onSteering(peer, originals)` hook latches quote-bearing input immediately before an actual steer attempt, without creating a native ACK or task/completion evidence. Ordinary additional steer input cannot clear an active review turn's exclusion.
50
+
51
+ The latest pulled input is kept separately from the last pushed delivery. Observer reads use the pull slot while it is current, but invocation answer matching retains its pushed request through an intervening inbox read; an explicit reply can still name the correct pushed or pulled invocation. A later push replaces the pull slot. Only `resetLastDelivery` after a confirmed native-session change clears both slots; adapter metadata reset/removal preserves the latest pulled input.
52
+
44
53
  Push peers receive bounded batches. Condensation is optional, excludes important, remote, and invocation messages, and keeps `originals` separate from `out` so replies, safe failures, and recovery still refer to what the peer was actually given. On an eligible safe retry, a failed batch falls back to individual envelopes, and a previously failed envelope goes alone so a poison item cannot block its neighbors. These bounds preserve queue progress when summarization or one recipient fails ([#713](https://github.com/STAIxBWLB/agent-hub/issues/713), [#553](https://github.com/STAIxBWLB/agent-hub/issues/553)).
45
54
 
46
55
  Pull-only peers catch up oldest-first under a byte budget, but the first envelope always goes even when it exceeds that budget so one oversized item cannot starve the queue; push peers use the digest batch limit. Pull records returned envelopes as completed at handout because the tool result is the readback, not a later native receipt. A busy Claude channel can also pull its own messages through `hub_inbox`; that avoids making a long-running turn an unbounded queue lock ([#493](https://github.com/STAIxBWLB/agent-hub/issues/493), [#713](https://github.com/STAIxBWLB/agent-hub/issues/713)).
47
56
 
48
57
  Delivery uncertainty normally holds the original and is never an automatic replay. The narrow exception is a hub-authored presence or workflow notice proven moot by current task evidence; mixed, unknown, or requested-stop rows remain held. This removes stale notices without treating arbitrary peer messages as seen ([#434](https://github.com/STAIxBWLB/agent-hub/issues/434)). Safe pre-acceptance failures may retry within a bounded attempt policy; uncertain results remain `needs_review` until an explicit resolution. A peer may retry only its own eligible row and only once per immutable retry lineage; an invocation uses its own ledger and is not replayed as an ordinary message retry ([#37](https://github.com/STAIxBWLB/agent-hub/issues/37), [#991](https://github.com/STAIxBWLB/agent-hub/issues/991)).
49
58
 
50
- A peer may settle its own local `needs_review` row as completed or discard while busy, with the observed revision and a public-safe reason; this records a delivery decision and never changes task state or redelivers the original. Another peer, a remote-room actor, or an active `dispatching`/`accepted` row remains refused. Invocation-bearing rows also remain person-settled because a transport action cannot assert the outcome of an invoked operation. `hub_inbox` may pass a `needs_review` hold to return newer queued messages and bounded id/revision metadata naming the held row, while leaving the uncertain originals held. Pause, recovery, lifecycle, storage, and active-delivery fences still apply. This lets the current recipient make progress without treating uncertain work as delivered ([#1079](https://github.com/STAIxBWLB/agent-hub/issues/1079), [#445](https://github.com/STAIxBWLB/agent-hub/issues/445), [#991](https://github.com/STAIxBWLB/agent-hub/issues/991)).
59
+ Failure diagnostics use the bounded, public-safe `failureCause` classes `peer
60
+ offline`, `adapter refused`, `push error`, `turn aborted`, and `timeout`, never
61
+ message text. A legacy absent cause remains unknown. Queue inspection exposes
62
+ this metadata without changing the receipt's state or proving that retry is
63
+ safe. Status counts only unresolved failed rows per peer, excluding rows with
64
+ an explicit resolution even when a retry leaves its parent in `failed`; the
65
+ metadata-only aggregate does not hydrate delivery payloads ([#1178](https://github.com/STAIxBWLB/agent-hub/issues/1178)).
66
+
67
+ A peer may settle its own local `needs_review` row as completed or discard while busy, with the observed revision and a public-safe reason; this records a delivery decision and never changes task state or redelivers the original. Another peer or a remote-room actor remains refused. Active `dispatching` rows remain refused; an `accepted` row is settleable only by its explicit push channel owner as described below. Invocation-bearing rows also remain person-settled because a transport action cannot assert the outcome of an invoked operation. `hub_inbox` may pass a `needs_review` hold to return newer queued messages and bounded id/revision metadata naming the held row, while leaving the uncertain originals held. Pause, recovery, lifecycle, storage, and active-delivery fences still apply. This lets the current recipient make progress without treating uncertain work as delivered ([#1079](https://github.com/STAIxBWLB/agent-hub/issues/1079), [#445](https://github.com/STAIxBWLB/agent-hub/issues/445), [#991](https://github.com/STAIxBWLB/agent-hub/issues/991)).
68
+
69
+ An explicit push channel may settle or discard its own `accepted` row with the observed revision and a public-safe reason. `accepted` means the channel transport accepted the notification, not that the session handled it. If neither a native completion nor an explicit disposition arrives within the configured `bus.accepted_settle_ms` window, the host moves the row to `needs_review` with a fixed timeout cause and never replays it. The channel's `hub_inbox` metadata includes its own accepted and held rows, with ids and revisions, even while an active accepted delivery blocks pulling. An important, task, or review timeout adds one ids-only status notice when a conductor is configured and its existing `conductor.feed` policy is on; `feed: off` remains an explicit opt-out. Timed-out rows stay held until a person or the explicit channel session records a disposition; task state does not settle them ([#1172](https://github.com/STAIxBWLB/agent-hub/issues/1172)).
70
+
71
+ An `answer` envelope (#1053) is a person's late answer to a question a peer asked with `hub_ask`. The hub builds it: `kind: "answer"`, `from: user`, addressed to the asking peer only, with the deterministic id `answer-<set>` (a republish after a crash is dropped by the bus's own dedupe) and `refs.question`. Code keys on `kind`, never on the sender, and an `answer` never supersedes the question it answers. Its body is the person's text, so the log line, the console tail and the dashboard feed show a fixed stub; only the recipient reads it. A question expiry is a hub `status` notice, never an answer.
51
72
 
52
73
  Invocation requests and results are single-envelope deliveries, not digest material. A reply is correlated to the request handed to that peer and is captured by the invocation path before ordinary publishing; uncertain outcomes stay with the invocation ledger because a missing result cannot be inferred from a transport receipt ([#445](https://github.com/STAIxBWLB/agent-hub/issues/445), [#991](https://github.com/STAIxBWLB/agent-hub/issues/991)).
53
74
 
54
75
  ## Cross-project transport
55
76
 
56
77
  Encrypted rooms add a screened message transport between local project hubs. Room identity, deadlines, and remote provenance stay bound to each delivery; traffic is not merged into a shared board. A separately pinned read-only invocation relay cannot transfer task or approval authority. This keeps remote delivery from acquiring the receiving project's roles or memory ([#941](https://github.com/STAIxBWLB/agent-hub/issues/941), [#992](https://github.com/STAIxBWLB/agent-hub/issues/992)).
78
+
79
+ ## Review observer provenance (#1056)
80
+
81
+ An admitted person comment package or anchored-finding review request carries the host-only opaque `refs.reviewPackage`. The intended recipient keeps its public body; observer projections show an IDs-only stub. A PII task still uses its existing private stub and authority rules. Peer-supplied review markers are discarded. The bus derives `refs.reviewResponse` for a response to the current delivered review input or a retained named parent, so a direct echo cannot enter logs, stream projections or memory observers as ordinary chat. This marker grants no task, console or PII capability.
82
+
83
+ The provenance cache retains at most 128 delivered parent IDs per peer and a globally bounded response cache; peer removal drops its parent binding. It holds identifiers only, not excerpts. This boundary attributes current turns and named parents. Arbitrary paraphrases from later vendor-native session history need a native session/turn provenance protocol; content matching is not used to guess them.
84
+
85
+ Person comment packages retain their terminal delivery evidence for explicit idempotent publication recovery. Reviewer requests use the distinct `reviewer-request-` marker and their terminal rows remain prunable. A saved person package whose latest delivery failed or was discarded refuses explicit resend with that state and queue inspection guidance; it never reports queued success or replays the effect. Owner-addressed review packages and verdicts stay uncertain until an explicit disposition; task or reviewer movement alone is not consumption proof. Durable open sent comments and anchored finding scope protect memory notes naming that task after a restart or intervening chat; they grant no task or PII authority. Capture and other turn-wide observers follow the current delivered review input or an anchored-verdict submission latched to that input, not peer-wide board history. Ordinary new input releases that turn exclusion only where the transport supplies replacement evidence; the restored pull-only rule below still applies. Separate delivery holds retain their own dispositions. The journal retains bounded peer/session hold and release receipts using session/input ID hashes, never text. Completeness and integrity metadata confirm whether absence is meaningful: no matching held record in an intact, readable, complete store is clean, including a new session after restart. Unreadable or corrupt storage remains unknown and excluded. A validated matching release confirms only its own session while legacy coverage is incomplete.
86
+
87
+ A missing stable native session id does not refuse a review handout. The host records a peer-scoped incomplete marker and keeps an unknown-held scope in RAM, without turning launch, attach or socket ids into a native identity. For a never-identified peer with no positive or incomplete SID row, the next verified ordinary replacement in that daemon run can release that RAM scope and its peer marker. If any earlier positive or incomplete SID row exists while the current SID cannot be matched, delivery still proceeds but the observer state stays unknown and held; an ordinary RAM proof cannot release it. Status names that peer as unidentified and directs reconnection so its real session id can be known. A newly confirmed SID uses its own state; another SID's retained rows are not cleared. Journal write failure still refuses before handout. Presence-only traffic and additional input within a known active held turn cannot clear exclusion.
88
+
89
+ Legacy coverage becomes complete only when journal evidence shows no unsettled pre-upgrade review-scoped delivery remains; this changes provenance metadata, never delivery disposition or replay. Its bounded candidate set is fixed from preexisting handed work at open/import; guarded deliveries created after that capture do not prolong the current daemon's fence. Reopen/import while coverage is incomplete conservatively recaptures every handed non-terminal row present then, including a newer guarded B; after the original A settles, coverage may therefore remain incomplete and a fresh SID without its own receipt remains unknown until B also reaches a terminal disposition. A timeout to needs_review still requires explicit resolution. This accepted recapture ceiling neither refuses handout nor creates a sticky hold from legacy coverage alone. If this window matters in operation, the upgrade path is a persisted ids-only legacy fence carried across recovery. Legacy coverage alone does not create a sticky restored pull-only hold. For an identified session, a positive held row or that peer's own incomplete marker can restore exclusion. A verified ordinary replacement or confirmed native-session change clears only that peer's incomplete marker; a session change does not discard another session's positive held row. On the first confirmed SID after a sessionless period, any current RAM quote hold is persisted under that SID before the peer marker is cleared. Positive session-bound restored holds remain excluded until confirmed native identity changes. Sessionless peers do not acquire an unbound sticky hold.
90
+
91
+ Once coverage is complete, NULL clean-session receipts may be pruned because complete absence preserves clean state. Positive and uncertain holds are not evicted to satisfy the 4096-row bound. Integrity metadata and counts are updated atomically with these changes. Public status names unavailable provenance and its next step without exposing note text or raw database errors. There is no automatic corrupt-state reset. Recovery preserves the receipts, integrity metadata and session-bound exclusion.
92
+
93
+ Role reassignment cannot clear the original recipient's observer exclusion while a review envelope remains queued, recovery-deferred or unsettled. The journal supplies recipient/task-scoped metadata independently of current task roles; explicit disposition ends that delivery scope. Sent person comments independently retain their saved package recipient until resolution or person dismissal.
94
+
95
+ ## Room presence and broker backpressure (#1204 T1)
96
+
97
+ Ordinary observations reuse an already scheduled coalesced republish instead of
98
+ cancelling it and sealing a new frame on every awaited state flip. Responses and
99
+ forced refresh requests retain priority. A chat flushes earlier deferred presence
100
+ and reserves its own serial slot before yielding, preserving concurrent chat and
101
+ durable-outbox FIFO. Receipts retain their existing durable-commit/ACK path and do
102
+ not wait behind an additional ordinary observation. Actual-send exposure,
103
+ recipient, currency and capacity fences remain unchanged.
104
+
105
+ Presence carries a signed optional `ephemeral: true` frame header. The broker can
106
+ read this metadata but cannot decrypt the presence body. It retains only the
107
+ latest ephemeral frame per sender for currently connected, ready recipients;
108
+ disconnect and broker restart remove ephemeral recipients. Durable frames keep
109
+ the existing queue caps, TTL and exactly-once receiver admission.
110
+
111
+ A link skips an unchanged local presence snapshot, except explicit peer refresh
112
+ requests/responses and a fresh connection. One sealed frame is sent at a time
113
+ until its broker storage response, so a refused sequence cannot be overtaken.
114
+ A correlated `queue_full` retains the frame (applications remain in the local
115
+ outbox) and retries after 250 ms, doubling to a 30 s ceiling. Stop/refresh clears
116
+ connection timers; no recipient receipt or application completion is inferred.
117
+ T1 does not change acknowledgement deadlines, restart replay or receipt outbox
118
+ persistence, which belong to T2. Existing layer caps remain authoritative.
119
+
120
+ ### Room wire compatibility (#1204 T1)
121
+
122
+ The link requests only the ephemeral feature with the WebSocket upgrade header
123
+ `x-agent-hub-ephemeral: 1`. A supporting broker includes `ephemeral: true` in its
124
+ ready response only to an opted-in client, preserving the exact legacy ready
125
+ shape for older hubs. Each new connection starts with the feature disabled.
126
+ Without the advertisement, presence retains the legacy frame shape; dedupe and
127
+ queue_full backoff still apply, but that broker retains presence for offline
128
+ members under its old queue rules. Upgrade the broker for ephemeral retention.
129
+
130
+ A supporting broker forwards ephemeral presence only to ready members that
131
+ opted in. Older members do not see upgraded members' presence until they upgrade. Application
132
+ chat frames keep the legacy shape. This is the one-bit compatibility bridge;
133
+ full codec feature advertisement belongs to #1204 T4.
134
+
135
+ Ephemeral replacement preserves a recipient's outstanding one-packet transport
136
+ slot until its matching ACK or the original TTL/grace bound expires. Only the
137
+ pending replacement coalesces; a newer observation never refreshes the handed
138
+ packet's deadline. Unsupported recipients never receive the new frame shape.
139
+
140
+ A refresh request
141
+ remains on non-reply snapshots until all asked members supply validated presence;
142
+ replies never request a reply. The bounded round resets on reconnect or roster
143
+ change. Periodic idle refresh in T3 repairs a crossed request even without a peer-state
144
+ change; latest-per-sender coalescing and the existing dedupe ceiling remain.
145
+
146
+ ## Room liveness and epoch changes (#1204 T3)
147
+
148
+ Only a connection whose broker negotiated ephemeral presence publishes periodic
149
+ `requestReply` snapshots, every 30 seconds by default. Replies never request
150
+ replies. Three missed local refresh periods (90 seconds) make that member's room
151
+ presence `unknown` in its exposed-peer status. Aging is advisory only: it changes
152
+ no outbound admissibility, native peer state or authority. Sending remains governed
153
+ by the signed roster, exposure, link and Bus proofs; the broker can queue for a
154
+ member that is not live. An explicit signed offline presence keeps its existing
155
+ handling. Freshness uses local observation time, never a saved snapshot or a sender
156
+ clock; restart needs a fresh observation for its displayed liveness. Invalid time
157
+ or clock rollback makes that advisory view unknown without changing transport gates.
158
+ Status computes the advisory view without mutating the clock or delivery state.
159
+ The refresh period and clock are injectable for fixtures.
160
+
161
+ A legacy broker gets no periodic refresh or aging. Identical-snapshot dedupe and
162
+ state-change publication remain, with at most one final offline observation at
163
+ stop. Its view says `liveness not tracked (broker without ephemeral)` and asks
164
+ the person to upgrade the broker. Optional room-only `presence` and `liveness`
165
+ metadata keep the closed native `PeerState` type unchanged; older readers ignore
166
+ the added fields. No new codec or full capability handshake is introduced; T4
167
+ remains separate.
168
+
169
+ Stop is asynchronous and idempotent, with a synchronous publication fence. It
170
+ attempts one signed final offline observation behind an already attempted FIFO
171
+ head, then disconnects within two seconds. Durable application intents and
172
+ receipts survive. A blocked or failed final observation falls back to aging at
173
+ supporting brokers; it does not stop a remote process or grant native authority.
174
+
175
+ Before an actual epoch change, the person preview lists only already sealed,
176
+ unresolved RoomStore delivery ids. The commit quiesces link application
177
+ publication and rechecks that exact id set and the current roster digest.
178
+ RoomStore atomically quarantines old active intents as `needs_review` with the
179
+ existing `boundary_refused` reason and drops their sendable ciphertext. The local
180
+ Bus holds use `room epoch rotated; inspect ahub queue show <id>`; current room status exposes `holdReason: "epoch rotated"`.
181
+ Bus-only unsealed work stays queued. This is no completion, replay or cancellation;
182
+ terminal rows and person dispositions remain unchanged. T2 already supplied the
183
+ crash-gap quarantine; T3 adds the person preview and named local hold.
184
+
185
+ ## Room application retry and settlement (#1204 T2)
186
+
187
+ Room sends retain one immutable application id and original queue expiry across
188
+ fresh ciphertext attempts. A distinct root delivery may reuse a retained application
189
+ only with the same canonical content hash and peer/recipient tuple, no person
190
+ disposition or permanent hold, and the original creation/expiry times. Legacy
191
+ NULL content hashes refuse cross-root reuse. Receiver admission keeps the existing application-id
192
+ dedupe horizon (30 days / 10,000 identities with settled-first eviction).
193
+ This is exactly once within dedupe ceiling. Automatic resend can reach that
194
+ ceiling after a settled identity is evicted, permitting duplicate admission under
195
+ a fresh ciphertext sequence. Pinning retryable identities or #737 receipt
196
+ confirmation is the upgrade path.
197
+ A typed refusal before durable enqueue requeues the original without uncertainty
198
+ or poison retry accounting. Online readiness and the ACK deadline trigger retries;
199
+ the ACK deadline is not a terminal disposition. Queue TTL or a permanent refusal
200
+ ends automatic sending. An ordinary remote chat uncertainty row does not block later queued work.
201
+ Invocation uncertainty retains its person-only ledger hold and is never automatically
202
+ replayed or settled from late transport evidence. Active invocation receipts update
203
+ only transport metadata, never native execution or task-offer read evidence.
204
+
205
+ Each retry rechecks current room/member identity, exposure and privacy against the
206
+ saved immutable application. A restart reconstructs active remote deliveries under
207
+ their existing ids rather than treating transport loss as native consumption. A
208
+ late authenticated recipient receipt can settle an unresolved remote hold once,
209
+ with an ids-only event. Person dispositions, stale adapters and changed membership
210
+ remain authoritative; native delivery and task-offer read evidence are unaffected.
211
+
212
+ Roster, pause, lifecycle and global recovery fences also apply to resends. A
213
+ readiness loss after durable enqueue retains that delivery's id and original TTL;
214
+ confirmed precommit absence can requeue. A restored active journal row with no
215
+ matching row in an intact RoomStore becomes `needs_review` for inspection and is
216
+ never replayed from missing evidence. A failed payload-proof read never schedules
217
+ an immediate retry: transient failures wait for an online or ACK-deadline wake,
218
+ and permanently unavailable ciphertext is held for review with a metadata-only
219
+ transition. Metadata settlement and quarantine remain available while sending is held.
220
+ Authoritative Bus person dispositions are reconciled into RoomStore before
221
+ startup replay and incoming receipt processing. A local transport result may
222
+ remain as evidence beside that disposition, but cannot override it or restart
223
+ sending. Status reads remain read-only.
224
+
225
+ A completed inbound admission commits its signed receipt ciphertext to the local
226
+ RoomStore outbox before transport ACK of the chat. Pending receipts replay on
227
+ reconnect until broker storage confirmation. Waiting for broker storage before
228
+ chat ACK would deadlock at capacity one. The durable outbox removes T1's RAM-loss
229
+ exception; broker storage and transport ACK still do not prove native execution.
230
+ ACK after durable local commit of either the receipt or the inbox entry.
231
+ Deferred application admission may ACK its durable crypto inbox entry so it does
232
+ not pin broker capacity; later admission commits and replays the same durable
233
+ receipt path before retiring its local work.
@@ -30,6 +30,9 @@ paths:
30
30
  - src/hub/task-repository.ts
31
31
  - src/hub/no-writes.ts
32
32
  - src/hub/review-findings.ts
33
+ - src/hub/review-anchor.ts
34
+ - src/hub/review-comments.ts
35
+ - src/hub/review-material.ts
33
36
  - src/hub/workflow.ts
34
37
  - src/hub/deliverables.ts
35
38
  - src/hub/deliverable-files.ts
@@ -39,6 +42,7 @@ paths:
39
42
  - src/hub/ci-watch.ts
40
43
  - src/hub/research-campaign.ts
41
44
  - src/hub/research-experiments.ts
45
+ - src/hub/research-evidence.ts
42
46
  - src/hub/research-runner.ts
43
47
  - src/hub/task-approval.ts
44
48
  - src/hub/schedules.ts
@@ -87,6 +91,70 @@ Review independence and declared scope are different controls. Selection exclude
87
91
 
88
92
  Task text and private-turn words can reach owners, reviewers, overlap notices and memory. Public views use the host's current privacy projection; PII tasks remain stubs for unauthorized observers, and private turns cannot write ordinary shared task text. The projection belongs at the shared boundary because filtering individual callers would leave another observer exposed ([#198](https://github.com/STAIxBWLB/agent-hub/issues/198), [#230](https://github.com/STAIxBWLB/agent-hub/issues/230)).
89
93
 
94
+ ## Claim evidence records (#931 T1)
95
+
96
+ The #901 owner boundary exposes one canonical `taskClaimIdentity(task, slot)`.
97
+ `claimId` is `claim:<task id>:<created>:<1-based slot>`, so a reset that reuses a
98
+ task number cannot inherit an earlier task's evidence. `claimRevision` is the
99
+ existing `claimListId(task.plan?.claims)`, the opaque 16-hex ordered-list hash
100
+ already stamped in person verdicts. `recordClaimVerdict` and the evidence writer
101
+ use that same helper; neither a second claim/verdict store nor a numeric claim
102
+ counter is introduced. Exact-list restoration retains #901's existing verdict
103
+ semantics. The list-level ceiling is conservative: any claim edit, reorder or
104
+ replacement stales every link bound to the changed list. Per-claim revisions
105
+ would be the upgrade path if that over-invalidation becomes a problem.
106
+
107
+ Existing `hub_task_show` and `ahub task show --json` views expose a research
108
+ task's current text-free `claims: [{slot, claimId, claimRevision}]` bindings,
109
+ derived only from that canonical helper. PII tasks expose none, and dev views
110
+ keep their existing shape. A caller copies the returned binding rather than
111
+ reimplementing plan hashing; a changed list requires a fresh view before record.
112
+
113
+ `EvidenceLinkV1` carries exactly `id`, `claimId`, `claimRevision`, `sourceId`,
114
+ `rawSha256`, `projectionSha256`, `projectionVersion`, `locator`, `relation`,
115
+ `recordedBy`, `taskId` and `revision`. Relations are `supports`, `contradicts` or
116
+ `context`, all declared provenance rather than an entailment decision. `locator`
117
+ stores proposed zero-based, half-open Unicode scalar-value `start`/`end` bounds
118
+ with a nonempty span of at most 4096. T1 does not convert offsets, resolve text,
119
+ accept page/section/excerpt fields or claim a projection is verified.
120
+
121
+ The flat `hub_evidence_record` input omits host-owned `recordedBy`; `revision`
122
+ defaults to 1 and `id` is optional for initial writes. A later revision names
123
+ its existing ID and the next revision number. Initial IDs are deterministic for
124
+ the same normalized input and actor. Replaying identical canonical bytes at an
125
+ existing ID/revision writes nothing; conflicting bytes or an out-of-sequence
126
+ revision refuse. Every accepted revision remains in the append-only evidence
127
+ table. #901 person-verdict history and scorecard counts remain the sole judgment
128
+ record, unaffected by evidence relation labels.
129
+
130
+ Tasks prechecks sequence and claim association before screening and immediately
131
+ before append; the Board transaction repeats them as the race fence. Ordinary
132
+ sequence/association refusals are pre-effect and name the expected next revision
133
+ when applicable. Invalid stored evidence directs the person to stop the hub,
134
+ restore a trusted project `hub.db` backup, restart and retry; T1 does not name an
135
+ unimplemented evidence-read command.
136
+
137
+ `Tasks.evidenceRecord` admits only the current in-progress owner of a stored
138
+ research-profile task, with current claim binding and registered source/raw hash.
139
+ Standalone tasks do not need a campaign, and role-catalog guidance grants no
140
+ authority. Recovery/shutdown holds, private turns, PII tasks, private sources,
141
+ unknown sources, stale claims and foreign-task writes refuse before effects.
142
+ Every awaited preparation is followed by current task/source/actor checks; the
143
+ append transaction also fences task incarnation, owner, flow and claim revision.
144
+ Current metadata privacy is checked again after asynchronous screening and immediately before append; a failed or unavailable policy refuses. Successful evidence receipts, including replay, are owner task effects for the existing no-write advisory.
145
+ Public receipts contain only link ID, revision and task ID. The tool is excluded
146
+ from argument/result capture and from read-only publication; dev-profile tool
147
+ lists retain their existing shape.
148
+
149
+ Projection hashes/version are caller-declared T1 bindings; only the raw source
150
+ hash is checked against registry metadata. Locator resolution, Unicode offset
151
+ conversion and current-privacy public revalidation are T2. Evidence read/CLI
152
+ surfaces, complete inventory bindings and synthesis readiness remain later work.
153
+ Stored evidence adds a conditional `research_evidence` recovery feature and
154
+ `{links, revisions, digest}` integrity section. An absent section is the empty
155
+ store, preserving older recovery records; an empty database requires no new
156
+ feature. Provenance never manufactures a person verdict or readiness result.
157
+
90
158
  ## Read models and policy extensions
91
159
 
92
160
  Agent list reads return bounded, newest-updated compact pages built from public rows, with a keyset cursor and explicit detail/ID reads. Console compatibility is separate. Pagination is not a frozen snapshot during concurrent edits; its purpose is bounded communication without bypassing private-row projection ([#1083](https://github.com/STAIxBWLB/agent-hub/issues/1083)).
@@ -102,3 +170,17 @@ The board stores facts about worktrees and never a verdict. `worktree_override`
102
170
  The PR state has two sources and no others. The board's own evidence says `merged` only through the guarded merge's record (`sdlc.merge`); a cancelled task or a task with no record says nothing about its PR and reads `unknown`. The person's explicit refresh (`worktrees_refresh_prs`, `src/hub/worktree-prs.ts`) reads the PR through `readSdlcPr`, bounded and one at a time, and keeps `open`, `merged` or `closed` with GitHub's own change time in the daemon's memory. `worktree-shelf.ts` holds no GitHub access, so a panel read, `ahub worktrees` and the inbox alert make no GitHub call.
103
171
 
104
172
  A worktree whose activity could not be read is live and never settled. The shelf reads git asynchronously under a total budget and answers `complete: false` when it is cut; the daemon's event loop is never held. The daemon deletes no worktree and no local branch: its git port allows three read argv shapes, and removal exists only in the person's own `ahub worktrees tidy --confirm`, which is console class and issues `git worktree remove` and `git branch -d`, never `--force` or `-D`. Operations: `docs/operations.md`, "The worktree shelf (#1057)".
173
+
174
+ ## Anchored review packages (#1056)
175
+
176
+ `ReviewFinding.anchor` uses the exact-pinned `@staix/agent-console` 0.1.3 DOM-free `./contract` entry through type-only imports. The generated declaration bridge is removed. `test/acon-review-contract.test.ts` checks the unchanged shared fixtures against both the published acon conformance functions and the hub validator; the project retains its Bun-only TypeScript libraries. Contract conformance and hub writes are separate: hub writes allow 1-4 selectors, 512 UTF-8 bytes for an exact quote, 64 each for prefix and suffix, 256 for a hunk header, 2048 for a comment body and 64 comments per task. The hub additionally caps paths at 4096 bytes, projection name/version and structural scheme at 128 each, structural ref at 512 and verified author identity at 512. These bounds keep each stored thread within its fixed read bound. Quote selector text remains exact in storage; `sanitize()` applies when it is rendered beside hub headers. The hub limits count UTF-8 bytes, independently of acon's character limits: 171 Hangul syllables exceed the 512-byte exact-quote cap. Refusals identify the field, bound and next step. Unknown fields and selectors refuse before effects. A finding path, when present, must equal its anchor path. Findings without anchors retain the existing head buckets and Important carry-over rule.
177
+
178
+ All person comment writes go through `Tasks`. The console can put or delete a draft without a peer envelope. An explicit send binds the selected immutable anchors and bodies to one package, changes their lifecycle to sent, appends one count-only task history entry and attempts one review envelope. A failed or uncertain notification never retries automatically. Its receipt distinguishes queued from unconfirmed delivery; neither means the owner read it. The saved package retains its immutable body, envelope identity, original visibility binding and versioned publication-evidence contract. Its serialized record must fit 32768 UTF-8 bytes before any sent write. A previously unpublished package is admitted only while every selected comment remains sent and its originally visible text remains public under current policy; a missing binding, dismissal or privacy change refuses without rewriting or publishing the old body. An explicit confirmed Send with `packageId` recovers only that saved package. Durable queue or delivery evidence makes this idempotent, including after restart; failed or discarded deliveries refuse with their actual state and `ahub queue` inspection guidance rather than reporting queued or replaying the effect. Unknown evidence and legacy packages refuse with inspection guidance. Person-package terminal receipts are retained alongside the package rather than pruned with ordinary delivery history. Quote-bearing reviewer requests use a distinct `reviewer-request-` marker and their terminal rows follow ordinary pruning. This retains a full receipt per package; package archival with an IDs-only tombstone is the upgrade path. Deleting a sent comment dismisses it without rewriting its anchor. Only a final successful owner completion may claim resolved comment IDs; a queued or failed check leaves them sent. That claim is not person verification or a review verdict.
179
+
180
+ The actor is the console person, not a conductor or a peer. acon authorship comes only from the server-built `ActionMeta` argument. Client body fields cannot supply an author, timestamp or authority. The standalone dashboard is read-only; acon's shared `ui.review` kit owns selection and Send. Older acon hosts without the kit retain the read-only view.
181
+
182
+ Placement is computed before reads and sends, never persisted over the stored anchor. Equal content hashes are anchored; a different hash with a uniquely matching exact quote is moved, with context breaking ties; unresolved changes are outdated. Missing, denied, private or over-budget material is unavailable. Projection identity mismatches are outdated before hash comparison. A display range from moved placement never becomes a new stored selector. An unavailable public comment retains its stored thread in the person view, labelled unavailable. Send refuses before any effect and names each unavailable selected comment. Actual PII/private comments remain count-only. Carried finding placement belongs to one asynchronous review admission. Rare synchronous role re-offers without that material report unavailable until the next refresh, rather than reusing old coordinates.
183
+
184
+ Periodic dashboard snapshots perform no comment-material Git or GitHub reads. Explicit task open or refresh requests the review section; a failed read marks only that section unavailable. Material uses the recorded task head and never substitutes the worktree HEAD for a missing commit. Material comes only from guarded immutable Git snapshots in the task's declared refs, plan paths or sections, or complete same-head verified PR paths. Every operation shares eight distinct files, eight seconds including head/PR preparation and 12000 serialized presentation bytes. Both sides share the budget. Truncated material cannot establish placement. Raw hashes bind bytes; projected hashes bind only UTF-8 projected text. The projection name is `text-projection:<extension>` and its version is the existing configured converter fingerprint. This is an opaque configuration identity, not an upstream semantic version; a converter version protocol would be the upgrade path.
185
+
186
+ PII comments remain board-local and public views expose counts only. Private paths retain only supplied lineRange or structural selectors; no coordinate is invented when unsafe selectors are removed. Public review packets carry a host-only opaque package marker so their intended owner receives the packet while logs, operational tails, incoming labels, capture and memory observers receive no quote or body. This marker does not turn an ordinary task into a PII turn. Structured anchored verdicts retain the GitHub verdict mirror with a verdict-only headline when quote provenance is present. Decision metadata, contradiction and safe recurring criteria retain their existing paths; quote-bearing finding proposals remain excluded. The configured PII screen checks reviewer prose before owner-facing publication without receiving source quote selectors. Durable open sent comments and outstanding anchored findings bind shared-memory admission only to the task a note names and its recipient, across restart and intervening chat. Sent comments hold those task-bound writes until resolution or person dismissal; resolved or dismissed comments alone do not retain reviewer history scope. They do not hold notes for an unrelated task or an untagged note after ordinary new input. Anchored-finding scope is conservatively held until the task closes, including later fixed dispositions; a persisted review-package epoch is the path to narrower closure. The metadata scope read is cached against database change stamps and fails closed above 500 role tasks or 16 MiB of UTF-8 history. This exclusion does not hide the owner's summary or check output from its reviewer or owner. Review requests retain trusted observer provenance whenever historical anchors or non-draft comments can be reflected in the completion text, including after comment resolution. Current input provenance separately holds all shared-memory writes and observer sinks for an actually delivered quote-bearing review input or a newly submitted anchored verdict, even if the verdict is refused or immediately closes the task. This exclusion is latched for the input turn until a permitted ordinary next-turn handoff replaces it; restored pull-only sessions instead require a changed native identity. Resolving comments or ending the task alone does not clear it. Bounded ID-only journal receipts preserve held session/input provenance across daemon restart. No matching held record in a complete, intact and readable store is clean; missing expected prior-session records and unreadable, corrupt or unconfirmed session coverage remain unknown. A validated matching release receipt confirms only its own identity. Restored pull-only sessions retain their hold until the native identity changes, regardless of inbox handout. An empty in-memory map alone is not replacement proof. Presence-only traffic and additional inputs within the same active turn cannot clear a latched exclusion. Committed inbox handouts carry the same current-input provenance as push input without claiming a native ACK. Local-worker batches latch before executing any tool, and their final answer and retained history obey the same exclusion. Owner-addressed review outcomes cannot be automatically settled merely because task or reviewer state moved. Original saved-package recipients retain comment scope while comments remain sent, and queued/deferred or unsettled delivery records retain their actual recipients independently of current task roles. A comment dismissal is not a delivery-consumption receipt; any separate uncertainty hold requires explicit disposition. Recovery treats an absent comment section as empty and checks the optional section's counts and digest when present.
package/docs/dogfood.md CHANGED
@@ -11,14 +11,20 @@ agent-hub is developed with agent-hub. This page lists every feature, whether it
11
11
  | Feature | State here | Measured by |
12
12
  | --- | --- | --- |
13
13
  | Distinguishable sessions and sender provenance (#1036, `session_identity`) | on after release and upgrade: a connection that names a peer id but is not that peer's current session is a distinct `<peer>~<origin>` session that can send and read, holds none of the peer's roles and replaces nobody; the channel plugin binds to its own project; every envelope and attach event records the connection. Nothing is refused to a connection that worked before. The recovery label `session_identity` applies once an unsettled delivery holds a session's message | `grep '"session":true' .agenthub/state/events.jsonl` lists every message a foreign session sent, with its minted id and no path or text; a recipient's header shows `from "claude~<id>"` for it; test/identity-provenance.test.ts (AC1, AC3, AC4, recovery label) and test/identity-channel-project.test.ts (AC2, real plugin against a new and an old hub) hold it. Not authentication: a same-user process can always act as the user |
14
- | Cross-project task handoff with state follow-back (#604, `task_handoff`) | on after release and upgrade: `ahub task handoff` and `hub_task_handoff` register work for another registered project that runs agent-hub on this machine as an ownerless proposal on its board; the requesting peer is told each state change as a `kind: task` message. Nothing is queued, and the origin has no authority there | `ahub board --handoffs` lists what this project handed over with the last state each target reported; `ahub board` and `ahub task show` in the target name the origin project and peer; test/handoff-daemon.test.ts runs two real daemons (AC1 to AC5 and the recovery label), test/handoff-core.test.ts and test/handoff-board.test.ts hold the pure core and the tables |
15
- | Staged project upgrades (#1039) | off: available for the person to upgrade projects independently; this development hub is not upgraded by a fixture | test/published-protocol-compat.test.ts uses the published 0.25.0 client/plugin and reverse daemon; test/staged-upgrade.test.ts checks independent holds and scoped effects; test/staged-runtime-daemon.test.ts checks a real release-root daemon after mutable install removal; test/board-schema-guard.test.ts checks named pre-effect refusal and WAL metadata; `test/project-runtime.test.ts` and `test/project-runtime-lifecycle.test.ts` cover updated resolver retention/refusal and real lifecycle/dashboard routing with synthetic captured daemons; `test/runtime-capture-prune.test.ts` covers references, bounded retention and publication races; `test/upgrade-stopped-project.test.ts` verifies no preservation for stopped projects. These fixtures do not prove published pre-0.26 CLI behavior |
14
+ | Claude channel attach evidence (#1171) | source and fixture coverage after merge and plugin/daemon upgrade; native Claude push proof remains unverified until a live `ahub claude` session | `ahub status` shows `channel` or `tools-only` with its evidence label; a marker-inheriting non-channel parent remains pull-only and its delivery is read by `hub_inbox` (`test/claude-channel-evidence.test.ts`, `test/daemon.test.ts`); inspect native TUI attaches separately, since fixtures use a synthetic parent row |
15
+ | Accepted channel delivery settlement (#1172) | source and fixture coverage after merge and plugin/daemon upgrade; native channel handling and long-running-session behavior remain unverified | `bus.accepted_settle_ms` defaults to 10 minutes; explicit channel `hub_inbox` metadata shows accepted/held ids and revisions even when a pull is blocked; a timeout becomes `needs_review` without replay; an important/task/review timeout yields one ids-only status notice when the configured conductor feed is on, with `feed: off` remaining an opt-out; daemon and MCP fixtures cover timeout and opt-out; live accepted-to-completed/timeout behavior remains unverified |
16
+ | Cross-project task handoff with state follow-back (#604, `task_handoff`) | on after release and upgrade: `ahub task handoff` and `hub_task_handoff` register work for another registered project that runs agent-hub on this machine as an ownerless proposal on its board; the requesting peer is told each state change as a `kind: task` message. Nothing is queued, and the origin has no authority there | `ahub board --handoffs` lists what this project handed over with the last state each target reported; `ahub board` and `ahub task show` in the target name the origin project and peer; a handoff for a spec issue that already has an open task there is refused with the task ids unless `duplicate_ok` gives a reason, and the receiving conductor can cancel an unaccepted handoff (#1175; the share of refused handoffs and the cancels the person no longer has to run show whether duplicates still reach the board); a handoff the target did not confirm (a timeout, #1203 T1) is reported as `outcome_unknown` with a key and a retry with `--key` files it once, and a follow-back names the target's task (`refs.handoff`) so a local task of the same number cannot drop it (count `outcome_unknown` refusals, and any target task filed twice for one origin: there should be none; from T2 (#733) the origin also asks every 5 minutes and when a target restarts, so a missed notify arrives within a period: compare how long a handoff row stays behind its target (its `updated` against the target's state), and count `changes requested` and `reopened` messages, which used to be silent; from T3 (#734) `ahub task handoff --list` shows each handoff's age and whether its target is reachable, unreachable (with the cause) or moved, `--sync` asks the targets now, a wire-version refusal is `older_target` and an instance mismatch is `mismatch` instead of "not running", and a failed notify or catch-up logs one line per target and cause per 15 minutes: count the `handoff catch-up of ... failed` and `... skipped` lines in `hub.log` by cause, how often `--list` shows a target unreachable or moved for more than a day, and whether `--sync` is ever needed after a restart (it should not be: the catch-up reads the state within a period); T1 and T2 and T3 of #1203 are now all built; test/handoff-daemon.test.ts runs two real daemons (AC1 to AC5, #1175, the recovery label and #1203 T1 AC1 and AC4, T2 AC2, AC3 and AC5, T3 AC6 and AC7), test/handoff-core.test.ts and test/handoff-board.test.ts hold the pure core and the tables |
17
+ | Staged project upgrades (#1039) | off: available for the person to upgrade projects independently; this development hub is not upgraded by a fixture | test/published-protocol-compat.test.ts uses both the published 0.25.0 protocol-floor and 0.29.0 release N-1 clients/plugins and reverse daemons; test/staged-upgrade.test.ts checks independent holds and scoped effects; test/staged-runtime-daemon.test.ts checks a real release-root daemon after mutable install removal; test/board-schema-guard.test.ts checks named pre-effect refusal and WAL metadata; `test/project-runtime.test.ts` and `test/project-runtime-lifecycle.test.ts` cover updated resolver retention/refusal and real lifecycle/dashboard routing with synthetic captured daemons; `test/runtime-capture-prune.test.ts` covers references, bounded retention and publication races; `test/upgrade-stopped-project.test.ts` verifies no preservation for stopped projects. These fixtures do not prove published pre-0.26 CLI behavior |
16
18
  | Approval head binding and re-review (#1047, #1121) | source/fixture coverage; the #1121 stale-head protections and actionable refusals await release and upgrade; live re-file verification not run | `test/review-head-binding.test.ts` covers both-head refusals, moved-PR commitless re-files and omitted-head stale approvals; `test/task-review-refile.test.ts` covers owner re-submit, reviewer withdrawal, legacy verdict heads, merged/cancelled refusals and held-screen races; `test/sdlc-queue.test.ts` and `test/sdlc-merge.test.ts` reject stale approval at queue and dispatch. `ahub task show <id> --json` exposes `approval.head`, `latestHead`, `mismatch` and `current`; verdict history retains `reviewedHead` |
17
19
  | Default agent set started by `ahub up` (#608, `start.agents`) | off here: this repository's agents are started by their own terminals; it is a machine-local setting and a committed config cannot turn it on. On for a person who runs `ahub setup` and `ahub up` on a project that lists agents. | each `ahub up` prints one line per agent (started, already attached, or the refusal with the manual command); the `peer_start` replies in `hub.log` and the terminal provider's recorded starts show whether a listed agent attached, and `ahub status` shows its state after |
18
20
  | `ahub down` (counterpart of `ahub up`) | on: an alias of `ahub kill` for stopping this project's hub | the person stops the hub with `ahub down` and `ahub status` then reports it stopped; test/identity.test.ts keeps it person-only (`console`) and labelled `down`, and test/help.test.ts holds its help entry |
21
+ | Person takeover of a channel peer (#1174) | source and isolated fake-session coverage; active after release and plugin upgrade; no live handover receipt yet | one explicit takeover request, old next-tool person-detachment cause, standby attachment identity, no process stop or automatic delivery replay; fake CLI/console authority and stale-generation regressions |
19
22
  | Encrypted rooms (#446 X1) | off; source-only crypto/wire, with no broker, room link, invitation CLI or delivery enabled | real WebCrypto vectors and negative wire fixtures; atomic memory fake ports establish ordering only; durable X3 state and the X7 two-machine person receipt remain unverified |
20
23
  | Blind room broker (#446 X2) | off; standalone source/fixture work, no public TLS service or hub room link configured here | fake-client auth/fan-out/revocation/restart, ciphertext-only child-process canary, owner admission/offline room reset, queue caps/TTL/FIFO/migration, safe directory modes, and person-only CLI tests; live proxy/account/X7 receipts remain unverified |
21
- | Room membership and presence (#446 X3) | off; no person room enrollment or exposure configured here | isolated fake-hub/broker tests for OOB enrollment, MITM refusal, durable sequence/replay/grace, explicit fingerprint confirmation/abort, refused-frame retirement, exposure revocation and ids-only presence; room list/status for ids and link state; X4 chat and X7 two-machine live receipt remain separate and unverified |
24
+ | Room membership and presence (#446 X3) | off; no person room enrollment or exposure configured here | isolated fake-hub/broker tests for OOB enrollment, MITM refusal, durable sequence/replay/grace, explicit fingerprint confirmation/abort, refused-frame retirement, exposure revocation and ids-only presence; room list/status for ids and link state; #1204 T1 fixtures cover unchanged-presence dedupe, 1,000 flips with an offline member, latest-only ephemeral broker retention, old-broker wire fallback and same-frame queue_full backoff; CI task 742 adds counter-bounded deferred-presence reuse, concurrent chat ciphertext/Store FIFO and post-close publication regressions (test/rooms-presence-coalescing-742.test.ts, test/bus-closed-publication-742.test.ts); Linux diagnostic counts and targeted hosted macOS acceptance (unchanged AC1 787.72 ms; controlled idle-entry 783.51 ms) are in docs/verification/2026-11-06-rooms-presence-cost.md; no live rooms receipt; X4 chat and X7 two-machine live receipt remain separate and unverified |
25
+ | Presence publication cost (#1204, #740) | source and isolated broker coverage; installed rooms remain off here | unchanged 1,000-flip AC1 test under its 20 s guard; transient pause/resume journal-write count, explicit manual-pause persistence, requestReply and exposure fences; hosted macOS is the acceptance gate |
26
+ | Room automatic resend and late settlement (#1204 T2) | off here; source and isolated in-process broker coverage after validation; release and person enrollment/exposure required; live two-machine behavior remains unverified | AC2-AC4: receiver offline for 15 minutes then each application admitted once; pre-enqueue refusal retries without uncertainty; ACK deadline resends within the original TTL; signed late receipt settles once; local receipt commit survives restart before broker storage; no person-disposition override or native/task-read evidence |
27
+ | Room liveness and epoch-change preview (#1204 T3, #736) | source and isolated fixtures, validation pending; rooms remain off here and installed/two-machine behavior is unverified | `rooms-liveness-link-736.test.ts`: signed offline within five seconds, idle crossed-request convergence and no reply loop, synchronous stop fence/FIFO preservation; `rooms-presence-aging-736.test.ts`: three local periods to advisory unknown without changing outbound admissibility, legacy untracked behavior, restart and clock rollback; `rooms-rotation-736.test.ts`: sorted ids-only preview, changed id-set/digest refusal, sealed hold versus Bus-only queue, unchanged person/terminal disposition; `rooms-liveness-status-736.test.ts`: native state preserved, broker upgrade guidance and named epoch-rotated cause; defaults 30 s refresh, 90 s aging, 2 s stop bound; exact-head gate and live proof remain separate |
22
28
  | Worktree shelf (#1057): `ahub worktrees`, `tidy`, `tidy --confirm <fingerprint>`, the Agents panel section, Park/Pin, the explicit PR refresh and the inbox alert | fixture-only until a person uses it, and only after release and upgrade: this repository keeps every task in `.worktrees/<name>`, so it is the first real use, but nothing here has run `tidy --confirm` yet, and `worktrees.quiet_days` stays 7 until the owner has used it for a week | `ahub worktrees` live and settled counts over a week, and how many settled rows a person then Pins (a Pin means a rule was wrong); `tidy` preview refusals by reason (`task not approved or cancelled`, `unpushed commits`, `branch changed since preview`); after a confirm, `git worktree list` against the preview; test/worktree-shelf.test.ts, test/worktree-tidy.test.ts and test/worktree-panel.test.ts (a PII row stays live and redacted, a branch switch refuses, no GitHub call on a read, the daemon removes nothing) |
23
29
  | Message bus, digests, priorities | on | `ahub report`: messages, digests, overflow, undeliverable; delivery latency from events |
24
30
  | Control-send recipient normalization (#963) | active after release and hub upgrade; this PR does not change the running hub | string recipients reach only the named peer, invalid shapes publish nothing, omitted/empty arrays preserve broadcast; real native recipient behavior remains unverified |
@@ -45,7 +51,7 @@ agent-hub is developed with agent-hub. This page lists every feature, whether it
45
51
  | `ahub deliverables` and the dashboard badge (#509b) | on after the release: `ahub deliverables [--check]` on this repository's board; only tasks that named deliverables appear, so it lists document-shaped work only | the person runs `ahub deliverables --check` after a document task is approved and the next time its file is edited: a `changed since review` row names the edit; `Deliverables n` chips on cards; `not measured now` in the sheet means nobody ran `--check` since the daemon started |
46
52
  | Roles per kind and form (#525-1, #525-3, #525-4, #525-5) | on: claude holds the implementer role in the project checkout; isolated terminal and headless Claude implementer support is on, but neither form is declared here | tasks by owner; verified owner isolation and execution-root refusals; headless task-session resumes, inbox task-session refusals, fresh notices and person approval outcomes; missing or mismatched root must never admit a write owner |
47
53
  | Codex planner sandbox and role visibility (#525-6) | source/fixture coverage after merge and release; six declared planner/implementer/reviewer instances are not configured here, and the person live leg is unverified | fake thread-start/resume read-only confirmation and refusal before adoption; planner-only Claude mutating-tool grants withheld; status form/capable roles/isolation and task planner/holder-reason parity across CLI, console, dashboard and explain; the native six-instance receipt in `docs/smoke.md` is recorded separately when run |
48
- | Floor policy (#999 T3, `action_policy.floor`) | source and test after merge and release; off here (a person turns it on in the untracked `config.local.json`), and the running hub takes it after upgrade | `test/action-floor.test.ts` (every allowed, asked and opaque case, status guard rows) and `test/daemon-action-floor.test.ts` (the Pi permit path through the real daemon: allowed calls run with no prompt, force and default-branch pushes, tags, `gh release`, `--admin` and other repositories ask); T4's live receipt records it under Orca |
54
+ | Floor policy (#999 T3, `action_policy.floor`) | source and test after merge and release; off here (a person turns it on in the untracked `config.local.json`), and the running hub takes it after upgrade | `test/action-floor.test.ts` (every allowed, asked and opaque case, status guard rows) and `test/daemon-action-floor.test.ts` (the Pi permit path through the real daemon: allowed calls run with no prompt, force and default-branch pushes, tags, `gh release`, `--admin` and other repositories ask); T4's live receipt records it under Orca. `2>/dev/null`-style redirects to `/dev/null` and `/dev/tty` no longer classify as destroy (#725, #1193; `/dev/stdout`, `/dev/stderr` and `/dev/fd/<n>` stay destroys because on Linux they reopen and truncate the file fd 1 or 2 already points at): active after release and plugin upgrade, unverified in a live agent-research session; the metric is the count of `always-ask: destroy` prompts on commands that only redirect to `/dev/null` or `/dev/tty`, which should reach zero, and `test/action-class.test.ts` (the `#725 device redirect` rows, including the four aliases, `/dev/fd/3` and lookalikes) |
49
55
  | Confirmation-point inventory (#999 T0) | source and test coverage; `ahub autonomy status` reads the inventory after this feature is released, with no default prompt change from the data alone | `test/confirmation-matrix.test.ts`: every adapter-catalog kind has a tool-approval and a classed-action row per environment, kinds facts equal the definitions, cited source lines exist; the `unknown` rows are the list T4's live receipt must close |
50
56
  | Project autonomy preset (#999 T1) | off on this repository until released and explicitly enabled by the person; project scope only | focused person-only, retained-consent, metadata-independent off and authenticated/fallback preset undo tests; the preset writes `action_policy.floor` (#999 T3) and off removes it; status leads with a peer table separating current modes from desired defaults, groups identical floor verdicts and unknowns once with peer lists (#1137), and reads each floor class from the host verdict (`floorEnforcement`), naming the unverified guard where the hub does not see the call first; the Orca zero-prompt native task remains T4 |
51
57
  | Machine autonomy and used Claude homes (#999 T2) | off until released and explicitly opted in by the person; no real home is changed by fixture tests | trusted-default/unsafe-default startup consent, all-project preview/apply, init inheritance with unchanged absent-file ask defaults, and isolated per-home plugin/hook setup/status tests; #1069 covers preserving project/local plugin rows while configuring only user scope, plus recording `start.agents` after a failed home step; metric: applied/refused registered targets and missing/unknown discovered-home states; native Orca receipt remains T4 |
@@ -54,12 +60,15 @@ agent-hub is developed with agent-hub. This page lists every feature, whether it
54
60
  | Symbol preparation and historical recall (#684 phase 3 follow-ups) | off until release | exists calls per no-reviewer completion; raw-signature fail recall; indexed-definition/unknown labels; unchanged note/candidate bytes and neutral cleanup failures |
55
61
  | Overlap notices | on; plan symbols match by normalized name (arguments stripped, a bare name matches the last segment, two qualified names must be equal; #682 phase 1b) | `ahub report`: overlap warnings, task pairs; false-overlap rate, and symbol-only matches on bare common names (#682) |
56
62
  | Backlog phase grooming and issue planning (#759) | off until released and the running hub supports the new planning fields; relinks for cards #138/#287 remain a post-landing planner action | related-phase duplicate count; unrelated same-issue duplicate controls; planning revisions with `fields: ["sdlc"]` or `[]`; old spec acceptance absent after relink/undo |
63
+ | Person-run backlog legs (#1176, `needsPerson`) | on after release: this repository parks live-receipt legs (tasks 138, 181, 188, 468, 594) reserved to an agent only as a placeholder; marking them `ahub backlog person <id> <rev> true` keeps the hint and stops auto pull and the idle sweep offering them | `ahub backlog list` ends with `waiting on you: #N...` and `ahub status` carries `WAITING ON YOU`; the `backlog pulled` and `idle sweep` history of a marked card stays empty; `test/person-run-cards.test.ts` pins every gate. Marking the five tasks is a person's command after release, not part of the PR |
64
+ | Backlog return with a reservation edit (#1177, #1154) | on after release: a person or planner moving an untouched card to another peer, or parking it with no reservation, runs `ahub backlog return <id> <rev> <reason...> --reserved <peer>` or `--unreserve` instead of a plan then a return | the card's history shows one `backlog returned` entry carrying the reservation change (not a separate `planned` entry before it); `test/backlog-return-reserved.test.ts` runs the real CLI against a daemon for both flags, the default and the refusals |
57
65
  | Planner WIP caps and backlog pull (#655, #718) | on (`tasks.wip_limits.codex = 2`); reserved-pull handoff on after release; the active / review-wait / reserved split (#784) shows in `ahub status`, the console Peers detail and the dashboard legend after release | `ahub status` split per peer (a full cap held by review-wait shows as such); board history/offer readback: reserved `backlog pulled` -> `assigned` -> `accepted` for the same peer; stale or unowned offers, capacity refusals and held-ready entries |
58
66
  | Planned backlog starts (#1076) | source/fixture coverage; active after release and hub upgrade | future planned cards appear as `scheduled` in planner grooming; no auto-pull, `backlog_ready` notice or ready-queue owner offer before `plannedStart`; exact-boundary and after-date eligibility; queue-pressure origins remain retryable until due |
59
67
  | Task sweep (stalls, idle owners) | on | stall and sweep events per week |
60
68
  | Turn-free coordination | off: experimental; needs a separate measured trial | coordination events, conflict notices |
61
69
  | Pull-only peers (agy) | on; `hub_inbox` drains to 64 KiB (#713) | unread offers, empty-inbox polling (#681); queued count and oldest queued age per pull-only peer in `ahub status`; reads that said `N more waiting` |
62
70
  | Bulk clear of failed queue rows and the settled-row filter (#1096, `queue_discard_states`) | on after release and upgrade: `ahub queue discard --peer <id>` also takes the peer's unsettled failed rows and `ahub queue list` hides settled rows (`--all` shows them); fixture coverage only (`test/queue-discard.test.ts`: real daemon and CLI, a fake old hub for the capability refusal). A person using it on a real backlog is not yet recorded | `ahub queue discard --peer` runs that name a failed count, `queue list` stderr lines `N settled deliveries hidden`, and the failed-row count per peer in `ahub status` falling after a discard; a refusal that names `queue_discard_states` means an older hub is still running |
71
+ | Failed-delivery cause and status signal (#1178) | source and isolated fixture coverage after validation; active after release and hub upgrade; real-backlog use is not yet recorded here | `ahub queue show` and `queue list --json` expose the fixed public-safe cause (legacy absence stays unknown); `ahub status` names each nonzero unresolved failed count and the peer-specific discard step; resolution removes the count, including retry parents; new journal, bus, output and daemon regressions cover cause privacy and metadata-only counts |
63
72
  | Completed-change notices, grouped and superseding (#713) | on after release; accepted tasks only | queued `is done and touches` notices per peer (one per done, not per task and per repeat); `superseded completed-change notice` discards; `ahub queue discard` uses |
64
73
 
65
74
  ## Review and lifecycle
@@ -73,6 +82,8 @@ agent-hub is developed with agent-hub. This page lists every feature, whether it
73
82
  | Console symbol-overlap advice (#684, #379) | on after release; read-only task detail, observed names only | other task IDs/shared paths and screened different-symbol verdict; no pair involving PII, no claim on unknown/incomplete evidence |
74
83
  | Dashboard task sheet Code group (#684 phase 2c) | on after release; read-only, observed names only, none for a private task | task sheet Code group shows the changed symbols or `unknown (no code facts)`; symbol clicks that reach a non-empty graph once phase 3 lands |
75
84
  | Review brief, review findings, Important gate (#481 S3) | advise | Important findings per PR, rounds to approval |
85
+ | Anchored review comments and findings (#1056) | source and synthetic-fixture coverage; active after release and hub upgrade; the owner's live document package-format confirmation remains open | draft/edit peer turns (target 0); one envelope and one history count per explicit Send; anchored/moved/outdated/unavailable counts; console-only refusals and quote/body canaries in logs, capture and public views; immutable shared acon fixtures; explicit refresh, task-bound memory versus current-turn capture scope, pruned reviewer requests, refused terminal resend, anchored-batch sibling remember/propose refusals, invocation answers after mid-turn inbox pulls, actionable provenance refusals, anchored-submission final-answer/history canaries, same-session restart retention, real sessionless WsPeer handoff and ordinary-input release, unidentified earlier-SID holds and reconnect status, safe first-SID confirmation, legacy coverage completion and NULL-receipt pruning, corrupt-provenance status, clean new-session admission, evidence-backed sticky restored pull-only holds, saved-package recovery and final-only resolution regression fixtures |
86
+ | Memory sync health and write policy (#1173, #743) | source adoption of exact-pinned agent-mem 0.21.0 and isolated fixtures; not deployed here; AC4 bounded diagnostics available, AC5 successful-result count projection only; default-host v3 sync lacks fresh-attestation integration; legacy activation blocked on agent-mem#281; #1173 stays open; activation, live rollout and successful person-home sync unverified | automatic sync outcome/cause/count; SDK successful-placement result projection with withheld/exported counts (fixture seam; active-v3 sync unverified); legacy declined-sync refusal names inactive placement and the missing core activation command; bounded SDK inspection of physical declined IDs and safe rule categories, with explicit incomplete results; one failure log per unchanged known signature; existing declined long-write and kept-short promotion fences |
76
87
  | Spec acceptance (#481 S1) | advise | build entries without acceptance |
77
88
  | Lifecycle identity linked by the hub (#694, #723) | on in advise; links at propose and accept from the title and `refs.repo`, with a groom backfill as the safety net | open tasks still showing `issue identity missing` right after propose (should be only titles that name no issue or a qualified title without `refs.repo`) |
78
89
  | Planner plan review (#481 S9) | advise | plans reviewed before build |
@@ -163,6 +174,7 @@ agent-hub is developed with agent-hub. This page lists every feature, whether it
163
174
  | Cross-hub read invocations (#446 X6) | off until both persons expose peers and independently pin the exact capability/digest/read class; discovery is manual; two-daemon broker/crypto fixtures are the verification target, live X7 receipts remain unverified | source/target invocation ids, request admission versus capability result and result transport outcomes; needs_review counts and no duplicate native dispatch |
164
175
  | Dogfood scorecard (#687) | on after release; read-only weekly receipt | `ahub report --dogfood --since 7d --json`; bounded evidence and explicit unknowns |
165
176
  | Claim verdicts and fact_check provenance (#901) | source/fixture coverage; the live research pilot (a person entering verdicts with `ahub task claim`) remains unverified | `ahub report --dogfood` research rows: `claims.reachedPersonVerdict`, per-slot `personVerdict`/`modelLabel`/`agreement`, `factCheck.selection`/`passed`/`failed`/`missingKeysPerRun`; null until a verdict or a keyed check is recorded |
177
+ | Claim evidence links (#931 T1) | source/isolated fixtures after merge; this dev-profile repository publishes no evidence writer and has no live research evidence pilot | binding from `hub_task_show` / `ahub task show --json` records support/counterevidence and refuses after a claim edit; PII/Dev views expose no bindings; unknown/stale/private/foreign writes leave no revision, duplicate replay adds none, changed links retain prior revisions; ids-only receipts and capture canaries; projection/Unicode resolution, private-upgrade readback and synthesis readiness remain T2/T3, not verified by T1 |
166
178
  | Research campaigns (#930 T1) | source and CPU-only fixtures; no real campaign here; the public study CLI and tools are the T3 row below | draft versus child counts, stable unit/task ids across approved revisions and restart, intake reuse, stale role/source/revision refusals, private views, rollback and research_records recovery floors; no live/model receipt claimed |
167
179
  | Study CLI, tools and readback (#930 T3, #601) | Fake peers and a real daemon over its control socket, plus the real `ahub` process against that daemon; no native peer or live model has run a campaign here, so fake versus native evidence stays separate (the `research-live-example` claim in docs/profiles.md is still open) | `test/study-daemon.test.ts` (person's draft, approve and close; no agent reaches an approve or close op; planner drafts and cannot approve; read-only show; a private source shows no source id, question, scope or path; handoff only from the bound owner with every missing field named; the three-source, two-peer example moving from none to incomplete to complete), `test/output-cli.test.ts` (parse before connecting, `--json` byte for byte), `test/study-guidance.test.ts` (guidance names only published tools) |
168
180
  | Research dispatch and coverage (#930 T2) | Source/isolated fixtures only; no live research campaign | `test/research-dispatch.test.ts` and projection/recovery fixtures: no expired/revised dispatch, named-budget coverage, handoffs and shared ledger |
@@ -182,7 +194,11 @@ agent-hub is developed with agent-hub. This page lists every feature, whether it
182
194
  | Guarded merge and result recording (#786 T1/T2, #899) | `auto_merge = "dry-run"` first in this repository's `.agenthub/sdlc.toml`; `on` after one dry-run report matches a manual merge; the report prints for a person by default and as a document with `--json` | `ahub sdlc merge <task> --dry-run` reports the mode, the decision, each refusing gate and the `gh` command it would run, readable without `--json`; `--json` is byte for byte the view printed before #899 (`test/sdlc-output.test.ts`); merges the hub made against merges a person or reviewer made by hand; host merge/cleanup records and owner-notice status; fake-gh ordering/idempotency proof, no live cleanup claim |
183
195
  | Peer `DOING` column (#878) | on after the release for Claude sessions started with `ahub claude` and the Codex TUI; ACP peers show it only when their agent sends a plan | `ahub status` shows a title or current item for an active Claude and Codex peer without a hub tool call; `[private]` during PII work; the column disappears when no peer has a value |
184
196
  | Pi/local session titles (#1055) | available after release for hub-owned Pi/local sessions; native Pi setter/readback is probed, installed use and the live restart receipt remain unverified (#1143) | first `hub_set_title` succeeds once, later agent writes return `already_named`, and person rename replaces it; live status shows the title or a screened unnamed label while `status.json` omits both |
197
+ | Floating pet retirement (#1012) | pointer available after release; acon Mini use on this repository remains unverified | `ahub pet --window` and `--window --close` print the `acon ui` Mini pointer with exit 0 for person/agent/live-test; no window host starts; retired `--palette` prints the same pointer without writing config, legacy `pet.window` is ignored once, terminal pet frames remain covered by their unchanged tests |
185
198
  | Console and dashboard | on; #719 pending-history reads and bounded native launch retries take effect after release | control-request latency, connection timeouts and launch retry notices; daemon CPU/RSS samples, measured separately from lifetime CPU |
199
+ | Completions: a run a person started settles into a result record (#1054 T2/T3, `completions`) | source and fake-adapter coverage only until released: a completion exists only for an adapter with a code-verified settle signal (Claude, Codex, Pi, local, Kimi/Jcode headless, Qwen); Kimi in its own TUI, agy and person-typed Codex turns create none, so this repository's TUI-driven agents (Claude, Kimi) will show few until a signal lands. The person reads them with `ahub results` (unread marked, `--read`, `--read-all`, `--unread-last`), as a `watch` need in `ahub status` and `ahub console` (and, from #1054 T6, in the Results section of the Agents view and in the acon inbox: see the next row) | `completion` events count `done`, `failed`, `limit` and `interrupted` with `ms`, and `completion_seen` events count how many results a person actually read and on which surface (the share of unread results older than a day shows whether results are noticed at all); the share of `interrupted` over all shows how often a restart cuts a run, and a run that never settles shows as an open row in `hub.db` `completion_runs`; `test/completions.test.ts`, `test/completions-daemon.test.ts` and `test/completions-receipts.test.ts` pin the rule, AC1 to AC7 and the privacy sinks; no prompt or reply text is ever stored |
200
+ | Results in the dashboard and the acon inbox (#1054 T6, T7) | on after release; with a console that advertises `inbox.result` and `action.meta` (acon 0.1.3 does) the person sees unread results as `result` items in the acon inbox, and in any case in the Results section of the Agents view with Mark read, Mark all read and Mark last unread again; the one-line detail is the head of the request, read from the bus at display and never stored. This repository's TUI-driven agents (Claude, Kimi) create few completions until a settle signal lands, so the first live receipt is a headless run or a Codex or Pi session started from the console | the share of results marked read from `acon` versus `dashboard` versus `terminal` (`completion_seen` `surface`), how many unread results are older than a day, and whether any `result` item reached an acon console without the features (it must not: the plugin publishes only with both); `test/results-plugin-1054.test.ts`, `test/results-dashboard-1054.test.ts`, `test/results-panel-1054.test.ts` and `test/completion-detail-1054.test.ts` pin the item, the receipt authority, the page and the privacy canaries (AC6 plugin part, AC8). The item is checked against acon's own published `inboxItemProblems` and `stableAtProblems` (`@staix/agent-console/conformance`, 0.1.3), but it is not yet seen in a running acon console: that is the one open live receipt, and it needs acon with `inbox.result` and a person |
201
+ | Questions for the person: `hub_ask` (#1053 T2 to T6, `questions`) | available after release; source and fake-adapter coverage only: no live receipt per client yet (T1 measures the MCP tool-call timeouts and confirms the 45 s and 300 s wait windows). A person answers in `ahub console` (`A`), with `ahub answer`, or in the acon inbox form where the console advertises `inbox.form` and `action.meta` (the form rendering is agent-console#27; before it ships the item is a plain decision with an `open` verb) | `question` events count `asked`, `answered`, `pending`, `superseded`, `cancelled`, `expired` and `unavailable` with `latencyMs` and `surface` (`terminal`, `console` and `acon` show which surface people use); the share of `pending` over `asked` shows whether the wait window is long enough, `expired` shows questions nobody saw, the `question` need in `ahub status` shows how long a question waits, and `test/questions.test.ts`, `test/questions-daemon.test.ts`, `test/questions-console.test.ts`, `test/questions-console-daemon.test.ts`, `test/questions-plugin.test.ts` and the Pi bridge test pin the paths; no question or answer text is ever in the events, the log or memory |
186
202
  | Usage view (#683) | CLI (`ahub usage`, U2), console Usage tab `7` (U3) and dashboard Usage view (U4) shipped | its own panels |
187
203
  | Kimi usage from its wire log (#285 phase 2) | on after the release when `budget.kimi_tokens_5h` is set; this repository sets it | `tokens` and `usage` (`kimi_wire`) events per Kimi turn, the `tokens` window in `ahub budget`, and the count of `kimi usage unknown` notices; the live check against Kimi's `/usage` (AC11) stays opens |
188
204
  | Doctor memory-capture rows from agent-mem setup state (#1008) | on after release; `ahub doctor` runs here and reads `amem setup <tool> --json` (a preview that writes nothing) for `codex` and `kimi`; no dotfiles text and no `dot ai memory status` call | `memory capture: codex` and `memory capture: kimi` rows read ok (set up), warn with `amem setup <tool> --apply` (pending), warn with `client missing` (the native CLI is not installed) or unknown (no `amem`, a refusal or unparseable output); the Codex row notes the native `/hooks` review; a fixture run proves `dot` is never called |
@@ -198,6 +214,9 @@ agent-hub is developed with agent-hub. This page lists every feature, whether it
198
214
  | Benchmarks (#251, #637) | off: `bench.enabled = false`. The task-routing gate (#637 part 1) is on in CI for every PR: `routing-receipt` fails a change to `templates/routing.toml` without a matching receipt; the model-routing gate (#637 part 2) is the second step of the same workflow: `scripts/check-model-routing-receipt.ts` fails a change to the model-routing keys without a matching `-modelrouting-` receipt, and wants a by-hand `--live` half when an alias or judge moves | bench receipts; `docs/verification/*-routing-*.md` and `*-modelrouting-*.md` receipts; a `routing-eval` check run on every PR with `eval_pass_rate=` and `eval_suite=` in its title (name it as `eval_check` to make it the eval gate); `bun scripts/benchmarks/routing-gate.ts` and `model-routing-gate.ts` print the default's numbers (100% on both splits); the live half (served model ids) has not been run against a gateway yet |
199
215
  | Disposable smoke runner (#787 Q2) | off on this repository: it refuses this checkout; use a new temporary fixture; real-account Q2 remains unverified | sanitized receipt checks/counts/timings, zero owned survivors on timeout; fake transport tests are separate |
200
216
  | Plain corpus evidence in ask and task briefs (#510b) | off here until a person configures plain `corpus.paths`; vaults remain refused; no corpus body is written to amem | `note:<path>#heading` ids in ask evidence or public task briefs; PII/deny and stale-context refusal regressions |
217
+ | acon inbox snapshot speed and bound (#727, #1196) | on after release and `ahub setup`: the acon plugin asks every running project at once (not one after another), shares one computation among concurrent polls, serves a result younger than 1 s as it is and one up to 10 s old at once while one refresh runs, drops the cache on any action, waits at most 2 s for a project and keeps that project's last rows meanwhile, and returns at most 50 items (decisions first, then reviews, results, candidates, alerts) with a `more` count. Measured on the owner's Mac before the change: 2.5 s for 434 B in the console, 0.84 s median when run alone here, against a 2 s `pollMs`. Unverified: the owner's live console p95 after the change | the inbox `snapshot` wall time seen from the acon console on the owner's Mac (target p95 under 1 s), `test/acon-inbox-727.test.ts` (parallel asking, one computation for 20 callers, cache and background refresh, invalidation by an action, the 50-item bound and `more`, the project budget and a p95 over a five-project fleet), and the uncached median over this machine's real registry (0.84 s before, 0.37 s after) |
218
+ | Global CLI and plugin step after `ahub upgrade` (#718, #1179) | on after release: a completed or scheduled upgrade ends with `bun add -g @staix/agent-hub@<version>` and `ahub setup` when the global CLI or the shared Claude plugin is older than the daemon it installed (a handed-over upgrade speaks for the older CLI that started it), and says nothing when both are current or the plugin cannot be read; `release-prepare` writes the same step into every new `Upgrading to <v>` note and `release-tag` refuses a release without it. Unverified: a live handed-over upgrade on the owner's Mac, where the old CLI starts the new coordinator through `bun x` | the upgrade's own output on the owner's Mac after the next release, whether `ahub doctor` still warns afterwards (it should not once the person followed the step), `test/runtime-version-warning.test.ts`, `test/upgrade-interactive.test.ts`, `test/upgrade-cli.test.ts` (a real hub with a fake `claude` and `npm`), `test/release-prepare.test.ts` and `test/release-tag.test.ts` |
219
+ | Hard-coded values inventory ratchet (#719, #1180) | on now: `scripts/check.sh` runs `bun scripts/inventory-hardcoded-values.ts --check`, so a new constant must be classified in `scripts/inventory-hardcoded-values.classifications.json` before it merges, and the 1222 keys still unclassified sit in `scripts/inventory-hardcoded-values.baseline.json`, which only shrinks. This does not close #1180: those 1222 are classified later in reviewed batches (`docs/verification/2026-11-05-hardcoded-values-ratchet.md`) | the unknown-key count (the baseline's length) falling to 0 over the follow-up batches and `check: OK` staying green with no key added to the baseline, `test/inventory-hardcoded-values.test.ts` |
201
220
 
202
221
  ## Decisions
203
222
 
package/docs/events.md CHANGED
@@ -46,6 +46,9 @@ marked `private: true`, and PII tasks `pii: true`.
46
46
  | `permission_mode` | `peer`, `from`, `to` (`ask`, `ask-when-needed`, `never-ask`): a person set a peer's permission mode (`from` equals `to` when it was already in force) or confirmed a never-ask default (issue #242) |
47
47
  | `settings` | `key` (a registry key such as `permission.kimi` or `routing.classes.implement.peers`), `from`, `to` (the value before and after: the one in force for a setting that applies at once, the stored one for a setting read at a start; a closed word, a boolean, a list of peer ids, a route id, or null for none), `source` (`dashboard`, `terminal`), and `undo: true` for an undo: a person changed a setting from the dashboard or `ahub settings` (issue #269). A mode change also writes its `permission_mode` event |
48
48
  | `permission` | `id`, `peer`, `event` (`requested`, then one of `answered`, `expired`, `cancelled`) and, on the closing event, `latencyMs`, `surface` (`console`, `dashboard`, `terminal`: where a person answered; absent when the hub closed the request) and `option` (the kind of the option chosen: `allow_once`, `allow_always`, `reject_once`, `reject_always`): one approval request relayed to the console and how it ended. `expired` is `approvals.timeout_s` without an answer. `cancelled` with a `surface` is a person's deny in the console or by `ahub permit <id> deny` (a deny on the dashboard is `answered` with a reject option); without one, the hub withdrew the request (its call or turn ended, the peer was stopped, or the hub is stopping). Never the title or the tool |
49
+ | `question` | `set`, `peer`, `event` (`asked`, `answered`, `pending`, `superseded`, `cancelled`, `expired`, `unavailable`) and, where they apply, `latencyMs`, `person` (only with `surface: acon`: the console's verified identity, one bounded line), `surface` (`terminal`: `ahub answer`; `console`: the `A` key; `dashboard`: reserved, no surface sets it today; `acon`: the plugin's answer with the console's person meta), `reason` (`superseded`, `stopped`, `turn_aborted`, `session_changed`, `expired`, `unattended`), `questions` (a count) and `private`: one question a peer asked the person through `hub_ask` and how it ended (#1053). Never a prompt, an option, an answer or any other text. The attention need a question raises (`question`, in `status.needs` and `attentionItems`) is not an event: it carries the set id, the peer and the time only |
50
+ | `completion` | `id`, `peer`, `outcome` (`done`, `failed`, `limit`, `interrupted`), `ms` (start of the run to settle): a run a person started settled (#1054). A run starts at the `turn_start` that consumes the person's message and settles only when the adapter's `settle` signal fired and nothing else is pending (no queued continuation, open permission or question, owned child, running check or held delivery); a restart cut short settles `interrupted`, once. An explicit person Stop and a budget pause create none. Never the prompt, the reply or any other text: the envelope ids live in `hub.db` and are read through the usual privacy path |
51
+ | `completion_seen` | `id`, `surface` (`terminal`, `console`, `dashboard`, `acon`): a person marked a result read (#1054). One per receipt written, never from a snapshot read or a listing, never the prompt or the reply. `surface` is where the person marked it: `dashboard` is the Results section of `ahub ui` and of the acon panel (the panel's own buttons), `acon` is the acon inbox's `result_seen` or "Mark all read" with the console's verified person meta, `terminal` is `ahub results`. Mark-unread deletes a receipt and records no event. The detail line shown beside a result (the head of the starting prompt) is built at display and is in no event |
49
52
  | `pii_screen` | `task` (absent for a budget hand-off or a note not about a task), `item` (`task`, `summary`, `review note`, `handoff`, `note`), `label` (`pii`, `clear`), `source` (`regex`: a pattern matched and no call was made; `screen`; `unknown`: no verdict, handled as PII), `category` (`name`, `student_id`, `phone`, `address`, `grade`, `health`, `other`), `miss` (why there was no verdict: `too long`, `off campus`, `timeout`, `unreadable`, `failed`), `ms` (absent for `regex`): one PII screen verdict while `signals.pii_screen = "local"` (issue #198); never the text |
50
53
  | `context_pressure` | `peer`, `source` (`claude_statusline`, `codex_token_usage`, `acp_usage_update`), `measuredAt` (epoch ms of the reading), `used` (the fraction of the context window in use, 0 to 1), `window` (its size in tokens, or null): a fresh reading of a peer's native context reached `context.gate`. Recorded once per crossing, until a reading below the gate or a new native session rearms it, and not while the peer is paused or offline or the hub is in recovery or stopping |
51
54
  | `quota` | `peer`, `windows` (`id`, `used`, `resetsAt`), `hard`, `measuredAt` (when the reading was taken, if not when it arrived: Claude's numbers come through a file) |
package/docs/memory.md CHANGED
@@ -1,6 +1,6 @@
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.18.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.
3
+ Agent-hub consumes the exact-pinned first-party [`@staix/agent-mem`](https://github.com/STAIxBWLB/agent-mem) library (the exact pin is in [package.json](../package.json); 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
 
@@ -260,3 +260,19 @@ Only an open, visible Memory view requests dashboard code work. Console task det
260
260
  Machine-local `corpus.paths` can supply public `note:<path>#heading` evidence to `ahub ask` and task briefs. It is separate from durable amem notes: the hub writes no corpus body into the index or store, never reads an Obsidian vault from disk, and applies deny/`pii.paths` before reads and full-note screening before excerpts. Native memory counts exclude corpus rows. Existing brief item caps and priority fail notes remain in force.
261
261
 
262
262
  PII questions and private task contexts do not query the corpus or shared observers. PII ask inference requires a positive `onCampus()` result, the pinned local fixed model and `onCampusOnly: true` through dispatch; unknown locality returns evidence without a model answer. Task/privacy/policy changes across awaits discard a brief before observation. Ask answers recheck the PII and note-PII verdicts of their gathered task rows and the caller-private context before dispatch and publication; unrelated board activity does not discard an answer. Corpus paths may be enabled with memory off; they are never exported as persistent memory automatically.
263
+
264
+ ## Automatic sync health (#1173)
265
+
266
+ Profile readiness and sync health are separate. `ahub status` and the dashboard Memory row show the current daemon's last automatic sync attempt: not run, not-configured, stopped, ok, or failed with a closed cause, declined count when known, and a next step. Upstream error text, remote URLs, row IDs and private patterns do not enter this status. This state is process-local; a restart starts at not run until its first attempt. Missing remote configuration is the neutral `not-configured` state; an interrupted/stopped attempt is neutral `stopped`. Both keep their safe next step and produce no failure log under `quietSyncError`. A successful attempt resets the failure episode.
267
+
268
+ Sync uses the configured agent-mem standalone policy, narrowed by the hub's current deny paths, PII patterns and project-private paths. Direct long-term writes clear that same policy before append. The core promotion callback rechecks each authored note payload: a declined note stays short while allowed notes can promote. Source logs are not rewritten. A policy is captured per attempt; the existing profile/selector and hub policy-version fences still apply, without claiming an atomic cross-process policy transaction.
269
+
270
+ An unchanged failure emits one safe log line per episode. A complete bounded policy inspection identifies the declined ID set for failure dedupe without logging those IDs or their text. An incomplete or unavailable inspection keeps the set explicitly unknown rather than treating it as empty. Other failures use a private error hash; no raw error enters status or logs.
271
+
272
+ `ahub memory declined [--json]` is a person-only, read-only inspection. It reports physical outbound row IDs and safe policy categories, never title, body, path nodes or literal private patterns. Reads defer promotions and recheck the profile binding. Forgotten originals still matter: `forget` changes materialized visibility but leaves the source row in append-only history. Do not use it as a promise to clear a sync refusal.
273
+
274
+ The exact-pinned agent-mem 0.21.0 supplies bounded `inspectSyncPolicy` inspection for shared-v1 and generation profiles. A complete result lists the declined IDs and safe rule categories for the inspected physical outbound history, including originals hidden by a later forget. An unavailable, stale or bounded-out inspection remains explicitly incomplete; it never proves that no declined rows exist. The hub uses the public SDK rather than private helpers, snapshots of full history or its own portable log reader.
275
+
276
+ The package also owns per-row local placement. The hub adopts only the projection of a successful trusted SDK placement result: `ok` with its verified withheld/exported counts. This is tested through an injected SDK-result seam, not an activated store. Agent-mem 0.21.0 requires a fresh `placementAttestation` for each v3 sync; the default hub does not supply one, so its active-v3 sync remains unsupported until legitimate host-attestation integration is added. The hub never fabricates that proof. Count projection does not delete or rewrite notes, change policy or promote a declined row; transport failures remain failures.
277
+
278
+ Legacy stores without active per-row placement still refuse a sync containing declined rows. Agent-mem 0.21.0 provides no activation command; the hub neither invents one nor supplies a local fallback or fabricated activation proof. The missing core activation path is tracked by [agent-mem#281](https://github.com/STAIxBWLB/agent-mem/issues/281). Complete bounded diagnostics address #1173 AC4; The AC5 result projection is ready, while default-host active-v3 sync still requires supported activation and fresh-attestation integration. #1173 remains open until #281 ships and the hub adopts its supported path. Source adoption and isolated fixtures do not establish activation, deployed behavior or a successful sync of the person's memory home.