@staix/agent-hub 0.13.0 → 0.14.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.
- package/CHANGELOG.md +13 -0
- package/README.md +6 -4
- package/docs/agent-notes/adapters.md +1 -1
- package/docs/agent-notes/budget.md +2 -1
- package/docs/agent-notes/daemon.md +8 -1
- package/docs/agent-notes/tasks.md +7 -4
- package/docs/agent-notes/tests.md +1 -1
- package/docs/dogfood.md +10 -7
- package/docs/operations.md +45 -13
- package/docs/quickstart.md +6 -2
- package/docs/sdlc.md +23 -2
- package/docs/security.md +9 -5
- package/docs/specs/2026-09-19-agent-hub-design.md +38 -0
- package/docs/verification/2026-10-22-codex-reviewer-isolation.md +14 -0
- package/docs/verification/2026-10-23-0.14.0.md +69 -0
- package/docs/verified.json +20 -15
- package/package.json +1 -1
- package/plugins/agent-hub/.claude-plugin/plugin.json +1 -1
- package/plugins/agent-hub/server.js +91 -20
- package/src/adapters/codex-review-isolation.ts +18 -7
- package/src/adapters/definitions/builtin-metadata.ts +1 -1
- package/src/adapters/definitions/native-cli-entry-codex.ts +13 -0
- package/src/adapters/reviewer-print.ts +34 -7
- package/src/cli/help.ts +2 -1
- package/src/cli/main.ts +5 -0
- package/src/cli/output.ts +47 -2
- package/src/cli/status-lines.ts +1 -1
- package/src/hub/board.ts +22 -1
- package/src/hub/daemon.ts +84 -14
- package/src/hub/hub-tools.ts +2 -1
- package/src/hub/instances.ts +14 -4
- package/src/hub/manager.ts +2 -2
- package/src/hub/sdlc-metrics.ts +260 -0
- package/src/hub/sdlc.ts +5 -3
- package/src/hub/spec-read.ts +31 -0
- package/src/hub/task-code-group.ts +52 -0
- package/src/hub/tasks.ts +172 -22
- package/src/hub/ui.ts +3 -0
- package/src/hub/usage-rollup.ts +58 -0
- package/src/ui/index.html +273 -2
package/docs/sdlc.md
CHANGED
|
@@ -20,7 +20,7 @@ The built-in lifecycle follows the [AI-Native SDLC Playbook](https://claude.com/
|
|
|
20
20
|
| review | Deploy | assigned reviewer | current head-bound verdict, no pending change request/open thread; AC checklist under Compliance | approval, when enabled; open Important findings block in enforce |
|
|
21
21
|
| merge | Deploy | person | current-head readiness, ordered touching PRs | shown; no mutation |
|
|
22
22
|
| release | Deploy | person | merged commit, version/tag, docs freshness/live receipt checklist | person authorization and optional tag hook below |
|
|
23
|
-
| maintain | Maintain | person/planner | follow-up intent and operational evidence | shown; metrics
|
|
23
|
+
| maintain | Maintain | person/planner | follow-up intent and operational evidence | shown; metrics and control bands from S7 (`ahub sdlc metrics`) |
|
|
24
24
|
|
|
25
25
|
Stored `implement` and `verify` stage ids read as `build` and `test`. A write that names the old id stores the id the configuration lists, so new enabled-lifecycle receipts use the canonical ids. The default remains off, preserving existing off-mode output, receipt and schema shapes. Status, explain, console and dashboard name the playbook stage beside enabled lifecycle ids.
|
|
26
26
|
|
|
@@ -35,7 +35,7 @@ Stored `implement` and `verify` stage ids read as `build` and `test`. A write th
|
|
|
35
35
|
| S4 | Red-first tests and test integrity | Bug-label metadata, source-labelled red-first proof and changed pre-existing test blockers |
|
|
36
36
|
| S5 | Continuous evals for agent configuration | Pending gate, dependent on #251/#266; no eval pass claimed |
|
|
37
37
|
| S6 | Production approval gate | Person-only commit/version/docs receipt and opt-in Git tag pre-push guard; report stays read-only |
|
|
38
|
-
| S7 | Metrics, deterministic control bands and follow-up intent |
|
|
38
|
+
| S7 | Metrics, deterministic control bands and follow-up intent | `ahub sdlc metrics`, optional `[bands]` with log, diagnose and propose tiers, one card per breach episode; no PR readback and no security baseline (design spec amendment) |
|
|
39
39
|
| S8 | Compliance acceptance-criteria checklist | Head-bound checklist retained; legacy workflow and phase compatibility deprecated; task import removed |
|
|
40
40
|
| S9 | Planner grooming and plan review | Read-only digest, current plan-review receipt at build admission, all-mode WIP and accepted-untouched return |
|
|
41
41
|
| S10 | Planner cost governance | Typed review/CI scope, premature-review admission, identical-tree CI reuse and read-only cost report |
|
|
@@ -221,6 +221,27 @@ The digest is bounded to 500 task rows (open work plus structured cancelled supe
|
|
|
221
221
|
|
|
222
222
|
S9 ceilings: a private build task with an issue reference cannot satisfy enforce admission: privacy forbids the external issue metadata read, and no person override for a metadata-free plan receipt exists. Grooming pointers are coalesced only while queued, per board revision; there is no minimum time interval after delivery, so a busy board may produce one delivered pointer per revision.
|
|
223
223
|
|
|
224
|
+
# SDLC metrics and control bands (#481 S7)
|
|
225
|
+
|
|
226
|
+
`ahub sdlc metrics [--since <YYYY-MM-DD|ISO time>]` (the planner tool `hub_sdlc_metrics`; planner or person only, read-only) derives these from board history: time from spec acceptance to plan review, from acceptance to the first verdict and (when a merge time is supplied) to merge; the first-pass approval rate and rework rounds; the first-pass check rate; same-head CI reruns; stale-head re-reviews; and open stalls by kind. Each is a count, mean, median, p90 and max over the tasks that have it. `unknown` is the word and a count (`unknown: N`) for a task that does not have it: a metric the board cannot derive is never zero. A private task is counted as withheld and read for nothing else. The report is a pure function of the board history, so a fixture history gives the same report every time.
|
|
227
|
+
|
|
228
|
+
What each metric reads, and what it does not:
|
|
229
|
+
|
|
230
|
+
- `first_pass_check` is the hub's own completion check (`check passed` or `check failed` at `hub_task_done`), not PR CI. It is `unknown` for every task where completion checks are off, which is the default.
|
|
231
|
+
- `stale_head_re_reviews` counts review rounds whose verdict was bound to a different head than the review was requested for (the `done`'s recorded head against the verdict's findings head), so it needs both heads on the round. A task with no such round is `unknown`, not 0. (A `done` after an `approved` is not a signal: the board cannot produce one.)
|
|
232
|
+
- `acceptance_to_merge_ms` and `same_head_ci_reruns` need a PR readback. This report makes none, so they read `unknown` here; `ahub sdlc costs` carries the PR-based review rounds, CI runs and reruns.
|
|
233
|
+
- `stalls` counts open stalls on public tasks by kind and the dones recorded without writes.
|
|
234
|
+
|
|
235
|
+
`.agenthub/sdlc.toml` may set `[bands.<metric>]` for `acceptance_to_plan_ms`, `acceptance_to_first_review_ms`, `first_pass_approval`, `rework_rounds`, `first_pass_check` or `stale_head_re_reviews`. Every field is optional and bounded: `window` (4 to 200, default 20), `min_samples` (3 to 200, default 8, at most `window`), `log_sigma` 2, `diagnose_sigma` 3, `propose_sigma` 4 (ordered, 0.5 to 20), `drift_run` (3 to 50, default 6) and `min_delta` (the smallest step worth any tier: an hour for the times, one round, half a rate). An unknown metric or field is refused with its path. The newest value is compared with the mean and population standard deviation of the `window` values before it, on the bad side only (a longer time, more rework, a lower rate). The deviation is floored per metric (half an hour, or half a round or a rate) so a flat baseline does not turn one step into thousands of deviations. Fewer than `min_samples` before it fires nothing, and so does a step under `min_delta`, drift included. A run of `drift_run` newest values all more than one deviation on the bad side is at least `diagnose` even when none reaches a sigma tier, provided the newest step clears `min_delta`. Tiers:
|
|
236
|
+
|
|
237
|
+
- `log`: one hub notice per breach episode, remembered in the board database so a restart does not repeat it.
|
|
238
|
+
- `diagnose`: one read-only `triage` backlog task for the planner to inspect the histories behind the metric. It asks for no edit of files, branches or the board.
|
|
239
|
+
- `propose`: one `plan` backlog card that names the metric and its evidence (the newest value, the task that produced it, where the episode began, the baseline and the deviations).
|
|
240
|
+
|
|
241
|
+
`rework_rounds` and `stale_head_re_reviews` are final only when a task is approved: a task still in flight is `unknown` for both and the series places them at the approval, so a provisional value cannot move and start a second episode inside one shift. The stale-head count compares the head a `done` requested with the head the verdict is bound to; the verdict head prefers the lifecycle receipt's `head` or `localHead` over the commit, so a task whose receipt head and commit disagree can read one spurious stale round. Release metrics (interval, lead time, hotfix rate), release bands and the per-release gates-run record are not built; they need release history and S6, and are the next slice. A breach is an episode: the run of non-ok values of one metric, keyed `<metric>:#<task that started it>:<tier>` in the card (`Band breach key:`). The key comes from history alone, so a sustained shift keeps one key however many tasks finish inside it, and a restart finds the same one. A metric with an open band card (a backlog or open task, not approved or cancelled) writes nothing more, so a value that grows from `diagnose` to `propose` inside one episode adds no second card. When the planner closes the card and the metric returns to ok, a later breach is a new episode and a new card. The daemon evaluates every five minutes, only when the board or the band configuration changed since the last evaluation that ran, and not while it is stopping or recovering or has a storage error; a held pass, mode off or no bands consumes nothing, so the breach is written when the hold clears. A failed evaluation is one `hub.log` line. Detection never calls a model, the cards hold fixed words, numbers and task ids and no task text, and the hub opens no GitHub issue: external registration stays a person's decision.
|
|
242
|
+
|
|
243
|
+
Bands act only when the lifecycle `mode` is `advise` or `enforce`: in `off` the writer is idle, though `ahub sdlc metrics` still reports each configured band's tier. A scheduled security scan baseline is not part of this slice.
|
|
244
|
+
|
|
224
245
|
# Planner review and CI cost governance (#481 S10)
|
|
225
246
|
|
|
226
247
|
At plan review, batch small related fixes into one area branch and PR; keep release preparation separate from feature work. A plan can name `review: {scope: "none" | "delta" | "full", reviewers: N}` and `ci: "focused" | "full" | "reuse"`. None requires zero reviewers; delta/full require one to eight. These are expected costs, not permission to bypass the task's reviewer, CI or privacy gates. Use one reviewer for a small change; split a large review by area with one receipt owner. After a fix, review the delta against the previous findings. S2 will compare planned scope with completed coverage.
|
package/docs/security.md
CHANGED
|
@@ -176,7 +176,7 @@ Please report vulnerabilities privately through GitHub's "Report a vulnerability
|
|
|
176
176
|
- The exchange creates a separate random session, valid for one hour without renewal, in an `HttpOnly; SameSite=Strict; Path=/` cookie named for this listener's port. Sessions and tickets live only in memory and die with the daemon. The page itself keeps one more cookie, `agent-hub-theme` (issue #246): script-set, `SameSite=Strict`, kept for a year for the whole `127.0.0.1` host, and read back only as one of `system`, `light` or `dark`. A second script-set cookie, `agent-hub-office` (issue #264), keeps the Office view choice under the same attributes and is read back only as `off`, `flat` or `isometric`. The #391 view split adds two more script-set cookies under the same attributes: `agent-hub-view`, read back only as one of the seven view names, and `agent-hub-column-focus`, read back only as one of the five board columns or empty. HTTP is loopback-only; the cookie is not a substitute for the origin checks.
|
|
177
177
|
- Every request must have the exact listener Host. Every data or action request, including the ticket exchange and snapshot polling, must be POST with the exact listener Origin and JSON content type. Foreign and missing origins, missing or expired sessions, oversized bodies and unknown actions are rejected. There is no CORS support. The only unauthenticated GET is the fixed, data-free HTML shell. No files or paths are served dynamically.
|
|
178
178
|
- Responses are non-cacheable, cannot be framed, suppress referrers, and use a content security policy restricting scripts/styles to the shipped inline content and connections to this origin. Browser text is rendered with textContent, never interpreted as HTML.
|
|
179
|
-
- The browser receives a bounded redacted event stream, peer states and queue counts, public task views (with a task's screened changed-symbol observation, #684: ids, paths, names and kinds, never source or line spans), budget windows, pending approvals, the settings rows (closed values and ids, the file each comes from) and each startable peer's start plan (issue #269), benchmark run summaries (issue #251): the newest runs in `~/.agenthub/bench/`, whichever project on this machine ran them, by suite name, arm label, state, the task id in progress, counts, tokens and times, never suite text, and the office view's optional fields (issue #264): each peer's open turn (start time, attributed task id, attribution kind), a `checking` flag on tasks whose check is running, and warning `links` of overlapping, conflicting or cohort tasks (task and peer ids only, never paths, symbols or reasons; a PII task never joins a link, and a private or PII-attributed turn exposes no task id). Private envelope bodies and PII task text never cross this endpoint. Local-worker and Pi approval titles can contain PII, so their details remain terminal-only; the dashboard identifies the request and directs the operator to `ahub tail` before allowing it. Budget checkpoint summaries are also omitted.
|
|
179
|
+
- The browser receives a bounded redacted event stream, peer states and queue counts, public task views (with a task's screened changed-symbol observation, #684: ids, paths, names and kinds, never source or line spans, and its Code group, #684 phase 2c: those symbols marked against the plan's names, each name re-screened by the public metadata screen; a private task has no group and its plan names never enter the snapshot), budget windows, pending approvals, the settings rows (closed values and ids, the file each comes from) and each startable peer's start plan (issue #269), benchmark run summaries (issue #251): the newest runs in `~/.agenthub/bench/`, whichever project on this machine ran them, by suite name, arm label, state, the task id in progress, counts, tokens and times, never suite text, the Usage view (#683 U4), computed only while the Agents view is open: peer ids, UTC days and token counts with their source, budget pause, resume and reset events without their reason text, and per-task rows of the task id, a PII flag and token counts, never a title, and the office view's optional fields (issue #264): each peer's open turn (start time, attributed task id, attribution kind), a `checking` flag on tasks whose check is running, and warning `links` of overlapping, conflicting or cohort tasks (task and peer ids only, never paths, symbols or reasons; a PII task never joins a link, and a private or PII-attributed turn exposes no task id). Private envelope bodies and PII task text never cross this endpoint. Local-worker and Pi approval titles can contain PII, so their details remain terminal-only; the dashboard identifies the request and directs the operator to `ahub tail` before allowing it. Budget checkpoint summaries are also omitted.
|
|
180
180
|
- The closed action list includes permission response, peer pause/resume, console message, task proposal/assignment and the settings actions below (set, undo, preview, reload). Project-board actions add revision-fenced planning, Backlog pull/return/split/undo and release planning/state changes through Tasks; they do not tag, publish or write GitHub state. These use the existing daemon/task paths and budget pause rules. `start_peer` (issue #269) starts a peer from a closed list in the mode a person set, with a command the hub assembles: the request carries a peer id and nothing else. There is no generic control proxy, task-detail read, shell, file access, free-form configuration edit, budget override, launch of a caller-chosen command or daemon shutdown API.
|
|
181
181
|
- The listener stops with the daemon. Expired sessions must be reopened with `ahub ui`; the page does not silently obtain new credentials.
|
|
182
182
|
|
|
@@ -188,7 +188,7 @@ Please report vulnerabilities privately through GitHub's "Report a vulnerability
|
|
|
188
188
|
- A settings session is as strong as the console for these settings and no stronger elsewhere. A mode change is the console's own request with the same refusals; tool approvals stay deny-only for Pi and local; there is still no shell, file, task-detail or launch-command API. `ahub ui --settings` and `ahub settings` pass the same identity gate as `ahub permission`, which is an operating policy, not authentication. The hostile-agent boundary above applies unchanged: an agent with an unrestricted shell as the same user can impersonate the console, and so can open a settings session.
|
|
189
189
|
- Every change writes a `settings` event (the key, the value before and after, the source `dashboard` or `terminal`) and a line in `hub.log` and open consoles; opening a settings session is logged too. The unified dashboard (`ahub ui --all`) forwards settings actions as an ordinary session and opens no settings session.
|
|
190
190
|
- Configuration reload (issue #439). The daemon watches `config.json`, `config.local.json`, `routing.toml` and `routing.local.toml` and re-reads the complete candidate after a change, as the dashboard's Reload configuration and `ahub settings reload` do; an invalid candidate applies nothing. Each changed key is then authorized on its own against its file's actual git provenance. A privacy tightening (local-only PII, the model screen on, added PII patterns, narrower roles or capability lists, unless it moves the planner or conductor) and a safe registered setting apply from any file. Every other change, machine-local fields included, keeps its running value when its file is tracked or git cannot answer. From an untracked file such a guarded change is adopted only by `ahub settings reload` in an interactive terminal, after it lists each guarded key with its full before and after values and the person types `y` within 30 seconds; the confirmation is bound to the file contents and accepted state it was shown for. File watches and dashboard reloads, a settings session's included, never confirm, and no reload changes the roster: membership moves only through the roster setting. Keys whose consumers are bound at start (memory, the gateway and MLX, checks, the privacy and sandbox policy of existing sessions, the terminal provider and others) stay pending until a restart.
|
|
191
|
-
- Start modes. A start the hub makes itself (the dashboard's Start control, `peer_start` from a console child, the conductor's `hub_peer_start`, `pi.auto_start`) takes the peer's start mode. Through settings only a settings session or the terminal can set it to headless; a config file can also set `peers.<id>.start_mode`, the shared one included (it is not a machine-local field: headless was the only form for Pi, Kimi and the local worker before this change, and for Jcode before 0.12.39, a hub-made headless Codex start is the app-server the hub already ran for it, and a start mode grants nothing a permission mode or a capability controls). A declared extra instance accepts only `headless` from any surface; a Claude channel instance accepts no start mode, and a headless Claude instance must have roles exactly `["reviewer"]` and no worktree. For a TUI the hub opens a terminal running `<runtime> <its own entry point> --project <this project> <peer>` (with `--mode tui` for Pi, Kimi and Jcode): every word is the hub's own, the command line is those words each quoted for a POSIX shell, and no request field, tool argument or agent message is part of it. In a `terminal.open` template `{command}` is that quoted line and `{title}` and `{cwd}` are raw text for argv elements of their own; each placeholder is substituted once, so a project path that itself contains a placeholder, a quote or `$` stays one word. A provider's own error text goes to `hub.log`, not to the caller. The terminal comes from `terminal.open`, a machine-local command template that config trust refuses from a committed file and that no settings surface can write, or from Orca. The opened terminal's environment carries no agent marker, so `ahub` there has the console's identity. Without a provider the start is refused with the command to run by hand; it is never downgraded to headless. After a successful headless start of a watch-capable peer (Kimi, Qwen) the hub also opens `<runtime> <its own entry point> --project <this project> watch <peer>` through the same provider under the same fixed-words rule, at most once per peer per 30 s and never for an already attached peer or a declared instance; the pane has the console's identity, and a missing or failing provider leaves the start standing and reports the command (#286). A conductor can start a peer but cannot choose its mode.
|
|
191
|
+
- Start modes. A start the hub makes itself (the dashboard's Start control, `peer_start` from a console child, the conductor's `hub_peer_start`, `pi.auto_start`) takes the peer's start mode. Through settings only a settings session or the terminal can set it to headless; a config file can also set `peers.<id>.start_mode`, the shared one included (it is not a machine-local field: headless was the only form for Pi, Kimi and the local worker before this change, and for Jcode before 0.12.39, a hub-made headless Codex start is the app-server the hub already ran for it, and a start mode grants nothing a permission mode or a capability controls). A declared extra instance accepts only `headless` from any surface; a Claude channel instance accepts no start mode (one that implements or verifies needs its own worktree, and a planner or reviewer one refuses a worktree, #525-1), and a headless Claude instance must have roles exactly `["reviewer"]` and no worktree. For a TUI the hub opens a terminal running `<runtime> <its own entry point> --project <this project> <peer>` (with `--mode tui` for Pi, Kimi and Jcode): every word is the hub's own, the command line is those words each quoted for a POSIX shell, and no request field, tool argument or agent message is part of it. In a `terminal.open` template `{command}` is that quoted line and `{title}` and `{cwd}` are raw text for argv elements of their own; each placeholder is substituted once, so a project path that itself contains a placeholder, a quote or `$` stays one word. A provider's own error text goes to `hub.log`, not to the caller. The terminal comes from `terminal.open`, a machine-local command template that config trust refuses from a committed file and that no settings surface can write, or from Orca. The opened terminal's environment carries no agent marker, so `ahub` there has the console's identity. Without a provider the start is refused with the command to run by hand; it is never downgraded to headless. After a successful headless start of a watch-capable peer (Kimi, Qwen) the hub also opens `<runtime> <its own entry point> --project <this project> watch <peer>` through the same provider under the same fixed-words rule, at most once per peer per 30 s and never for an already attached peer or a declared instance; the pane has the console's identity, and a missing or failing provider leaves the start standing and reports the command (#286). A conductor can start a peer but cannot choose its mode.
|
|
192
192
|
|
|
193
193
|
## Multi-project manager (protocol 7)
|
|
194
194
|
|
|
@@ -196,7 +196,9 @@ The manager has its own loopback session server and authenticated local control
|
|
|
196
196
|
endpoint. Browsers provide registered project IDs and expected daemon instance
|
|
197
197
|
IDs; they cannot submit arbitrary paths, ports, PIDs or control tokens. The
|
|
198
198
|
manager forwards the same closed, redacted dashboard operations as a project-local
|
|
199
|
-
page.
|
|
199
|
+
page. A snapshot request's `view` is forwarded only when it is one of the page's
|
|
200
|
+
seven view names (#683 U4), and the project hub checks it again. It cannot expose
|
|
201
|
+
private task history or allow local-worker tools.
|
|
200
202
|
|
|
201
203
|
Per-project control handshakes verify project, root and instance against the
|
|
202
204
|
runtime manifest. Shutdown removes only the owning instance's runtime files and
|
|
@@ -371,14 +373,16 @@ Stall causes are persisted and deduplicated by cause and replanning revision. Pl
|
|
|
371
373
|
|
|
372
374
|
A declared instance whose only role is reviewer gets review requests, task messages addressed to it and the person's messages; agent chat, digests and presence are discarded and journaled, and it gets no memory recall (#223). The headless Claude reviewer runs one new `claude -p` per review with `--setting-sources ""`, `--strict-mcp-config`, `--no-session-persistence`, tools limited to Read, Grep, Glob, `hub_review` and `hub_task_show`, and `dontAsk`. Its prompt holds hub facts and a bounded diff, never the builder's summary, and its `hub_task_show` drops the builder's notes. Changes touching `local.deny` or `pii.paths` are withheld and passed as `Read` deny rules, which Claude enforces, not the hub. Private tasks are never given to it. Its `hub_review` counts only while the child started for that task still runs; a stop or exit revokes it first, and surviving processes hold the delivery as `needs_review` and keep the reviewer busy.
|
|
373
375
|
|
|
374
|
-
The fresh Codex reviewer (#519) needs `review_mode: "fresh"` and a compat-admitted Codex (receipt-verified 0.146.0 and 0.163.0; `ahub agents` shows the current policy); an unverified/untrusted version is unavailable. Before each review it requires a model-free isolation receipt: binary and config unchanged, only the two hub tools in the configured MCP inventory, plugins, hooks and Apps disabled (Apps by a separate feature readback), and a named filesystem profile whose fixtures prove public reads, private-path denial (exact, glob and symlink), standard host credential denial and write denial. Credential and tool-home roots are denied even with an empty `local.deny`; that list does not cover every personal file. A dangling or unreadable credential root refuses the reviewer, and the reason names only its path class (`~/.docker`, `$DOCKER_CONFIG`), never the path or its contents. Its shell is sandboxed read-only, not Claude's Read/Grep/Glob whitelist. Network and web search are configured disabled but not observed: managed requirements can still select cached web search, and no run has shown the review selecting the profile through `default_permissions`. A failed receipt or start never falls back to a continuing native session, and a child that ends without a saved verdict is never approval. The agy reviewer slot is an unavailable placeholder and never runs.
|
|
376
|
+
The fresh Codex reviewer (#519) needs `review_mode: "fresh"` and a compat-admitted Codex (receipt-verified 0.146.0 and 0.163.0; `ahub agents` shows the current policy); an unverified/untrusted version is unavailable. Before each review it requires a model-free isolation receipt: binary and config unchanged, only the two hub tools in the configured MCP inventory, plugins, hooks and Apps disabled (Apps by a separate feature readback), and a named filesystem profile whose fixtures prove public reads, private-path denial (exact, glob and symlink), standard host credential denial and write denial. Credential and tool-home roots are denied even with an empty `local.deny`; that list does not cover every personal file. A dangling or unreadable credential root refuses the reviewer, and the reason names only its path class (`~/.docker`, `$DOCKER_CONFIG`), never the path or its contents. A failed per-review start names one class from a closed list (`version`, `binary`, `mcp_inventory`, `plugins_hooks_apps`, `sandbox_proof`, `credential_root`, `timeout`, `unknown`) with a fixed sentence in the unavailable reason, `hub.log` and the `reviewer` line of `ahub status`; it never carries a path, native CLI text or a token (#722). Its shell is sandboxed read-only, not Claude's Read/Grep/Glob whitelist. Network and web search are configured disabled but not observed: managed requirements can still select cached web search, and no run has shown the review selecting the profile through `default_permissions`. A failed receipt or start never falls back to a continuing native session, and a child that ends without a saved verdict is never approval. The agy reviewer slot is an unavailable placeholder and never runs.
|
|
375
377
|
|
|
376
378
|
The fresh Kimi reviewer (#533) needs `review_mode: "fresh"` and a compat-admitted Kimi Code (receipt-verified 2.2.0 and 2.3.0; `ahub agents` shows the current policy) with its native OAuth login; an unverified/untrusted version, or a failed preparation, is unavailable. The [2.3.0 receipt](verification/2026-10-22-kimi-qwen-compat.md) is model-free and does not prove real login or a model review. It is a native tool-authority boundary, not a sandbox: no Seatbelt wraps it, and same-user native code is not isolated from the filesystem or network. Each review starts a new child in a private `KIMI_CODE_HOME` with managed-provider-only config, a clean temporary working directory and an empty skills directory. Its agent may use only `hub_review` and `hub_task_show`, served by a loopback MCP session bound to the task, head and review revision that never carries the hub's control token; it has no native read, file or shell tool and sees only the screened review brief and host facts. Its `credentials/` and `oauth/` directories and `device_id` link to the person's native ones, so refresh keeps Kimi's own lock and atomic rename; no token is copied and `KIMI_DISABLE_OAUTH_LOCK` is never set. A stop revokes its verdict authority before termination; killing it mid-refresh may require a native re-login. Private tasks are never given to it, and a child that ends without a saved verdict is never approval.
|
|
377
379
|
|
|
378
|
-
The review brief (#520) withholds private changes whole (both sides of a move) and counts them; a rule file that fails the private-data screen is named, not shown. Attributes and rules are read at the merge base, so a change cannot loosen its own review. Optional changed symbols (#684) come from ripwire 0.6.7 on the daemon's host PATH, run as an owned process on a disposable snapshot of the task's commit; each path, name and kind passes the hub's public metadata screen before the brief, the console's task detail or the dashboard shows it, a private task stores and shows none, and a failed or missing read is `unknown`, never a gate. Spec acceptance, plan review, plan conformance, premature-review, cost, test-integrity (#481 S4), issue and PR identity linking (#694) and dogfood scorecard (#687) reads fetch GitHub metadata only (edit times, check states, thread authorship and resolution, changed file names, open PR heads, workflow run conclusions), never issue bodies, comments or patches. The CI workflow keeps `contents: read` and `actions: read`.
|
|
380
|
+
The review brief (#520) withholds private changes whole (both sides of a move) and counts them; a rule file that fails the private-data screen is named, not shown. Attributes and rules are read at the merge base, so a change cannot loosen its own review. Optional changed symbols (#684) come from ripwire 0.6.7 on the daemon's host PATH, run as an owned process on a disposable snapshot of the task's commit; each path, name and kind passes the hub's public metadata screen before the brief, the console's task detail or the dashboard shows it, a private task stores and shows none, and a failed or missing read is `unknown`, never a gate. Spec acceptance, plan review, plan conformance, premature-review, cost, test-integrity (#481 S4), issue and PR identity linking (#694) and dogfood scorecard (#687) reads fetch GitHub metadata only (edit times, check states, thread authorship and resolution, changed file names, open PR heads, workflow run conclusions), never issue bodies, comments or patches. Issue identity linking at propose and accept (#723) parses the task's title and refs locally, reads nothing from GitHub and skips PII tasks. The CI workflow keeps `contents: read` and `actions: read`.
|
|
379
381
|
|
|
380
382
|
Person release authorization (#481 S6). `ahub sdlc authorize-release <version>` is taken from the console role only and refused under `--live-test`. It binds the daemon checkout's commit, package version, push remote identity and a fresh-docs verdict, lives only in daemon memory (a restart revokes it) and is logged with its commit, version and docs digest. `ahub sdlc release-guard -- <argv>` is a read-only preflight that agents may run, except during a private or PII turn; it never executes the command and refuses ambient target selectors (`GIT_DIR`, `GH_REPO`, npm prefix and registry settings and the like). The pre-push hook that `ahub init --release-guard` installs refuses an unauthorized or unknown `v*` tag push, also with the daemon down, and passes other pushes through. It and the release block in `AGENTS.md` are a cooperative guard, not an operating-system boundary: `git push --no-verify`, a custom `core.hooksPath` or another Git invocation bypasses the hook, and `gh release create` and `npm publish` are checked only when the preflight is run (#312, #313).
|
|
381
383
|
|
|
384
|
+
SDLC metrics and control bands (#481 S7). `ahub sdlc metrics` and `hub_sdlc_metrics` are planner or person only, read-only, and refused during a private or PII turn. A private task is counted as withheld and read for nothing else; the report calls no model and makes no GitHub read. Band breaches are written only by the daemon's evaluator through Tasks, never while it is stopping, recovering or has a storage error. A `diagnose` or `propose` card is a backlog task (`triage` or `plan`) whose text is fixed words, numbers and task ids, never task text; it passes the PII signal check and the candidate-text screen before it is written, and no GitHub issue is created. A `log` breach is one hub notice, remembered in the board's `band_notices` table as a marker (`<metric>:#<task>:log`, checked against a fixed pattern) and a time, nothing else.
|
|
385
|
+
|
|
382
386
|
## Bounded task specs and isolated checks (#418)
|
|
383
387
|
|
|
384
388
|
`hub_task_spec` reads only the GitHub issues named by a current owner's or reviewer's task in this project's origin repository, including the validated canonical GitHub intake source when its title/body has no issue token. Bare `#N` references count only in the title; detail references require `owner/repo#N` or `https://github.com/owner/repo/issues/N`. Bare detail numbers denote hub tasks, and arbitrary file paths are not parsed as issue references. A qualified reference cannot begin after a slash or a name character, so `src/hub/tasks.ts#12` is a file path. Routing uses this same parser. Without a GitHub project identity, bare title numbers do not require spec access, but explicit qualified/URL references, `refs.repo` and imported GitHub identity still fail closed. `refs.repo`, which `hub_task_propose` and `hub_task_accept` take since #476 to scope overlap per repository, is metadata only: it grants no file access or execution root, and a spec read for a task naming another repository refuses. The daemon resolves authenticated and container executables outside the project, pins their absolute paths and uses fixed arguments; repository-writable PATH shims are refused. Runner Git resolves its host executable through a restricted PATH. For specs, the model receives no credential or arbitrary authenticated URL capability. Foreign/transferred issues, more than three references, bodies over 64 KiB and aggregate results over 80 KiB fail explicitly. Spec fetches share a 25-second budget inside the control client's 30-second cap; each gh call remains bounded to 15 seconds. Current PII, secret and private-path policy withholds the complete spec rather than returning a partial contract. PII turns and PII tasks refuse these tools. The independent `spec_read` and `check_run` capabilities apply when a peer has a narrowed capability list. Arguments and results are excluded from capture. Proven deterministic pre-effect guards return PreEffectToolRefusal so Pi settles the exact refusal receipt; generic transport/storage failures and failures after durable receipt/output changes remain uncertain, never reclassified from error text.
|
|
@@ -4088,6 +4088,22 @@ These rules are settled here and land with hub tasks #284-#286; until then the S
|
|
|
4088
4088
|
|
|
4089
4089
|
- S11 follow-up invariants: contiguous planning revisions changing exactly `["release"]` retain the S9 reviewed execution plan; other or missing planning history invalidates it. Cut-time CI cost reads are limited to the scoped PR ids (at most 16 in one repository); read errors or unbounded/mixed scope remain unknown and cannot abort a valid cut. Train history is capped at 256 events: configuration no-ops append nothing, meaningful configuration reserves cut/shipment slots, and capacity refusals are pre-effect.
|
|
4090
4090
|
|
|
4091
|
+
### S7 metrics and control bands amendment (#481, hub task #200)
|
|
4092
|
+
|
|
4093
|
+
- `ahub sdlc metrics [--since]` and the planner tool `hub_sdlc_metrics` are a pure projection of board history (`src/hub/sdlc-metrics.ts`: no clock, no I/O, no model). `Tasks.sdlcMetrics` is the one read, for a planner or the person, refused during a private turn; private tasks are counted as withheld and read for nothing else. Anything the board cannot derive is `null`/`unknown` and counted, never 0.
|
|
4094
|
+
- Scope cuts, each deliberate:
|
|
4095
|
+
- **No PR readback.** The report reads only the board. Time to merge and same-head CI reruns need a merged PR and its check runs, which `ahub sdlc costs` already reads under its own bounds (16 PRs, 20 s, public tasks only); a second reader of the same GitHub surface would duplicate those bounds and their privacy revocation checks. Both read `unknown` here, and the pure functions accept them as optional inputs so a later slice can supply them.
|
|
4096
|
+
- **S10's `costBandProposals` is not consumed.** It returns per-PR upper-limit breaches over complete merged-PR rows, a different data set (review rounds, CI runs and reruns per merged PR) from board-history metrics, and it takes absolute limits rather than a rolling baseline. S10 left the baseline, configuration and automatic card scheduler to S7 and delivered the pure breach data only; S7 builds the scheduler for its own metrics and leaves the PR cost metrics' bands for the slice that also reads PRs, so one configuration and one dedupe rule cover both.
|
|
4097
|
+
- **No security-scan baseline.** The last S7 line (a scheduled security scan with a recorded baseline feeding the propose tier) is not built. No scan source exists in the repository to baseline, and inventing one would claim a security control nothing enforces.
|
|
4098
|
+
- **No release metrics, no release bands, no gates-run record.** S11 recorded release cut and shipment times and left the bands and proposals over them to S7 (release interval, lead time from approval to release, and a band on the hotfix rate that proposes a planning card). S7 here reads task history only, so none of those is built: release history is a separate table with its own bounds and privacy rules, and a hotfix rate needs shipped releases to have a baseline at all. The per-release "gates run" record is S11's explicit deferral to S6/S7 and stays deferred: a cut record is not gate-execution evidence, and recording gates needs the S6 authorization path. These are the next slice, and `[bands]` has no release metric names until then, so a typo cannot silently configure one.
|
|
4099
|
+
- **`first_pass_check` is the hub's completion check, not PR CI.** It reads `check passed` and `check failed` at `hub_task_done`, so it is `unknown` wherever completion checks are off (the default). The issue's "first-pass CI success" is the PR-based figure and is not available without the readback above.
|
|
4100
|
+
- **Stale-head re-reviews need head evidence.** A review round is stale when its verdict is bound to a different head than its request (`reviewCost.head` on the done against `reviewFindings.head` on the verdict). A task without both is `unknown`. A `done` after an `approved` is not a signal: the board's move table has no way out of `approved`.
|
|
4101
|
+
- Control bands are optional `[bands.<metric>]` tables of `sdlc.toml`, validated by `bandsConfig` with field-level paths. The newest value is compared with the mean and population standard deviation of the preceding `window` values, on the bad side only, with a per-metric deviation floor, a `min_samples` floor, a `min_delta` floor that every tier including the drift rule honours, and a drift rule (`drift_run` newest values all over one deviation on the bad side is at least `diagnose`).
|
|
4102
|
+
- Rework rounds and stale-head re-reviews are final only when a task is approved, so a task still in flight is `unknown` for both and a series places them at the approval. Otherwise a provisional value would move at each verdict and could start a second episode inside one shift after its card was closed. Residual ceiling: the verdict head prefers the lifecycle receipt's `head` or `localHead` over `refs.commit` (`findingHead`) while the request head is `reviewCost.head`, so a task whose receipt head and commit disagree can read one spurious stale round; one shared head rule for both sides is the upgrade.
|
|
4103
|
+
- A breach is an episode: the run of non-ok values for one metric. `BandResult.episode` is the task that started the run, derived from history alone, so a restart finds the same key. `Tasks.proposeFromBands` is the only writer. `log` is one hub notice, recorded in the board's `band_notices` table so it is announced once. `diagnose` is one read-only `triage` backlog task and `propose` one `plan` backlog card, keyed `<metric>:#<episode>:<tier>` in the card. A metric with an open band card writes nothing more, so a value growing from diagnose to propose inside one episode adds no second card, and a sustained shift is one card. Card text is fixed words, numbers and task ids, screened like the recurring-finding card; the hub opens no GitHub issue.
|
|
4104
|
+
- The daemon evaluates every five minutes, only when the board's status revision or the band configuration moved since the last evaluation that ran, and not while it is stopping, in recovery or has a storage error (the same holds as the task sweep). A pass that was held, in mode off or without bands consumes nothing, so a breach seen then is written when the hold clears and bands enabled by a reload on an idle board are evaluated at the next tick. A failed evaluation is one `hub.log` line. Ceiling: one evaluation reads every task with its history; a history window is the upgrade if a board passes several thousand tasks.
|
|
4105
|
+
- Bands write cards only in `advise` or `enforce`; in `off` the writer is idle while the report still shows each configured band's tier.
|
|
4106
|
+
|
|
4091
4107
|
## Antigravity optional implementer peer (#485 P1)
|
|
4092
4108
|
|
|
4093
4109
|
Planner ownership ruling, 2026-10-19: the person-only `ahub agy` launcher owns exactly `plugin.json`, `mcp_config.json` and `hooks.json` inside `.agents/plugins/agent-hub/`. Apply symlink, hard-link and foreign-entry refusal and managed cleanup to all three files. Never write project-level `.agents/hooks.json`, another `.agents/` path or the person's `~/.gemini` settings. This amends the earlier two-file list in #485 AC3. AC0 must prove plugin-local hooks load without a global setting; failure stops implementation rather than falling back to manual configuration.
|
|
@@ -4930,6 +4946,15 @@ Use `summarizeByTask`, `costMetrics` and bounded SDLC GitHub helpers. Bound file
|
|
|
4930
4946
|
- The `usage` op now folds the log with `catchUp(250)` before it answers (one 8 MiB pass per poll left a hub that had just started on a big log answering from the first pass: found by the live panel on a copy of the real 47 MB log, where TODAY read `no records` for peers that had used tokens). When the budget runs out the reply has `behind: true`; the console says `still catching up` and the CLI warns on stderr.
|
|
4931
4947
|
- Ceiling: the limits history is the newest 1000 events; the panel only needs each peer's last three resets. The dashboard panel is slice U4 and calls the same op.
|
|
4932
4948
|
|
|
4949
|
+
## Amendment: the dashboard Usage view (#683 U4)
|
|
4950
|
+
|
|
4951
|
+
- The Usage view is part of the Agents view, under Usage budgets (`src/ui/index.html`, `renderUsage`), and reads the same roll-up as `ahub usage` and the console panel: lifetime totals with the peers whose figure is unknown, the last 14 UTC calendar days of stacked tokens per peer, the open budget pauses with the last 20 pause, resume and reset events, and a task table (state, owner, PR, input, output, cache read, cache write, total).
|
|
4952
|
+
- `uiSnapshot` builds `usage` only when the request names `view: "agents"` (the page sends its open view with every poll, like it sends `memory`), so a hidden view costs no roll-up work on the 1 s poll. It folds the log with `catchUp(100)`; when that returns false the snapshot carries `usage.behind: true` and the view says the figures are a lower bound until it catches up, never the folded part as the whole.
|
|
4953
|
+
- `UsageRollup.daily(days, now)` returns the last `days` calendar days ending today, clipped to the first day with a record. A day with no record is a row with `empty: true` and the view shows `no record`, never zero. Every counter keeps its source (`native`, `hub` for a count, `at least` for a partial sum) and `unknown` stays the word.
|
|
4954
|
+
- A private task is the public title `[pii]` on the board (`Tasks.publicView`) or a `pii` roll-up row; the view shows `#id [pii]` and never a title, and the snapshot carries ids and numbers only.
|
|
4955
|
+
- Colours are design tokens only; the view is one column at 600 px and its tables and chart scroll inside themselves. `test/ui-usage.test.ts` runs the page's own `renderUsage` over a snapshot fixture (PII marker absent, unknown not zero, the lower-bound note) and checks the view's token pairs for contrast in both themes.
|
|
4956
|
+
- Ceiling: the view recomputes the daily and task aggregates on each poll while the Agents view is open; a materialised daily table is the upgrade if that ever costs more than a few ms on a large log.
|
|
4957
|
+
|
|
4933
4958
|
## Amendment: the console changed-symbols block (#684 phase 2b)
|
|
4934
4959
|
|
|
4935
4960
|
- `src/cli/console-symbols.ts` (pure data) and `taskFields` in `console-state.ts`: Enter on a task in Tasks adds `symbols` and `symbols note` after the plan field and drops the raw `codeSymbols` field. It reads the public `codeSymbols` that `task_show` already carries (#684 2a) and the task's `plan.symbols`; it asks the hub for nothing new.
|
|
@@ -4939,3 +4964,16 @@ Use `summarizeByTask`, `costMetrics` and bounded SDLC GitHub helpers. Bound file
|
|
|
4939
4964
|
- Names go through the console's field renderer, so they are quoted; at most 40 are listed and the rest are counted. The note says deleted definitions are not reported.
|
|
4940
4965
|
- Not in the original 2b slice: overlap lines (the read-only `symbolOverlaps` projection and console block follow in #379 above) and the related-notes count (phase 4, no hub op returns it).
|
|
4941
4966
|
|
|
4967
|
+
## Amendment: reserved backlog pulls offer their exact owner (#718)
|
|
4968
|
+
- A planner pull of an existing backlog task with `reserved` set admits that exact peer before any task write. It cannot silently fall back to another peer when the reserved peer is excluded, unavailable, at planner WIP capacity or otherwise ineligible. Existing planning authority, revision, dependency, release, person-order and SDLC plan gates still apply. Authority, routing/WIP eligibility and plan/flow fences are rechecked after asynchronous screening or plan readbacks and immediately before the admitted write.
|
|
4969
|
+
- `Board.pullAssigned` records `backlog pulled` and the owner assignment in one atomic task transition. Both writes commit or roll back together; board reads and deferred callback payloads both expose the committed assigned row, while history retains both events. Pull increments the planning revision, clears any old `offer`, and the existing assignment path creates a fresh owner offer bound to the new assignment flow revision. A stale prior owner request is withdrawn through the existing delivery path rather than reused as the new offer.
|
|
4970
|
+
- The committed assignment remains fact if subsequent routing notice or task delivery fails. Existing typed undelivered-assignment handling reports that failure; an assignment superseded while awaiting delivery follows the existing supersession handling, without rollback or a second owner offer. This change adds no separate delivery mechanism.
|
|
4971
|
+
- An unreserved backlog pull retains its lazy, ownerless To Do transition and existing planning undo. A reserved pull now assigned/offered to its peer cannot be undone through planning; the existing planner-or-person untouched-return path remains the recovery route when its own conditions hold. Acceptance is still a separate peer action and does not occur as part of pull.
|
|
4972
|
+
- No new model call, role authority, configuration or privacy exception is introduced. Existing screened task/note views, PII routing and refusal boundaries apply. Dogfood starts after release and checks the same-peer `backlog pulled` -> `assigned` -> `accepted` sequence, fresh offer binding, stale/unowned offers and capacity refusals.
|
|
4973
|
+
## Amendment: the dashboard task sheet's Code group (#684 phase 2c)
|
|
4974
|
+
|
|
4975
|
+
- `src/hub/task-code-group.ts` (`codeGroup`) classifies a task's public `codeSymbols` against `plan.symbols` with the review brief's own matcher (`symbolsOverlap`) and is the only place that does for the dashboard (the console classifies with `classifySymbols` in `console-symbols.ts`, over the same matcher); the UI snapshot carries the result as `codeGroup` on each task. The page (`codeGroupSection` in `src/ui/index.html`) draws it and never matches names.
|
|
4976
|
+
- A private task has no `codeGroup`, so the sheet shows no Code group for it and the snapshot carries none of its plan names. A stored receipt (one that still has `repositoryKey`), a missing one or an `unknown` one is `{ state: "unknown" }` and the sheet reads `unknown (no code facts)`. Symbol ids that fail `publicMetadataText` or the id shape (`symbol:<name>@<path>`) are dropped and the group is marked incomplete, so an untouched symbol may have changed. Every stored plan name is read (the stored plan allows 50 names of 300 characters, and the group uses the same bounds); one that is malformed or fails the screen is counted in `planWithheld`, the group is marked incomplete, and the sheet says a symbol marked unplanned may be in the plan, so a withheld name never shows a plan departure that did not happen. The counts on the sheet are the totals, not the listed part.
|
|
4977
|
+
- At most 40 symbols and 40 untouched names are listed, with the count of the rest. Advisory only: ripwire does not report deleted symbols.
|
|
4978
|
+
- A symbol button closes the sheet, sets the Memory view's `node` to the symbol's canonical id (`symbol:<name>@<path>`) and switches to the Memory tab. The daemon's memory view reads a node of up to 512 characters when it starts with `symbol:` (200 otherwise) so a receipt id is never truncated into another node. Memory entries attached to that symbol appear once phase 3 links them; until then the graph shows the symbol node alone.
|
|
4979
|
+
- Colours are design tokens only, and the group wraps at phone width with 44 px targets.
|
|
@@ -45,3 +45,17 @@ Central compatibility policy marks 0.146.0 and 0.163.0 as verified. Other versio
|
|
|
45
45
|
## Evidence boundaries
|
|
46
46
|
|
|
47
47
|
The network setting remains configured disabled, without a separate network probe. Web search is requested disabled; effective model-turn mode is not read back. Returned exec argv is checked, but no run proves exec selecting the named default permission profile. No account/model review, TUI interaction or native Linux result is claimed. These runtime tests exercise the installed macOS binary; same-user filesystem/config mutation after the final metadata check remains outside atomic snapshot guarantees. The supported credential-root list is bounded, not a classifier for every private host file. Hosted CI and the changed-source gate are separate code-verification evidence recorded on the PR.
|
|
48
|
+
|
|
49
|
+
## Per-review start failure classes (#722)
|
|
50
|
+
|
|
51
|
+
Date: 2026-10-23. Platform: macOS arm64, Codex 0.163.0 (Homebrew `/opt/homebrew/bin/codex` and the fnm launcher), no model request, login or credential copy. Every CLI run used a temporary HOME and CODEX_HOME with only PATH, TMPDIR and LANG inherited; the person's real `~/.codex` was never passed to an agent CLI (its file names and table names were read, never values).
|
|
52
|
+
|
|
53
|
+
Reproduction of the reported `start_failed`, model-free, with the production `codexReviewerBackend.prepare`:
|
|
54
|
+
|
|
55
|
+
- Empty temporary homes: the receipt was produced (14 evidence entries) on both launchers, in about 0.3 to 0.4 s.
|
|
56
|
+
- Synthetic ambient configuration shaped like the real one (features, a marketplace, a plugin, four MCP servers with per-tool tables and an env table, a `hooks.json`): the receipt was produced in each of four variants.
|
|
57
|
+
- Each proof command (`--version`, `features list`, `mcp list --json`, `debug models --bundled`) took 54 to 82 ms, idle and with 12 busy loops running, against the 2 s per-command and 12 s total budgets.
|
|
58
|
+
- The proof commands did not rewrite `config.toml` (inode, size and mtime unchanged), so the config fence does not trip on them.
|
|
59
|
+
- This machine's credential roots: none is a dangling link, and the checkout is inside none of them.
|
|
60
|
+
|
|
61
|
+
So no hub bug was reproduced in the proof legs, and the first failure on 2026-10-22 happened in a daemon whose environment is not reproducible from the file system alone. The fix is the diagnosis: the refusal now carries one of the closed classes `version`, `binary`, `mcp_inventory`, `plugins_hooks_apps`, `sandbox_proof`, `credential_root`, `timeout`, `unknown`. It is in the unavailable reason, a `hub.log` line and the `reviewer` line of `ahub status`, and never holds a path, native CLI text or a token (a credential root is named by its path class). The next `start_failed` on this machine therefore names its leg. Fixture tests cover each class (`test/codex-review-isolation.test.ts`, `test/reviewer-pool-daemon.test.ts`, `test/output.test.ts`).
|
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
# 0.14.0 release verification
|
|
2
|
+
|
|
3
|
+
This record covers release preparation and source review. It does not claim publication, installation, a running-hub restart or a live TUI launch.
|
|
4
|
+
|
|
5
|
+
## Candidate
|
|
6
|
+
|
|
7
|
+
Version 0.14.0, from merged main at 11623553. On 2026-10-23 the person directed the release. It includes features, so the minor version is bumped (AGENTS.md, Releasing step 1).
|
|
8
|
+
|
|
9
|
+
- Roles capability per kind and form (#525-1, #725). A Claude channel instance may implement or verify in its own worktree. `planner` needs a kind that can plan, both for instances and for `roles` entries keyed by an instance id. This repository's claude holds the implementer role.
|
|
10
|
+
- The dashboard Usage view (#683 U4, #726) and the task sheet's Code group (#684 phase 2c, #731).
|
|
11
|
+
- SDLC metrics and control bands (#481 S7, #730): `ahub sdlc metrics`, `hub_sdlc_metrics`, and `[bands.<metric>]` with the log, diagnose and propose tiers.
|
|
12
|
+
- The compact `ahub board` (#724, #728): one line per task, with `--full` keeping the wrapped layout.
|
|
13
|
+
- Fixes:
|
|
14
|
+
- `ahub codex --as` for a headless reviewer (#720, #721);
|
|
15
|
+
- reviewer start-failure cause classes (#722, #727);
|
|
16
|
+
- issue identity linked at propose and accept (#723, #729);
|
|
17
|
+
- reserved backlog pulls offered to their reserved peer (#718, #732).
|
|
18
|
+
- Still open:
|
|
19
|
+
- #689, config editing;
|
|
20
|
+
- #684 phases 3 and 4;
|
|
21
|
+
- agent-mem#13 P3 (board #391) and P4 (#392);
|
|
22
|
+
- #719, the post-upgrade daemon CPU (#386);
|
|
23
|
+
- the release-metric bands and gates-run record (#481, recorded in the S7 amendment).
|
|
24
|
+
|
|
25
|
+
## Reviews
|
|
26
|
+
|
|
27
|
+
The planner reviewed each PR. Where a PR changed shared storage, concurrency, privacy or the review path, an independent read-only reviewer also checked it in a detached worktree. Every codex-connector thread was fixed or answered, and resolved, before merge. Each PR merged on green CI at its exact head; a rebase that touched only CHANGELOG or the spec was rechecked to keep the code identical to the approved head.
|
|
28
|
+
|
|
29
|
+
| PR | Final head | Review | CI |
|
|
30
|
+
| --- | --- | --- | --- |
|
|
31
|
+
| #721 (#720) | see PR | Headless `--as` returns after the start | exact head, success |
|
|
32
|
+
| #725 (#525-1) | a4d0c284 | Connector P1: planner kept for instance-id roles, now gated in `effectiveInstanceRoles`; stale bundle rebuilt | exact head, success |
|
|
33
|
+
| #726 (#683 U4) | 4ac69e30 | Three rounds: behind flag, render tests, docs, then the manager forwarding `view` through an allowlist | exact head, success |
|
|
34
|
+
| #727 (#722) | 43334eb0 | Closed cause classes, no paths or native text; live check deferred to after this release | exact head, success |
|
|
35
|
+
| #728 (#724) | 5ebf02a5 | Connector P2: long owner id kept the row single-line; person approved | exact head, success (two unrelated macOS flakes rerun) |
|
|
36
|
+
| #729 (#723) | 32aa4f32 | Three connector threads fixed by the planner: ambiguous sibling refused, hint silent in mode off, backfill cursor | exact head, success |
|
|
37
|
+
| #730 (#481 S7) | af0b7451 | Three rounds: stale-head evidence, episode cards, held timer, spec amendment, rename to `first_pass_check`, drift floor | exact head, success |
|
|
38
|
+
| #731 (#684 2c) | e284dac2 | Two rounds: withheld plan names mark the group incomplete | exact head, success |
|
|
39
|
+
| #732 (#718) | 6a782550 | Two rounds: seed rot and an invalid seed detection fixed; explain previews the reserved-only pull | exact head, success |
|
|
40
|
+
|
|
41
|
+
## Document review
|
|
42
|
+
|
|
43
|
+
Twelve documents were stale against the 0.13.0 stamps (88caef80, and d4defd2c for tests.md); the agent-notes README went stale through daemon.md during this review. Read-only reviewers in four groups checked each one against `git log <stamp>..HEAD` over its paths and the nine PRs, and corrected every statement a merged change made wrong.
|
|
44
|
+
|
|
45
|
+
| Document | Result | Checked claims and correction |
|
|
46
|
+
| --- | --- | --- |
|
|
47
|
+
| `README.md` | corrected | Status 0.14.0; `ahub sdlc metrics` and bands; the dashboard Usage view; verification link. |
|
|
48
|
+
| `docs/quickstart.md` | corrected | Install pin `v0.14.0`. "Upgrading to 0.14.0" covers the compact board, Usage view, Code group, metrics and bands, issue links, reviewer cause classes, Claude instances, reserved pulls, `--as` and the Claude restart. The board row in the working-day table. |
|
|
49
|
+
| `docs/operations.md` | corrected | The Usage view `unavailable` state and omitted-limits note; the board column rule (min(30, longest title)), plus AFTER and OWNER at narrow widths; the Code group counts and `planWithheld`; the classes a Claude implementer instance can still be routed; the #525 refusals; `--as` for hub-started reviewers; cause classes; metrics and bands; S4 and S6 shipped, S5 pending; issue linking; reserved pulls. |
|
|
50
|
+
| `docs/security.md` | corrected | The snapshot data of the Usage view and Code group; the manager's `view` allowlist; Claude instance worktrees; cause classes; issue linking with no GitHub read; the metrics authority, band card text and `band_notices`. |
|
|
51
|
+
| `src/cli/help.ts` | corrected | `ahub sdlc metrics [--since <YYYY-MM-DD\|ISO time>]`. Every subcommand and flag in main.ts and launch.ts has a row. |
|
|
52
|
+
| `docs/agent-notes/adapters.md` | corrected | Version and native-policy refusals keep their own sanitized message; how `ReviewerPrintPeer` classes its own failures. |
|
|
53
|
+
| `docs/agent-notes/budget.md` | corrected | The roll-up's readers include the console panel and the dashboard; `UsageRollup.daily` rules. |
|
|
54
|
+
| `docs/agent-notes/bus.md` | no change | No bus source changed; identifiers spot-checked. |
|
|
55
|
+
| `docs/agent-notes/daemon.md` | corrected | Scope includes `task-code-group.ts`. Rules for: the `view` allowlist, the Usage view, the Code group, the band timer, the compact board (`STAGE_CODES` tracking sdlc.ts), Claude instance roles, and `--as` for reviewer variants. |
|
|
56
|
+
| `docs/agent-notes/tasks.md` | corrected | Scope includes `sdlc-metrics.ts`. The #718 reserved-pull rule; `planner` needs `plannerEligible`; the qualified-title rule rewritten to match the code. |
|
|
57
|
+
| `docs/agent-notes/README.md` | no change | Background only; the new daemon.md rules leave its rationale entries unchanged (stale through daemon.md, a listed path). |
|
|
58
|
+
| `docs/agent-notes/tests.md` | no change | The new tests follow the existing rules; the #732 seed sentence matches seeds.json. |
|
|
59
|
+
|
|
60
|
+
AGENTS.md area lists and the `docs/verified.json` paths gain `sdlc-metrics.ts`, `usage-rollup.ts`, `task-code-group.ts`, `spec-read.ts` and `status-lines.ts`.
|
|
61
|
+
|
|
62
|
+
Follow-ups noted by the reviewers, not changed in this release:
|
|
63
|
+
- A person-started Claude implementer instance is refused only for implement, test and bulk_edit tasks (`ownerIsolation`). Other classes can still route to it until #525-5. operations.md now says so.
|
|
64
|
+
- The #725 planner filter also drops `planner` for configured ACP agents (they have no `projection`). This is documented now, but the PR did not announce it.
|
|
65
|
+
- The #684 2c spec amendment says `codeGroup` screens symbol ids; the screening happens earlier, in `publicView`.
|
|
66
|
+
|
|
67
|
+
## Checks
|
|
68
|
+
|
|
69
|
+
The `scripts/check.sh` full gate, `node scripts/check-docs.mjs --require-fresh`, and CI on the release head are recorded in the release PR.
|
package/docs/verified.json
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
"schemaVersion": 1,
|
|
3
3
|
"documents": {
|
|
4
4
|
"README.md": {
|
|
5
|
-
"verifiedAgainst": "
|
|
5
|
+
"verifiedAgainst": "d21a9ccc0a7916f5fb885343c3a585e813027d67",
|
|
6
6
|
"paths": [
|
|
7
7
|
"package.json",
|
|
8
8
|
"src/",
|
|
@@ -11,14 +11,14 @@
|
|
|
11
11
|
]
|
|
12
12
|
},
|
|
13
13
|
"docs/security.md": {
|
|
14
|
-
"verifiedAgainst": "
|
|
14
|
+
"verifiedAgainst": "d21a9ccc0a7916f5fb885343c3a585e813027d67",
|
|
15
15
|
"paths": [
|
|
16
16
|
"src/",
|
|
17
17
|
"templates/"
|
|
18
18
|
]
|
|
19
19
|
},
|
|
20
20
|
"docs/operations.md": {
|
|
21
|
-
"verifiedAgainst": "
|
|
21
|
+
"verifiedAgainst": "d21a9ccc0a7916f5fb885343c3a585e813027d67",
|
|
22
22
|
"paths": [
|
|
23
23
|
"package.json",
|
|
24
24
|
"src/",
|
|
@@ -35,7 +35,7 @@
|
|
|
35
35
|
]
|
|
36
36
|
},
|
|
37
37
|
"docs/quickstart.md": {
|
|
38
|
-
"verifiedAgainst": "
|
|
38
|
+
"verifiedAgainst": "d21a9ccc0a7916f5fb885343c3a585e813027d67",
|
|
39
39
|
"paths": [
|
|
40
40
|
"package.json",
|
|
41
41
|
"src/",
|
|
@@ -43,7 +43,7 @@
|
|
|
43
43
|
]
|
|
44
44
|
},
|
|
45
45
|
"docs/agent-notes/adapters.md": {
|
|
46
|
-
"verifiedAgainst": "
|
|
46
|
+
"verifiedAgainst": "d21a9ccc0a7916f5fb885343c3a585e813027d67",
|
|
47
47
|
"paths": [
|
|
48
48
|
"src/adapters/",
|
|
49
49
|
"src/pi/",
|
|
@@ -85,7 +85,7 @@
|
|
|
85
85
|
]
|
|
86
86
|
},
|
|
87
87
|
"docs/agent-notes/budget.md": {
|
|
88
|
-
"verifiedAgainst": "
|
|
88
|
+
"verifiedAgainst": "d21a9ccc0a7916f5fb885343c3a585e813027d67",
|
|
89
89
|
"paths": [
|
|
90
90
|
"src/hub/budget.ts",
|
|
91
91
|
"src/cli/statusline-tee.ts",
|
|
@@ -99,11 +99,12 @@
|
|
|
99
99
|
"src/adapters/registry.ts",
|
|
100
100
|
"src/hub/instance-budget.ts",
|
|
101
101
|
"src/hub/claude-state.ts",
|
|
102
|
-
"src/adapters/definitions/native-channel-state.ts"
|
|
102
|
+
"src/adapters/definitions/native-channel-state.ts",
|
|
103
|
+
"src/hub/usage-rollup.ts"
|
|
103
104
|
]
|
|
104
105
|
},
|
|
105
106
|
"docs/agent-notes/bus.md": {
|
|
106
|
-
"verifiedAgainst": "
|
|
107
|
+
"verifiedAgainst": "d21a9ccc0a7916f5fb885343c3a585e813027d67",
|
|
107
108
|
"paths": [
|
|
108
109
|
"src/hub/bus.ts",
|
|
109
110
|
"src/hub/envelope.ts",
|
|
@@ -118,7 +119,7 @@
|
|
|
118
119
|
]
|
|
119
120
|
},
|
|
120
121
|
"docs/agent-notes/daemon.md": {
|
|
121
|
-
"verifiedAgainst": "
|
|
122
|
+
"verifiedAgainst": "d21a9ccc0a7916f5fb885343c3a585e813027d67",
|
|
122
123
|
"paths": [
|
|
123
124
|
"src/hub/daemon.ts",
|
|
124
125
|
"src/hub/control-client.ts",
|
|
@@ -175,7 +176,9 @@
|
|
|
175
176
|
"src/adapters/configured-acp.ts",
|
|
176
177
|
"src/hub/config-trust.ts",
|
|
177
178
|
"src/cli/memory.ts",
|
|
178
|
-
"src/adapters/definitions/native-channel-state.ts"
|
|
179
|
+
"src/adapters/definitions/native-channel-state.ts",
|
|
180
|
+
"src/hub/task-code-group.ts",
|
|
181
|
+
"src/cli/status-lines.ts"
|
|
179
182
|
]
|
|
180
183
|
},
|
|
181
184
|
"docs/agent-notes/local-worker.md": {
|
|
@@ -199,7 +202,7 @@
|
|
|
199
202
|
]
|
|
200
203
|
},
|
|
201
204
|
"docs/agent-notes/tasks.md": {
|
|
202
|
-
"verifiedAgainst": "
|
|
205
|
+
"verifiedAgainst": "d21a9ccc0a7916f5fb885343c3a585e813027d67",
|
|
203
206
|
"paths": [
|
|
204
207
|
"src/hub/tasks.ts",
|
|
205
208
|
"src/hub/board.ts",
|
|
@@ -226,11 +229,13 @@
|
|
|
226
229
|
"src/hub/no-writes.ts",
|
|
227
230
|
"src/adapters/registry.ts",
|
|
228
231
|
"src/hub/review-findings.ts",
|
|
229
|
-
"src/hub/plan-conformance.ts"
|
|
232
|
+
"src/hub/plan-conformance.ts",
|
|
233
|
+
"src/hub/sdlc-metrics.ts",
|
|
234
|
+
"src/hub/spec-read.ts"
|
|
230
235
|
]
|
|
231
236
|
},
|
|
232
237
|
"docs/agent-notes/tests.md": {
|
|
233
|
-
"verifiedAgainst": "
|
|
238
|
+
"verifiedAgainst": "d21a9ccc0a7916f5fb885343c3a585e813027d67",
|
|
234
239
|
"paths": [
|
|
235
240
|
"test/",
|
|
236
241
|
"scripts/check.sh",
|
|
@@ -249,7 +254,7 @@
|
|
|
249
254
|
]
|
|
250
255
|
},
|
|
251
256
|
"docs/agent-notes/README.md": {
|
|
252
|
-
"verifiedAgainst": "
|
|
257
|
+
"verifiedAgainst": "d21a9ccc0a7916f5fb885343c3a585e813027d67",
|
|
253
258
|
"paths": [
|
|
254
259
|
"src/cli/console-state.ts",
|
|
255
260
|
"src/cli/attention.ts",
|
|
@@ -258,7 +263,7 @@
|
|
|
258
263
|
]
|
|
259
264
|
},
|
|
260
265
|
"src/cli/help.ts": {
|
|
261
|
-
"verifiedAgainst": "
|
|
266
|
+
"verifiedAgainst": "d21a9ccc0a7916f5fb885343c3a585e813027d67",
|
|
262
267
|
"paths": [
|
|
263
268
|
"src/cli/main.ts",
|
|
264
269
|
"src/cli/launch.ts",
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "agent-hub",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.14.0",
|
|
4
4
|
"description": "Channel between Claude Code and the agent-hub daemon: peer messages from Codex, Kimi and the local worker arrive as channel events; hub_send replies.",
|
|
5
5
|
"author": {
|
|
6
6
|
"name": "Young Joon Lee",
|