@staix/agent-hub 0.12.18 → 0.12.20

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 (66) hide show
  1. package/CHANGELOG.md +21 -0
  2. package/README.md +3 -3
  3. package/docs/agent-notes/adapters.md +2 -0
  4. package/docs/agent-notes/bus.md +1 -0
  5. package/docs/agent-notes/daemon.md +2 -1
  6. package/docs/agent-notes/models.md +3 -0
  7. package/docs/agent-notes/tasks.md +3 -2
  8. package/docs/events.md +4 -1
  9. package/docs/operations.md +419 -17
  10. package/docs/quickstart.md +1 -1
  11. package/docs/security.md +18 -11
  12. package/docs/smoke.md +91 -0
  13. package/docs/specs/2026-09-19-agent-hub-design.md +412 -3
  14. package/docs/specs/2026-09-20-pi-local-models-design.md +2 -0
  15. package/docs/specs/2026-09-20-upgrade-recovery-design.md +287 -0
  16. package/docs/specs/2026-10-04-switchyard-source-port-design.md +10 -0
  17. package/docs/verified.json +13 -13
  18. package/package.json +1 -1
  19. package/plugins/agent-hub/.claude-plugin/plugin.json +1 -1
  20. package/plugins/agent-hub/server.js +63 -25
  21. package/src/adapters/claude-channel.ts +61 -20
  22. package/src/adapters/codex-appserver.ts +3 -0
  23. package/src/adapters/local-worker.ts +4 -0
  24. package/src/cli/console-state.ts +325 -102
  25. package/src/cli/console.ts +44 -23
  26. package/src/cli/help.ts +120 -0
  27. package/src/cli/identity.ts +3 -3
  28. package/src/cli/launch.ts +2 -0
  29. package/src/cli/main.ts +171 -75
  30. package/src/cli/preview.ts +1 -0
  31. package/src/cli/reset.ts +194 -0
  32. package/src/cli/status-lines.ts +9 -5
  33. package/src/cli/terminal-recovery.ts +140 -36
  34. package/src/cli/upgrade-runtime.ts +274 -56
  35. package/src/cli/upgrade.ts +366 -41
  36. package/src/hub/ask.ts +1 -1
  37. package/src/hub/board.ts +15 -7
  38. package/src/hub/bus.ts +60 -4
  39. package/src/hub/child-process.ts +1 -0
  40. package/src/hub/conductor.ts +38 -6
  41. package/src/hub/control-client.ts +5 -3
  42. package/src/hub/daemon.ts +70 -12
  43. package/src/hub/events.ts +7 -1
  44. package/src/hub/execution-budget.ts +15 -6
  45. package/src/hub/hub-tools.ts +4 -4
  46. package/src/hub/inference.ts +136 -1
  47. package/src/hub/lifecycle.ts +7 -1
  48. package/src/hub/peers.ts +2 -0
  49. package/src/hub/recovery-store.ts +51 -3
  50. package/src/hub/report.ts +43 -1
  51. package/src/hub/restart.ts +25 -0
  52. package/src/hub/routing.ts +48 -16
  53. package/src/hub/snapshots.ts +3 -2
  54. package/src/hub/tasks.ts +236 -83
  55. package/src/local/deny.ts +2 -1
  56. package/src/models/mlx.ts +11 -8
  57. package/src/models/ollama.ts +2 -2
  58. package/src/models/relay.ts +190 -42
  59. package/src/models/route/relay-selector.ts +43 -31
  60. package/src/models/route/runtime.ts +17 -7
  61. package/src/models/route/signals.ts +12 -1
  62. package/src/models/route/stage.ts +57 -3
  63. package/src/omniroute/client.ts +1 -1
  64. package/src/ui/index.html +1 -0
  65. package/templates/AGENTS.block.md +1 -1
  66. package/templates/routing.toml +8 -0
package/docs/security.md CHANGED
@@ -4,21 +4,22 @@ agent-hub connects agents that can each run commands. This page says what the hu
4
4
 
5
5
  ## Trust boundaries
6
6
 
