@staix/agent-hub 0.12.45 → 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 +31 -0
- package/README.md +85 -116
- package/docs/agent-notes/README.md +6 -0
- package/docs/agent-notes/adapters.md +10 -7
- package/docs/agent-notes/budget.md +7 -1
- package/docs/agent-notes/bus.md +6 -4
- package/docs/agent-notes/daemon.md +14 -2
- package/docs/agent-notes/local-worker.md +2 -1
- package/docs/agent-notes/tasks.md +16 -3
- package/docs/agent-notes/tests.md +5 -3
- package/docs/dogfood.md +95 -0
- package/docs/events.md +14 -1
- package/docs/operations.md +290 -27
- package/docs/profiles.md +2 -2
- package/docs/quickstart.md +13 -3
- package/docs/sdlc.md +59 -8
- package/docs/security.md +26 -13
- package/docs/smoke.md +9 -4
- package/docs/specs/2026-09-19-agent-hub-design.md +797 -8
- package/docs/verification/2026-10-19-reviewer-isolation.md +2 -0
- package/docs/verification/2026-10-22-code-facts.md +78 -0
- package/docs/verification/2026-10-22-codex-reviewer-isolation.md +61 -0
- package/docs/verification/2026-10-22-kimi-qwen-compat.md +79 -0
- package/docs/verification/2026-10-23-0.13.0.md +89 -0
- package/docs/verification/2026-10-23-0.14.0.md +69 -0
- package/docs/verification/dogfood/2026-10-22.md +3109 -0
- package/docs/verified.json +22 -17
- package/package.json +1 -1
- package/plugins/agent-hub/.claude-plugin/plugin.json +1 -1
- package/plugins/agent-hub/server.js +702 -347
- package/src/adapters/agy/entry.ts +5 -4
- package/src/adapters/claude-channel.ts +20 -9
- package/src/adapters/codex-appserver.ts +18 -2
- package/src/adapters/codex-review-isolation.ts +29 -11
- package/src/adapters/compat.ts +140 -0
- package/src/adapters/definitions/builtin-metadata.ts +3 -2
- package/src/adapters/definitions/native-cli-doctor.ts +12 -5
- package/src/adapters/definitions/native-cli-entry-codex.ts +13 -0
- package/src/adapters/definitions/native-cli-entry-jcode.ts +9 -6
- package/src/adapters/definitions/native-cli-jcode-mcp.ts +9 -2
- package/src/adapters/definitions/native-cli-preview-common.ts +2 -0
- package/src/adapters/definitions/native-cli-preview-jcode.ts +8 -2
- package/src/adapters/definitions/native-cli-preview-qwen.ts +2 -1
- package/src/adapters/definitions/qwen-native.ts +7 -3
- package/src/adapters/definitions/qwen.ts +2 -1
- package/src/adapters/jcode.ts +72 -31
- package/src/adapters/kimi-review.ts +6 -3
- package/src/adapters/registry-types.ts +3 -1
- package/src/adapters/registry.ts +1 -0
- package/src/adapters/reviewer-print.ts +34 -7
- package/src/cli/agents.ts +40 -0
- package/src/cli/console-review.ts +54 -0
- package/src/cli/console-state.ts +124 -29
- package/src/cli/console-symbols.ts +74 -0
- package/src/cli/console-usage.ts +89 -0
- package/src/cli/console.ts +26 -1
- package/src/cli/help.ts +17 -6
- package/src/cli/identity.ts +29 -6
- package/src/cli/init.ts +109 -11
- package/src/cli/main.ts +127 -11
- package/src/cli/output.ts +91 -3
- package/src/cli/preview.ts +1 -1
- package/src/cli/sdlc-release-guard.ts +82 -0
- package/src/cli/status-lines.ts +1 -1
- package/src/hub/action-class.ts +362 -0
- package/src/hub/action-policy.ts +106 -0
- package/src/hub/board.ts +65 -9
- package/src/hub/budget.ts +71 -2
- package/src/hub/bus.ts +43 -3
- package/src/hub/checks.ts +80 -31
- package/src/hub/code-facts.ts +223 -0
- package/src/hub/config-apply.ts +10 -1
- package/src/hub/config-trust.ts +7 -4
- package/src/hub/daemon.ts +319 -34
- package/src/hub/dogfood-read.ts +199 -0
- package/src/hub/dogfood.ts +163 -0
- package/src/hub/envelope.ts +1 -1
- package/src/hub/events.ts +11 -2
- package/src/hub/hub-tools.ts +26 -3
- package/src/hub/inbox-polling.ts +25 -0
- package/src/hub/instances.ts +87 -5
- package/src/hub/manager.ts +2 -2
- package/src/hub/profiles.ts +7 -2
- package/src/hub/release-worktree.ts +56 -0
- package/src/hub/review-brief.ts +36 -0
- package/src/hub/sdlc-metrics.ts +260 -0
- package/src/hub/sdlc-queue.ts +31 -9
- package/src/hub/sdlc-release-authorization.ts +173 -0
- package/src/hub/sdlc.ts +54 -14
- package/src/hub/settings.ts +5 -2
- package/src/hub/snapshots.ts +93 -8
- package/src/hub/spec-read.ts +31 -0
- package/src/hub/symbol-normalizer.ts +26 -0
- package/src/hub/task-code-group.ts +52 -0
- package/src/hub/task-code-symbols.ts +90 -0
- package/src/hub/task-sweep.ts +3 -2
- package/src/hub/tasks.ts +455 -67
- package/src/hub/test-integrity.ts +162 -0
- package/src/hub/ui.ts +3 -0
- package/src/hub/usage-rollup.ts +419 -0
- package/src/local/sandbox.ts +5 -3
- package/src/ui/index.html +274 -2
- package/templates/AGENTS.block.md +1 -1
- package/templates/profiles/dev.json +6 -1
- package/templates/profiles/general.json +6 -1
- package/templates/profiles/research.json +6 -1
- package/templates/profiles/writing.json +6 -1
- package/templates/release-guard.block.md +6 -0
package/docs/quickstart.md
CHANGED
|
@@ -17,7 +17,7 @@ ahub setup # installs the Claude Code channel plu
|
|
|
17
17
|
Or use the matching GitHub release:
|
|
18
18
|
|
|
19
19
|
```bash
|
|
20
|
-
bun add -g github:STAIxBWLB/agent-hub#v0.
|
|
20
|
+
bun add -g github:STAIxBWLB/agent-hub#v0.14.0
|
|
21
21
|
ahub setup
|
|
22
22
|
```
|
|
23
23
|
|
|
@@ -30,6 +30,14 @@ and upgrade or crash recovery, see the [operations guide](operations.md). To
|
|
|
30
30
|
move running projects to a newer release later, run `ahub upgrade` in a
|
|
31
31
|
terminal: it reviews the plan with you, applies it and follows it.
|
|
32
32
|
|
|
33
|
+
## Upgrading to 0.14.0
|
|
34
|
+
|
|
35
|
+
At a terminal width `ahub board` now prints one line per task: the title is cut with `...`, dependencies move to an AFTER column, STAGE is the meter with a short next-step code (`intent: issue?`, `review: CI?`, `ready`) and a legend line explains them; `ahub board --full`, piped output without `COLUMNS` and `--json` keep the whole title and SDLC advice (#724). The dashboard's Agents view gains a Usage view under Usage budgets: lifetime totals, the last 14 UTC days of tokens per peer, the open pauses and recent limit events, and a task table in which a private task shows only as `#id [pii]` (#683 U4). A task sheet gains a Code group: the changed symbols marked `matching` or `unplanned` against the plan, the plan names nothing changed under as `untouched`, and a symbol opens the Memory tab on its node; a private task has no group (#684). `ahub sdlc metrics [--since <date>]` and the planner tool `hub_sdlc_metrics` (planner or person, read-only) report lead times, first-pass approval, rework rounds, stale-head re-reviews and stalls from board history, with `unknown` where the board cannot tell. An optional `[bands.<metric>]` table in `.agenthub/sdlc.toml` turns a breach into a hub notice, a read-only `triage` backlog task or a `plan` backlog card, once per episode, only while `mode` is `advise` or `enforce`, and never a GitHub issue (#481 S7; see [SDLC metrics and control bands](sdlc.md#sdlc-metrics-and-control-bands-481-s7)). In advise and enforce a task's issue identity is now linked when it is proposed or accepted, with no GitHub read; a title that names another repository (`agent-mem#13`) links only when `refs.repo` names it, and the propose result suggests setting it (#723). A fresh reviewer whose start fails now names a cause class (`version`, `binary`, `mcp_inventory`, `plugins_hooks_apps`, `sandbox_proof`, `credential_root`, `timeout` or `unknown`) on the `reviewer` line of `ahub status` and in `hub.log` (#722). A Claude channel instance in `.agenthub/config.local.json` may declare `implementer` or `verifier` and then needs its own `worktree`, while a planner or reviewer instance refuses one; the hub routes no code tasks to such an instance until #525-5, and a headless Claude instance stays reviewer-only. `planner` is refused on a pi, local or agy instance declaration and dropped, with a notice, from a `roles` entry for those kinds (#525-1; see [Roles per SDLC stage](operations.md#roles-per-sdlc-stage-525-planned)). A planner pull of a reserved backlog task now assigns it to the reserved peer and offers it at once; when that peer is offline, off roster, paused, full or excluded, the pull is refused with the reason and no other peer takes it (#718). `ahub codex --as <id>` for a headless fresh reviewer instance starts it and returns (#720). Restart Claude sessions so they load the 0.14.0 plugin. Coming from 0.12.45 or older, the notes below apply too.
|
|
36
|
+
|
|
37
|
+
## Upgrading to 0.13.0
|
|
38
|
+
|
|
39
|
+
One version policy now gates the Codex and Kimi fresh reviewers, Jcode, agy and Qwen (#691): `ahub agents` lists each installed CLI with its version and verdict, a version outside the verified ones (or, for Jcode and agy, outside their compatible line) is refused with the trust command, and `ahub agents trust <id> <version>` accepts that exact version on this machine; only the person can run it, and the reviewers' isolation proofs still run. See [agent CLI versions](operations.md#agent-cli-versions). If you use Jcode, run `ahub jcode` once per project: the hub-owned `.jcode/mcp.json` entry no longer names a peer, `ahub status` and `ahub doctor` show the old entry as not ready until it is rewritten, and the hub admits Jcode's hub tools only from an owned launch (#368). Extra Jcode instances, each with its own `jcode_home`, can be declared in `.agenthub/config.local.json` and started with `ahub jcode --as <id>` (#685; see [Jcode operations](operations.md#optional-jcode-peer)). Run `ahub init` once per project to refresh the hub block in `AGENTS.md`: it tells pull-only agents never to poll `hub_inbox` or CI on a timer and to run one heavy job per repository at a time (#681). `ahub usage` (with `limits` and `total`) and the console's Usage panel (`7`) show tokens per peer, task, issue, PR, release, day or model and the quota pause and reset history (#683); `ahub report --dogfood` prints a read-only scorecard (#687). In the console, `r` on a task in review now asks `a` (approve) or `c` (request changes, which needs a note) (#711), and a task's detail shows an overlaps block and, when ripwire 0.6.7 is on the daemon's PATH, its changed symbols, which the review brief lists too (#684). A pull-only peer now drains its queue up to 64 KiB per `hub_inbox` read, a queued delivery can be discarded while its peer is busy, and the person clears one peer's queue with `ahub queue discard --peer <id> --reason <text> --yes` (#713). `ahub sdlc authorize-release <version>` records the person's release approval, and `ahub init --release-guard` installs an optional tag pre-push guard (#481 S6; see [release approval](operations.md#release-approval-and-optional-tag-guard)). As announced since 0.12.x, `sdlc.workflow = "gsd-lite"` no longer loads and the `phase` receipt name is refused (#481): remove the setting and record a `criteria` checklist with `hub_sdlc_record`. Restart Claude sessions so they load the 0.13.0 plugin. Coming from 0.12.44 or older, the notes below apply too.
|
|
40
|
+
|
|
33
41
|
## Upgrading to 0.12.45
|
|
34
42
|
|
|
35
43
|
The dashboard is redesigned (#469): from 1024px a top bar and a left sidebar (a row of tabs below that), keys `1`-`7` switch views, `?` lists the keys and `Esc` closes a sheet; a task opens as a side sheet, and the plan, split, releases, delivery evidence and propose forms sit in a Plan sheet opened from the board toolbar. View names, ids and the view cookie are unchanged. A person can load a bundled ACP adapter module declared under `adapter_modules` (a `path` and its `sha256`) in untracked `.agenthub/config.local.json`: only the daemon imports it, at startup, so a changed declaration needs a restart; Settings and hub tools cannot write it, and a declaration in the shared `.agenthub/config.json` is ignored. A project that declares modules can be upgraded or recovered only between releases that advertise `external_adapter_modules`. See [Adding an AI tool](adding-an-ai-tool.md#5-external-adapter-modules-451-a4) (#451). `hub_review` and `ahub review ... --findings '<json>'` take structured findings, `max_nits` in `.agenthub/sdlc.toml` caps the nits per report (default 5), and in enforce an open Important finding refuses approval (#481 S3). Plans take optional `proof` and `risks`. In enforce, approval also needs every changed PR file covered by a literal `plan.paths` entry; otherwise amend the plan through `hub_task_accept` and call done again, or the person runs `ahub sdlc accept-departure <task> <head> <reason...>`, which lapses when the hub restarts (#481 S2). An unresolved review thread now settles only by resolution or a later reply from a GitHub user; any other author kind counts as a pending bot comment (#481). `ahub roster remove <peer>` also deletes the roster key of an instance no longer declared (#225). When `ahub kimi` refuses a `kimi_cmd` with no TUI form, it now says the value is the running hub's, retained until `ahub settings reload` (#654). A Claude session no longer gets `hub_inbox` advice while a delivery hold refuses its pulls (#221). A same-size rewrite of a large file within one filesystem timestamp tick is now told apart by its bytes (#428). Restart Claude sessions so they load the 0.12.45 plugin. Coming from 0.12.43 or older, the notes below apply too.
|
|
@@ -76,7 +84,7 @@ The 0.12.38 plugin reports a reply stopped by the hop cap as not delivered inste
|
|
|
76
84
|
|
|
77
85
|
Antigravity (`agy`) can join as an optional pull-only implementer (#485). It is off roster by default: install the Antigravity CLI 1.4.x (1.4.2 verified; other 1.4.x patches launch with a warning; on 1.4.3 the hub tools appear from the second TUI turn), run `ahub roster add agy`, then run `ahub agy` in a terminal. Its native permissions are unmanaged and it has no hub sandbox. See [Antigravity operations](operations.md#optional-antigravity-agy-peer).
|
|
78
86
|
|
|
79
|
-
Reviews can rotate across fresh Claude, Codex and Kimi reviewer sessions (#519, #533). Declare headless reviewer-only instances in `.agenthub/config.local.json`; Codex and Kimi join only with `review_mode: "fresh"`. Codex also needs
|
|
87
|
+
Reviews can rotate across fresh Claude, Codex and Kimi reviewer sessions (#519, #533). Declare headless reviewer-only instances in `.agenthub/config.local.json`; Codex and Kimi join only with `review_mode: "fresh"`. Codex also needs a compat-admitted Codex (receipt-verified 0.146.0 and 0.163.0; select it with `codex_bin` when needed), and Kimi needs a compat-admitted Kimi Code on PATH (receipt-verified 2.2.0 and 2.3.0), signed in with its own OAuth login. Run `ahub agents` for the current version policy; every fresh reviewer still needs a passing runtime preparation. The [2.3.0 receipt](verification/2026-10-22-kimi-qwen-compat.md) is model-free and does not establish real login or model readiness. A headless reviewer instance without `review_mode` keeps its native behavior and stays out of the pool. agy is not a reviewer: naming it in `review.pool` shows an unavailable skip in `ahub route explain`, never a review. Once a train is active or frozen, or `review.pool` is set or fresh reviewer instances are declared, `ahub upgrade` refuses a target release without that support, so an older release cannot take the project over. See [fresh reviewer pool](operations.md#fresh-reviewer-pool-519).
|
|
80
88
|
|
|
81
89
|
A guarded config edit that the hub keeps at its running value now shows as `config retained: <keys>; run ahub settings reload` in status, the console and the dashboard's Settings, and starting a peer whose launch keys are retained says it uses the running value (#542). See [settings](operations.md#settings-from-the-dashboard-and-the-terminal).
|
|
82
90
|
|
|
@@ -136,11 +144,13 @@ or set `OMNIROUTE_API_KEY`. The `.agenthub/routing.toml` that `ahub init` writes
|
|
|
136
144
|
| --- | --- |
|
|
137
145
|
| `ahub say [@peer] <text>` | talk; start with `[STATUS]` to let it batch, `[FYI]` for the record only |
|
|
138
146
|
| `ahub task propose [--class <c>] <title>` | put work on the board; the hub picks an owner and a reviewer, and the class if you name none |
|
|
139
|
-
| `ahub board` | who has what; `ahub task show <id>` for one task |
|
|
147
|
+
| `ahub board` | who has what, one line per task at a terminal width; `--full` for whole titles and SDLC advice, `ahub task show <id>` for one task |
|
|
140
148
|
| `ahub task cancel <id> --reason <text>` | close obsolete work without approving it or stopping a native turn |
|
|
141
149
|
| `ahub settings set planner <peer>` | pin the planner; status reports whether it came from settings, roles or the automatic choice |
|
|
142
150
|
| `ahub settings reload` | apply edited config files without a restart; guarded values wait for your `y` in this terminal, and restart-only keys stay pending |
|
|
143
151
|
| `ahub status` | peers, queues, pauses; `ahub budget` for quota windows |
|
|
152
|
+
| `ahub usage [--by peer\|task\|issue\|pr\|release\|day\|model]` | tokens per group, each number with its source; `ahub usage limits` for pauses and resets |
|
|
153
|
+
| `ahub agents` | which installed agent CLI versions the hub accepts; `ahub agents trust <id> <version>` accepts an untested one on this machine |
|
|
144
154
|
| `ahub memory profile-check` | a person's read-only check of running memory bindings in the current registry |
|
|
145
155
|
| `ahub ask <question>` | what the board, shared memory and the log say, with the ids the answer rests on |
|
|
146
156
|
| `ahub stop <peer>` | stop one headless peer (Kimi, headless Pi, `local`); the hub and the other peers keep running |
|
package/docs/sdlc.md
CHANGED
|
@@ -16,11 +16,11 @@ The built-in lifecycle follows the [AI-Native SDLC Playbook](https://claude.com/
|
|
|
16
16
|
| spec | Design | person/planner | issue identity and scope | spec acceptance/body drift checked at build entry |
|
|
17
17
|
| plan | Build | planner/owner | plan, branch, existing predecessor gates | current planner review required at build admission; literal-path conformance gates approval in enforce |
|
|
18
18
|
| build | Build | owner | PR identity/current head and local check on that head | entering review, when enabled |
|
|
19
|
-
| test | Test | verifier/reviewer | required checks and local gate on the reviewed head | approval, when enabled; integrity
|
|
19
|
+
| test | Test | verifier/reviewer | required checks and local gate on the reviewed head | approval, when enabled; bug test integrity below, evals pending S5 |
|
|
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
|
-
| release | Deploy | person | merged commit, version/tag, docs freshness/live receipt checklist |
|
|
23
|
-
| maintain | Maintain | person/planner | follow-up intent and operational evidence | shown; metrics
|
|
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 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
|
|
|
@@ -32,10 +32,10 @@ Stored `implement` and `verify` stage ids read as `build` and `test`. A write th
|
|
|
32
32
|
| S1 | Accepted intent/spec and spec drift | Person-only `ahub sdlc accept`, bound to the issue's body edit time; build entry needs it in enforce (see Spec acceptance below) |
|
|
33
33
|
| S2 | Accepted plan and diff conformance | Bounded proof/risks, metadata-only plan conformance and person-bound departure acceptance |
|
|
34
34
|
| S3 | Bugs, Security and Compliance review passes | Head-bound findings, configured nit cap, Important approval gate and recurring-finding amendment proposals |
|
|
35
|
-
| S4 | Red-first tests and test integrity |
|
|
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
|
-
| S6 | Production approval gate |
|
|
38
|
-
| S7 | Metrics, deterministic control bands and follow-up intent |
|
|
37
|
+
| S6 | Production approval gate | Person-only commit/version/docs receipt and opt-in Git tag pre-push guard; report stays read-only |
|
|
38
|
+
| S7 | Metrics, deterministic control bands and follow-up intent | `ahub sdlc metrics`, optional `[bands]` with log, diagnose and propose tiers, one card per breach episode; no PR readback and no security baseline (design spec amendment) |
|
|
39
39
|
| S8 | Compliance acceptance-criteria checklist | Head-bound checklist retained; legacy workflow and phase compatibility deprecated; task import removed |
|
|
40
40
|
| S9 | Planner grooming and plan review | Read-only digest, current plan-review receipt at build admission, all-mode WIP and accepted-untouched return |
|
|
41
41
|
| S10 | Planner cost governance | Typed review/CI scope, premature-review admission, identical-tree CI reuse and read-only cost report |
|
|
@@ -64,7 +64,7 @@ The acceptance-criteria checklist belongs to the Compliance pass over the same b
|
|
|
64
64
|
|
|
65
65
|
## Delivery and verification state
|
|
66
66
|
|
|
67
|
-
Head-bound review/approval gates, bounded custom definitions, read-only merge/release reports, S0/S8 vocabulary/checklist migration, S1 spec acceptance and S9 planner admission and S10 cost governance are implemented in source. S11 release-train governance ships in 0.12.37.
|
|
67
|
+
Head-bound review/approval gates, bounded custom definitions, read-only merge/release reports, S0/S8 vocabulary/checklist migration, S1 spec acceptance and S9 planner admission and S10 cost governance are implemented in source. S11 release-train governance ships in 0.12.37. S5, S7 and the native advise-through-merge/release cycle remain pending. S6 authorization and its optional tag hook are implemented in source. S4 test integrity is implemented in source. S2 plan conformance and S3 findings are implemented in source. Installation and native acceptance require separate receipts; a person authorization is consent, not native verification.
|
|
68
68
|
|
|
69
69
|
## Initial commands
|
|
70
70
|
|
|
@@ -154,7 +154,7 @@ actor = "person"
|
|
|
154
154
|
after = ["approve"]
|
|
155
155
|
```
|
|
156
156
|
|
|
157
|
-
These ids are guidance/receipt destinations. Publish neither merges nor releases. The read-only source-readiness report is implemented below. Native acceptance
|
|
157
|
+
These ids are guidance/receipt destinations. Publish neither merges nor releases. The read-only source-readiness report is implemented below. Native acceptance and research measurements remain unverified or pending; person authorization is recorded separately below.
|
|
158
158
|
|
|
159
159
|
|
|
160
160
|
# Structured documentation readiness evidence (#257)
|
|
@@ -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.
|
|
@@ -278,3 +299,33 @@ Review/status use the same bounded PR file metadata reader as the merge queue; n
|
|
|
278
299
|
The person may instead run `ahub sdlc accept-departure <task> <head> <reason...>`. Acceptance requires a currently known departure, never unknown or already covered conformance. The hub reads and captures the complete current repo/PR/head inventory, verified task repository scope and the plan fingerprint/revision; changing the plan, repository refs/scope, link, head or inventory invalidates that acceptance. Departure consent also expires on hub restart: amend the plan or accept again in the new host lifetime. This fail-closed ceiling prevents a rejected, unpersisted readback from reviving old consent; a durable host observation journal is the upgrade path. The reason is bounded to 2048 characters, sanitized and privacy-screened. There is no peer tool or live-test delegation for this person-only operation. Private PR inventories are unavailable to this remote readback; amend the private plan instead. Public private-task projections show only whether a departure acceptance exists.
|
|
279
300
|
|
|
280
301
|
The report names "plan revisions after the first observed push". Host-authored content revisions live in task history, independently of backlog `planningRevision`. The first verified remote head records the current revision/fingerprint; earlier pushes are explicitly unknown. Pending observations persist only with successful owner/reviewer transitions; status and queue reads do not append board history. Off mode captures no S2 observation or content revision. Plan edits made while off therefore do not count in revisions after the first observed push when reporting resumes. Revoking an existing person acceptance on a plan change still persists its invalidation, including while reporting is off. If startup loses a pending observation, the earlier interval remains unknown. This observation boundary is a ceiling until a webhook or Actions push receipt provides a true push-event source.
|
|
302
|
+
|
|
303
|
+
## Test integrity (#481 S4)
|
|
304
|
+
|
|
305
|
+
When mode is enabled, the linked issue's complete label metadata decides applicability: `bug` (case-insensitive) requires S4, known non-bug labels exempt it, and unavailable labels are unknown. No issue title/body or patch is read. Enforce blocks entering review and approval for unknown applicability, missing/invalid proof, incomplete file metadata, or modified/deleted pre-existing test files. Advise exposes the same report without blocking; off adds no S4 receipt or readback.
|
|
306
|
+
|
|
307
|
+
Record `redFirst` with two legs through `hub_sdlc_record` or `ahub sdlc record`. Both use the same bounded `check` name; `red` has `result: "failed"`, `green` has `result: "passed"`, each with `source: "local" | "container" | "ci"` and a full lowercase commit SHA. The red commit must occur in this PR, have one parent, and add only recognized test paths. Its preceding single-parent ancestry inside the complete PR commit set must touch only test paths, so implementation commits cannot precede red. Merge/ambiguous ancestry is refused. The green SHA must equal the current PR head. Local/container results remain labelled claims, CI legs require the named failed/passed CheckRun observed on their exact commits; agent-posted StatusContext entries cannot establish CI evidence. `checkEvidence` labels each leg `claimed` or `observed`; `redFirst: verified` describes validated structure and supplied results, not independent observation of local/container execution. CI cancellation, timeout or skipped results do not prove red-first failure. Relinking the repo, issue or PR drops the proof unless it is explicitly supplied again. In enforce, only the person may change an established repo/issue; an owner/reviewer may fill missing identity or relink the PR. This identity boundary also holds before the first read and after restart, so an owner cannot evade S4 by swapping a bug issue for a non-bug issue. Advise/off keep existing relink authority.
|
|
308
|
+
|
|
309
|
+
Example receipt (replace both SHAs):
|
|
310
|
+
|
|
311
|
+
```json
|
|
312
|
+
{"redFirst":{"red":{"source":"local","head":"bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb","check":"bun test test/regression.test.ts","result":"failed"},"green":{"source":"local","head":"aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa","check":"bun test test/regression.test.ts","result":"passed"}}}
|
|
313
|
+
```
|
|
314
|
+
|
|
315
|
+
Status/explain and merge-queue rows expose `testIntegrity` with applicability, red-first state, file-inventory state, source labels and modified/deleted test paths. Added tests are allowed. Test paths are under `test/`, `tests/` or `__tests__/`, or have a supported `.test`/`.spec` suffix; conventional `*_test.go`, `test_*.py`, `*_test.py`, `*_spec.rb` and `*Test.java` names also count. Renames/copies, unsafe paths, missing change types and truncated inventories stay unknown. Private tasks and metadata rejected by the current host privacy policy expose a neutral unknown report; receipt metadata is screened again on public output.
|
|
316
|
+
|
|
317
|
+
Reads share the existing 20 s lifecycle/queue allowance and fixed argument/output caps. Labels and check contexts are bounded to 100 each, PR commits to 100, and the red commit to fewer than 100 changed files; larger proof is unknown rather than silently accepted. The queue revalidates every linked issue's applicability with its final PR snapshot (up to 32 distinct issues). Metadata cannot detect a later commit gutting assertions in a test added by the PR; content-aware review remains required. These metadata/claim receipts do not prove a native/container run by themselves; no live advise-through-release receipt is claimed.
|
|
318
|
+
|
|
319
|
+
## Person release authorization (#481 S6)
|
|
320
|
+
|
|
321
|
+
From the running daemon's candidate checkout, the person runs `ahub sdlc authorize-release <version>`. The console-only operation binds approval to the exact current commit, its committed package version, the origin fetch/push identity and fresh committed documentation evidence. It rereads the head and remote after the documentation check. Agent shells, including `--live-test`, cannot grant it. An append-only metadata event records the approval id, commit, version and documentation digest/count. The authoritative ledger is daemon-owned memory: restart revokes every approval; audit history cannot restore one.
|
|
322
|
+
|
|
323
|
+
`ahub sdlc release <owner/repo>` shows a matching or stale authorization receipt. Authorization removes only the person-decision blocker. Native acceptance remains unverified and `ready` remains false until separately supported. No tag, workflow or publication is changed by authorization or readback.
|
|
324
|
+
|
|
325
|
+
The optional `ahub init --release-guard` writes instructions and an executable default Git `pre-push` hook. Preview with `--dry-run --json`. An existing regular hook is renamed to `pre-push.agent-hub-original`, preserving its inode and mode, and chained with the exact original arguments and stdin only after successful preflight. Ordinary later `init` calls retain the guard. Linked/hardlinked hooks, occupied backups and custom `core.hooksPath` are refused before project writes; integrate custom hook runners manually instead. The wrapper pins the current Bun and package entry paths; run init again after moving or replacing that installation.
|
|
326
|
+
|
|
327
|
+
For every actual pushed `v*` destination, the hook requires an exact semantic version and a matching receipt for the captured source object peeled to its commit. Missing consent, another commit/version/remote, daemon downtime or a reset ledger refuses the push. Version-tag deletion is refused. Branch-only pushes do not need a daemon and still reach the user's hook. The person can bypass with Git `--no-verify`; agents must never bypass it. This is a cooperative Git guard, not an OS capability boundary.
|
|
328
|
+
|
|
329
|
+
Before other release commands, agents run `ahub sdlc release-guard -- <literal argv...>`. Supported forms are one named-remote tag push, `gh release create v<version>` (an existing GitHub tag is read back; a new one requires `--target <full-sha>`), and `npm publish` from the approved checkout. Ambiguous selectors, alternate repositories, bulk pushes, workspaces and archive publishes refuse. The npm preflight refuses tracked worktree changes before and after rechecking the committed-doc verdict and HEAD, but does not attest the eventual tarball, npm configuration or lifecycle scripts. The command performs no release operation and its answer is not an execution grant. GitHub/npm enforcement remains instruction-based until adapter integration (#312/#313); rerun preflight after changing any target or candidate. Native install, live release and migration evidence remain separate obligations.
|
|
330
|
+
|
|
331
|
+
Tracked npm cleanliness compares the cached index to HEAD and raw tracked blob bytes without `git status`, clean/process filters or text converters. It refuses unknown evidence, gitlinks, transformed files and inventories above 4,096 entries, 4 MiB per file, 32 MiB total or 250 ms; untracked files remain outside this check and the eventual tarball is not attested.
|
package/docs/security.md
CHANGED
|
@@ -7,8 +7,8 @@ agent-hub connects agents that can each run commands. This page says what the hu
|
|
|
7
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`. The bearer header a Codex TUI sends to the proxy carries only the terminal launch id (#586): it binds terminal ownership and is not a credential. External clients cannot claim the console user's id or a hub-managed peer's id.
|
|
9
9
|
- **Permission prompts stay on unless an operator selects a permission mode.** `ask` leaves native decisions unchanged. `ask-when-needed` grants scoped Claude file tools or Pi/local writes and edits; Kimi and Codex use their own native policies. `never-ask` requires explicit runtime confirmation or a person's console `y` for a config default at hub start. Until that config confirmation the peer runs in `ask`. 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. Configured ACP kinds (#451) never get this exception: a request whose title spells a hub tool exactly still goes to the person approval path, and only a mapped native mode changes what the agent itself asks. 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. A Pi or `local` request whose call was cancelled or whose turn ended is withdrawn at once: a later answer is refused and grants nothing, an `Always allow` included. One shell command runs without a hub approval: the `!command` a person types in the managed Pi TUI. It stays inside the same sandbox and denylist, and the hub takes it only from the verified owner process of a Pi in TUI mode, while Pi is idle, on a reservation that is used once; a model's own bash call never takes that path (issue #253). `--unattended` turns prompts off, says so loudly, and is never the default.
|
|
10
|
-
- **A committed config cannot choose launch commands, credential files, data endpoints, permission modes or a wider sandbox.** The machine-local fields (`kimi_cmd`, `permission_modes`, `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`, `terminal.open`, `instances`) apply only from a config file git confirms nobody committed (`instances`
|
|
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. Two opt-in stores copy numbers from it into your home directory, shared by every project on the machine (directories mode 700, files 600): research records (issue #247, `research.enabled`) in `~/.agenthub/research/` hold per approved task the ids, states, counts, tokens, times and PII flag the events hold, a person's label from a closed list, and a hash in place of the project id; benchmark runs (issue #251) in `~/.agenthub/bench/` hold suite name and hash, arm label, the attached peers' ids, models and permission modes, task ids, outcomes, exit codes, counts, tokens and times, never suite text or a command's output.
|
|
10
|
+
- **A committed config cannot choose launch commands, credential files, data endpoints, permission modes or a wider sandbox.** The machine-local fields (`kimi_cmd`, `permission_modes`, `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`, `terminal.open`, `instances`, `action_policy`) apply only from a config file git confirms nobody committed (`instances` and `action_policy` are read only from an untracked `.agenthub/config.local.json`; in `config.json` they are ignored even when untracked): `.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, a permission mode, the command that opens a terminal, or a wider sandbox. A command in `checks` runs as you, outside the local worker's sandbox, like a git hook. So do the `setup` and `verify` commands of a benchmark suite (issue #251): `ahub bench run <suite.json>` is a person's command, refused from an agent session, and it resets the work tree for each attempt, so it runs only with `bench.enabled`, at the root of a clean repository that tracks nothing under `.agenthub/`. 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. The hub's own git calls outside the sandbox (snapshots, facts, config trust, project discovery, memory recall, the bench runner) all use the single hardened implementation in the exact-pinned first-party `@staix/agent-mem/platform` package, re-exported by `src/hub/git.ts`, which disables the programs a repository's configuration can name — the file-system monitor, hooks, external diff, the pager, and the credential and askpass helpers, with prompts failing closed — so a config that ever does get written cannot run code with the hub's rights. Of those, the monitor, hooks and external diff are reachable by commands the hub issues (the planted-program test exercises all three; a `diff --no-index` reads the repo's config, so that call also passes `--no-ext-diff` and `--no-textconv`); the pager is not (every call pipes stdout). Credential and askpass helpers are disabled for project repositories; memory sync alone may use credentials for its own memory repository, without prompts. The cost: `core.fsmonitor=false` also turns off a person's enabled built-in monitor for the hub's own calls, so the per-turn snapshot's `add -A` scans the whole tree (the snapshot Git calls of one burst share a 250 ms budget, each call at most 10 s; a boundary that runs out is recorded as unknown, never as a stale baseline) (#576). git has no global switch for filter drivers, and of the hub's commands only the snapshot's `add`/`restore` would run one (its driver command lives in the protected `.git/config`).
|
|
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. Budget pauses, resumes and quota resets carry the peer, window, times and the hub's own reason text, and a person's release authorization its commit, version and docs digest. The usage roll-up (#683) folds the usage, token and limit records into `hub.db` and joins issue, PR and release from the board when read; `ahub usage` and the console's Usage panel answer only the console and a configured planner, and a planner not during a private turn. It stays on the machine; `ahub export` only prints it. Two opt-in stores copy numbers from it into your home directory, shared by every project on the machine (directories mode 700, files 600): research records (issue #247, `research.enabled`) in `~/.agenthub/research/` hold per approved task the ids, states, counts, tokens, times and PII flag the events hold, a person's label from a closed list, and a hash in place of the project id; benchmark runs (issue #251) in `~/.agenthub/bench/` hold suite name and hash, arm label, the attached peers' ids, models and permission modes, task ids, outcomes, exit codes, counts, tokens and times, never suite text or a command's output.
|
|
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. The Pi continuity bookmark that recovery keeps across stop and archive (`.agenthub/state/recovery-continuity.json`, mode 600, at most 16 KiB) holds identities only too: operation and project ids, the project root, and Pi's session id, session file, backend and model name; a bookmark that is not a private regular file, is larger than that or names another project or operation reads as invalid, which is kept distinct from no bookmark.
|
|
@@ -59,6 +59,11 @@ is refused before write/edit effects.
|
|
|
59
59
|
Kimi executes its own tools outside that sandbox; Claude and Codex retain their
|
|
60
60
|
vendor boundaries. The never-ask confirmation names this difference. No mode
|
|
61
61
|
turns off the sandbox or grants a session-wide allow-always option.
|
|
62
|
+
The always-ask action policy (#636) is a foundation only: its classifier and
|
|
63
|
+
session budgets exist, `action_policy` is read only from an untracked
|
|
64
|
+
`.agenthub/config.local.json`, and no adapter enforces it yet, so it adds no
|
|
65
|
+
protection; each peer's status says `always-ask not enforceable: adapter
|
|
66
|
+
integration pending`.
|
|
62
67
|
|
|
63
68
|
Config defaults apply automatically only up to ask-when-needed. A never-ask
|
|
64
69
|
request from an applied untracked config is held as ask until the person
|
|
@@ -86,7 +91,7 @@ as that peer in tools mode. Messages and task changes retain that actor;
|
|
|
86
91
|
`say` defaults to status priority and important messages still require the peer's
|
|
87
92
|
capability. Conflicting or malformed markers fail closed. Refusals name only the marker variable names actually present, never their values. There is no `--as-user`.
|
|
88
93
|
By default, console operations are refused before connecting, including permission answers
|
|
89
|
-
and permission modes, queue resolution, budget overrides, benchmark runs, research
|
|
94
|
+
and permission modes, queue resolution and bulk discard, agent version trust, budget overrides, benchmark runs, research
|
|
90
95
|
backfill and task labels, stopping a peer (`ahub stop`), lifecycle and recovery
|
|
91
96
|
operations, and `ask`.
|
|
92
97
|
For live verification explicitly authorized by the user, an agent may invoke
|
|
@@ -112,7 +117,7 @@ and flag. The current daemon emits `agent_cli` with `delegated: true` and
|
|
|
112
117
|
`refused: false` for the negotiated outcome. Allowed-class commands retain the peer identity and capability checks, including
|
|
113
118
|
messages, proposals and reviews. Peer starts, pause and resume may delegate
|
|
114
119
|
operational console authority; task assign/escalate under the flag refuse. `--as-user`, person task labels,
|
|
115
|
-
person-authored planning/release/memory/goal writes, SDLC record, spec and plan-departure acceptance, benchmark run,
|
|
120
|
+
person-authored planning/release/memory/goal writes, SDLC record, spec and plan-departure acceptance, release authorization, agent version trust, benchmark run,
|
|
116
121
|
`ahub recovery reattach`, a `--fresh-session` dispose that would replace a repaired
|
|
117
122
|
session's conversation (or whose scope cannot be read), and the pet
|
|
118
123
|
composer are refused; presentation flags such as `--color` or `--verbose` before
|
|
@@ -171,7 +176,7 @@ Please report vulnerabilities privately through GitHub's "Report a vulnerability
|
|
|
171
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.
|
|
172
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.
|
|
173
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.
|
|
174
|
-
- The browser receives a bounded redacted event stream, peer states and queue counts, public task views, 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.
|
|
175
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.
|
|
176
181
|
- The listener stops with the daemon. Expired sessions must be reopened with `ahub ui`; the page does not silently obtain new credentials.
|
|
177
182
|
|
|
@@ -183,7 +188,7 @@ Please report vulnerabilities privately through GitHub's "Report a vulnerability
|
|
|
183
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.
|
|
184
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.
|
|
185
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.
|
|
186
|
-
- 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.
|
|
187
192
|
|
|
188
193
|
## Multi-project manager (protocol 7)
|
|
189
194
|
|
|
@@ -191,7 +196,9 @@ The manager has its own loopback session server and authenticated local control
|
|
|
191
196
|
endpoint. Browsers provide registered project IDs and expected daemon instance
|
|
192
197
|
IDs; they cannot submit arbitrary paths, ports, PIDs or control tokens. The
|
|
193
198
|
manager forwards the same closed, redacted dashboard operations as a project-local
|
|
194
|
-
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.
|
|
195
202
|
|
|
196
203
|
Per-project control handshakes verify project, root and instance against the
|
|
197
204
|
runtime manifest. Shutdown removes only the owning instance's runtime files and
|
|
@@ -246,16 +253,18 @@ live after a settings write, undo or reload (turning it off from an edited file
|
|
|
246
253
|
be set through an agent, tools or conductor surface. Claude's daemon response
|
|
247
254
|
withholds the scoped mode on these writes so an older hook grants nothing too.
|
|
248
255
|
|
|
249
|
-
Qwen
|
|
256
|
+
Agent CLI versions (#691). One policy gates the Codex and Kimi fresh reviewers, Jcode, agy and Qwen: a receipt-verified version passes, Jcode and agy also start an untested version inside their compatible line with a note, and any other version is refused unless the person trusts that exact version on this machine with `ahub agents trust <id> <version>`. The record is `agent-compat.json` in the hub home (`~/.agenthub`), written atomically with mode 600 and at most 64 entries; an unreadable, linked, oversized or invalid record trusts nothing. Agent shells and `--live-test` may read `ahub agents` but cannot trust or revoke. Trust lifts only the version pin: the fresh reviewers' runtime proofs still run before every child, and an untested version's native contract stays unverified.
|
|
257
|
+
|
|
258
|
+
Qwen Code's optional production ACP peer requires the qwen-code native identity, a compat-admitted version (receipt-verified 0.25.0 and 0.26.0)
|
|
250
259
|
and advertised mode ids before setting a policy or starting work. Its own tools
|
|
251
260
|
have no hub sandbox; auto-edit approves file edits, yolo every tool. Configured
|
|
252
261
|
never-ask still requires the person's confirmation. qwen_cmd is machine-local and
|
|
253
262
|
never editable through settings. Legacy benchmark adapters remain unmanaged by
|
|
254
263
|
this production table. Startup diagnostics withhold native host/payload details.
|
|
255
264
|
|
|
256
|
-
Jcode is optional, source/version-fenced and runs native tools without a hub sandbox or hub permission-mode mapping. .jcode joins the scoped-grant exclusion, including lexical/resolved aliases. Only a person writes its unique project MCP entry; foreign/override/symlink entries refuse. The hub owns a private daemon/socket and ACP group, never the global daemon, and copies no credentials. It accepts
|
|
265
|
+
Jcode is optional, source/version-fenced and runs native tools without a hub sandbox or hub permission-mode mapping. .jcode joins the scoped-grant exclusion, including lexical/resolved aliases. Only a person writes its unique project MCP entry; foreign/override/symlink entries refuse. The hub owns a private daemon/socket and ACP group, never the global daemon, and copies no credentials. It accepts a plain jcode release from 0.98.0 up to, not including, 1.0.0 (#529; only 0.98.1 and 0.99.0 are verified, and another version in that line starts with a note), or an exact version the person trusts (#691), and runs every owned jcode process (probe, serve, ACP and TUI) with `JCODE_NO_TELEMETRY=1`, because jcode's telemetry can report the provider account's email. Native diagnostics withhold payloads/hosts. Hub tools are bound to an owned launch (#368): the shared project entry names no peer, each owned Jcode process carries its own peer id and launch id, the daemon admits a hub-tools connection for a Jcode peer only with the id of that peer's current owned launch, and a tools-mode server started without a peer id refuses every tool. Extra Jcode instances (#685, at most 8) are declared only through `instances` in an untracked `.agenthub/config.local.json`, start headless, and each needs its own `jcode_home`, which may not be the base home or another instance's, nor nested in or around one, however it is spelled. Unproved conversation loads refuse; no fresh replacement is called a restore. A finished task's advisory about the project root checkout names the branch only after the hub's whole public-text screen (`Tasks.publicMetadataText`: a PII pattern, a secret shape and a project-private path) passes it and it is a plain ref name; otherwise a generic sentence carries no part of it (#572). Native TUI launch exposes its private socket/session tuple only to the authenticated console, bound to the same live owned socket and session after read-only native persistence proof. Tools/status do not receive it. The TUI has no single-writer guard: person input may interleave with ACP. Native policy remains unmanaged and closing the frontend does not stop ACP. An `important` message reaches a busy Jcode as a soft interrupt written to the hub's own private server socket for the owned session (#572); the hub never cancels a turn to deliver, and a steer that was sent but not acknowledged, or whose turn ended first, is held as `needs_review`, never replayed. Jcode has no switch to choose its MCP servers, so the owned serve and ACP processes load every server named in `<JCODE_HOME>/mcp.json` and the project's `.jcode/mcp.json`, `.mcp.json` and `.claude/mcp.json`, besides the hub's own; `ahub doctor` lists their names only. Point `jcode_home` at a separate home, or remove a server from those files, to keep it out.
|
|
257
266
|
|
|
258
|
-
Antigravity (`agy`, #485) is optional, cloud and off roster by default. The launcher accepts the Antigravity CLI 1.4.x line; only 1.4.2 has a native receipt, so another 1.4.x patch launches with a warning that it is unverified, and every other version is refused before a hub connection or plugin write (#589). It runs native tools under its own unmanaged permissions, without a hub sandbox or hub permission-mode mapping; headless, resume, unattended and native permission flags are refused. Its roles are capped at implementer and verifier, so it never plans, reviews or receives PII work. Only the person's `ahub agy` launcher writes its three plugin files in `.agents/plugins/agent-hub/` (`plugin.json`, `mcp_config.json`, `hooks.json`): linked or foreign entries refuse before any write, cleanup removes only that launch's matching files, and it never writes project-level `.agents/hooks.json` or the person's `~/.gemini` settings. Conductor starts are refused, and a declared agy extra instance is refused at config load (#625). Hook authority (`native_hook_launch`) comes only from that launcher, belongs to the console socket that began it and ends when that socket closes; hook input over 1 MiB is refused, and observations are bound to the daemon, launch, native session and project.
|
|
267
|
+
Antigravity (`agy`, #485) is optional, cloud and off roster by default. The launcher accepts the Antigravity CLI 1.4.x line; only 1.4.2 has a native receipt, so another 1.4.x patch launches with a warning that it is unverified, and every other version is refused before a hub connection or plugin write (#589) unless the person trusts that exact version (#691). It runs native tools under its own unmanaged permissions, without a hub sandbox or hub permission-mode mapping; headless, resume, unattended and native permission flags are refused. Its roles are capped at implementer and verifier, so it never plans, reviews or receives PII work. Only the person's `ahub agy` launcher writes its three plugin files in `.agents/plugins/agent-hub/` (`plugin.json`, `mcp_config.json`, `hooks.json`): linked or foreign entries refuse before any write, cleanup removes only that launch's matching files, and it never writes project-level `.agents/hooks.json` or the person's `~/.gemini` settings. Conductor starts are refused, and a declared agy extra instance is refused at config load (#625). Hook authority (`native_hook_launch`) comes only from that launcher, belongs to the console socket that began it and ends when that socket closes; hook input over 1 MiB is refused, and observations are bound to the daemon, launch, native session and project.
|
|
259
268
|
|
|
260
269
|
Configured ACP adapters (#451 A3) are declared only by the person, in a Git-confirmed untracked `.agenthub/config.local.json`. The whole declaration (command, argv, identity, roles and mode maps) is machine-local authority: a shared or tracked declaration is ignored with a notice, settings never edit it, and changes need a hub restart. The hub runs only the declared argv, never a module or callback. Argv that selects a leader, attach, connect or remote mode is refused, and a Grok declaration needs `--no-leader`. Each start requires the pinned native identity, an exact `session/load` echo on resume, and positive ownership of the spawned process and its original process group; unknown ownership refuses, and stop signals only that verified group. An agent that hands its work to a process outside that group is outside this boundary. Without a mode map native policy is unmanaged, and there is no hub sandbox either way. Configured kinds are cloud peers (never PII), headless only and off roster by default, and the hub makes no quota, context or controlled-recovery claims for them. A read-only instance needs the native read-only mode acknowledged before a prompt and is limited to read and review hub tools, both in the published MCP list and at the host; its native non-read tool requests are rejected, and no setting or runtime change can raise it. These agents' stderr and error text are withheld from logs, and previews redact the executable, argv and session id.
|
|
261
270
|
|
|
@@ -364,11 +373,15 @@ Stall causes are persisted and deduplicated by cause and replanning revision. Pl
|
|
|
364
373
|
|
|
365
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.
|
|
366
375
|
|
|
367
|
-
The fresh Codex reviewer (#519) needs `review_mode: "fresh"` and
|
|
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.
|
|
377
|
+
|
|
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.
|
|
379
|
+
|
|
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`.
|
|
368
381
|
|
|
369
|
-
|
|
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).
|
|
370
383
|
|
|
371
|
-
|
|
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.
|
|
372
385
|
|
|
373
386
|
## Bounded task specs and isolated checks (#418)
|
|
374
387
|
|
package/docs/smoke.md
CHANGED
|
@@ -1697,7 +1697,7 @@ Source is complete: head-bound gates (#333), gsd-lite checklists (#336), bounded
|
|
|
1697
1697
|
|
|
1698
1698
|
Unverified (tracked in #273): one real issue on this repository taken through merge and release under `sdlc.mode = "advise"`. Record the issue, PR, heads, each gate's blocker readback and `ahub sdlc release` output here. No live cycle or automatic merge is claimed.
|
|
1699
1699
|
|
|
1700
|
-
## Qwen Code
|
|
1700
|
+
## Qwen Code production peer (#256)
|
|
1701
1701
|
|
|
1702
1702
|
Unverified: real provider handshake, a native model/tool turn, context occupancy,
|
|
1703
1703
|
owned stop/recovery and the first authoritative quota refusal. Fake ACP tests are
|
|
@@ -1713,6 +1713,11 @@ Qwen recovery source/fake proof: saved ids pass through the driver and CLI to AC
|
|
|
1713
1713
|
|
|
1714
1714
|
Source/fixture proof is recorded in verification/2026-10-11-jcode-peer-contract.md; the live owned-daemon and ACP handshake on 0.94.0 and 0.98.1 in verification/2026-10-19-jcode-0.98.1.md. Unverified: real provider/model, native project MCP discovery and hub-tool receipt, native tool policy and stop/child teardown. Native automatic restoration is withheld because the pinned ACP load does not prove the id. No benchmark comparison or default replacement ran.
|
|
1715
1715
|
|
|
1716
|
+
### Extra Jcode instances (#685, #368)
|
|
1717
|
+
|
|
1718
|
+
Source and fixture coverage: `test/jcode-instances.test.ts` (declaration refusals, the bound of 8, home uniqueness, no relay or credential fields), `test/jcode-instance-peer.test.ts` (separate groups, sockets and homes, stopping one leaves the other, argv is the base argv plus the instance's `-p`, `--model` and `--provider-profile`, launch id per owned process, launcher, doctor) and `test/jcode-instance-daemon.test.ts` (home preflight, no file written under the home, status, hub-tools admission only for an owned launch, a forged `AGENTHUB_PEER_ID` refused, the MCP server with no peer id refusing every tool).
|
|
1719
|
+
|
|
1720
|
+
Unverified, a person's leg: sign in to Jcode against each instance's own `jcode_home` (including an OpenAI-compatible profile for the on-prem gateway), declare the instances in `.agenthub/config.local.json`, run `ahub jcode --as <id>`, send each one a task, and read back that `ahub status` lists each with its provider, model and profile, that `ahub stop <id>` leaves the others running, and that a Jcode started by hand in the project reports that its hub tools are refused. Observed on the real jcode 0.99.0 binary (#368, isolated `HOME` and `JCODE_HOME`, own `jcode serve` socket and `jcode acp --mcp-tools eager`, a probe MCP entry that names neither id): its MCP child received `AGENTHUB_PEER_ID` and `AGENTHUB_LAUNCH_ID` from the owned process env, next to the entry's own variables. An `acp` that attaches to an already running shared server instead hands its child that server's env, which is why the hub always starts its own private serve first. Then the whole path on the real binary (#368, isolated `HOME` and `JCODE_HOME`, a real daemon, the committed `plugins/agent-hub/server.js` as the MCP child, an extra instance `jcode-local` with a declared home): the project entry named no peer, `start` returned ok, the peer reached `idle`, and the one bundle child under that Jcode ran with `AGENTHUB_PEER_ID=jcode-local`, `AGENTHUB_MODE=tools` and its own `AGENTHUB_LAUNCH_ID` and held one established TCP connection to the hub, which the daemon admitted only because of that launch id. A first attempt with a profile name that Jcode did not know failed the start with the bounded refusal and left no process. Still not exercised: any real Jcode account, provider or model, a model turn that calls a hub tool, and a second instance running beside the base peer under the real binary.
|
|
1716
1721
|
|
|
1717
1722
|
## Project board (#263)
|
|
1718
1723
|
|
|
@@ -1948,16 +1953,16 @@ Complete these legs in a disposable project with authorized accounts after sourc
|
|
|
1948
1953
|
- Request a delta review and confirm the same eligible independent reviewing family receives a new session with prior findings. After an earlier same-family fallback, a newly available independent family still takes precedence. Exercise configured re-rotation separately. Stop or exhaust one reviewer and confirm the pure routing walk chooses the next eligible candidate; no failed start or child ending without a saved verdict becomes approval. Check the next candidate even when the missing verdict followed a vendor quota error; no text-based quota classifier is assumed.
|
|
1949
1954
|
- Make all pool candidates unavailable and confirm a blocked review awaiting planner/person action. Then test a different fresh session in same-family fallback only when no other family can run. Verify stale-child, changed-head and private-review verdict refusals.
|
|
1950
1955
|
- Verify compatibility separately: a Codex/Kimi headless reviewer-role declaration without `review_mode` keeps its native app-server/ACP behavior and existing worktree/permission options, and stays outside the fresh pool. An explicit fresh declaration must refuse an unsupported backend without downgrading.
|
|
1951
|
-
- The default logical pool is Claude, Codex, Kimi. Kimi is runnable
|
|
1956
|
+
- The default logical pool is Claude, Codex, Kimi. Kimi is runnable for a compat-admitted version when runtime preparation passes (receipt-verified 2.2.0 and 2.3.0; inspect `ahub agents`); an unverified/untrusted version or failed preparation is unavailable with its reason. Seed modulo includes only kinds with a declared candidate, even when blocked; undeclared kinds remain trailing trace entries. Verify seeds 0..5 with only Claude/Codex declared alternate 3/3, and an offline declared kind still retains its slot. Agy stays unavailable as a reviewer with exact reason `agy is an implementer peer (#485)`, including explicit pool requests. #234 adds no agy reviewer launch or live leg.
|
|
1952
1957
|
|
|
1953
1958
|
Record source head, exact executable version/hash, isolation receipt, task/head identities, route walk, distinct native session evidence and persisted verdict separately. Do not record credentials or raw transport environments. Codex sandboxed shell must not be described as Claude's Read/Grep/Glob-only whitelist. Network policy is configured disabled; no network-denial probe or native LLM isolation result has been recorded by this investigation.
|
|
1954
1959
|
|
|
1955
1960
|
## Kimi reviewer and task-bound MCP (#533)
|
|
1956
1961
|
|
|
1957
|
-
Kimi 2.2.0
|
|
1962
|
+
Kimi receipt-verified 2.2.0 and 2.3.0 are available when runtime preparation passes; a failed or unsupported preparation remains unavailable. The [2.3.0 receipt](verification/2026-10-22-kimi-qwen-compat.md) is model-free; the following authenticated evidence belongs to 2.2.0. Completed evidence includes account-free two-tool/native OAuth race checks, host/fake lifecycle refusals and authenticated fresh `changes_requested` verdicts. The complete-collector live run observed project parity/canary absence, authenticated exact-two hub inventory/calls, zero decoy requests, cleanup and unchanged real config hashes. Its original aggregate receipt remains failed because of an unattributed extra observer error; the later account-free GET reproduction/correction is not a new live pass. These separate outcomes are preserved in the R0/live report. The remaining actual interactive-TUI/coexistence and usage-observation checks are **UNVERIFIED**; they do not turn completed live/fake evidence into an unrun test. Person ruling 2026-10-19 (8281ec35) limits #234 to Kimi and task-bound MCP and supersedes all earlier agy reviewer implementation/admission/live-leg rulings. Agy is an implementer under #485; its retained reviewer slot remains unavailable with exact reason `agy is an implementer peer (#485)`. Earlier agy probes and unresolved meta-tool/resource findings remain historical in the [R0 report](verification/2026-10-19-kimi-agy-reviewer-isolation.md), not active smoke requirements here. Kimi's native-permission/shared-account ruling supersedes its earlier R0-only hold and Seatbelt requirement. The [spec amendment](specs/2026-09-19-agent-hub-design.md#kimi-reviewer-and-task-bound-mcp-amendment-533-hub-task-234) defines the active contract and residuals.
|
|
1958
1963
|
|
|
1959
1964
|
- For Kimi regression smoke, confirm a fresh child per review, no resume, source evidence supplied only by screened `hub_task_show`/review brief and host facts, and no native Read/file/shell tool. Confirm the in-process host MCP exposes only the task-bound `hub_review` and `hub_task_show` capability, without the full hub control token. Exercise wrong task/head/review revision, stale/replaced child, private review, malformed arguments, unknown tool and stopped-child refusals; verify no saved approval on any failure.
|
|
1960
|
-
- Kimi
|
|
1965
|
+
- For each compat-admitted Kimi version, confirm no Seatbelt wrapper, a private `KIMI_CODE_HOME` with managed-provider-only config, clean temporary cwd and explicit empty skills directory. Verify exactly the two `mcp__agent-hub__...` tools in model disclosure and invocation; reject native read/write/command and extra MCP calls. Confirm `credentials/` and `oauth/` are directory symlinks to the native locations, `device_id` links the native identity, no token copy/extraction/static provider is used, and `KIMI_DISABLE_OAUTH_LOCK` is absent. The concurrent account-free OAuth print stand-in proved two expired generations serialize refresh through shared directory links, but it was not an attached interactive TUI. Actual TUI refresh/atomic rename/lock coordination and shared-account quota/pause fanout remain separate live legs. Existing project MCP/config byte-parity observations remain recorded in the R0/live report. Record identities/metadata without token values.
|
|
1961
1966
|
- Kimi cancellation: revoke review authority before TERM, observe bounded grace while the OAuth lock is held, then confirm owned-group teardown and temporary-state cleanup. Use synthetic auth for forced mid-refresh kill checks; report the accepted possibility of re-login rather than repair auth or count an interrupted turn as approval. Keep real-account refresh/coexistence evidence separate from synthetic cancellation evidence.
|
|
1962
1967
|
- Recheck common routing, independent-family preference, recorded same-family fallback, no-verdict retry/exhaustion, head-bound persisted verdict, person-stop hold and shared-account budgets. Keep Kimi unavailable on a failed or missing receipt; agy remains unavailable under #485 regardless of historical reviewer probe results. Record executable version/hash, source head, tool/permission evidence and actual account/model results separately; source/configuration alone is not a live pass.
|
|
1963
1968
|
|