@staix/agent-hub 0.13.0 → 0.15.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 +30 -0
- package/README.md +19 -7
- package/docs/agent-notes/README.md +4 -0
- package/docs/agent-notes/adapters.md +18 -4
- package/docs/agent-notes/budget.md +5 -4
- package/docs/agent-notes/bus.md +1 -0
- package/docs/agent-notes/daemon.md +19 -7
- package/docs/agent-notes/tasks.md +15 -7
- package/docs/agent-notes/tests.md +4 -4
- package/docs/dogfood.md +18 -10
- package/docs/events.md +21 -7
- package/docs/operations.md +131 -27
- package/docs/profiles.md +1 -0
- package/docs/quickstart.md +13 -3
- package/docs/sdlc.md +40 -2
- package/docs/security.md +45 -14
- package/docs/specs/2026-09-19-agent-hub-design.md +112 -10
- package/docs/verification/2026-10-22-codex-reviewer-isolation.md +14 -0
- package/docs/verification/2026-10-23-0.14.0.md +69 -0
- package/docs/verification/2026-10-23-0.15.0.md +80 -0
- package/docs/verification/2026-10-23-daemon-responsiveness.md +44 -0
- package/docs/verification/2026-10-23-headless-claude-permissions.md +47 -0
- package/docs/verification/2026-10-23-terminal-claude-execution-root.md +41 -0
- package/docs/verified.json +34 -17
- package/package.json +1 -1
- package/plugins/agent-hub/.claude-plugin/plugin.json +1 -1
- package/plugins/agent-hub/server.js +1078 -327
- package/src/adapters/acp.ts +43 -7
- package/src/adapters/claude-channel.ts +26 -4
- package/src/adapters/claude-implementer.ts +243 -0
- package/src/adapters/claude-print-child.ts +90 -0
- package/src/adapters/claude-task-sessions.ts +80 -0
- package/src/adapters/codex-review-isolation.ts +18 -7
- package/src/adapters/compat.ts +4 -0
- package/src/adapters/conformance/enrollment.ts +2 -1
- package/src/adapters/conformance/fakes/acp-server.ts +3 -1
- package/src/adapters/definitions/builtin-metadata.ts +15 -3
- package/src/adapters/definitions/native-channel-state.ts +1 -1
- package/src/adapters/definitions/native-cli-entry-claude.ts +13 -8
- package/src/adapters/definitions/native-cli-entry-codex.ts +13 -0
- package/src/adapters/definitions/native-cli-entry-types.ts +2 -0
- package/src/adapters/definitions/native-factories.ts +8 -0
- package/src/adapters/kimi-wire-usage.ts +303 -0
- package/src/adapters/registry-types.ts +7 -3
- package/src/adapters/registry.ts +14 -2
- package/src/adapters/reviewer-print.ts +64 -60
- package/src/cli/console-config.ts +123 -0
- package/src/cli/console-state.ts +28 -1
- package/src/cli/console.ts +25 -1
- package/src/cli/facts-hook.ts +9 -5
- package/src/cli/help.ts +6 -4
- package/src/cli/launch.ts +10 -4
- package/src/cli/main.ts +52 -9
- package/src/cli/output.ts +75 -2
- package/src/cli/reset.ts +1 -1
- package/src/cli/settings-edit.ts +89 -0
- package/src/cli/status-lines.ts +1 -1
- package/src/cli/terminal-recovery.ts +1 -6
- package/src/hub/action-enforce.ts +82 -0
- package/src/hub/action-policy.ts +22 -10
- package/src/hub/board.ts +48 -3
- package/src/hub/budget.ts +5 -0
- package/src/hub/bus.ts +5 -5
- package/src/hub/claude-launch-proof.ts +33 -0
- package/src/hub/config-apply.ts +5 -0
- package/src/hub/config-edit-host.ts +173 -0
- package/src/hub/config-edit.ts +282 -0
- package/src/hub/config-tier.ts +89 -0
- package/src/hub/control-client.ts +24 -3
- package/src/hub/daemon.ts +450 -71
- package/src/hub/deliverable-files.ts +131 -0
- package/src/hub/deliverables.ts +125 -0
- package/src/hub/delivery-journal.ts +18 -2
- package/src/hub/events.ts +3 -1
- package/src/hub/hub-tools.ts +21 -4
- package/src/hub/instances.ts +19 -6
- package/src/hub/kimi-usage.ts +144 -0
- package/src/hub/manager.ts +3 -3
- package/src/hub/peers.ts +7 -0
- package/src/hub/permission-payload.ts +15 -0
- package/src/hub/routing.ts +118 -3
- package/src/hub/sdlc-metrics.ts +260 -0
- package/src/hub/sdlc.ts +5 -3
- package/src/hub/settings.ts +1 -1
- package/src/hub/spec-read.ts +31 -0
- package/src/hub/task-code-group.ts +52 -0
- package/src/hub/tasks.ts +374 -37
- package/src/hub/token-window.ts +33 -0
- package/src/hub/ui.ts +3 -0
- package/src/hub/usage-rollup.ts +74 -6
- package/src/hub/workflow.ts +16 -0
- package/src/ui/index.html +388 -3
|
@@ -8,7 +8,7 @@ Scope: tests, fakes and the test gate. Read before adding or changing a test or
|
|
|
8
8
|
- A test timeout that fires while `Bun.spawnSync` runs can start the next test inside spawnSync's own event loop (Bun 1.3.14 and 1.4.2; background: README). `scripts/check.sh` runs tests with a 20 s timeout and under `scripts/hang-watch.sh`; a test that spawns and reads the process table gets a timeout of its own, never above 20 s: an explicit larger timeout overrides the gate's `--timeout 20000`, so shorten the scenario instead.
|
|
9
9
|
- Sandboxed commands and the MLX server get a fixed environment (`PATH`, `HOME` and their own keys), so test knobs for their fakes travel in a wrapper script or argv, not in `process.env`; native peer children inherit the hub's environment minus the agent markers (`peerChildEnv`).
|
|
10
10
|
- Bind agent identity markers explicitly in native/CLI fixture children. The full gate starts the test runner without inherited agent markers (including `AGENTHUB_DELIVERY_ONLY`) and without an `ahub claude` session's hub variables (`AGENTHUB_PROJECT_DIR`, `AGENTHUB_STATE_DIR`, `AGENTHUB_INSTANCE_ID`, `AGENTHUB_LAUNCH_ID`); a new variable a native session exports and a fixture child reads joins that `env -u` list in `scripts/check.sh`. Never weaken production marker detection to make them pass.
|
|
11
|
-
- `bunfig.toml` preloads `test/hub-home.ts`, which first clears inherited `AMEM_HOME
|
|
11
|
+
- `bunfig.toml` preloads `test/hub-home.ts`, which first clears inherited `AMEM_HOME`, gives every test run (the gate and `bun test <file>` alike) a temp `AGENTHUB_HOME` unless one is set, and always replaces `KIMI_CODE_HOME` with a temp dir, an inherited value included (#285); scoped tests may set their own `AMEM_HOME` or `KIMI_CODE_HOME` after preload. Hub memory (#258) is on with any project config, and no test may write the person's `~/.agenthub`. Do not remove it; a test that needs its own home sets and restores the variable. The preload keeps an inherited `AGENTHUB_HOME`, which an agent shell exports as the person's hub home and `scripts/check.sh` does not unset: run `bun test` and the gate with `env -u AGENTHUB_HOME`.
|
|
12
12
|
- `test/registry-guard.test.ts` statically scans `src/` against the committed `test/registry-guard.baseline.json` (#451); a test never writes, regenerates or repairs that baseline, and a failing guard is fixed in the source by moving the policy into a definition (rule: `docs/agent-notes/adapters.md`); the baseline stays `[]`. Scanner behavior is pinned on inline sources in `test/registry-guard-fixtures.test.ts`, never on edits to real files.
|
|
13
13
|
- A fixture that needs a port takes it from a free-port probe, never a fixed number, which collides with whatever holds it on a shared runner (#548). The probe binds `127.0.0.1`, matching the loopback server that takes the port (#232).
|
|
14
14
|
- `ahub upgrade` without `--to` asks the registry (`npm view`) and hands a target newer than the CLI to `bun x` (#272): a test that reaches either puts a fake `npm` first on `PATH` and a recording stand-in for `process.execPath`, as `test/upgrade-cli.test.ts` does. The interactive screens are tested through a scripted `ScreenIO`; a CLI test that needs them marks `process.stdin` and `process.stdout` as terminals in a wrapper script.
|
|
@@ -16,7 +16,7 @@ Scope: tests, fakes and the test gate. Read before adding or changing a test or
|
|
|
16
16
|
- A bounded polling wait must assert its condition after the last attempt, or document why giving up is intentional. Append-only event histories wait for a correlated closing event, not removal of the opening event. Before changing a slow wait, identify the unreached state and repair its cause (#296).
|
|
17
17
|
- A test of turn or notice behavior that is not about the snapshot budget yields an I/O turn (`await new Promise(r => setImmediate(r))`) between captures: captures in one synchronous burst share the 250 ms budget and may correctly record unknown evidence. A daemon test whose assertion needs a turn's snapshot or end evidence passes `snapshotClock: () => 0` (`DaemonOptions.snapshotClock`, #661) so a slow runner cannot spend that budget; never lengthen the budget instead. A wait on concurrent-edit evidence correlates the expected turn ids and paths (#617).
|
|
18
18
|
- A request that a test expects a stop or shutdown to cut gets its rejection handler where the request is created, never after the awaited teardown: a late `catch` races the reset and the run fails on an unhandled rejection.
|
|
19
|
-
- Run `bun scripts/seeded-check.ts` before reporting the seeded-guard gate complete. Keep the `scripts/seeds.json` cases sequential; require the same named test green before its seeded assertion fails. Seed rot, survivors, setup/compiler failures, timeout/watchdog termination and process leaks fail the gate. A change that moves or rewrites a line a `scripts/seeds.json` case searches for updates that case's search and replacement in the same PR (seed rot fails the gate), keeping its mutant effect and named test. Never run the full seed runner from a unit test or mutate the source checkout; inspect preserved failed fixtures before removing them. CI runs it as four sequential Linux shards (`--shard i/4`) beside the ordinary check, separately from `scripts/check.sh`; the required `seeded guards` job passes only when every shard passed, or when the decision job reused a verified identical-tree full gate (`admission == 'reuse'`). Keep that job name: branch protection and `scripts/ci-gates.mjs` read it (#457).
|
|
19
|
+
- Run `bun scripts/seeded-check.ts` before reporting the seeded-guard gate complete. After changing seeded guard logic, run its affected seeds even when search-anchor counts are unchanged. Put asynchronous calls inside `resolves`/`rejects` expectations so a mutated refusal fails a named assertion. Keep the `scripts/seeds.json` cases sequential; require the same named test green before its seeded assertion fails. Seed rot, survivors, setup/compiler failures, timeout/watchdog termination and process leaks fail the gate. A change that moves or rewrites a line a `scripts/seeds.json` case searches for updates that case's search and replacement in the same PR (seed rot fails the gate), keeping its mutant effect and named test. Never run the full seed runner from a unit test or mutate the source checkout; inspect preserved failed fixtures before removing them. CI runs it as four sequential Linux shards (`--shard i/4`) beside the ordinary check, separately from `scripts/check.sh`; the required `seeded guards` job passes only when every shard passed, or when the decision job reused a verified identical-tree full gate (`admission == 'reuse'`). Keep that job name: branch protection and `scripts/ci-gates.mjs` read it (#457).
|
|
20
20
|
|
|
21
21
|
- `scripts/check.sh` pins the test environment (#610): `GIT_CONFIG_GLOBAL=/dev/null`, `NO_COLOR` unset, `TERM=xterm-256color`, temp root under the OS per-user temp dir, agent markers cleared. A test that needs a person's git config or color settings sets them itself. `--changed` selects tests with `scripts/changed-tests.mjs`: changed tests (nested suites included) plus every test that names a changed file directly; docs need none; any other changed file no test names, and any gate, package or shared-fixture change, runs everything.
|
|
22
22
|
- `scripts/check.sh` holds the repo's heavy-job slot (`<git common dir>/ahub-heavy.lock`, #398) for the whole run, so a test never runs the real gate; `test/check-lock.test.ts` runs a copy in a scratch repo.
|
|
@@ -27,8 +27,8 @@ Scope: tests, fakes and the test gate. Read before adding or changing a test or
|
|
|
27
27
|
|
|
28
28
|
- Keep CI's Bun version sourced from `.bun-version` through the pinned setup-bun action; update the version file rather than adding per-job version literals.
|
|
29
29
|
- Release verification requires both OS checks and the aggregate `seeded guards` success for an identical tree, a tag matching `package.json` on `origin/main`, and fresh ancestor-bound documentation stamps. Preserve the separate npm OIDC publish job and GitHub Release write job; neither job gets the other's credential authority.
|
|
30
|
-
- `test/conformance/` runs real adapters against fake transports. The shared kit and the transport fakes live in `src/adapters/conformance/` (public `@staix/agent-hub/conformance`); `test/conformance/kit.ts` and the matching `test/fakes/*.ts` only re-export them, so change the packaged file, and `--changed` runs everything for it; `test/conformance/enrollment.ts` enrolls every `ADAPTERS` definition and each `:
|
|
30
|
+
- `test/conformance/` runs real adapters against fake transports. The shared kit and the transport fakes live in `src/adapters/conformance/` (public `@staix/agent-hub/conformance`); `test/conformance/kit.ts` and the matching `test/fakes/*.ts` only re-export them, so change the packaged file, and `--changed` runs everything for it; `test/conformance/enrollment.ts` enrolls every `ADAPTERS` definition, each `:reviewer` variant and each `:implementer` (`headlessVariant`) variant. Record a case that does not apply in its `UNSUPPORTED` or `UNSUPPORTED_LEGS` map with a concrete reason naming the dedicated coverage, never `test.skip` or synthetic success, and never change native production behavior to satisfy a generic case.
|
|
31
31
|
|
|
32
32
|
- Keep the CI admission lock around the whole controller workflow, not just its decision job, in its per-PR (`check-pr-<number>`, `queue: single`: a newer pending run replaces the older) or main (`check-main`, `queue: max`) group, never with `cancel-in-progress: true` beside `queue: max` (GitHub refuses it) (#609); two groups admitting one tree at once is an accepted duplicate run, never a reason to fail closed on another tree. Preserve successful identical-tree full proof and required job names; a skipped job, pending controller or unavailable active inventory is never a successful full gate. In `readFullGateAdmission`, lost successful proof (404, malformed proof, a per-read timeout) only means a fresh gate runs; unknown active coverage and the global read ceilings (`GateReadLimitError`) stay unknown and refuse. CI checks out the PR head (`pull_request.head.sha`), not a merge with main: after a fix lands on main, merge main into the branch before reading CI as proof. CI reuse tests use fixed metadata fixtures and never dispatch real duplicate workflows.
|
|
33
33
|
|
|
34
|
-
- Headless-reviewer tests run `test/fakes/fake-claude-print.ts` through `command` or a PATH stand-in, and fresh Codex reviewer tests run `test/fakes/fake-codex-reviewer.ts` through `codex_bin`; a test that reaches the daemon's reviewer start must never run the real `claude` or `codex`. The fake's sandbox markers test host admission only, never native isolation. Fresh Kimi reviewer tests set and restore `KIMI_CODE_HOME` to a fixture native home (the backend links that home's `credentials/`, `oauth/` and `device_id`, else the person's `~/.kimi-code`) and run `test/fakes/fake-kimi-reviewer.ts` through `command` or a PATH stand-in, never the real `kimi`. agy tests put a fake `agy` first on PATH and set `HOME` to a temp dir before any transcript readback (`readAgyTranscript` reads `$HOME/.gemini`). Code-facts tests build `new CodeFacts({ env })` with a PATH whose first `ripwire` is a wrapper around `test/fakes/fake-ripwire.ts` (`FAKE_RIPWIRE_MODE` selects its failures), or inject Tasks' `prepareReviewBrief`; never rely on an installed ripwire, which CI does not have.
|
|
34
|
+
- Headless-reviewer tests run `test/fakes/fake-claude-print.ts` and headless Claude implementer tests run `test/fakes/fake-claude-implementer.ts` through `command` or a PATH stand-in (the implementer with `HOME` and `CLAUDE_CONFIG_DIR` in a temp dir), and fresh Codex reviewer tests run `test/fakes/fake-codex-reviewer.ts` through `codex_bin`; a test that reaches the daemon's reviewer or implementer start must never run the real `claude` or `codex`. The fake's sandbox markers test host admission only, never native isolation. Fresh Kimi reviewer tests set and restore `KIMI_CODE_HOME` to a fixture native home (the backend links that home's `credentials/`, `oauth/` and `device_id`, else the person's `~/.kimi-code`) and run `test/fakes/fake-kimi-reviewer.ts` through `command` or a PATH stand-in, never the real `kimi`. agy tests put a fake `agy` first on PATH and set `HOME` to a temp dir before any transcript readback (`readAgyTranscript` reads `$HOME/.gemini`). Code-facts tests build `new CodeFacts({ env })` with a PATH whose first `ripwire` is a wrapper around `test/fakes/fake-ripwire.ts` (`FAKE_RIPWIRE_MODE` selects its failures), or inject Tasks' `prepareReviewBrief`; never rely on an installed ripwire, which CI does not have.
|
package/docs/dogfood.md
CHANGED
|
@@ -11,9 +11,15 @@ agent-hub is developed with agent-hub. This page lists every feature, whether it
|
|
|
11
11
|
| Feature | State here | Measured by |
|
|
12
12
|
| --- | --- | --- |
|
|
13
13
|
| Message bus, digests, priorities | on | `ahub report`: messages, digests, overflow, undeliverable; delivery latency from events |
|
|
14
|
-
|
|
|
14
|
+
| Compact board (#724) | on | `ahub board` at 80, 120 and 200 columns: one line per task (test/board-compact.test.ts) |
|
|
15
|
+
| Backlog list table (#399) | on | `ahub backlog list` at a terminal width: one line per card by rank with the reserved owner and dependencies, and the count by owner (test/backlog-list-output.test.ts); `--json` for scripts |
|
|
16
|
+
| Task board, routing, roles | on (claude planner, reviewer and implementer; codex, jcode, agy implementers) | board history: lead time from propose to approve, review rounds, escalations |
|
|
17
|
+
| Planner walk and reviewer independence (#525-3) | on after the release: `ahub route explain <id>` prints the planner walk here, and with `claude` the only planner holder it names `claude` for every task (rotation among one holder); the rotation among several holders and a named planner are not exercised here, and nothing records the planner yet (525-2). Reviewer independence (I-2) is on: a peer that accepted or finished a task never reviews it | the `planner: claude (rotation)` line in `ahub route explain` for a task; I-2 shown as `reviewer user` with the trace line `every eligible reviewer implemented this task: the person reviews` on a task `claude` implemented (such as #399), and as `skipped, implemented this task` on the other candidates; a former implementer that is offline or paused is refused for that reason and leaves the outcome unchanged (the trace still ends `reviewer: none, done will approve directly` where no other reviewer exists) |
|
|
18
|
+
| Acceptance criteria ids in review (#507b) | on after the release: a task with a recorded `criteria` checklist lists its ids in the review request and `hub_review` unmet items that start with an id are recorded as `unmetCriteria`; tasks here rarely record a checklist, so most reviews are unchanged | the `Acceptance criteria` block in a review request and `unmetCriteria` in `ahub task show` history for tasks with a checklist; the share of changes-requested verdicts whose unmet items name an id; unmet items that name an id the checklist does not hold (a typo) |
|
|
19
|
+
| Deliverable records (#509a) | on after the release: `deliverables` on `hub_task_done` and `hub_sdlc_record`, hashed by the hub in this repository's folder; most tasks here are code with a PR and name none, so only document-shaped work (a report, a spec draft) uses it | the `Deliverables` block in a review request and `deliverables` in `ahub task show`; approvals refused with `deliverable changed since it was delivered`; records with `state: unknown` (a file the hub could not vouch for); claimed-hash refusals as a measure of agents trying to supply a hash |
|
|
20
|
+
| Roles per kind and form (#525-1, #525-3, #525-4, #525-5) | on: claude holds the implementer role in the project checkout; isolated terminal and headless Claude implementer support is on, but neither form is declared here | tasks by owner; verified owner isolation and execution-root refusals; headless task-session resumes, inbox task-session refusals, fresh notices and person approval outcomes; missing or mismatched root must never admit a write owner |
|
|
15
21
|
| Overlap notices | on; plan symbols match by normalized name (arguments stripped, a bare name matches the last segment, two qualified names must be equal; #682 phase 1b) | `ahub report`: overlap warnings, task pairs; false-overlap rate, and symbol-only matches on bare common names (#682) |
|
|
16
|
-
| Planner WIP caps and backlog pull (#655) | on (`tasks.wip_limits.codex = 2`) |
|
|
22
|
+
| Planner WIP caps and backlog pull (#655, #718) | on (`tasks.wip_limits.codex = 2`); reserved-pull handoff on after release | board history/offer readback: reserved `backlog pulled` -> `assigned` -> `accepted` for the same peer; stale or unowned offers, capacity refusals and held-ready entries |
|
|
17
23
|
| Task sweep (stalls, idle owners) | on | stall and sweep events per week |
|
|
18
24
|
| Turn-free coordination | off: experimental; needs a separate measured trial | coordination events, conflict notices |
|
|
19
25
|
| Pull-only peers (agy) | on; `hub_inbox` drains to 64 KiB (#713) | unread offers, empty-inbox polling (#681); queued count and oldest queued age per pull-only peer in `ahub status`; reads that said `N more waiting` |
|
|
@@ -28,16 +34,17 @@ agent-hub is developed with agent-hub. This page lists every feature, whether it
|
|
|
28
34
|
| Console changed-symbols block (#684 phase 2b) | on with 2a data; matching, unplanned and untouched against the plan; overlap lines follow in #379; related-note count remains phase 4 | task detail shows the block or `unknown (no code facts)`; no block for a private task |
|
|
29
35
|
| Console review guidance (#711) | on after release; `r` verdict choice, hints, visible result, agent-shell cause line, notice names the console | a person approving from the console without a failed first try; refusals that named the cause; `ahub review` calls from agent shells that were refused |
|
|
30
36
|
| Console symbol-overlap advice (#684, #379) | on after release; read-only task detail, observed names only | other task IDs/shared paths and screened different-symbol verdict; no pair involving PII, no claim on unknown/incomplete evidence |
|
|
37
|
+
| Dashboard task sheet Code group (#684 phase 2c) | on after release; read-only, observed names only, none for a private task | task sheet Code group shows the changed symbols or `unknown (no code facts)`; symbol clicks that reach a non-empty graph once phase 3 lands |
|
|
31
38
|
| Review brief, review findings, Important gate (#481 S3) | advise | Important findings per PR, rounds to approval |
|
|
32
39
|
| Spec acceptance (#481 S1) | advise | build entries without acceptance |
|
|
33
|
-
| Lifecycle identity linked by the hub (#694) | on in advise | tasks still showing `issue identity missing` or
|
|
40
|
+
| Lifecycle identity linked by the hub (#694, #723) | on in advise; links at propose and accept from the title and `refs.repo`, with a groom backfill as the safety net | open tasks still showing `issue identity missing` right after propose (should be only titles that name no issue or a qualified title without `refs.repo`) |
|
|
34
41
|
| Planner plan review (#481 S9) | advise | plans reviewed before build |
|
|
35
42
|
| Plan conformance (#481 S2) | advise | PRs with files outside the plan |
|
|
36
43
|
| Test integrity for bug fixes (#481 S4) | advise | bug PRs without a red-first receipt, modified pre-existing tests |
|
|
37
44
|
| Required checks | advise (`check (ubuntu-latest)`, `check (macos-latest)`, `seeded guards`) | first-pass CI rate, same-head reruns (`hub_sdlc_costs`) |
|
|
38
45
|
| Release authorization (#481 S6) | pending (#199) | releases with a receipt |
|
|
39
|
-
| SDLC metrics and control bands (#481 S7) |
|
|
40
|
-
| Fresh headless reviewers (#223, #519) | off: needs an `instances` entry in the person's `config.local.json` | reviews by fresh sessions, findings they add |
|
|
46
|
+
| SDLC metrics and control bands (#481 S7) | `ahub sdlc metrics` on; bands off until `[bands]` is set in `.agenthub/sdlc.toml`, and they write cards only in advise or enforce (in off the report still shows tiers). Completion checks are off here, so `first_pass_check` and the PR-based figures read unknown | its own report; breach episodes per week by tier, and band cards the planner accepted rather than dropped |
|
|
47
|
+
| Fresh headless reviewers (#223, #519) | off: needs an `instances` entry in the person's `config.local.json` | reviews by fresh sessions, findings they add; a per-review start failure names a closed cause class (#722): `ahub status` reviewer line and hub.log `[cause: ...]`, with the share of failures that are `unknown` |
|
|
41
48
|
| Completion checks per class | off: the implementer already runs `scripts/check.sh`; a hub-run copy would double the heavy-slot use | time to verdict when on |
|
|
42
49
|
|
|
43
50
|
## Memory and knowledge
|
|
@@ -53,9 +60,9 @@ agent-hub is developed with agent-hub. This page lists every feature, whether it
|
|
|
53
60
|
| Feature | State here | Measured by |
|
|
54
61
|
| --- | --- | --- |
|
|
55
62
|
| Quota budgets and pauses | on | pauses, resets, time paused (#683) |
|
|
56
|
-
| Token usage per peer and task | `ahub report` and, from the roll-up in `hub.db`, `ahub usage [--by peer\|task\|issue\|pr\|release\|day\|model]`, `usage limits` and `usage total` (#683 U2); console and dashboard
|
|
63
|
+
| Token usage per peer and task | `ahub report` and, from the roll-up in `hub.db`, `ahub usage [--by peer\|task\|issue\|pr\|release\|day\|model]`, `usage limits` and `usage total` (#683 U2); console tab `7` (U3) and dashboard Usage view (U4) shipped | tokens per merged PR |
|
|
57
64
|
| Context gate | off: `context.gate = 0` | context-pressure events |
|
|
58
|
-
| Always-ask action classes (#636) |
|
|
65
|
+
| Always-ask action classes (#636) | on for Claude (PreToolUse hook) and hub-owned ACP peers in ask mode, after the release (#312); Codex, Pi and the local worker pending (#313) | `permission` events for `Action approval` requests, the `actionPolicyInfo` rows of `ahub status`, budget refusals; a `never-ask` Kimi is listed as not enforceable, not claimed |
|
|
59
66
|
| Permission modes | on, person-chosen | approval prompts and their wait times |
|
|
60
67
|
| Pi and the local worker on DGX | off: DGX unreachable recently; Pi is off the roster | turns served on-prem, relay errors |
|
|
61
68
|
| PII routing | on | PII tasks and where they ran |
|
|
@@ -66,10 +73,11 @@ agent-hub is developed with agent-hub. This page lists every feature, whether it
|
|
|
66
73
|
| Feature | State here | Measured by |
|
|
67
74
|
| --- | --- | --- |
|
|
68
75
|
| Dogfood scorecard (#687) | on after release; read-only weekly receipt | `ahub report --dogfood --since 7d --json`; bounded evidence and explicit unknowns |
|
|
69
|
-
| Console and dashboard | on | control-request latency, connection timeouts
|
|
70
|
-
| Usage view (#683) | CLI (`ahub usage`, U2)
|
|
76
|
+
| Console and dashboard | on; #719 pending-history reads and bounded native launch retries take effect after release | control-request latency, connection timeouts and launch retry notices; daemon CPU/RSS samples, measured separately from lifetime CPU |
|
|
77
|
+
| Usage view (#683) | CLI (`ahub usage`, U2), console Usage tab `7` (U3) and dashboard Usage view (U4) shipped | its own panels |
|
|
78
|
+
| Kimi usage from its wire log (#285 phase 2) | on after the release when `budget.kimi_tokens_5h` is set; this repository sets it | `tokens` and `usage` (`kimi_wire`) events per Kimi turn, the `tokens` window in `ahub budget`, and the count of `kimi usage unknown` notices; the live check against Kimi's `/usage` (AC11) and the `ahub doctor` row `kimi usage source` are deferred to #285's later phases |
|
|
71
79
|
| Settings reload | on | reloads applied, pending restarts |
|
|
72
|
-
| Config editing from the console and dashboard (#689) |
|
|
80
|
+
| Config editing from the console and dashboard (#689) | on after release; `ahub settings edit` and console key `c` run here, the dashboard Structure group needs `ahub ui --settings`; executable keys stay console only | `config_edit` events (edits applied by tier and source), refusals shown with the loader reason, restarts left pending |
|
|
73
81
|
| Upgrade and recovery | on, per release | upgrade receipts in `docs/verification/` |
|
|
74
82
|
| Agent CLI version policy and trust (#691) | on after the release: Codex 0.163.0 verified by the [2026-10-22 receipt](verification/2026-10-22-codex-reviewer-isolation.md); Kimi 2.3.0 and Qwen 0.26.0 verified by the [model-free receipt](verification/2026-10-22-kimi-qwen-compat.md), with account/provider live legs unverified; agy 1.4.4 in-line | model-free isolation receipts, `ahub agents` verdicts, versions trusted per machine, lead time from an `agent-compat` issue to a receipt |
|
|
75
83
|
| Benchmarks (#251, #637) | off: `bench.enabled = false`; routing gate pending | bench receipts |
|
package/docs/events.md
CHANGED
|
@@ -40,7 +40,7 @@ marked `private: true`, and PII tasks `pii: true`.
|
|
|
40
40
|
| `turn_start` | `peer`, `turn` (`<peer>#<hub run>.<n>`, unique across restarts). A turn follows the adapter: pausing a busy peer does not end it |
|
|
41
41
|
| `turn_end` | `peer`, `turn`, `ms`, `tokens` (when the adapter reported any during the turn), `files` and `snapshotMs` (when snapshots are on: how many files the turn changed, and the time both snapshots took); optional numeric `task`, `attribution` (`delivery`, `single_open`, `unattributed`) frozen at turn start, `pii: true` for an attributed PII task |
|
|
42
42
|
| `tokens` | `peer`, `n` (tokens added since the previous report), optional numeric `task`, `attribution` (`delivery`, `single_open`, `unattributed`), `pii: true` for an attributed PII task |
|
|
43
|
-
| `usage` | `peer`, `source` (`omniroute`, `claude_transcript`, `codex_appserver`), opaque `id`, optional `measuredAt` (provider/source time), the `requestedModel`, `servedModel` and `provider` labels, and any provider-reported counters (`inputTokens`, `outputTokens`, `cacheReadTokens`, `cacheWriteTokens`, `reasoningTokens`, `totalTokens`); optional numeric `task`, `attribution` (`delivery`, `single_open`, `unattributed`), `pii: true` for an attributed PII task. Missing counters stay unknown. |
|
|
43
|
+
| `usage` | `peer`, `source` (`omniroute`, `claude_transcript`, `codex_appserver`, `kimi_wire`), opaque `id`, optional `measuredAt` (provider/source time), the `requestedModel`, `servedModel` and `provider` labels, and any provider-reported counters (`inputTokens`, `outputTokens`, `cacheReadTokens`, `cacheWriteTokens`, `reasoningTokens`, `totalTokens`); optional numeric `task`, `attribution` (`delivery`, `single_open`, `unattributed`), `pii: true` for an attributed PII task. Missing counters stay unknown. |
|
|
44
44
|
| `task` | `id`, `event` (the board history event, e.g. `proposed`, `assigned`, `done`, `check failed`, `blocked`, `ready`), `by`, `state`, `owner`, `reviewer`, `class`, `pii`, and `reason` when the board recorded a move reason (`manual`, `budget`, `offline`, `idle`, `rejections`, `declined`, `inference_failed`, `delivery_failed`) |
|
|
45
45
|
| `overlap` | `task`, `owner`, `others` (`task`, `owner`, `paths`, and `symbols` when plans name the same symbol; a name that matches a PII pattern is left out, so either list can be empty), the structured twin of the console notice |
|
|
46
46
|
| `permission_mode` | `peer`, `from`, `to` (`ask`, `ask-when-needed`, `never-ask`): a person set a peer's permission mode (`from` equals `to` when it was already in force) or confirmed a never-ask default (issue #242) |
|
|
@@ -56,12 +56,26 @@ marked `private: true`, and PII tasks `pii: true`.
|
|
|
56
56
|
|
|
57
57
|
Token usage by adapter:
|
|
58
58
|
|
|
59
|
-
- Kimi (
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
59
|
+
- Kimi (#285 phase 2): consumption is the hub's own count of Kimi's session wire log, never an ACP total. The `{used, size}`
|
|
60
|
+
`usage_update` Kimi sends is context occupancy: it is shown as the peer's native context reading (source
|
|
61
|
+
`acp_usage_update`, phase 1) and writes no `tokens` event. The reader (`src/adapters/kimi-wire-usage.ts`, the only code that knows
|
|
62
|
+
the format) finds the session directory from `session_index.jsonl` (resolved with `realPath`, kept inside `<kimi home>/sessions`)
|
|
63
|
+
and reads `agents/*/wire.jsonl` incrementally from saved offsets, sub-agents included, at a turn end, when a `usage_update`
|
|
64
|
+
arrives and when the peer goes offline (a turn start only binds the session and fixes the history floor, it reads nothing): no
|
|
65
|
+
watcher, no timer. One call reads at most a fixed byte bound across all of the session's files (the rest waits for the next call),
|
|
66
|
+
and two agent entries that are one file count once. Each `usage.record` row becomes a `usage` event
|
|
67
|
+
(`source: kimi_wire`, `inputTokens` = Kimi's `inputOther`, `outputTokens`, `cacheReadTokens`, `cacheWriteTokens`, `totalTokens`
|
|
68
|
+
including cache reads, `servedModel`, `measuredAt` = Kimi's row time) and a `tokens` event of the counted share. The id is an
|
|
69
|
+
opaque hash of session, agent and byte offset; no path, session id or text reaches an event. Only the four integers, the model
|
|
70
|
+
label and the time are copied from a row. The counted share is `inputOther + inputCacheCreation + output` (`kimiCountedTokens`,
|
|
71
|
+
one function): cache reads are recorded and shown, never counted against `budget.kimi_tokens_5h`. The rolling 5 h sum of the
|
|
72
|
+
counted share against that ceiling is the `tokens` window (`hub count, kimi wire log, of kimi_tokens_5h N`). Rows already in the
|
|
73
|
+
session's log when the hub first sees it (a resumed session) are history and are not counted. An unreadable index, a missing,
|
|
74
|
+
shrunk or replaced file (a replacement is told by a hash of the consumed head and tail kept with each offset, since Linux can reuse the inode), a truncated last line, an unknown row shape or an untested wire `protocol_version` (only 1.5 is known)
|
|
75
|
+
makes the count unknown: one notice (`kimi usage unknown (<reason>)`), the `tokens` reading is dropped, and no figure is reported
|
|
76
|
+
until the 5 h window the gap touched has passed (a transient read error or truncated line heals on the next read). Wire format
|
|
77
|
+
followed: protocol 1.5 as written by Kimi Code 2.3.0 (the issue read 2.1.1; the hub verifies 2.2.0 and 2.3.0 in `compat.ts`). The
|
|
78
|
+
live check against Kimi's own `/usage` (#285 AC11) is not done yet.
|
|
65
79
|
- Codex (app-server `thread/tokenUsage/updated`): recorded as the growth of the thread's running total, so
|
|
66
80
|
compaction estimates, usage-limit refreshes and the replay to a reattaching connection add nothing. A thread
|
|
67
81
|
started under the hub counts from zero; a resumed thread's first update is its history and only sets the
|
package/docs/operations.md
CHANGED
|
@@ -150,6 +150,15 @@ per peer (`#id [pii]` for a private task, never a title). The panel asks the hub
|
|
|
150
150
|
`usage unavailable`. The numbers come from the roll-up of `ahub usage`, so the console and the CLI agree. A request folds the event log first, within 250 ms;
|
|
151
151
|
when a very large log is not folded yet the answer carries `behind: true`, the panel says `still catching up` and `ahub usage` prints a warning on stderr, so a partial number is never shown as the whole.
|
|
152
152
|
|
|
153
|
+
The dashboard's Usage view (#683 U4) sits in the Agents view, under Usage budgets, and shows the same roll-up: the lifetime, without-cache and cache-read totals with
|
|
154
|
+
the peers whose figure is unknown, the last 14 UTC days as stacked columns per peer (a day with no record is a `no record` column, never zero), a limits table with the
|
|
155
|
+
open pauses and the last 20 pause, resume and reset events, and a task table (state, owner, PR, input, output, cache read, cache write, total). Every figure names its
|
|
156
|
+
source like the console does and `unknown` stays the word. A private task shows `#id [pii]` and never its title. The page asks for the figures only while the Agents view is open
|
|
157
|
+
(`view: "agents"` in the snapshot request), so a hidden view costs no roll-up work on the 1 s poll. When the roll-up is still behind a very large log the snapshot carries
|
|
158
|
+
`usage.behind: true` and the view says the figures are a lower bound until it catches up; a roll-up that cannot be read shows `unavailable`, never the last figures. When there
|
|
159
|
+
are more than 20 limit events the table says how many older ones it leaves out and that `ahub usage limits` lists them. Colours are design tokens only, the view reflows to one column at 600 px, and its
|
|
160
|
+
tables and chart scroll inside themselves.
|
|
161
|
+
|
|
153
162
|
Reviewing from the console (#711): on a task in review in Tasks (a selected row, or the open task detail), `r` asks which verdict first on the prompt line,
|
|
154
163
|
`review #372 "<title>": a approve c request changes Esc cancel` (the title is quoted and cut, a private task shows `[pii]`). `a` puts `review 372 approved ` on
|
|
155
164
|
the input line with a guide line above it (`Enter approves #372. You can type a note first. Esc cancels.`); `c` puts `review 372 changes_requested ` there and
|
|
@@ -721,7 +730,7 @@ stay first. Stream: one aligned row per event (time, kind, sender, body in monos
|
|
|
721
730
|
the section head, above the scrolling list. Memory: result rows show title, kind and age, the selected row is marked with an accent
|
|
722
731
|
edge, and the graph and the detail pane use the colour tokens. Settings: each group is a section with a short header, a key that waits
|
|
723
732
|
for a restart carries a "restart pending" badge, and "Pending restart" shows a count; the text lines, Preview, Save and Undo are as
|
|
724
|
-
they were. Message uses the shared form styles.
|
|
733
|
+
they were. A Structure group edits structural configuration in a settings session (#689, see Structural configuration below). Message uses the shared form styles.
|
|
725
734
|
|
|
726
735
|
Icons replace the symbol glyphs: the star mark, the stream arrow and the Office "checking" mark are sprite icons (twelve Tabler icons
|
|
727
736
|
in all), and the Office draws each condition (idle, busy, offline, paused, waiting on approval) as a shape (ring, disc, cross, bars, raised hand)
|
|
@@ -961,6 +970,17 @@ ahub board
|
|
|
961
970
|
ahub task show <task-id>
|
|
962
971
|
```
|
|
963
972
|
|
|
973
|
+
At a terminal width `ahub board` prints one line per task (#724): TITLE is cut
|
|
974
|
+
with `...`, dependencies are an AFTER column (`#113,+2`), and STAGE is the
|
|
975
|
+
progress meter with the stage and a short next-step code (`intent: issue?`,
|
|
976
|
+
`review: CI?`, `ready`), explained by a legend line. A column that is `-` on
|
|
977
|
+
every row is left out. When TITLE would get fewer than 30 columns (or fewer
|
|
978
|
+
than its longest title), REVIEWER, CLASS and AGE give way in that order, then
|
|
979
|
+
STAGE drops the stage name; at a width still too narrow AFTER goes and OWNER is
|
|
980
|
+
cut.
|
|
981
|
+
`ahub board --full`, `ahub task show <id>` and piped output without `COLUMNS`
|
|
982
|
+
keep the whole title and the full SDLC advice.
|
|
983
|
+
|
|
964
984
|
The owner accepts with `hub_task_accept`, makes the change, and reports with
|
|
965
985
|
`hub_task_done` including a summary and refs. The reviewer receives a review
|
|
966
986
|
request and responds with `hub_review`. A console review uses:
|
|
@@ -1037,6 +1057,8 @@ brief's own (a pair of qualified names must be equal, otherwise the last segment
|
|
|
1037
1057
|
an incomplete observation says an untouched symbol may have changed; a private task shows nothing. Names are quoted like every agent-written string, at most 40 are
|
|
1038
1058
|
listed and the rest are counted. The block is advice: deleted definitions are not reported, so it never proves a plan was or was not followed.
|
|
1039
1059
|
|
|
1060
|
+
The dashboard's task sheet has the same block as a "Code" group (#684 phase 2c): the changed symbols as buttons marked `matching` or `unplanned` against the plan, the plan names nothing changed under as `untouched`, and the same advisory line (deleted symbols are not reported, an incomplete observation may hide a changed one). The hub classifies them (`codeGroup` on each task of the UI snapshot, from the same public observation the console reads), so the page never matches names itself. A symbol opens the Memory tab with its canonical node (`symbol:<name>@<path>`) as the graph start; the list is capped at 40 with the count of what is left out, and a summary line counts matching, unplanned and untouched before the cap. A plan name the hub withholds (screened out or malformed) is counted, and the line then says a symbol marked unplanned may be in the plan. Without code facts the group reads `unknown (no code facts)`, and a private task has no group at all, so the sheet shows nothing for it.
|
|
1061
|
+
|
|
1040
1062
|
Task detail also shows an `overlaps` block (#379), using the hub's read-only
|
|
1041
1063
|
`symbolOverlaps` projection of its existing open-task/other-owner overlap selection.
|
|
1042
1064
|
Each row names the other task ID and screened shared paths. A row says "same file,
|
|
@@ -1065,7 +1087,7 @@ approved; caught (changes were requested and the owner's redo was approved);
|
|
|
1065
1087
|
contradicted (within a week, work on the same file or symbol failed its check
|
|
1066
1088
|
or review); escalated (after the reviewer asked for changes). A record counts
|
|
1067
1089
|
tasks, not verdicts. `ahub task show <id>` lists a task's outcomes and `ahub
|
|
1068
|
-
route explain` shows each reviewer's record with the implementer. With
|
|
1090
|
+
route explain` shows each reviewer's record with the implementer. `ahub route explain <id>` also prints the planner walk (`planner candidate X: ...` lines, then `planner: X (rotation | named | kept)`): the rotation among the rostered planner holders by task id (#525-3), shown for information, since nothing records or enforces a per-task planner yet. A peer that accepted or finished a task never reviews it; when every eligible reviewer did, the person reviews and the trace says so. With
|
|
1069
1091
|
`"review": { "adaptive": true }` in `.agenthub/config.json`, reviewers with at
|
|
1070
1092
|
least `min_reviews` (5) reviews of that implementer in the class are ordered by
|
|
1071
1093
|
how their reviews held up, ahead of quota but after idle before busy; off by
|
|
@@ -1212,7 +1234,7 @@ and its end `budget_resume`, with the window, the reset time and who ended it (`
|
|
|
1212
1234
|
`ahub budget resume`). A window's reset writes `quota_reset` when its `resetsAt` moves forward or its `used`
|
|
1213
1235
|
falls by 0.5 or more inside one window id, once per peer, window and `resetsAt`. These events hold ids and
|
|
1214
1236
|
numbers only, add no protocol version and are ignored by a reader that does not know them. `ahub report`
|
|
1215
|
-
does not show them: `ahub usage` and `ahub usage limits` read them (below), and the console's Usage tab shows each peer's last resets (#683 U3). A dashboard
|
|
1237
|
+
does not show them: `ahub usage` and `ahub usage limits` read them (below), and the console's Usage tab shows each peer's last resets (#683 U3). A dashboard Usage view shows the same roll-up in the Agents view (#683 U4, see [Operator console and panels](#operator-console-and-panels)).
|
|
1216
1238
|
|
|
1217
1239
|
`ahub usage` reads the same events from an incremental roll-up in `hub.db` instead of rescanning the log (#683):
|
|
1218
1240
|
|
|
@@ -1222,16 +1244,16 @@ ahub usage limits [--peer <id>] [--limit <n>] [--json] # current quota windows
|
|
|
1222
1244
|
ahub usage total [--json] # lifetime development tokens since the first event, per peer and overall
|
|
1223
1245
|
```
|
|
1224
1246
|
|
|
1225
|
-
The daemon folds `events.jsonl` into `usage_rollup`, `token_rollup` and `limit_events` from a stored byte offset, at most 8 MiB per pass
|
|
1247
|
+
The daemon folds `events.jsonl` into `usage_rollup`, `token_rollup` and `limit_events` from a stored byte offset, at most 8 MiB per pass, with a 20 ms scheduling slice checked between whole records (one record is indivisible),
|
|
1226
1248
|
only whole lines, and each pass commits its rows and the new offset together, so a crash repeats a pass and never half-applies one. A rebuild
|
|
1227
|
-
from byte 0 gives the same rows (tested). A log that was replaced by a shorter one is folded again from the start.
|
|
1249
|
+
from byte 0 gives the same rows (tested). A log that was replaced by a shorter one is folded again from the start. Before time slicing, on this project's 47 MB,
|
|
1228
1250
|
238k-line log the first fold took about 170 ms and a `usage` command about 95 ms, against about 150 ms for `ahub report --by task`, which
|
|
1229
1251
|
rescans the whole file on each call.
|
|
1230
1252
|
`issue`, `pr` and `release` are the board's current links of each task, read when you ask, so a task linked later is counted under its issue
|
|
1231
1253
|
for the history it already spent. A task with no link, and a record with no task, are `unattributed`.
|
|
1232
1254
|
|
|
1233
1255
|
Every number names its source: `native` (the provider's own counter), `hub count` (the hub's own `tokens` increments, which cover peers that
|
|
1234
|
-
report no usage record, such as Jcode
|
|
1256
|
+
report no usage record, such as Jcode), or `unknown`. Kimi's rows are `native`: the hub reads Kimi's own per-request counters from its session wire log and records them as `kimi_wire` usage records (#285 phase 2). Unknown is never shown as 0. `TOTAL` includes cache reads and `WITHOUT CACHE`
|
|
1235
1257
|
removes them; Claude's total is the sum of its four reported counters because its transcript carries none (`ahub report` still shows it as unknown), and a source whose input already
|
|
1236
1258
|
contains the cached part (Codex, the gateway) gets no derived total. A lifetime total that mixes a native and a hub-counted peer is labelled
|
|
1237
1259
|
`hub count`. When a peer is unknown the overall figure is `partial, at least`: a lower bound that leaves the named peers out, never a sum that counts them
|
|
@@ -1584,9 +1606,18 @@ stays the default until an evaluation says otherwise (`docs/cooperbench.md`).
|
|
|
1584
1606
|
|
|
1585
1607
|
## Always-ask action policy foundation (#636)
|
|
1586
1608
|
|
|
1587
|
-
The classifier, local configuration and session budget counter
|
|
1609
|
+
The classifier, local configuration and session budget counter decide; since slice 2 the Claude PreToolUse hook and ACP `session/request_permission` enforce them. Codex, Jcode, Pi and the local worker are not enforced yet (slice 3, #313): their `ahub status` rows still say `always-ask not enforceable: adapter integration pending`. A headless Claude implementer (#525-4) runs the same hook check (its facts hook returns `ask` for a classed call), but its status row also still says `adapter integration pending`. A default-on policy adds a pre-tool check only where the hub sees the call before it runs, and each peer's status row says whether it does.
|
|
1610
|
+
|
|
1611
|
+
What is enforced, and where it is not:
|
|
1588
1612
|
|
|
1589
|
-
|
|
1613
|
+
| Peer | Enforced | Not enforceable (the status row says why) |
|
|
1614
|
+
| --- | --- | --- |
|
|
1615
|
+
| Claude (`ahub claude`) | the managed PreToolUse hook returns `ask` for a classed call in `ask`, `ask-when-needed` and `never-ask` alike, and `ask` beats every allow the hook would add | a session with no hub permission hook, or one whose attached session has not yet run it (the launch record outlives the process, so it proves nothing alone: the status row says `no live Claude session has run the hub permission hook yet`); an `--unattended` launch (`--dangerously-skip-permissions` skips every permission check, hooks included); a hub that cannot answer the hook leaves Claude's own policy |
|
|
1616
|
+
| Kimi and other hub-owned ACP peers (headless) | `session/request_permission` for a classed call is never auto-approved (not even for a hub-owned tool title), carries `Action approval (<classes>): <command>`, withholds `allow_always`, and under `ahub --unattended` still waits for a person and expires as a denial | a peer in its native `ask-when-needed` (`yolo`) or `never-ask` (`auto`) mode: Kimi sends no request for a call that mode allows, so the hub cannot see it; the status row says `its native mode never-ask sends no request for a call it allows`. Switch the peer to `ask` to enforce. A request whose payload the agent did not report is not classed (unknown follows the mode) |
|
|
1617
|
+
|
|
1618
|
+
Class budgets: the approval of a classed ACP call offers one extra choice, `Allow <class> xN for this session`, when the class budget is above one. Only that choice (never an ordinary allow) spends the ledger: it allows this call and the next N-1 of the same classes in the same native session, then asks again; several requests open at once share it, so a budget of N never lets more than N approved calls through. A new native session, a changed policy or a stopped, crashed or restarted peer starts from zero, even when it resumes the same session id. A budget of one is the single call the ordinary allow already covers. Claude's own permission dialog cannot carry that choice, so every classed Claude call asks.
|
|
1619
|
+
|
|
1620
|
+
Only ignored, untracked `.agenthub/config.local.json` can hold `action_policy`. Its optional keys are `enabled` (default true), `always_ask` (a distinct subset of publish, external-write, destroy, install; default all), and `budgets` (per-class integers 0..1000; defaults 1, 5, 3, 2 respectively). Shared `config.json` cannot set this policy and gets no confirmation offer. A tracked or unknown-trust local file is ignored. Use `ahub settings set action_policy.enabled <true|false>` or `ahub settings set action_policy.budgets.<class> <n>` for registered settings; change `always_ask` in the local file and use the existing confirmed terminal reload. Policy publication invalidates cached grants and pending approval challenges. Startup still reads the untracked local policy without a retained confirmation, although the Claude and ACP gates now enforce it; this ceiling stays open (#312/#313).
|
|
1590
1621
|
|
|
1591
1622
|
The ledger asks first and after the approved class budget is spent. Adapter integrations must request that class allowance explicitly; an ordinary one-time answer does not seed the cache, and a cached budget never suppresses a prompt required by the native mode. One approval includes its own invocation; budget zero asks for every invocation. A fresh native session resets counters, and status polls do not bind sessions. Unknown command syntax retains native permission handling. This foundation never changes a sandbox or bypasses a denied permission; per-adapter enforcement tests land with their integrations.
|
|
1592
1623
|
|
|
@@ -1775,7 +1806,9 @@ if another write replaces that record, preview again. For busy roster removal,
|
|
|
1775
1806
|
the panel names `ahub roster remove <peer> --now`, which explicitly stops an owned
|
|
1776
1807
|
runtime or detaches a native session during its active turn; the panel does not force it.
|
|
1777
1808
|
Esc cancels, and pasted confirmation text cannot
|
|
1778
|
-
write. This panel uses the current authenticated daemon connection.
|
|
1809
|
+
write. This panel uses the current authenticated daemon connection. `c` edits
|
|
1810
|
+
structural configuration and `R` prefills `restart` while a restart is pending
|
|
1811
|
+
(see [Structural configuration](#structural-configuration-from-the-console-and-the-dashboard-issue-689)).
|
|
1779
1812
|
|
|
1780
1813
|
The dashboard's Settings section and `ahub settings` show and change the same
|
|
1781
1814
|
closed list of settings (issue #269). Run them from a plain terminal:
|
|
@@ -1863,12 +1896,46 @@ Pi relay model aliases (`pi.dgx_coding`, `pi.dgx_fast`) and `routing.pi` retain
|
|
|
1863
1896
|
waits for `y` in `ahub console` at each hub start. Tool approvals stay
|
|
1864
1897
|
deny-only for Pi and local in every session.
|
|
1865
1898
|
- Commands and binaries, credential files, gateway URLs and the local worker's
|
|
1866
|
-
reach are never editable here:
|
|
1899
|
+
reach are never editable here: change them with `ahub settings edit` in a
|
|
1900
|
+
terminal, each confirmed by typing its name (tier 3 below), or edit
|
|
1901
|
+
`.agenthub/config.local.json` yourself.
|
|
1867
1902
|
- Every change is announced in `hub.log` and open consoles with its source and
|
|
1868
1903
|
recorded as a `settings` event. `ahub settings` needs a running hub; an older
|
|
1869
1904
|
hub answers the request as unknown and the CLI advises upgrading. The unified
|
|
1870
1905
|
dashboard (`ahub ui --all`) acts as an ordinary session.
|
|
1871
1906
|
|
|
1907
|
+
### Structural configuration from the console and the dashboard (issue #689)
|
|
1908
|
+
|
|
1909
|
+
Instances, the review and approval blocks and the lifecycle can be edited without an editor. The hub reads the candidate with the loaders it starts with (`readInstanceConfig` and `validateInstances`, `loadConfig` with its effective-config bounds, `checkJcodeInstanceHomes`, `sdlcConfig`), shows a diff and writes only after a person confirms. Three tiers:
|
|
1910
|
+
|
|
1911
|
+
1. **Registry settings** (everything above) are unchanged.
|
|
1912
|
+
2. **Structural keys**, editable in the console and in a dashboard settings session:
|
|
1913
|
+
- `instances.<id>` with `kind`, `roles`, `start_mode`, `review_mode`, `readOnly`, `worktree` and `permission_mode`, in `config.local.json`;
|
|
1914
|
+
- `review.adaptive`, `review.min_reviews`, `review.pool`, `review.rerotate`, `snapshots.keep`, `approvals.timeout_s` and `context.gate`, in `config.local.json`;
|
|
1915
|
+
- `mode` and `required_checks` in `.agenthub/sdlc.toml`. Only those two lines change: comments and every other table stay as written, and a layout the editor cannot change faithfully (a key repeated, or one inside a multi-line string) is refused with its own reason.
|
|
1916
|
+
|
|
1917
|
+
`roster.*`, `tasks.wip_limits.*`, `permission_modes`, `peers` and the rest of the registry keep their own `ahub settings set` path.
|
|
1918
|
+
3. **Executable and secret-locating keys** are console only: the rest of `NEVER_EDITABLE` (`kimi_cmd`, `jcode_cmd`, `codex_bin`, `pi.cmd`, `checks`, `mlx`, `omniroute`, `terminal`, `adapters`, `adapter_modules` and the like) and the Jcode instance fields `jcode_home`, `provider`, `model` and `provider_profile`. The console shows "this runs code with the daemon's rights, or chooses where secrets are read from" and writes only when the key's exact name is typed, one key at a time; `--yes` never confirms one. The dashboard lists each of them with the console command that changes it and nothing else: no value, no path, no input. A forged dashboard request for one is refused by the hub.
|
|
1919
|
+
|
|
1920
|
+
In the console (`ahub console`, Settings tab `6`): `c` asks what to edit (`1` an instance, `2` the lifecycle, `3` another key), asks each field, shows the hub's diff or its loader reason, asks for a reason, then `y`. Esc cancels at any step, and pasted text never confirms. `R` appears while a restart is pending and prefills `restart`; the command still asks its own `y`. In the dashboard, open `ahub ui --settings`: the Structure group has the same forms; outside a settings session it is read only.
|
|
1921
|
+
|
|
1922
|
+
```bash
|
|
1923
|
+
ahub settings edit config.local.json set instances.codex-review '{"kind":"codex","roles":["reviewer"],"start_mode":"headless","review_mode":"fresh"}' --reason "add the fresh reviewer"
|
|
1924
|
+
ahub settings edit sdlc.toml set mode '"enforce"' --preview
|
|
1925
|
+
ahub settings edit config.local.json remove review.pool --yes
|
|
1926
|
+
ahub settings backups # the newest backups of each file
|
|
1927
|
+
```
|
|
1928
|
+
|
|
1929
|
+
Rules:
|
|
1930
|
+
- A missing `start_mode` on the fresh `codex-review` reviewer shows the loader's reason (`fresh requires reviewer-only roles, headless mode ...`), names the field and writes nothing.
|
|
1931
|
+
- The edit carries the revision (a sha256, or `absent`) of the file it was read from. The hub checks it when it plans the edit and again immediately before it writes, so a file changed on disk since is refused with a hint to reload and review again.
|
|
1932
|
+
- The previous file is kept in `<state dir>/config-backups/` (the newest five of each file; `ahub settings backups` lists them) and the new one replaces it atomically. A `config.local.json` that git tracks is never written. Unknown fields and unknown keys are refused by name, never dropped.
|
|
1933
|
+
- A whole instance that holds a native home, account or model (`jcode_home`, `provider`, `model`, `provider_profile`) is tier 3 to remove or replace, so a dashboard can neither see nor delete those values; an existing instance changes leaf by leaf (`instances.<id>.<field>`), and `clear` removes one field. A preview or write shows a tier 3 value only as `(set)`, and a write changes exactly one tier 3 key, counting the keys the file adds (removing two Jcode instances is two writes, each confirmed by its own typed name). The audit record gives a tier 3 key no value hash at all, because a model or provider name is easy to recover from an unsalted hash; the file revisions bind the change instead. An instance already in `config.local.json` is changed leaf by leaf even before a restart has started it. If `.agenthub` or `config.local.json` is a symlink, the Settings view reports its revision as `unavailable` and no edit is planned. An edit of `sdlc.toml` keeps the file's own line endings.
|
|
1934
|
+
- Every write records a `config_edit` event: the file, each key path with a short hash of its value before and after (never a value), the file revisions before and after, the tier, the source (`dashboard` or `terminal`) and `by: person`. The notice and `hub.log` name key paths only.
|
|
1935
|
+
- A changed instance declaration is restart-bound: it is listed as `pending restart` (`instances`) and takes effect at the next hub start. A changed `sdlc.toml` is read once at start and is listed as `sdlc.mode` or `sdlc.required_checks` until the running hub holds the same value. `review`, `snapshots`, `approvals` and `context.gate` publish through the one reload path, one key at a time.
|
|
1936
|
+
- Person only: an agent shell or a hub tool is refused (`ahub settings` is a console command in the identity gate, and the three control requests answer only the console role). Recovery or shutdown holds every edit.
|
|
1937
|
+
- The hub must run this release: an older hub answers `unknown message` and the command says to upgrade and restart it.
|
|
1938
|
+
|
|
1872
1939
|
## Start modes: TUI by default, headless as opt-in
|
|
1873
1940
|
|
|
1874
1941
|
A peer that has a TUI the hub attaches to starts in it by default (issue #269).
|
|
@@ -3306,9 +3373,12 @@ ownership, bind servers or start agents, sidecars or models.
|
|
|
3306
3373
|
`ahub status`, `ahub tail` and the dashboard show native context occupancy,
|
|
3307
3374
|
source and measurement freshness. Claude readings come from the status-line
|
|
3308
3375
|
tee installed by `ahub claude`; Codex readings come from its current thread's
|
|
3309
|
-
native token-usage updates. Kimi
|
|
3310
|
-
(`{used, size}`, source `acp_usage_update`)
|
|
3311
|
-
|
|
3376
|
+
native token-usage updates. Kimi's context occupancy comes from its ACP `usage_update`
|
|
3377
|
+
(`{used, size}`, source `acp_usage_update`), not consumption, so no token total is counted from it. Its consumption is counted from its
|
|
3378
|
+
session wire log (`<kimi home>/sessions/.../agents/*/wire.jsonl`, #285 phase 2) and reported as the `tokens` window of
|
|
3379
|
+
`budget.kimi_tokens_5h` (source `hub count, kimi wire log, of kimi_tokens_5h N`; cache reads are not counted); an unreadable log shows
|
|
3380
|
+
unknown with one notice, never a short count. Until the OAuth probe or `ahub budget set kimi ...` feeds the plan quota, the provider's
|
|
3381
|
+
own 5 h and weekly ratios stay unknown. Not yet verified live against Kimi's `/usage` (#285 AC11); the adapter accepts the
|
|
3312
3382
|
update whenever it names the current session, because 2.1.1's source text
|
|
3313
3383
|
emits it after the turn settles (read from the source, not yet observed live
|
|
3314
3384
|
through the hub; docs/smoke.md), so the peer may already be idle.
|
|
@@ -3509,7 +3579,7 @@ that the file is untracked; a dropped value is reported by the settings result.
|
|
|
3509
3579
|
|
|
3510
3580
|
## SDLC artifact receipts and gates
|
|
3511
3581
|
|
|
3512
|
-
The playbook lifecycle (#257, #481) is off without `.agenthub/sdlc.toml`. When enabled its stages are intent, spec, plan, build, test, review, merge, release and maintain, grouped as Plan, Design, Build, Test, Deploy and Maintain. Advise shows blockers; enforce checks implemented head-bound review and approval gates. Stored implement/verify receipts read as build/test; new enabled receipts use canonical ids. See [the SDLC contract](sdlc.md) for gates, required check names, local receipts, console-only readbacks and the S0-S8 delivery table. A person accepts a spec issue with `ahub sdlc accept <task> --issue <n>` (S1). The planner records plan reviews with review and CI scope (`hub_plan_review`, `ahub sdlc plan-review`) and grooms with `ahub backlog groom`; `ahub backlog return <task> <revision> <reason>` returns accepted, untouched work (S9). `ahub sdlc costs [owner/repo]` reports review rounds and CI runs of merged PRs without writing the board (S10). S11 release trains ship in 0.12.37 (see [release scheduling](#planner-release-scheduling)). S3 structured review findings (an open Important finding blocks approval in enforce; `max_nits` in `sdlc.toml` caps nits) and S2 literal-path plan conformance with the person-only `ahub sdlc accept-departure <task> <head> <reason...>` are in source; see [findings](sdlc.md#structured-review-findings-481-s3) and [plan conformance](sdlc.md#plan-conformance-481-s2). S4-
|
|
3582
|
+
The playbook lifecycle (#257, #481) is off without `.agenthub/sdlc.toml`. When enabled its stages are intent, spec, plan, build, test, review, merge, release and maintain, grouped as Plan, Design, Build, Test, Deploy and Maintain. Advise shows blockers; enforce checks implemented head-bound review and approval gates. Stored implement/verify receipts read as build/test; new enabled receipts use canonical ids. See [the SDLC contract](sdlc.md) for gates, required check names, local receipts, console-only readbacks and the S0-S8 delivery table. A person accepts a spec issue with `ahub sdlc accept <task> --issue <n>` (S1). The planner records plan reviews with review and CI scope (`hub_plan_review`, `ahub sdlc plan-review`) and grooms with `ahub backlog groom`; `ahub backlog return <task> <revision> <reason>` returns accepted, untouched work (S9). `ahub sdlc costs [owner/repo]` reports review rounds and CI runs of merged PRs without writing the board (S10). `ahub sdlc metrics [--since <YYYY-MM-DD|ISO time>]` (the planner tool `hub_sdlc_metrics`, planner or person, read-only) reports lead times, first-pass approval and check rates, rework rounds, stale-head re-reviews and open stalls from board history, with `unknown` counted and never 0; optional `[bands.<metric>]` in `sdlc.toml` add control bands that the daemon checks every five minutes in advise or enforce, writing a hub notice (`log`), a read-only `triage` backlog task (`diagnose`) or a `plan` backlog card (`propose`), one per breach episode and never a GitHub issue (S7, see [metrics](sdlc.md#sdlc-metrics-and-control-bands-481-s7)). With the lifecycle on, a task's issue identity is linked when it is proposed or accepted, from the one issue it names (an imported GitHub issue, or a `#N`, `owner/repo#N` or issue URL in its title); a title that names a sibling repository (`agent-mem#13`) links only when `refs.repo` names it, and without `refs.repo` the propose result says to set it. `ahub backlog groom` also backfills up to 32 links per call, within 1 s (#723). S11 release trains ship in 0.12.37 (see [release scheduling](#planner-release-scheduling)). S3 structured review findings (an open Important finding blocks approval in enforce; `max_nits` in `sdlc.toml` caps nits) and S2 literal-path plan conformance with the person-only `ahub sdlc accept-departure <task> <head> <reason...>` are in source; see [findings](sdlc.md#structured-review-findings-481-s3) and [plan conformance](sdlc.md#plan-conformance-481-s2). S4 [test integrity](sdlc.md#test-integrity-481-s4) and S6 [release authorization](#release-approval-and-optional-tag-guard) are in source too. S5 continuous evals, release metrics and bands, and a full live advise/release cycle remain pending. Owners/reviewers record bounded identities through `hub_sdlc_record`; these grant no additional authority. Off output, receipt and schema shapes stay unchanged.
|
|
3513
3583
|
## Optional Qwen Code peer
|
|
3514
3584
|
|
|
3515
3585
|
Select Qwen per project with `ahub roster add qwen`; it defaults off and is absent
|
|
@@ -3620,6 +3690,8 @@ A bundled adapter module is authorized by the person with `adapter_modules` (at
|
|
|
3620
3690
|
# Acceptance-criteria checklist (#481)
|
|
3621
3691
|
|
|
3622
3692
|
- The task reviewer/person records a head-bound `criteria` receipt with criterion ids matching the issue's AC ids, required/result fields and hashed evidence refs. This checklist belongs to the Compliance review pass. Use `ahub sdlc record <id> '<receipt JSON>'`; [schema and example](sdlc.md#acceptance-criteria-checklist).
|
|
3693
|
+
- Deliverables (#509a, docs/sdlc.md, Deliverable records): `hub_task_done` and `hub_sdlc_record` take `deliverables: [{path, format, version?}]`; the hub hashes the files itself (a claimed hash is refused), in a plain folder with no git or PR, under the owner's execution root (its own worktree for a declared worktree instance; an unproven root refuses and reads `unknown`), and a reviewer's approval is refused with `deliverable changed since it was delivered` when a file changed since delivery (the owner calls `hub_task_done` again, or the person approves the current bytes). `ahub task show <id>` shows each file's state (`unchanged`, `changed`, `missing`, `unknown`) and `changedSinceReview`; a PII task or `pii.paths` file shows id, hash and size only.
|
|
3694
|
+
- The review request lists the recorded criteria with their ids and results, and a `hub_review` unmet item that starts with a recorded id (`AC2: ...`) is recorded on the verdict as `unmetCriteria` (#507b); `ahub task show` lists those ids in the history. A task with no checklist, and a PII task, are unchanged.
|
|
3623
3695
|
- Advise shows incomplete/stale checklist blockers; enforce checks them at approval. The `criteria` gate does not need a workflow selector. Dependencies stay with `ahub task propose --after`; checklist task import is removed.
|
|
3624
3696
|
- `status <id>`, `explain <id>` and `export <id>` use the same current board/config projection. Export keeps sanitized state and source hashes, with no external text, extra authority or verification claim.
|
|
3625
3697
|
- Migration from the retired workflow and its compatibility window (until hub 0.13.0) are described in [Retired workflow compatibility](sdlc.md#retired-workflow-compatibility-252-481-s8). Stored checklist task lists are ignored with a visible note; they never change board dependencies.
|
|
@@ -3725,7 +3797,7 @@ are described below. Native planner-cycle evidence remains unverified.
|
|
|
3725
3797
|
|
|
3726
3798
|
## Planning and phased releases (#263)
|
|
3727
3799
|
|
|
3728
|
-
Use `ahub backlog list` to read public staged work, then `ahub backlog plan <id>
|
|
3800
|
+
Use `ahub backlog list` to read public staged work (a table by rank with the reserved owner, dependencies and cut titles, then a count by reserved owner; `--full` and piped output keep whole titles and `--json` prints the raw listing), then `ahub backlog plan <id>
|
|
3729
3801
|
<revision> <fields-json> <reason>` to set rank, priority (0-3), release id, planned
|
|
3730
3802
|
start/due UTC milliseconds or pin. `pin` accepts true/false; `undo` needs the
|
|
3731
3803
|
current revision and latest eligible edit. The dashboard provides local-time
|
|
@@ -3784,6 +3856,14 @@ unapproved dependencies, frozen scope and overriding person P0 order/reservation
|
|
|
3784
3856
|
The person uses `ahub backlog pull-ready <id> <revision> <reason>` or Pull within
|
|
3785
3857
|
WIP on a card. Explicit manual pull/assignment retain their existing authority.
|
|
3786
3858
|
|
|
3859
|
+
A pull of a reserved backlog task (#718) assigns it to exactly that reserved peer
|
|
3860
|
+
in the same write and sends it a fresh owner offer, so it can accept at once. A
|
|
3861
|
+
reserved peer that cannot take it (offline, paused, excluded, at its WIP limit)
|
|
3862
|
+
refuses the pull before any change, naming the peer and its reason; it is not
|
|
3863
|
+
passed over to another peer. `ahub route explain` previews the same reserved-only
|
|
3864
|
+
choice. An unreserved pull keeps reserving a slot as above. A reserved offer that
|
|
3865
|
+
was never started can return untouched and keeps its reservation.
|
|
3866
|
+
|
|
3787
3867
|
Capacity defaults to one per peer. Each declared instance has its own `tasks.wip_limits.<instance>` key. `ahub settings set tasks.wip_limits.codex 2`
|
|
3788
3868
|
uses the existing machine-local registry path; integers 0..32 apply live (the next
|
|
3789
3869
|
admission reads the current value), with 0 disabling planner admission for that
|
|
@@ -4099,11 +4179,24 @@ run yourself in this project outside the hub has no peer id: its hub tools refus
|
|
|
4099
4179
|
every call with that reason and never default to `jcode`, and a process that only
|
|
4100
4180
|
sets `AGENTHUB_PEER_ID` without an owned launch is refused by the hub.
|
|
4101
4181
|
|
|
4102
|
-
An extra Claude instance is
|
|
4103
|
-
|
|
4104
|
-
`
|
|
4105
|
-
|
|
4106
|
-
`claude
|
|
4182
|
+
An extra Claude instance is one you start yourself: declare it with `roles` (for example
|
|
4183
|
+
`"claude-review": { "kind": "claude", "roles": ["reviewer"] }`) and run `ahub claude --as claude-review` in a
|
|
4184
|
+
terminal. It keeps its own `claude-*.claude-review.json` state files. A planner or reviewer instance reads the
|
|
4185
|
+
project checkout and refuses `worktree`. From #525-1 an `implementer` or `verifier` instance is accepted and
|
|
4186
|
+
requires its own `worktree` (for example `"claude-impl": { "kind": "claude", "roles": ["implementer"],
|
|
4187
|
+
"worktree": ".worktrees/claude-impl" }`). `ahub claude --as claude-impl` validates that registered branch worktree and starts Claude there while keeping its canonical hub connection. Its private launch record stores the execution root. The channel reports its own canonical cwd (verified model-free with Claude 2.1.309); the daemon grants owner isolation only when that root, the current launch id, the recorded root and its own worktree validation all agree. Missing, legacy or mismatched evidence still permits a channel attachment, but write-owner routing is refused with `owner admission: declared write owner requires a verified isolated execution root`. Status shows the isolation refusal. A detach, replacement claim or launch-record change invalidates the proof. Facts and permission grants use the verified execution root; an unverified implementer gets no automatic grant. Recovery records retain that execution root, checked against the current declaration, separately from the Orca grouping root. This is same-user launch evidence, not kernel filesystem isolation. A Claude instance holding only planner or reviewer roles still uses the canonical checkout. A headless Claude reviewer retains its reviewer-only declaration. A headless implementer is available with the separate declaration below (#525-4).
|
|
4188
|
+
|
|
4189
|
+
A headless Claude implementer (#525-4) is declared separately:
|
|
4190
|
+
|
|
4191
|
+
```json
|
|
4192
|
+
"claude-impl": { "kind": "claude", "roles": ["implementer"], "start_mode": "headless", "worktree": ".worktrees/claude-impl", "permission_mode": "ask" }
|
|
4193
|
+
```
|
|
4194
|
+
|
|
4195
|
+
Create and register the attached-branch worktree first; the hub never creates it implicitly. `ahub claude --as claude-impl` asks the hub to start the instance and returns. Idle holds no native child. Task deliveries use a persisted UUID per task: first `--session-id`, later `--resume`. Unscoped chat reuses a session only when exactly one task is in progress for the instance; otherwise it is ephemeral. Positive missing-transcript evidence starts fresh with a notice; unknown read evidence retains the id. Reset includes the per-peer session map. A child inbox read refuses a queued batch attributed to another task and leaves it queued for that task's own session.
|
|
4196
|
+
|
|
4197
|
+
Both permission modes use native `default`, managed settings/hooks and the instance's strict tools-mode MCP binding. `ask` routes writes and shell calls to the person's approval. `ask-when-needed` may explicitly allow safe project file tools through the facts hook in the execution root; protected configuration, paths outside that worktree and shell/git writes still ask. There is no `acceptEdits` or `never-ask` bypass. A failed hook decides nothing and may be followed by a valid person allow. Prompt failure, timeout, disconnect and a stale child binding never grant permission. Approvals use the existing console and `approvals.timeout_s`; no answer returns deny. A stop holds uncertain delivery until the owned process group is confirmed gone; a failed cleanup keeps the peer busy.
|
|
4198
|
+
|
|
4199
|
+
The [native permission receipt](verification/2026-10-23-headless-claude-permissions.md) covers Claude Code 2.1.309 with a deterministic loopback fixture and no real account/model call. It is not sandbox proof or a runtime receipt gate. The real account/model/person interaction remains a separate live leg.
|
|
4107
4200
|
|
|
4108
4201
|
A headless Claude reviewer is run by the hub instead (#223): declare
|
|
4109
4202
|
`"claude-review": { "kind": "claude", "roles": ["reviewer"], "start_mode": "headless" }`
|
|
@@ -4135,7 +4228,7 @@ From 0.12.37 reviews can rotate across fresh headless Claude and Codex reviewers
|
|
|
4135
4228
|
}
|
|
4136
4229
|
```
|
|
4137
4230
|
|
|
4138
|
-
Start them with `ahub claude --as claude-review`, `ahub codex --as codex-review` and `ahub kimi --as kimi-review`; stop with `ahub stop <id>`. Fresh Claude and Codex variants share the project checkout; the Kimi variant runs in a clean temporary working directory and reviews only the screened brief and host facts. All fresh variants refuse a custom worktree or permission mode. Codex and Kimi require `review_mode: "fresh"`; without it, an existing headless instance with the reviewer role keeps its native app-server/ACP behavior, worktree and permission options and does not join this pool. Claude retains its #223 automatic headless-reviewer declaration. The agy reviewer slot remains automatically unavailable (`agy is an implementer peer (#485)`). A failed or unsupported fresh start never downgrades to a native continuing session. Ordinary implementer instances retain their own adapters. The default logical pool order is Claude, Codex, Kimi. Only configured kinds with at least one declared reviewer count as slots, including declared ones that are offline, paused, quota-limited or failing, so availability never renumbers them; configured kinds with no declaration are listed after the walk as `no declared reviewer`. `review.rerotate` defaults to false. Pool selection starts at PR number modulo the number of declared kind slots, or task id when there is no PR, then skips the implementer's family and unavailable candidates. Delta review prefers the preceding eligible independent family in a fresh child. A preceding same-family fallback does not outrank a newly available independent family. `ahub route explain` shows the same pure walk.
|
|
4231
|
+
Start them with `ahub claude --as claude-review`, `ahub codex --as codex-review` and `ahub kimi --as kimi-review`; stop with `ahub stop <id>`. For the Claude and Codex reviewers the command asks the running hub to start the reviewer and returns: nothing runs in that terminal, it takes no launch arguments, and `--print-command` or `--dry-run` prints a hub-start note instead of a launch (#720). Fresh Claude and Codex variants share the project checkout; the Kimi variant runs in a clean temporary working directory and reviews only the screened brief and host facts. All fresh variants refuse a custom worktree or permission mode. Codex and Kimi require `review_mode: "fresh"`; without it, an existing headless instance with the reviewer role keeps its native app-server/ACP behavior, worktree and permission options and does not join this pool. Claude retains its #223 automatic headless-reviewer declaration. The agy reviewer slot remains automatically unavailable (`agy is an implementer peer (#485)`). A failed or unsupported fresh start never downgrades to a native continuing session. Ordinary implementer instances retain their own adapters. The default logical pool order is Claude, Codex, Kimi. Only configured kinds with at least one declared reviewer count as slots, including declared ones that are offline, paused, quota-limited or failing, so availability never renumbers them; configured kinds with no declaration are listed after the walk as `no declared reviewer`. `review.rerotate` defaults to false. Pool selection starts at PR number modulo the number of declared kind slots, or task id when there is no PR, then skips the implementer's family and unavailable candidates. Delta review prefers the preceding eligible independent family in a fresh child. A preceding same-family fallback does not outrank a newly available independent family. `ahub route explain` shows the same pure walk.
|
|
4139
4232
|
|
|
4140
4233
|
Codex requires a verified `codex-cli` (0.146.0 or 0.163.0) or one the person trusts on this machine, and a passing bounded runtime receipt before its model child starts. Both verified versions passed the [2026-10-22 model-free production admission](verification/2026-10-22-codex-reviewer-isolation.md). A refusal names the installed and verified versions. Either set `codex_bin` to a verified executable, or run `ahub agents trust codex-review <version>`: trust lifts only the version pin, and every proof below still runs before each child (#691). The producer checks native binary/config stability, configured MCP inventory with only the two hub tools, effective Apps/plugins/hooks disablement, an explicit empty legacy `notify` command and a named filesystem profile. Apps needs separate feature readback because configured inventory omits runtime connectors. Web search is requested disabled; its effective turn mode is not read back and managed requirements can select Cached (see the verification ceiling). Each launch proves public-read, exact/glob/symlink private-denial, standard host credential denial and write-denial in temporary fixtures. Standard credential and configured tool-home/XDG subtrees are denied independently of project `local.deny`. Its sandboxed shell is filesystem read-only with private paths denied and network configured disabled; it is not Claude's Read/Grep/Glob-only tool whitelist. No credential home is copied.
|
|
4141
4234
|
|
|
@@ -4145,7 +4238,7 @@ A dangling or unreadable standard/configured credential root makes the Codex rev
|
|
|
4145
4238
|
|
|
4146
4239
|
agy is not a reviewer (person decision, 2026-10-19): it is an optional implementer peer (see [Optional Antigravity peer](#optional-antigravity-agy-peer)), and its retained reviewer slot is outside the default pool and always unavailable with `agy is an implementer peer (#485)`. A manual pool accepts only registered reviewer kinds (Claude, Codex, Kimi, agy), so local/Pi are refused. An explicit pool without declared candidates blocks for person action; an omitted pool retains legacy routing without declarations and falls back to an eligible planner when fresh candidates are exhausted. The exhausted pool walk remains recorded; without an eligible planner, the person reviews. A manual pool may name agy, or a Kimi reviewer whose preparation fails; the reason appears as an unavailable skip, and they never run or count as approval. Do not substitute plan mode for isolation.
|
|
4147
4240
|
|
|
4148
|
-
A failed start or any child ending without a saved verdict advances to the remaining eligible candidates; no quota meaning is guessed from text. A same-family fallback is a distinct fresh reviewer session. Exhaustion records `blocked: no reviewer can run`; with no explicit pool the eligible planner fallback is retained, while an explicit pool or absent eligible planner leaves person action. It never auto-approves. A per-review isolation preparation failure excludes that peer from later tasks until a verified restart. Recovery targets must advertise `reviewer_pool` when a pool is explicitly configured or fresh reviewer instances are declared. Verdict history retains `reviewContext` and adds `reviewerFamily` and `reviewerReason` from the host.
|
|
4241
|
+
A failed start or any child ending without a saved verdict advances to the remaining eligible candidates; no quota meaning is guessed from text. A same-family fallback is a distinct fresh reviewer session. Exhaustion records `blocked: no reviewer can run`; with no explicit pool the eligible planner fallback is retained, while an explicit pool or absent eligible planner leaves person action. It never auto-approves. A per-review isolation preparation failure excludes that peer from later tasks until a verified restart. Its reason names one cause from a closed list (`version`, `binary`, `mcp_inventory`, `plugins_hooks_apps`, `sandbox_proof`, `credential_root`, `timeout`, `unknown`) with a fixed hint, on the peer's `reviewer` line in `ahub status` and in a `hub.log` line, and never a path, native CLI text or token (#722). Only the Codex preparation classifies its failures; other reviewers report `unknown`. Recovery targets must advertise `reviewer_pool` when a pool is explicitly configured or fresh reviewer instances are declared. Verdict history retains `reviewContext` and adds `reviewerFamily` and `reviewerReason` from the host.
|
|
4149
4242
|
|
|
4150
4243
|
Fake-CLI tests and CI cover these host contracts. [Offline isolation evidence](verification/2026-10-19-reviewer-isolation.md) and fake-CLI tests do not prove an account/model review or observed network denial. Native acceptance remains a separate [manual smoke leg](smoke.md#reviewer-pool-native-legs-519-2026-10-19); existing #223 Claude behavior has no new live proof here. Kimi's authenticated fresh-verdict receipts, and its still-open interactive-TUI coexistence and usage checks, are tracked in [Kimi smoke legs](smoke.md#kimi-reviewer-and-task-bound-mcp-533).
|
|
4151
4244
|
|
|
@@ -4192,7 +4285,7 @@ For `gh release create` and `npm publish`, use the exact literal command after `
|
|
|
4192
4285
|
|
|
4193
4286
|
Use `ahub sdlc train report <release-id>` for scope readiness and recorded release metrics. The planner creates future release rows, activates one train, and assigns tasks through the release-only hub tools. A due date requests a cut review; it never clears unfinished work. Move missed tasks to the next row with a reason, then use `ahub sdlc train cut <release-id> <revision>` from the person's terminal or `hub_release_cut` as the planner. The cut retains a short host admission lease through freeze and defers on held gate slots or unknown/quota-limited peer windows with a real quota source. Accounts without a quota reader (Kimi included when `kimi_tokens_5h` is zero) report `no quota source`; manual and budget-record pauses still defer. It does not run release preparation or publish anything. Schedule bundle/check/docs work under separate heavy admission and retain the person's native verification, authorization and upgrade window. A hotfix records its enumerated cause and keeps every gate.
|
|
4194
4287
|
|
|
4195
|
-
The per-release "gates run" record is
|
|
4288
|
+
The per-release "gates run" record is not built: S7 shipped task metrics and bands without it, and release metrics with the gates-run record are the next slice. Person ruling 2026-10-19 supersedes the earlier S11 upgrade-admission ruling: upgrades use pre-S11 recovery readiness (no busy peers, pending approvals resolved, completion checks settled) and retain the existing operation checks. S11 adds no host-admission, quota or all-idle requirement to upgrade. An active/frozen configured train still requires recovery-target `release_trains` compatibility. Release-cut admission remains unchanged: follow its refusal's applicable exit by stopping or resuming the paused peer, adjusting `ahub budget set`, or using `ahub release drop`. Missing/shadowed `dot` requires the resource controller or a PATH correction for the cut; nested admission requires retrying the cut outside the outer lease.
|
|
4196
4289
|
|
|
4197
4290
|
|
|
4198
4291
|
## Codex cancellation identity notice
|
|
@@ -4202,10 +4295,21 @@ could not identify a cancelled native turn. Reattach the native session before
|
|
|
4202
4295
|
further hub delivery. A late completion keeps this hold; verified native
|
|
4203
4296
|
reattachment or TUI detachment clears it. The notice contains fixed guidance, without native ids.
|
|
4204
4297
|
|
|
4205
|
-
## Roles per SDLC stage (#525
|
|
4298
|
+
## Roles per SDLC stage (#525)
|
|
4299
|
+
|
|
4300
|
+
The design for planner, implementer and reviewer instances per stage is in the spec section "Amendment: roles per SDLC stage (#525)". Slice 525-1 is available: role capability per kind and form, Claude channel instances as implementer or verifier in their own worktree, and `planner` refused on builtin kinds that cannot plan (pi, local and agy): an instance declaring it is refused with `this kind cannot plan`, and a `roles` entry keeps its other roles with the notice `roles.<peer>: ignored roles outside the adapter ceiling`. A configured ACP agent (`adapters`) is different: it may hold `planner` in its `roles` or in a `roles` entry and then plans like any peer, but it can never be pinned with the `planner` setting or chosen as the automatic planner, because it has no host projection (#735). Slices 525-3, 525-4 and 525-5 are available too:
|
|
4301
|
+
|
|
4302
|
+
- 525-3: `assign()` walks the planner holders (rotation by task id over every holder, never renumbered by availability; a named planner exact; a recorded planner kept; none for a PII task) and `ahub route explain <id>` prints that walk. Nothing records the planner yet, so the walk is information only. A peer that accepted or finished a task never reviews it, a named reviewer and the declared reviewer pool included; when no other candidate is left the person reviews.
|
|
4303
|
+
- 525-4: a headless Claude implementer runs one owned child per turn in its declared worktree, with a separate persisted session per task (`start_mode: "headless"`, permission `ask` or `ask-when-needed`; `never-ask` refused).
|
|
4304
|
+
- 525-5: terminal Claude implementer instances own write-class tasks once the daemon has verified their execution root.
|
|
4206
4305
|
|
|
4207
|
-
The
|
|
4306
|
+
The operator steps for 525-1, 525-4 and 525-5 are in "Additional headless peer instances" above; each later slice (525-2, 525-6) adds its own here when it ships, and until then those instance rules are current. The planned changes an operator will see:
|
|
4208
4307
|
|
|
4209
|
-
- A Claude instance with `["implementer"]` and a `worktree`, as a terminal instance (started with `ahub claude --as <id>`) or as `start_mode: "headless"` (`permission_mode` `ask` or `ask-when-needed`; `never-ask` refused).
|
|
4210
4308
|
- The `planner` setting accepts a list of peers or instance ids; each task records its own planner, and only that planner or the person runs planner tools on it.
|
|
4211
|
-
- A
|
|
4309
|
+
- A plan's author does not record its own plan review while another planner is available.
|
|
4310
|
+
|
|
4311
|
+
## Daemon responsiveness and launch connects (#719)
|
|
4312
|
+
|
|
4313
|
+
Periodic peer-roster and delivery-supervision reads select pending journal metadata/rows, excluding completed payload history. Queue revision reads do not construct a recovery snapshot. Explicit queue/history commands retain terminal history. The pending-state index supports these reads without sorting historical payloads.
|
|
4314
|
+
|
|
4315
|
+
Native launcher connections retry transport timeouts/unclean closes with bounded backoff for at most 15 seconds, printing `hub busy, retrying` once. The ordinary control-connect timeout remains 3 seconds. An immediately unreachable listener, authentication refusals, wire mismatch and project/instance mismatch are terminal. Every retry rereads the descriptor and preserves the invocation's delegated-audit instance pin. Usage queries retain their existing catch-up budgets and report `behind` until complete; time slicing never advances beyond the last atomically folded record.
|
package/docs/profiles.md
CHANGED
|
@@ -49,6 +49,7 @@
|
|
|
49
49
|
|
|
50
50
|
# Project profile overrides
|
|
51
51
|
|
|
52
|
+
- Acceptance criteria are profile-independent (#507b): a task's recorded criteria checklist (docs/sdlc.md, Acceptance-criteria checklist) is listed in every profile's review request, ahead of the profile's `review_checklist` lines, and an unmet item that starts with a criterion id names it; a profile only changes the wording of its checklist, never the criteria or their ids.
|
|
52
53
|
- `profile_overrides` in shared/local config selects project data over the chosen shipped profile. `classes`, `role_text` and `catalog_roles` maps merge by key across layers; `plan_keys`, `agents`, `example_checks` and `review_checklist` replace as units. A local `profile` selects the active base for both init and native contracts.
|
|
53
54
|
- Classes may add or remap profile aliases to existing base classes. Existing base names keep their meanings; class maps are bounded to 32 override entries. Task storage and route explanations preserve the alias, including a dev custom alias, while routing/budgets/checks use its base. Other tasks under custom dev data retain the selected profile semantics too, so document sections do not become whole-path overlaps. Unmodified dev rows retain their old shape.
|
|
54
55
|
- `plan_keys` selects distinct existing plan fields, not new board fields. Task proposals and their schemas use the selection. Accept schemas retain legacy fields for already existing tasks/profile changes.
|