7
- - **Other agents' text is untrusted.** Every message that crosses from one peer to another is framed as untrusted input (a channel tag with `meta.source` for Claude, a fixed header line plus a standing instruction for the others). A message body cannot forge the hub's own headers: such lines are quoted (`sanitize`). Replies inherit a hop count capped at 3, so agents cannot ping-pong forever; neither a digest nor a steer can reset it.
7
+ - **Other agents' text is untrusted.** Every message that crosses from one peer to another is framed as untrusted input: a channel tag with `meta.source` for Claude started with `ahub claude`; otherwise a fixed header line (`frame()`, `[agent-hub message from ...]`) with an instruction that says it is untrusted, the plugin's server instructions for a tools-only Claude reading `hub_inbox` and a standing instruction for the others. A message body cannot forge the hub's own headers: such lines are quoted (`sanitize`). Replies inherit a hop count capped at 3, so agents cannot ping-pong forever; neither a digest nor a steer can reset it.
8
8
  - **The control link is loopback plus a secret.** The daemon and the Codex proxy bind 127.0.0.1 only. The control WebSocket requires a per-run token (`.agenthub/state/control-token`, mode 600), and both servers refuse any request that carries an `Origin` header: any web page can open a WebSocket to localhost, and browsers always send `Origin`. External clients cannot claim the console user's id or a hub-managed peer's id.
9
- - **Permission prompts stay on.** `ahub claude` and `ahub codex` add nothing that weakens the agents' own prompts. Kimi's and `local`'s permission requests are relayed to the console and cancelled after `approvals.timeout_s` (default 120 s) of silence; the macOS notification for a waiting request carries the peer and the tool name only. The one exception is the hub's own tools (`hub_send` and the task tools, matched by exact name): Kimi's requests for them are approved once without a prompt and logged by name, the same trust Codex gets through `approval_mode` in the hub's config. They invoke hub-owned operations rather than arbitrary file or shell tools, and every call passes the hub's own checks. Identity comes from the exact permission title, or from the earlier tool-call title bound to the same call id and resolved against the configured MCP servers when the permission title contains argument JSON (Qwen). Payload text and unrelated display titles never establish identity. A payload longer than the console shows is marked as cut and never offers a session-wide grant. `--unattended` turns prompts off, says so loudly, and is never the default.
9
+ - **Permission prompts stay on.** `ahub claude` and `ahub codex` add nothing that weakens the agents' own prompts. Kimi's and `local`'s permission requests are relayed to the console and cancelled after `approvals.timeout_s` (default 120 s) of silence; the macOS notification for a waiting request carries the peer and the tool name only. The one exception is the hub's own tools (`hub_send` and the task tools, matched by exact name): Kimi's requests for them are approved once without a prompt and logged by name, the same trust Codex gets through `approval_mode` in the hub's config. They invoke hub-owned operations rather than arbitrary file or shell tools, and every call passes the hub's own checks. Identity comes from the exact permission title, or from the earlier tool-call title bound to the same call id and resolved against the configured MCP servers when the permission title contains argument JSON (Qwen). Payload text and unrelated display titles never establish identity. For ACP agents, a payload longer than the console shows is marked as cut and never offers a session-wide grant. Pi's write, edit, shell and git write requests are relayed the same way and can be answered `Always allow <tool> until Pi restarts`: later calls of that tool by the same Pi start skip the prompt but stay inside the path guard and sandbox. The grant covers every later call of that tool whatever its payload, is kept in memory only, cannot be given from the dashboard, and each call it allows is logged by tool name. `--unattended` turns prompts off, says so loudly, and is never the default.
10
10
  - **A committed config cannot choose launch commands, credential files, data endpoints or a wider sandbox.** The machine-local fields (`kimi_cmd`, `codex_bin`, `pi.cmd`, `checks`, `mlx.bin`, `mlx.runtimeDir`, `mlx.modelPath`, `omniroute.urls`, `omniroute.access_hosts`, the `omniroute` key files, `memory.worker_url`, `local.read_allow`, `local.bash_network`, `local.network_allow`) apply only from a config file git confirms nobody committed: `.agenthub/config.json` or `.agenthub/config.local.json`, matched by file identity so no other spelling the file system accepts slips past, and `.agenthub` itself not a committed symlink or submodule. Without a repository, or when git fails, they keep their defaults; an empty value always means the default. "Untracked" is answered by the repository that contains the project: a checkout copied or extracted into an unrelated repository, or into an ignored directory of one, is trusted like your own files. So a cloned repository cannot choose a launch command, a completion check, a gateway to send a key file to, a memory endpoint, or a wider sandbox. A command in `checks` runs as you, outside the local worker's sandbox, like a git hook. Nothing in `routing.toml` or in task text is ever run. `routing.toml` and the other shared fields still come from the checkout, and they matter: `routing.toml` picks the models the local worker and the hub's inference use at your gateway and can turn the PII constraint off, and roles and budget shape who does what. Review them in a repository you do not trust.
11
11
  - **Telemetry holds no bodies.** `.agenthub/state/events.jsonl` (issue #40) records envelope ids, routing and sizes, task ids and states, overlapping paths and token counts, and the task id (with a PII flag) that each usage and token record is attributed to. It never records a message body, a task title or detail, and marks private (PII) envelopes and tasks as such. It stays on the machine; `ahub export` only prints it.
12
12
  - **Snapshots stay in your repository.** Per-turn snapshots (issue #33) are git objects in the project's own object store, written through a temporary index; nothing is referenced, pushed or copied elsewhere, and `git gc` prunes them. They hold what the work tree held, including untracked files that are not ignored, so keep secrets in ignored files. They carry the repository's own permissions, and nothing caps their disk use but `git gc`. A turn of a peer holding an open PII task is not snapshotted; a PII file left in the project is snapshotted by later turns like any other file. `ahub undo` restores only files whose current content is exactly what the turn left.
13
13
  - **The edit hook reads, never decides.** `ahub check-path --hook` (issue #32) reads hub.db and returns context for Claude and a line for you; it sets no permission decision, so your permission rules stay in charge. It names other owners' task ids, titles and states, which then reach Claude's model; PII tasks are left out.
14
14
  - **The session record holds identities only.** `.agenthub/state/sessions.json` (issue #37, mode 600) keeps each attached peer's recovery metadata: launch options, session and thread ids, Pi's session file path. No message or task text; loss notices name deliveries by id, sender and public task title.
15
15
  - **Context checkpoints have a separate text record.** Native occupancy readings carry numeric metadata only. An optional context checkpoint persists its summary in `.agenthub/state/context-checkpoint-<peer>.json` with mode 0600 and attempts shared-memory storage when enabled. Completion requires the outstanding request id, current peer/native session and transport generation; new connection claims invalidate old requests. Private turns, every open PII task associated with the peer as owner or reviewer (including pending review), and PII-pattern summaries are refused before persistence or cloud memory. Its text is never broadcast, and context pressure does not pause, reassign or replace the session.
16
- - **The local worker is boxed in.** Paths are resolved through symlinks and must stay inside the project; a secrets denylist (`.env*`, keys, credential files, the hub's own state) applies to its file tools, its git arguments and its memory capture alike; `.git` and `.agenthub` are not writable. Writes, edits, shell commands and mutating git wait for approval, and the approver sees what will be written or run, with control characters escaped. Everything it executes runs under the macOS sandbox, attended or not. Since 0.10 the profile starts from deny default (issue #39): commands run and read only the system, toolchain and project directories (and the project's git dir and the selected developer dir; with network on, the public CA bundles); no writes outside the project, its git dir and a temp dir of its own (`TMPDIR`, made for each command and removed when it ends; issue #63); no `.git/hooks` or `.git/config` writes; no network, loopback included, unless `local.bash_network`; with it, only through the hub's egress proxy to the hosts in `local.network_allow` (issue #65), which refuses names that resolve to internal addresses (behind NAT64 with the well-known prefix, by the IPv4 address the answer carries; a network-specific prefix is not recognised), so claude-mem and the Codex app-server stay out of reach (`"direct"` opens everything, until 0.13.0). One channel stays open: `trustd`, which TLS clients need, can fetch a certificate's AIA or OCSP URL on a command's behalf, outside the proxy. The allow-default profile of 0.9 and earlier was removed in 0.12.0 (issue #83); a `local.sandbox` setting is ignored with a note. The shared temp dirs (the user's and `/private/tmp`) are closed. Without the sandbox there is no `bash` tool.
16
+ - **The local worker is boxed in.** Paths are resolved through symlinks and must stay inside the project; a secrets denylist (`.env*`, keys, credential files, the hub's own state and the state archives `ahub reset --all` leaves) applies to its file tools, its git arguments and its memory capture alike; `.git` and `.agenthub` are not writable. Writes, edits, shell commands and mutating git wait for approval, and the approver sees what will be written or run, with control characters escaped. Everything it executes runs under the macOS sandbox, attended or not. Since 0.10 the profile starts from deny default (issue #39): commands run and read only the system, toolchain and project directories (and the project's git dir and the selected developer dir; with network on, the public CA bundles); no writes outside the project, its git dir and a temp dir of its own (`TMPDIR`, made for each command and removed when it ends; issue #63); no `.git/hooks` or `.git/config` writes; no network, loopback included, unless `local.bash_network`; with it, only through the hub's egress proxy to the hosts in `local.network_allow` (issue #65), which refuses names that resolve to internal addresses (behind NAT64 with the well-known prefix, by the IPv4 address the answer carries; a network-specific prefix is not recognised), so claude-mem and the Codex app-server stay out of reach (`"direct"` opens everything, until 0.13.0). One channel stays open: `trustd`, which TLS clients need, can fetch a certificate's AIA or OCSP URL on a command's behalf, outside the proxy. The allow-default profile of 0.9 and earlier was removed in 0.12.0 (issue #83); a `local.sandbox` setting is ignored with a note. The shared temp dirs (the user's and `/private/tmp`) are closed. Without the sandbox there is no `bash` tool.
17
17
  - **Capabilities are enforced, not suggested.** `capabilities` (issue #39) is checked by the daemon where task operations and messages arrive, so a peer cannot get round it by phrasing. A peer can never answer a permission request: the control link takes `permit` from the console role only, and a message that quotes a permit command is just text. Both bind the hub's own tool paths: a vendor agent with its own shell in the project (Codex, Claude, Kimi) can read `.agenthub/state/control-token` and connect as the console, which only the local worker's and Pi's sandbox prevents.
18
18
  - **PII has an enforced path.** A task matching `signals.pii_patterns` goes to `local` or to nobody; its text is absent from other peers' envelopes, the console stream, the log and the board listing; the console user reviews it; `local` answers such a turn to the console only, keeps it out of its history, refuses it when the only gateway is off campus, and may not save notes or spin off tasks during it. Nothing about it is sent to claude-mem, whose observer is a cloud model.
19
- - **Free text is screened too.** On an ordinary task, a done summary (with its check output: a match withholds the whole note), a review note, an unmet item or a budget handoff that matches a PII pattern (issue #69) is not saved to claude-mem, and every peer, `local` included, gets a stub naming `ahub task show <id>`; the board keeps the text, `hub.log` notes the withholding by task id only, and `ahub ask` shows such a note only when its model is reached on campus. `hub_remember` refuses a match; what a vendor agent writes to its own memory is not screened.
19
+ - **A model screen can add to the patterns (issue #198).** With `signals.pii_screen = "local"` in `routing.toml` (default `off`; it applies while `constraints.pii = "local_only"`), a newly proposed task is read by a model before the hub does anything else with it: until the verdict it is on no board, in no envelope, before no triage model, and the hub makes no brief or note call for it. The proposer's own channel is not covered: when the local worker proposes it in an ordinary turn, claude-mem capture of that worker's tool calls (below) sends the arguments whatever the verdict. The screen runs on the model on this machine (MLX or Ollama, loopback only) or, without one, on the gateway only while `onCampus()` holds, and the gateway call is refused once more right before transport if the gateway is behind Access; task text never goes off campus to be screened. When the on-device generation slot is taken (Pi generating, another screen), the screen goes to the campus gateway instead of waiting, and on campus a device that fails or is still loading after 60% of the deadline hands over to the gateway too; off campus it waits for the slot and the device within the deadline. With the legacy MLX provider the screen may start an `mlx_lm.server`, as Pi's relay does, and it stays running until `ahub models stop`. Its answer is `pii` or `clear` plus a category from a closed list (name, student id, phone, address, grade, health, other). Anything else, a timeout (8 s, one call and one generation slot per item), a missing or off-campus model, or text longer than 6000 UTF-8 bytes (about 2000 Hangul syllables) is `unknown`, and an unknown task is a PII task: this is the hub's one model call that fails closed, so with the screener down every new task waits for `local`. An unknown verdict has the costs of any PII task, even when a second look clears it later: opening it lifts every silent turn-free cohort for good and turns facts off while it is open, and off campus a proposal without a class is refused (triage is not asked for a PII task there), with an error that says so. Off campus with the on-device slot taken, a task stays `unknown` until it is screened again: while it is still proposed and no peer ever owned it, the hub's release timer (every 60 s, one task at a time, the least tried first, at most 10 times per task in a hub run; never text too long to read) asks again, and `clear` lifts its `pii` signal and routes it through the class peers (a named owner is not remembered; a reserved one is offered first, as always). A task `local` took keeps its PII path, also once it is declined back to nobody. A pattern match is PII without a call. The task's history records the source of its `pii` signal (`regex`, `screen` with the category, or `unknown` with the reason), an unknown verdict is a console line naming the task id only, and `events.jsonl` gets a `pii_screen` record with the item kind, label, source, category, reason and latency, never the text. A routing explanation of a draft does not call the screen and says so.
20
+ - **Free text is screened too.** On an ordinary task, a done summary (with its check output: a match withholds the whole note), a review note, an unmet item or a budget handoff that matches a PII pattern (issue #69) is not saved to claude-mem, and every peer, `local` included, gets a stub naming `ahub task show <id>`; the board keeps the text, `hub.log` notes the withholding by task id only, and `ahub ask` shows such a note only when its model is reached on campus. `hub_remember` refuses a match; what a vendor agent writes to its own memory is not screened. With the model screen on, the same items (and `hub_remember` notes) go through it as well, one call each and only after the call's own checks (owner, state, verdict, a running check, memory enabled) pass: a `pii` or `unknown` verdict withholds the item exactly like a pattern match, and the board marks the history entry (`withheld`) so later views (task lists, completed-change notices, `ahub ask`, a reviewer replaced after a restart) keep withholding it. A budget hand-off is screened once per hand-off, whatever the number of tasks it moves; an escalation's reason is the hub's own text and is not screened. Pattern-only, without the model: the check output tail in a done note (only the summary is screened), a plan given with `hub_task_accept`, a decline reason, a task's refs (paths, branch), and context checkpoint summaries. Not screened at all (an existing gap, not new with the screen): the local worker's claude-mem capture records its tool calls in ordinary turns with their raw arguments, including the hub tools, so a title, detail, done summary, review note or `hub_remember` text it writes reaches claude-mem without a pattern or model check.
20
21
  - **Secrets stay where they are read.** The gateway key and Cloudflare Access values are read inside the gateway client and go only into request headers: never into logs, errors, envelopes, tool output, memory, or the generated Switchyard config (the key travels by environment variable name). Subscription logins of Claude, Codex and Kimi are never proxied or pooled.
21
- - **The hub's own model calls are fenced.** Digest condensation and task triage read agent-written text as data; their output is capped text framed as untrusted, or a value checked against a closed list. It is never used as a route, a peer id, a tool call or an instruction.
22
+ - **The hub's own model calls are fenced.** Digest condensation, task triage and the PII screen read agent-written text as data; their output is capped text framed as untrusted, or a value checked against a closed list. It is never used as a route, a peer id, a tool call or an instruction.
22
23
 
23
24
  ## What is not defended
24
25
 
@@ -48,12 +49,18 @@ It does not make the token inaccessible to an agent with unrestricted project
48
49
  shell access. The existing OS sandbox and loopback authentication boundaries apply.
49
50
 
50
51
  Steering tools require an explicit `conductor` role, independent of default-allow
51
- capabilities. Only one peer may hold that role. A conductor may inspect public
52
- state, assign or escalate work, start supported headless peers and place its own
53
- delivery holds. It cannot answer approvals, resolve durable deliveries, override
54
- budget pauses or release a human hold. Pending approval summaries exclude titles;
55
- PII tasks remain public stubs. Conduct events contain ids only. Supervision feeds
56
- use structured reasons and never carry check output or approval bodies.
52
+ capabilities. Only one peer may hold that role. Two task-scoped exceptions need
53
+ no role: a task's current owner and reviewer may read its public view with
54
+ `hub_task_show`, and the peer that proposed a task may redirect it with
55
+ `hub_task_assign` while it is proposed, nobody ever accepted it and its latest
56
+ move other than the hub's own was not made from the console (to another peer
57
+ only with `assign` when its capabilities are listed).
58
+ A conductor may inspect public state, assign or escalate work, start supported
59
+ headless peers and place its own delivery holds. It cannot answer approvals,
60
+ resolve durable deliveries, override budget pauses or release a human hold.
61
+ Pending approval summaries exclude titles; PII tasks remain public stubs.
62
+ Conduct events contain ids only. Supervision feeds use structured reasons and
63
+ never carry check output or approval bodies.
57
64
 
58
65
  ## Reporting
59
66
 
package/docs/smoke.md CHANGED
@@ -2,6 +2,32 @@
2
2
 
3
3
  `scripts/check.sh` covers everything against fakes. The legs below need real accounts and an interactive terminal, so they are run by hand and recorded here.
4
4
 
5
+ ## ahub reset on a scratch project (#214)
6
+
7
+ Not yet run. A person runs it in a terminal, on a scratch project only, never
8
+ on a project whose hub state matters:
9
+
10
+ 1. `ahub init`, `ahub up`, attach Claude with `ahub claude` and one headless
11
+ peer; queue work for an offline peer with `ahub say @<peer> ...` and
12
+ `ahub pause <peer>`; propose one task.
13
+ 2. Quit Claude Code (its status line tee writes into the state directory) and
14
+ run `ahub kill`, so nothing else changes the state files, and take their
15
+ checksums. `ahub reset`: the listing shows ids and counts only, and
16
+ `hub.db`, `hub.db-wal` and the other state files keep their checksums
17
+ (`hub.db-shm`, SQLite's shared-memory index, may change on any read).
18
+ 3. `ahub up` and `ahub claude` again, then `ahub reset --yes`: the hub stops
19
+ first; `ahub up` then shows no queued or needs_review delivery
20
+ (`ahub queue list`), no hold or pause, and the same board; the reset says
21
+ to relaunch Claude with `ahub claude`.
22
+ 4. `ahub reset --all --yes`: `.agenthub/archive/state-<UTC time>/` is 0700 and
23
+ git-ignored, the state directory holds only `project.json`, and `ahub up`
24
+ starts with the same project id, an empty board and an empty queue.
25
+ 5. Restore the archive as `docs/operations.md` describes (the current state
26
+ directory goes into `.agenthub/archive/` too) and check the board from
27
+ step 1 is back.
28
+
29
+ Record the version, the commands, each result and anything that differed.
30
+
5
31
  ## Operator console and conductor candidate (#190, #191, #193-#195)
6
32
 
7
33
  Candidate 0.12.17, protocol 15, observed on 2026-10-09 KST. The final Claude
@@ -321,6 +347,23 @@ ahub setup # Claude Code channel plugin from thi
321
347
 
322
348
  Claude channels are a research preview: `ahub claude` passes `--dangerously-load-development-channels plugin:agent-hub@agent-hub`.
323
349
 
350
+ ## Claude without channel evidence (#205)
351
+
352
+ Not run live yet. On a candidate with the rebuilt plugin bundle (protocol 16),
353
+ record only what was observed:
354
+
355
+ 1. Start a plain `claude` in the project (not `ahub claude`). `ahub status`
356
+ shows `claude` `idle` with `tools-only: messages wait for hub_inbox; ...`.
357
+ Run `ahub say @claude "<unique text>"`: status shows `queued 1`, `ahub queue
358
+ list --peer claude` has no `accepted` or `dispatching` row, and no pushed row
359
+ appears in the TUI. Ask the session to call `hub_inbox`: it returns the text
360
+ under an `[agent-hub message from "user"` header, status shows `queued 0`,
361
+ and the queue list shows one `completed` row `read through hub_inbox`.
362
+ 2. Start `ahub claude` in the same project (AC3). It takes the peer and the
363
+ plain session stands by. A new `ahub say @claude "<unique text>"` appears as
364
+ a native channel row, its delivery reaches `accepted`, and `hub_delivery_done`
365
+ or a correlated reply settles it `completed`. Status shows no `tools-only`.
366
+
324
367
  ## npm package preparation (issue #4)
325
368
 
326
369
  Verified locally on 2026-09-19 with Bun 1.3.14, using an actual `npm pack` tarball:
@@ -1252,3 +1295,51 @@ the sentinel. Missing served-model evidence remains `identified: false`.
1252
1295
  Before recording live acceptance, run a bounded unavailable-local/healthy-remote
1253
1296
  leg and retain the sanitized JSON plus process cleanup confirmation. Fixture
1254
1297
  verdicts do not certify provider availability or a real local generation.
1298
+
1299
+ ## 0.12.18 live checks: task attribution and console colors (#200, #201, 2026-10-09)
1300
+
1301
+ The agent-hub project's own hub was upgraded from 0.12.17 to 0.12.18 by the
1302
+ user with the 0.12.18 coordinator, run from a plain terminal (an agent shell is
1303
+ refused by the identity gate). Claude (the session writing this) and Pi on
1304
+ `dgx/coding` were attached for the checks. Times are UTC.
1305
+
1306
+ - **The upgrade left Claude held.** Operation `c671d786` (source 0.12.17,
1307
+ protocol 15, running) was created at 05:26:55, the 0.12.18 daemon started
1308
+ the same second, and the operation completed with the project verified. Ten deliveries Claude had accepted but never settled (three board review
1309
+ requests, seven Codex chats, all handled before the upgrade) were moved to
1310
+ `needs_review` with "peer disconnected before delivery settlement", and
1311
+ Claude's queue was held behind them. `ahub queue resolve` is a console
1312
+ command, so only the user can release it; the unsettled acceptances are #205.
1313
+ - **#200 AC4, attribution on a live turn.** Board task #19 (class `test`,
1314
+ owner Pi) was proposed at 05:30:03, accepted at 05:30:07, done at 05:34:52
1315
+ and approved at 05:35:16. `ahub report --by task` at 05:35:06 attributed Pi's
1316
+ one turn and 602,255 tokens to #19 by its delivery. Unattributed: 2,159,396
1317
+ of 38,171,695 token increments (5.7%) and 21 of 116 usage records (18.1%),
1318
+ all Claude's own user-driven turns, which have no task-bearing delivery and
1319
+ no single open task. The `before attribution` bucket held 35,410,044 tokens
1320
+ and 95 records from before the upgrade. Both shares equal the JSON
1321
+ `unattributed` counts over `totals`, and the JSON carried task ids, class and
1322
+ outcome but no title or detail text.
1323
+ - **Pi could not run the check itself.** Its sandbox denies
1324
+ `.agenthub/state/` (the `ahub` binary under the bunx cache and `events.jsonl`
1325
+ both failed with EPERM). It reported every check as not run instead of
1326
+ passing it, and the reviewer ran them.
1327
+ - **#201 AC7, idle console CPU.** Sampled with `ps` cumulative CPU time while
1328
+ nothing happened on the hub: plain `ahub console`, 0.11 s over 60 s
1329
+ (about 0.18%); `ahub console --panels`, 0.46 s over 120 s from 05:52:30
1330
+ (about 0.38%, 0.21 to 0.25 s per minute, not growing).
1331
+
1332
+ ## Console layout and readability: how to check (#213, #201 AC2)
1333
+
1334
+ Not run yet. Record the terminal, its theme and the result per step.
1335
+
1336
+ - In a light and a dark terminal profile, run `ahub console --panels` at 80x24
1337
+ with at least one pending approval, one task and one `needs_review` delivery.
1338
+ - Check that the rules, the bracketed active tab, the tab counts, every table's
1339
+ header row and the footer hint are readable, and that titles, approval details
1340
+ and body text use the default foreground.
1341
+ - Repeat with `--color=never`: the active tab, selection marker and states must
1342
+ still read without color.
1343
+ - Press `?`, Enter on an approval and Tab back to the stream; check that the key
1344
+ table, the labeled detail and the stream's rule above the footer read in both
1345
+ themes.