@staix/agent-hub 0.12.43 → 0.12.45
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 +38 -3
- package/README.md +3 -3
- package/THIRD_PARTY_NOTICES.md +29 -0
- package/docs/adding-an-ai-tool.md +228 -0
- package/docs/agent-notes/README.md +14 -0
- package/docs/agent-notes/adapters.md +61 -42
- package/docs/agent-notes/budget.md +2 -2
- package/docs/agent-notes/daemon.md +16 -10
- package/docs/agent-notes/local-worker.md +4 -3
- package/docs/agent-notes/tasks.md +13 -6
- package/docs/agent-notes/tests.md +9 -7
- package/docs/operations.md +163 -12
- package/docs/quickstart.md +10 -2
- package/docs/sdlc.md +26 -8
- package/docs/security.md +11 -6
- package/docs/specs/2026-09-19-agent-hub-design.md +273 -8
- package/docs/verification/2026-10-21-0.12.44.md +54 -0
- package/docs/verification/2026-10-21-469-r1-dashboard-tokens.md +43 -0
- package/docs/verification/2026-10-21-469-r2-dashboard-shell.md +54 -0
- package/docs/verification/2026-10-21-469-r3-dashboard-board.md +51 -0
- package/docs/verification/2026-10-21-469-r4-dashboard-views.md +77 -0
- package/docs/verification/2026-10-21-469-r6-dashboard-motion-states.md +82 -0
- package/docs/verification/2026-10-21-grok-config-acp-t0.md +30 -0
- package/docs/verification/2026-10-22-0.12.45.md +60 -0
- package/docs/verification/assets/469-r1-board-1440-dark.png +0 -0
- package/docs/verification/assets/469-r1-board-1440-light.png +0 -0
- package/docs/verification/assets/469-r1-board-390-dark.png +0 -0
- package/docs/verification/assets/469-r1-board-390-light.png +0 -0
- package/docs/verification/assets/469-r2-agents-1440-dark.png +0 -0
- package/docs/verification/assets/469-r2-agents-768-dark.png +0 -0
- package/docs/verification/assets/469-r2-board-1024-light.png +0 -0
- package/docs/verification/assets/469-r2-board-1440-dark.png +0 -0
- package/docs/verification/assets/469-r2-board-1440-light.png +0 -0
- package/docs/verification/assets/469-r2-board-390-dark.png +0 -0
- package/docs/verification/assets/469-r2-board-390-light.png +0 -0
- package/docs/verification/assets/469-r2-board-768-light.png +0 -0
- package/docs/verification/assets/469-r3-board-1440-dark.png +0 -0
- package/docs/verification/assets/469-r3-board-1440-light.png +0 -0
- package/docs/verification/assets/469-r3-board-390-light.png +0 -0
- package/docs/verification/assets/469-r3-detail-1440-dark.png +0 -0
- package/docs/verification/assets/469-r3-detail-1440-light.png +0 -0
- package/docs/verification/assets/469-r3-detail-390-dark.png +0 -0
- package/docs/verification/assets/469-r3-plan-1440-light.png +0 -0
- package/docs/verification/assets/469-r3-plan-390-light.png +0 -0
- package/docs/verification/assets/469-r4-agents-1440-dark.png +0 -0
- package/docs/verification/assets/469-r4-agents-1440-light.png +0 -0
- package/docs/verification/assets/469-r4-agents-390-dark.png +0 -0
- package/docs/verification/assets/469-r4-memory-1440-dark.png +0 -0
- package/docs/verification/assets/469-r4-memory-1440-light.png +0 -0
- package/docs/verification/assets/469-r4-memory-390-dark.png +0 -0
- package/docs/verification/assets/469-r4-message-1440-light.png +0 -0
- package/docs/verification/assets/469-r4-office-1440-dark.png +0 -0
- package/docs/verification/assets/469-r4-office-1440-light.png +0 -0
- package/docs/verification/assets/469-r4-settings-1440-dark.png +0 -0
- package/docs/verification/assets/469-r4-settings-1440-light.png +0 -0
- package/docs/verification/assets/469-r4-stream-1440-light.png +0 -0
- package/docs/verification/assets/469-r4-stream-390-light.png +0 -0
- package/docs/verification/assets/469-r6-agents-empty-390-dark.png +0 -0
- package/docs/verification/assets/469-r6-agents-empty-390-light.png +0 -0
- package/docs/verification/assets/469-r6-board-1440-light.png +0 -0
- package/docs/verification/assets/469-r6-board-390-dark.png +0 -0
- package/docs/verification/assets/469-r6-board-empty-1440-dark.png +0 -0
- package/docs/verification/assets/469-r6-board-empty-1440-light.png +0 -0
- package/docs/verification/assets/469-r6-disconnected-1440-dark.png +0 -0
- package/docs/verification/assets/469-r6-disconnected-1440-light.png +0 -0
- package/docs/verification/assets/469-r6-disconnected-390-dark.png +0 -0
- package/docs/verification/assets/469-r6-skeleton-1440-light.png +0 -0
- package/docs/verification/assets/469-r6-skeleton-390-dark.png +0 -0
- package/docs/verification/assets/469-r6-toasts-1440-dark.png +0 -0
- package/docs/verification/assets/469-r6-toasts-1440-light.png +0 -0
- package/docs/verification/assets/469-r6-toasts-390-light.png +0 -0
- package/docs/verified.json +38 -20
- package/package.json +5 -1
- package/plugins/agent-hub/.claude-plugin/plugin.json +1 -1
- package/plugins/agent-hub/server.js +10287 -9560
- package/src/adapters/acp-owned-group.ts +124 -0
- package/src/adapters/acp.ts +92 -25
- package/src/adapters/adapter-api.ts +2 -0
- package/src/adapters/agy/definition.ts +4 -4
- package/src/adapters/agy/entry.ts +4 -1
- package/src/adapters/channel-peer.ts +143 -0
- package/src/adapters/claude-channel.ts +21 -12
- package/src/adapters/codex-appserver.ts +17 -2
- package/src/adapters/configured-acp.ts +124 -0
- package/src/adapters/conformance/acp.ts +114 -0
- package/src/adapters/conformance/codex.ts +191 -0
- package/src/adapters/conformance/enrollment.ts +43 -0
- package/src/adapters/conformance/factories.ts +17 -0
- package/src/adapters/conformance/fake-paths.ts +12 -0
- package/src/adapters/conformance/fakes/acp-server.ts +267 -0
- package/src/adapters/conformance/fakes/acp-watchdog-agent.ts +10 -0
- package/src/adapters/conformance/fakes/app-server.ts +119 -0
- package/src/adapters/conformance/fakes/codex-bin.ts +15 -0
- package/src/adapters/conformance/fakes/jcode-agent.ts +91 -0
- package/src/adapters/conformance/fakes/model-server.ts +39 -0
- package/src/adapters/conformance/fakes/pi-rpc.ts +32 -0
- package/src/adapters/conformance/index.ts +11 -0
- package/src/adapters/conformance/kit.ts +50 -0
- package/src/adapters/conformance/local-pi.ts +100 -0
- package/src/adapters/conformance/owned-acp.ts +44 -0
- package/src/adapters/definitions/builtin-metadata.ts +60 -39
- package/src/adapters/definitions/native-channel-state.ts +41 -0
- package/src/adapters/definitions/native-cli-entry-kimi.ts +9 -1
- package/src/adapters/definitions/native-cli-entry.ts +7 -19
- package/src/adapters/definitions/native-cli-preview-qwen.ts +1 -1
- package/src/adapters/definitions/native-cli-preview.ts +2 -11
- package/src/adapters/definitions/native-identity-defaults.ts +14 -0
- package/src/adapters/definitions/qwen.ts +7 -9
- package/src/adapters/definitions/reviewer-variants.ts +3 -7
- package/src/adapters/external-metadata.ts +48 -0
- package/src/adapters/external-module-scan.ts +126 -0
- package/src/adapters/external-modules.ts +240 -0
- package/src/adapters/jcode.ts +4 -0
- package/src/adapters/local-worker.ts +4 -0
- package/src/adapters/pi.ts +6 -0
- package/src/adapters/public.ts +7 -0
- package/src/adapters/registry-types.ts +28 -16
- package/src/adapters/registry.ts +115 -17
- package/src/adapters/reviewer-print.ts +4 -0
- package/src/cli/agy-mcp.ts +3 -2
- package/src/cli/configured-acp-entry.ts +58 -0
- package/src/cli/console-state.ts +9 -4
- package/src/cli/facts-hook.ts +2 -1
- package/src/cli/help.ts +27 -16
- package/src/cli/identity.ts +8 -7
- package/src/cli/launch.ts +5 -4
- package/src/cli/main.ts +101 -32
- package/src/cli/memory.ts +13 -10
- package/src/cli/output.ts +1 -0
- package/src/cli/preview.ts +9 -0
- package/src/cli/recovery-guidance.ts +1 -1
- package/src/cli/terminal-recovery.ts +16 -13
- package/src/cli/upgrade-labels.ts +1 -1
- package/src/cli/upgrade-presentation.ts +1 -1
- package/src/cli/upgrade-restore-state.ts +6 -4
- package/src/cli/upgrade-runtime.ts +25 -7
- package/src/cli/upgrade.ts +3 -2
- package/src/hub/board.ts +37 -8
- package/src/hub/budget.ts +9 -2
- package/src/hub/bus.ts +6 -2
- package/src/hub/claude-state.ts +2 -37
- package/src/hub/conductor.ts +8 -6
- package/src/hub/config-apply.ts +37 -17
- package/src/hub/config-reload.ts +42 -11
- package/src/hub/config-trust.ts +1 -1
- package/src/hub/context-window.ts +6 -0
- package/src/hub/control-client.ts +4 -1
- package/src/hub/crash.ts +8 -6
- package/src/hub/daemon.ts +412 -328
- package/src/hub/facts.ts +76 -13
- package/src/hub/hub-tools.ts +57 -5
- package/src/hub/instance-config.ts +33 -12
- package/src/hub/instance-execution.ts +23 -11
- package/src/hub/instances.ts +47 -22
- package/src/hub/lifecycle-schema.ts +5 -3
- package/src/hub/memory-profile.ts +16 -3
- package/src/hub/no-writes.ts +50 -0
- package/src/hub/peers.ts +9 -0
- package/src/hub/permission-mode.ts +9 -7
- package/src/hub/plan-conformance.ts +83 -0
- package/src/hub/review-findings.ts +104 -0
- package/src/hub/roster.ts +35 -6
- package/src/hub/routing.ts +21 -20
- package/src/hub/sdlc-queue.ts +19 -4
- package/src/hub/sdlc-release.ts +16 -1
- package/src/hub/sdlc.ts +27 -12
- package/src/hub/settings.ts +54 -16
- package/src/hub/snapshots.ts +32 -6
- package/src/hub/start-mode.ts +10 -8
- package/src/hub/task-sweep.ts +8 -2
- package/src/hub/tasks.ts +358 -60
- package/src/hub/ui.ts +2 -2
- package/src/memory/capture.ts +2 -1
- package/src/ui/index.html +563 -77
- package/src/ui/office-model.ts +3 -1
- package/src/ui/peer-palette.ts +44 -0
- package/templates/AGENTS.block.md +1 -1
- package/templates/profiles/general.json +1 -1
- package/templates/profiles/research.json +1 -1
- package/templates/profiles/writing.json +1 -1
|
@@ -1,9 +1,11 @@
|
|
|
1
1
|
# Tests
|
|
2
2
|
|
|
3
|
-
Scope: tests, fakes and the test gate. Read before adding or changing a test or fake under `test/`, or editing `scripts/check.sh`, `scripts/hang-watch.sh`, `scripts/seeded-check.ts` or `scripts/seeds.json`.
|
|
3
|
+
Scope: tests, fakes and the test gate. Read before adding or changing a test or fake under `test/` or `src/adapters/conformance/`, or editing `scripts/check.sh`, `scripts/hang-watch.sh`, `scripts/seeded-check.ts` or `scripts/seeds.json`.
|
|
4
4
|
|
|
5
|
+
- A fake's gated action that never sees its gate skips the action; never fall through to a late, unsynchronized action. Owned-group tests inject `AcpOwnedGroupPorts`; a test never signals a process group it did not spawn.
|
|
6
|
+
- When extracting production closures into a VM fixture, bind every referenced host port and actual imported policy helper. Keep native identity checks on real unstarted instances; never replace a policy predicate with an always-true stub or weaken existing assertions to accommodate a moved dependency.
|
|
5
7
|
- Tests that are not about batching build the bus with `batchMs: 0`; with the default 15 s window a lone status envelope looks like a lost message.
|
|
6
|
-
-
|
|
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.
|
|
7
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`).
|
|
8
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.
|
|
9
11
|
- `bunfig.toml` preloads `test/hub-home.ts`, which first clears inherited `AMEM_HOME` and gives every test run (the gate and `bun test <file>` alike) a temp `AGENTHUB_HOME` unless one is set; scoped tests may set their own `AMEM_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.
|
|
@@ -12,19 +14,19 @@ Scope: tests, fakes and the test gate. Read before adding or changing a test or
|
|
|
12
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.
|
|
13
15
|
- Cross-process fixtures wait for the consuming client to process its handshake (the channel connected diagnostic), not merely the daemon's attached/idle state. Deadline-routing fixtures hold and release responses at the fallback decision; do not place a fixed sleep between deadline fractions and assume scheduler headroom (#287). Receipt-order fixtures use the normal watchdog for ordinary I/O, trigger expiry after observed native acceptance, and await a consumed transport barrier before asserting on late results.
|
|
14
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).
|
|
15
|
-
- 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 wait on concurrent-edit evidence correlates the expected turn ids and paths (#617).
|
|
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).
|
|
16
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.
|
|
17
|
-
- 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. 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. 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).
|
|
18
20
|
|
|
19
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.
|
|
20
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.
|
|
21
|
-
- The whole-suite watchdog is `AHUB_CHECK_HANG_S` (default in `scripts/check.sh`, hosted values for both OSes in `.github/workflows/check.yml`; #378
|
|
23
|
+
- The whole-suite watchdog is `AHUB_CHECK_HANG_S` (default in `scripts/check.sh`, hosted values for both OSes in `.github/workflows/check.yml`; #378, #593): raise it only when the suite was still progressing at the cutoff, never for a hung test, and keep the per-test 20 s timeout. The check job's `timeout-minutes` must leave room above that value for setup: raise them together.
|
|
22
24
|
- A seed's named test must run on Linux, where the seeded job runs: a darwin-only test reports no result there and fails the job (#382). Cover a macOS-only protection with its regression test instead.
|
|
23
25
|
|
|
24
26
|
- 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.
|
|
25
27
|
- 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.
|
|
26
|
-
- `test/conformance/` runs real adapters against fake transports. Record a case that does not apply
|
|
28
|
+
- `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 `:reviewer` 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.
|
|
27
29
|
|
|
28
|
-
- 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. CI reuse tests use fixed metadata fixtures and never dispatch real duplicate workflows.
|
|
30
|
+
- 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.
|
|
29
31
|
|
|
30
32
|
- 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`).
|
package/docs/operations.md
CHANGED
|
@@ -635,6 +635,79 @@ Private envelopes and PII tasks stay redacted. Local-worker and Pi tool details
|
|
|
635
635
|
allow decisions stay in `ahub tail` / `ahub permit`; the page can deny them.
|
|
636
636
|
No dashboard port is opened until requested. See [the session security model](security.md#local-dashboard-sessions-issue-6).
|
|
637
637
|
|
|
638
|
+
### Dashboard design tokens (#469 R1)
|
|
639
|
+
|
|
640
|
+
The page's style block starts with one token block that owns every colour, the 4px spacing scale (4, 8, 12, 16, 24, 32), the
|
|
641
|
+
type scale (12, 13, 14, 16, 20) and the radius rule: 6px for controls, 10px for surfaces (panels, cards, the dialog), full
|
|
642
|
+
radius for chips and avatars. Neutrals are one cool family with three surface levels (page, panel, raised) and one accent for
|
|
643
|
+
primary actions, focus and selection; the state colours keep their meaning. The dark values are written twice, once for
|
|
644
|
+
`data-theme="dark"` and once for the system preference, and a test keeps the two identical. Two shadows exist for raised
|
|
645
|
+
elements only (`--shadow-2` is the dialog's; `--shadow-1` waits for the board cards in R3). Each peer colour has a light and a
|
|
646
|
+
dark variant of one hue, generated from the registry's colour by `peerPaletteThemeCss` (`src/ui/peer-palette.ts`) so it clears
|
|
647
|
+
3:1 on every surface. System fonts only. The layout, the views, the keys and the behaviour are unchanged by R1; the contrast
|
|
648
|
+
test (`test/office-view.test.ts`) covers the text pairs, the accent, the dots, disabled controls and every peer colour in both
|
|
649
|
+
themes. The Office scene keeps its own rules until R5.
|
|
650
|
+
|
|
651
|
+
### Dashboard app shell and keys (#469 R2)
|
|
652
|
+
|
|
653
|
+
From 1024px up, a sticky top bar holds the project context, the connection status (a coloured dot and its text, green when live
|
|
654
|
+
and red when disconnected or closed), the attention badge, the theme switch and a shortcuts button; the seven views sit in a
|
|
655
|
+
left sidebar with an icon and a label, and the approval count still rides on Agents. Below 1024px the same nav is one row of
|
|
656
|
+
tabs that scrolls sideways inside itself, so the page never scrolls sideways. The view names, their order, the
|
|
657
|
+
`agent-hub-view` cookie and every id are unchanged.
|
|
658
|
+
|
|
659
|
+
Keys: `1` to `7` switch to Board, Office, Stream, Agents, Message, Memory and Settings, `?` opens the key list, and `Esc` closes
|
|
660
|
+
the key list or the task detail (both are native dialogs). View keys are ignored while you type in an input, a textarea or a
|
|
661
|
+
select, with Ctrl, Alt or Cmd held, and while a dialog is open. Icons come from one inline SVG sprite of nine Tabler icons;
|
|
662
|
+
the MIT notice is in `THIRD_PARTY_NOTICES.md`. Nothing is fetched and no package is added.
|
|
663
|
+
|
|
664
|
+
### Dashboard board, side sheets and the Plan sheet (#469 R3)
|
|
665
|
+
|
|
666
|
+
A board card shows the title (two lines at most), the owner as a round avatar in the peer colour with its initial, one badge that
|
|
667
|
+
holds the state and the priority ("in review P1") and the release when there is one. Everything else moved to the task detail,
|
|
668
|
+
which keeps the same dialog and opens as a sheet pinned to the right edge (full screen below 600px). The detail groups its
|
|
669
|
+
lines into Status, People, Dates and Links, and leaves out a group with nothing in it. Pull, Assign, the Ready and Waiting
|
|
670
|
+
subheads, "Show N older" and drag and drop are unchanged.
|
|
671
|
+
|
|
672
|
+
The five board forms (plan a task, split a backlog task, releases, delivery evidence, propose a task) are in a Plan sheet, opened by
|
|
673
|
+
the Plan button in the board toolbar. It is a native dialog, so `Esc` closes it, and view keys do nothing while it is open. The
|
|
674
|
+
fields, ids and actions are the ones the forms had. The sheets slide in on `transform` and `opacity` and not at all when the
|
|
675
|
+
system asks for reduced motion.
|
|
676
|
+
|
|
677
|
+
### Dashboard Agents, Stream, Memory, Settings, Message and the Office colours (#469 R4, R5)
|
|
678
|
+
|
|
679
|
+
Agents: a peer is one row with its name and state, a summary line (queued messages and "Working on #n" or "No active task") and a
|
|
680
|
+
single "Details" fold that holds the diagnostic lines it used to list. A status notice stays visible outside the fold. Approvals
|
|
681
|
+
stay first. Stream: one aligned row per event (time, kind, sender, body in monospace); the filter chips and Follow latest sit in
|
|
682
|
+
the section head, above the scrolling list. Memory: result rows show title, kind and age, the selected row is marked with an accent
|
|
683
|
+
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
|
|
684
|
+
for a restart carries a "restart pending" badge, and "Pending restart" shows a count; the text lines, Preview, Save and Undo are as
|
|
685
|
+
they were. Message uses the shared form styles.
|
|
686
|
+
|
|
687
|
+
Icons replace the symbol glyphs: the star mark, the stream arrow and the Office "checking" mark are sprite icons (twelve Tabler icons
|
|
688
|
+
in all), and the Office draws each condition (idle, busy, offline, paused, waiting on approval) as a shape (ring, disc, cross, bars, raised hand)
|
|
689
|
+
next to its words. Condition names are unchanged. The Office scene names its colours only through the same tokens as the rest of the page, and the
|
|
690
|
+
contrast test checks every token an Office rule uses, in both themes.
|
|
691
|
+
|
|
692
|
+
### Dashboard keyed rendering, motion and states (#469 R6)
|
|
693
|
+
|
|
694
|
+
The page still polls `/snapshot` once a second and still skips a redraw when a container's JSON did not change. What changed is how
|
|
695
|
+
a changed container is redrawn: task cards, peer rows and memory rows are kept per id. A card, row or memory entry whose own data
|
|
696
|
+
did not change keeps its DOM node, so hover, an open "Details" fold and a focused card survive a poll that changed something
|
|
697
|
+
else. One that changed is replaced by a fresh node, and if it held focus the focus moves to the replacement. A card that moves to
|
|
698
|
+
another column fades in; one that disappears is simply removed.
|
|
699
|
+
|
|
700
|
+
Motion moves `transform` and `opacity` only, 120 to 200 ms, ease-out: button press, a short fade when you switch views, a card
|
|
701
|
+
arriving in a column, the toast, and the side sheets. The Office keeps its own motion. Nothing new loops. With the system setting
|
|
702
|
+
"reduce motion" every transition and animation in the page is off.
|
|
703
|
+
|
|
704
|
+
States: until the first snapshot arrives, after a project switch too, the board and the peer list show grey blocks shaped like them. Each empty view says what
|
|
705
|
+
fills it and the command that does it (`ahub task propose`, `ahub claude` or `ahub codex`, `ahub say`, `ahub permit`, `ahub
|
|
706
|
+
remember`, `ahub goal new`). When a poll fails the connection status turns red, the content dims to 60 percent but stays readable,
|
|
707
|
+
and a line at the top gives the time of the last snapshot and says polling continues. Notices are a stack of up to four toasts in
|
|
708
|
+
the bottom corner (`aria-live="polite"`): a success fades after 4 seconds (and stays under reduce motion), an error stays until
|
|
709
|
+
you press Dismiss.
|
|
710
|
+
|
|
638
711
|
## Multiple projects and worktrees
|
|
639
712
|
|
|
640
713
|
Version 0.4.0 adds one independent hub per repository or Git worktree.
|
|
@@ -1301,7 +1374,10 @@ stays the default until an evaluation says otherwise (`docs/cooperbench.md`).
|
|
|
1301
1374
|
file that falls out of the 64 a peer touched is named until the peer reads
|
|
1302
1375
|
that back. Only regular files
|
|
1303
1376
|
of 256 KB or less inside the project are read, re-resolved at every read and
|
|
1304
|
-
opened without following links; larger ones are named without a diff
|
|
1377
|
+
opened without following links; larger ones are named without a diff, compared
|
|
1378
|
+
by size and modification time, or by their bytes while written in the last 2 s
|
|
1379
|
+
(4 MiB or less each, 16 MiB per store per event-loop turn), since a same-size
|
|
1380
|
+
rewrite within one filesystem timestamp tick keeps the stat. Facts never go through the bus or the delivery journal;
|
|
1305
1381
|
`events.jsonl` records `fact`, `fact_ack` and `capability` events with bytes
|
|
1306
1382
|
and latencies.
|
|
1307
1383
|
- Integration. A member's `hub_task_done` is a completion intent. The member
|
|
@@ -1588,7 +1664,7 @@ A guarded file edit that the daemon keeps at its accepted value remains visible
|
|
|
1588
1664
|
| Routing | `routing.stay_switch`, `routing.classes.<class>.peers`, `.escalate_to`, `.route`, `.pi_backend` | `routing.local.toml` | at once |
|
|
1589
1665
|
| Switches | `research.enabled`, `approvals.notify`, `snapshots.enabled` | `config.local.json` | live, without disturbing an active turn |
|
|
1590
1666
|
| Switches | `coordination` | `config.local.json` | newly created tasks only |
|
|
1591
|
-
| Switches | `task_sweep.enabled`, `.auto_reassign`, `.interval_s`, `.unaccepted_min`, `.idle_min`, `.review_min`, `.ready_min`, `.ladder_min` | `config.local.json` | live;
|
|
1667
|
+
| Switches | `task_sweep.enabled`, `.auto_reassign`, `.interval_s`, `.unaccepted_min`, `.idle_min`, `.review_min`, `.ready_min`, `.ladder_min`, `.no_writes_turns` | `config.local.json` | live; `.no_writes_turns` sets the turn threshold independently; other keys reschedule the sweep timer |
|
|
1592
1668
|
| Routing | `tasks.wip_limits.<peer>` | `config.local.json` | live |
|
|
1593
1669
|
| Routing | `tasks.escalate_after` (1-10, default 3: consecutive `changes_requested` before a task moves to the next `escalate_to` peer) | `config.local.json` | live; the next verdict uses it |
|
|
1594
1670
|
| Roles | `planner` (the one peer that plans) | `config.local.json` | at once |
|
|
@@ -1690,6 +1766,15 @@ A peer that has a TUI the hub attaches to starts in it by default (issue #269).
|
|
|
1690
1766
|
never downgraded to headless. `ahub settings` lists each start mode, and the
|
|
1691
1767
|
dashboard's Start control says which mode a start will use and where its
|
|
1692
1768
|
terminal comes from.
|
|
1769
|
+
- **A headless start gets a watch pane** (#286 AC10): when the hub itself
|
|
1770
|
+
starts Kimi or Qwen headless (Kimi when its start mode is `headless`), it
|
|
1771
|
+
also opens `ahub watch <peer>` through the same provider, a fixed command
|
|
1772
|
+
with no caller text, at most once per 30 seconds per peer. The headless
|
|
1773
|
+
start has already succeeded, so with no provider, or one that fails, the
|
|
1774
|
+
start still stands and its result and the hub's notice give the command to
|
|
1775
|
+
run by hand. A start in TUI mode opens the peer's own terminal instead, as
|
|
1776
|
+
above; an already attached peer, a declared extra instance and a peer
|
|
1777
|
+
without the watch capability open no pane.
|
|
1693
1778
|
- **Dashboard Start:** the control is locked while its request is in flight. A confirmed answer unlocks it immediately, so a retry reaches the hub's own 30-second terminal reopen guard and shows any known refusal. The unified dashboard waits 35 seconds, covering a terminal provider's 30-second cap plus a 5-second response margin; a slower headless start can still end unconfirmed while the hub continues. Check the Peers panel before retrying. An unconfirmed unified-dashboard start holds that peer's control for the shared 30-second reopen bound, or until its `starts` snapshot confirms attachment, across redraws and project switches within the same hub instance; the row states the retry bound. The project-local dashboard keeps its usual confirmed-answer behavior. Other forwarded actions keep their 10-second wait; the manager opens no settings session.
|
|
1694
1779
|
- The conductor cannot choose the mode: `hub_peer_start` takes the peer's own.
|
|
1695
1780
|
- Recovery restores each peer in the mode its recorded session had. After an
|
|
@@ -1705,7 +1790,9 @@ entries and fields. It contains machine paths and the peer identity, not a token
|
|
|
1705
1790
|
with a warning. `--print-command` shows the change and command without writing.
|
|
1706
1791
|
The configured `kimi_cmd` must be a single executable or end in exactly `acp`;
|
|
1707
1792
|
the TUI uses the executable or drops that final word. Other wrappers are refused
|
|
1708
|
-
with the headless command rather than guessed.
|
|
1793
|
+
with the headless command rather than guessed. The launcher uses the running
|
|
1794
|
+
hub's `kimi_cmd`; when that value has no TUI form, the refusal says so, since an
|
|
1795
|
+
edited file value waits for `ahub settings reload`.
|
|
1709
1796
|
|
|
1710
1797
|
The TUI is pull-only. Its model reads `hub_inbox` at the start of work, between
|
|
1711
1798
|
steps of longer work and before reporting, and shares results, and mid-work
|
|
@@ -1925,7 +2012,7 @@ bridge currently receives no positive per-server activation signal.
|
|
|
1925
2012
|
|
|
1926
2013
|
A Claude session started with `ahub claude` receives pushes when it is idle. While it is busy:
|
|
1927
2014
|
|
|
1928
|
-
- Every hub tool result carries fresh counts, `(N queued hub message(s), M important hub message(s) waiting: call hub_inbox before starting long local work)`, when important work waits. A zero count clears an earlier hint.
|
|
2015
|
+
- Every hub tool result carries fresh counts, `(N queued hub message(s), M important hub message(s) waiting: call hub_inbox before starting long local work)`, when important work waits. A zero count clears an earlier hint. Under a delivery hold that refuses pulls, the counts and held rows stay but the `hub_inbox` advice is dropped.
|
|
1929
2016
|
- `hub_inbox` reads the daemon queue (and any local failed-push buffer) in a channel session too. A read is one `completed` delivery, like a tools-only read; holds refuse it the same way.
|
|
1930
2017
|
- An important message is pushed into the running turn (steered) instead of waiting for the turn to end. Mid-turn reading was verified on Claude Code 2.1.302; an older client that drops it leaves the delivery `accepted` until a person resolves it. Ordinary messages still wait for the idle push or `hub_inbox`.
|
|
1931
2018
|
- Do not use `[FYI]` for PR ready, done, submitted, ruling-needed, blocker or changed-contract messages: they need action. `hub_task_done` and `hub_review` already notify their recipients.
|
|
@@ -2899,6 +2986,8 @@ The daemon advertises `task_cancel`; a current CLI refuses cancellation against
|
|
|
2899
2986
|
|
|
2900
2987
|
## Task idle sweep
|
|
2901
2988
|
|
|
2989
|
+
Writing-owner no-op detection (#498) is independent of the time sweep: `task_sweep.no_writes_turns` defaults to 2, and 0 disables its notices. Known file changes, HEAD/shared-branch ref movement and task-tool effects reset the streak. A canonical snapshot with recorded branch, worktree-path or PR/head evidence is unknown. Negative canonical evidence also needs exclusive worktree scope and no `.worktrees` directory, so uncommitted work in a separate checkout is not reported as empty. Done advisories use the first acceptance in the current ownership span; plan updates do not discard earlier touches.
|
|
2990
|
+
|
|
2902
2991
|
The between-turn task sweep (#186) is disabled by default. Set `task_sweep` in
|
|
2903
2992
|
`.agenthub/config.json` (or its machine-local override) to enable it:
|
|
2904
2993
|
|
|
@@ -3063,7 +3152,7 @@ consumption, so no token total is counted from it; the adapter accepts the
|
|
|
3063
3152
|
update whenever it names the current session, because 2.1.1's source text
|
|
3064
3153
|
emits it after the turn settles (read from the source, not yet observed live
|
|
3065
3154
|
through the hub; docs/smoke.md), so the peer may already be idle.
|
|
3066
|
-
Pi and other unsupported surfaces show unknown.
|
|
3155
|
+
Pi, Qwen (until a native source is verified, #625) and other unsupported surfaces show unknown.
|
|
3067
3156
|
A stale or disconnected reading is unknown, not 0%. Codex's accumulated session
|
|
3068
3157
|
usage is never used as context occupancy. In the tail (`ahub tail` and the
|
|
3069
3158
|
console's stream) a reading prints once and repeats only when the line changes
|
|
@@ -3260,7 +3349,7 @@ that the file is untracked; a dropped value is reported by the settings result.
|
|
|
3260
3349
|
|
|
3261
3350
|
## SDLC artifact receipts and gates
|
|
3262
3351
|
|
|
3263
|
-
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)). S2-S7 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.
|
|
3352
|
+
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-S7 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.
|
|
3264
3353
|
## Optional Qwen Code peer
|
|
3265
3354
|
|
|
3266
3355
|
Select Qwen per project with `ahub roster add qwen`; it defaults off and is absent
|
|
@@ -3281,7 +3370,7 @@ Modes: ask maps to default, scoped frequency maps to auto-edit, never-ask maps t
|
|
|
3281
3370
|
yolo. Qwen yolo approves every tool outside any hub sandbox; its own vendor bounds
|
|
3282
3371
|
apply. Auto and plan are not hub mode mappings. Unknown quota refusals are not
|
|
3283
3372
|
classified as authoritative quota; until a real pinned refusal is recorded, pause
|
|
3284
|
-
by hand.
|
|
3373
|
+
by hand. Context occupancy shows unknown until a native source is verified; it no longer uses Kimi's usage parser (#625). Roles
|
|
3285
3374
|
do not establish that Qwen can reliably replace Kimi before the live leg.
|
|
3286
3375
|
|
|
3287
3376
|
Qwen controlled recovery and automatic crash resume load the recorded conversation with ACP session/load. The emitted launcher passes --session-id; a controlled plan with a missing or unsafe identity refuses before effects. Preview redacts the id. A custom executable argv keeps its wrapper arguments before appended model flags. Native recovery remains unverified.
|
|
@@ -3309,18 +3398,62 @@ Native policy is unmanaged and has NO hub sandbox; no permission_modes.jcode or
|
|
|
3309
3398
|
|
|
3310
3399
|
## Optional Antigravity (agy) peer
|
|
3311
3400
|
|
|
3312
|
-
Antigravity is optional and off roster by default. Use `ahub roster add agy`, then run `ahub agy` in a terminal: it runs the Antigravity CLI as a fresh pull-only TUI in that terminal. It accepts the 1.4.x line: 1.4.2 is the verified version, any other 1.4.x launches with a stderr warning (agy updates itself), and other versions are refused (#589). The running hub must advertise `native_hook_launch`; an older hub refuses before any write with an upgrade-or-restart remedy. Only the person's launcher starts it: conductor starts are refused, there is no controlled restore or resume, and
|
|
3401
|
+
Antigravity is optional and off roster by default. Use `ahub roster add agy`, then run `ahub agy` in a terminal: it runs the Antigravity CLI as a fresh pull-only TUI in that terminal. It accepts the 1.4.x line: 1.4.2 is the verified version, any other 1.4.x launches with a stderr warning (agy updates itself), and other versions are refused (#589). The running hub must advertise `native_hook_launch`; an older hub refuses before any write with an upgrade-or-restart remedy. Only the person's launcher starts it: conductor starts are refused, there is no controlled restore or resume, and a declared agy extra instance is refused when the config loads (#625). Headless/print, resume (`--conversation`, `--continue`, `--new`), `--mode`, `--dangerously-skip-permissions` and `--unattended` are refused until their native mapping is verified; `--print-command` previews without writing.
|
|
3313
3402
|
|
|
3314
3403
|
The launcher writes exactly `plugin.json`, `mcp_config.json` and `hooks.json` in `.agents/plugins/agent-hub/`, refusing linked or foreign entries before any write, and removes only its own matching files when the TUI ends. It never writes project-level `.agents/hooks.json` or the person's `~/.gemini` settings. Run `ahub init` afterwards to ignore the untracked plugin directory; a tracked plugin is written with a warning. The native model sees the hub tools under the server name `agent-hub_agent-hub`.
|
|
3315
3404
|
|
|
3316
3405
|
Like Kimi's TUI, agy is pull-only: it reads `hub_inbox` and shares results with `hub_send`, messages do not interrupt it, and automatic routing never picks it. Its roles are capped at implementer and verifier; a plan or review assignment and PII work are refused. Native permissions are unmanaged, with no hub sandbox, hub permission mode, quota source or session resume. Plugin hooks bound to that launch carry the hub's turn facts and report each completed native turn. A native read is recorded as a peer `Read` fact: `view_file` (`AbsolutePath`) in 1.4.2 and `read_file` (`Path`) in 1.4.3; a `StartLine`/`EndLine` range counts as a partial read, never a whole-file view (#555). The live TUI/account leg is unverified ([T0 receipt](verification/2026-10-19-agy-p1-t0.md)). The 1.4.3 T0 re-run keeps 1.4.3 unverified: its `call_mcp_tool` schema and native tool names changed, and the TUI attaches the hub tools only from the second model turn ([1.4.3 receipt](verification/2026-10-21-agy-1.4.3-t0.md), #607).
|
|
3317
3406
|
|
|
3407
|
+
## Configured ACP adapters (#451 A3)
|
|
3408
|
+
|
|
3409
|
+
A tool that speaks standard ACP over stdio can join a project without a code change. The declaration is machine-local authority: put it only in an untracked `.agenthub/config.local.json` (in `config.json` or a tracked file it is ignored with a notice), then restart the hub. The hub compiles its adapter catalog once at startup, so adding, changing or removing a declaration needs a restart; settings list the change as pending restart and never edit `adapters`.
|
|
3410
|
+
|
|
3411
|
+
{
|
|
3412
|
+
"adapters": {
|
|
3413
|
+
"myagent": {
|
|
3414
|
+
"transport": "acp",
|
|
3415
|
+
"command": "/opt/myagent/bin/myagent",
|
|
3416
|
+
"args": ["acp"],
|
|
3417
|
+
"displayName": "My Agent",
|
|
3418
|
+
"roles": ["implementer", "verifier"],
|
|
3419
|
+
"identity": { "name": "<initialize agentInfo.name>", "version": "<agentInfo.version>" },
|
|
3420
|
+
"permission": { "ask": "<native mode id>", "ask-when-needed": "<native mode id>", "never-ask": "<native mode id>" },
|
|
3421
|
+
"readOnly": "<native read-only mode id>"
|
|
3422
|
+
}
|
|
3423
|
+
}
|
|
3424
|
+
}
|
|
3425
|
+
|
|
3426
|
+
- The kind matches `[a-z][a-z0-9-]`, has at most 32 characters, and may not collide with a builtin peer, an `ahub` command or a reserved id. A project can declare at most 64 kinds. Unknown fields refuse the config.
|
|
3427
|
+
- `roles` is a non-empty subset of `planner`, `implementer`, `verifier` and `reviewer`. `conductor` is refused here; only explicit project role policy can appoint one.
|
|
3428
|
+
- `identity` is the name and version that ACP `initialize` reports. A start without it, or with a mismatch, refuses before any prompt.
|
|
3429
|
+
- `args` may not select a leader, attach, connect or remote mode (`--leader*`, `--attach*`, `--connect*`, `--remote*`): only a foreground process the hub owns is supported. A Grok declaration (kind `grok`, a `grok` executable or identity name `grok`) must also pass a standalone `--no-leader` before any `--`. Grok 1.0.62 itself is not supported ([T0 receipt](verification/2026-10-21-grok-config-acp-t0.md)).
|
|
3430
|
+
- `permission` maps hub modes to native ACP mode ids. Only mapped modes can be selected, and the session must advertise and acknowledge each one. Without a map (or with an empty one) native policy is unmanaged: `ahub permission <kind> <mode>` and `permission_modes.<kind>` are refused. No hub sandbox applies either way.
|
|
3431
|
+
- `readOnly` names the native mode that read-only instances use (below). It is not applied to the kind itself.
|
|
3432
|
+
|
|
3433
|
+
ahub roster add myagent # configured kinds are off roster by default
|
|
3434
|
+
ahub myagent # headless; asks the running hub to start it
|
|
3435
|
+
ahub myagent --session-id <id> # load a recorded native session; the agent must echo the id
|
|
3436
|
+
ahub myagent --print-command # preview: executable, argv and session id are withheld
|
|
3437
|
+
ahub stop myagent
|
|
3438
|
+
|
|
3439
|
+
`ahub <kind>` accepts only `--mode headless`, `--as <instance>`, `--session-id <id>` and one of `--print-command`/`--dry-run`; model, route, unattended and other launch overrides are refused. The running hub must advertise `configured_acp` and list the kind as loaded; otherwise the command tells you to upgrade or restart. If the file changed since the hub started, the start warns and uses the running definition. A conductor may start the kind with `hub_peer_start` (headless, with no session override). There is no TUI form: `peers.<kind>.start_mode` accepts only `headless`, and `ahub watch` is not available. Default routing never picks a configured kind; it gets work through its roles, an explicit owner or reviewer, or a reservation.
|
|
3440
|
+
|
|
3441
|
+
Before admission the hub verifies the native identity, a valid session id, the requested native mode and positive ownership of the spawned process and its process group. A failure refuses the start with a generic reason; the agent's stderr and error text are withheld from the log. `ahub stop` signals only that original verified group. If teardown cannot be confirmed, the peer stays offline but held, and new starts are refused until `ahub stop <kind>` succeeds.
|
|
3442
|
+
|
|
3443
|
+
Extra instances use the existing `instances` block. For example, `"myagent-review": { "kind": "myagent", "roles": ["reviewer"], "readOnly": true }` is started with `ahub myagent --as myagent-review`. Unlike the kind, a declared instance is on the roster by default. `readOnly: true` requires the kind's `readOnly` mode, and the session must advertise and acknowledge that mode before a prompt. Native permission requests other than read, search and think are rejected. The instance is offered only `hub_send`, `hub_inbox`, `hub_delivery_done`, `hub_review`, `hub_task_show`, `hub_task_list`, `hub_memory_search|show|graph` and `hub_status`; the hub refuses every other hub tool. Its permission mode is locked: settings show it as read-only, no write, undo or `ahub permission` changes it, and `permission_mode` next to `readOnly` is refused.
|
|
3444
|
+
|
|
3445
|
+
Configured kinds are cloud peers and never receive PII work. Quota and context are unknown, and a kind with no budget source is exempt from quota accounting, so pause it by hand if its provider refuses. After a crash the hub resumes a recorded session through `session/load` only when the agent echoes the same id, and it never reuses a recorded model. Controlled recovery (`ahub upgrade`, `restart`, `recovery`) refuses a running configured kind: stop it, then start it again with its session id. While the project declares adapters, the upgrade or restart target must advertise `configured_acp`.
|
|
3446
|
+
|
|
3447
|
+
## External adapter modules (#451 A4)
|
|
3448
|
+
|
|
3449
|
+
A bundled adapter module is authorized by the person with `adapter_modules` (at most 16 rows of `path` and `sha256`) in an untracked `.agenthub/config.local.json`; in `config.json` or a tracked file it is ignored with a notice, and settings never edit it. Only daemon startup imports a module: settings, reload, previews and CLI reads never do, so a change needs a restart. The module must be a single self-contained ESM file inside the project (no symlinked, world-writable or oversized file) whose bytes match the pin, and it may import only Node/Bun builtins and `@staix/agent-hub/adapter`; the scanner also refuses any `require`, `eval` or `Function` word token. Each import must settle within 2 s. A module that fails any check is refused on its own, with a bounded reason in the status reply, while startup and the other adapters continue; its roster row stays inert. A loaded kind is headless, cloud (never PII), off roster until `ahub roster add <kind>`, and has no controlled recovery: stop it and start it again with its recorded session id. While modules are declared, the upgrade or restart source and target must advertise `external_adapter_modules`. Bundling, the accepted definition subset and the conformance kit (`@staix/agent-hub/conformance`, for module CI only) are in [Adding an AI tool](adding-an-ai-tool.md#5-external-adapter-modules-451-a4). A pin authorizes code that runs with the daemon's rights; it is not a sandbox ([security](security.md)).
|
|
3450
|
+
|
|
3318
3451
|
# Headless ACP watch (#286)
|
|
3319
3452
|
|
|
3320
3453
|
- A person opens `ahub watch kimi` or `ahub watch qwen` in a second terminal. `--no-thoughts` hides thought items; `--color=auto|always|never` controls colour. `q` or Ctrl+C leaves the view, keeping the peer running. Answer approvals in `ahub console` or with `ahub permit`.
|
|
3321
3454
|
- Watch replays bounded memory history from this daemon, then displays live updates. It is the hub's read-only view, with no prompt input. It does not read native session files.
|
|
3322
3455
|
- Kimi's default is its own pull-only TUI (#316). Watch requires `ahub kimi --mode headless`; its own TUI remains visible in its native terminal. Qwen requires roster participation and verified ACP 0.25.0. `ahub qwen --mode tui` remains refused.
|
|
3323
|
-
- Protocol stays 16; a same-protocol older daemon answers the new request as unknown, which the CLI reports.
|
|
3456
|
+
- Protocol stays 16; a same-protocol older daemon answers the new request as unknown, which the CLI reports. Since 0.12.44 a headless start that the hub itself makes for Kimi or Qwen opens this view automatically through the terminal provider (see Start modes; #286).
|
|
3324
3457
|
- Native T0 limits: [ACP watch verification](verification/2026-10-11-acp-watch-t0.md).
|
|
3325
3458
|
|
|
3326
3459
|
# Acceptance-criteria checklist (#481)
|
|
@@ -3768,7 +3901,9 @@ the builder's summary or agent chat, and the board records its verdicts as
|
|
|
3768
3901
|
the planner reviews and the task's assignment note says `reviewer=planner (fallback)`.
|
|
3769
3902
|
A missing
|
|
3770
3903
|
`peer_instances` feature on an old daemon or upgrade target requires a supporting
|
|
3771
|
-
version before launching or restoring extras.
|
|
3904
|
+
version before launching or restoring extras. An older recovery source without it
|
|
3905
|
+
is refused only while a declared instance is live or unreadable; a source whose
|
|
3906
|
+
status shows every declared id offline proceeds.
|
|
3772
3907
|
|
|
3773
3908
|
### Fresh reviewer pool (#519)
|
|
3774
3909
|
|
|
@@ -3803,11 +3938,19 @@ An additional implementer must have an existing registered Git worktree and
|
|
|
3803
3938
|
attached branch at `.worktrees/<instance-id>`, named explicitly by `worktree` in
|
|
3804
3939
|
its declaration. The hub validates it and does not create it in this phase.
|
|
3805
3940
|
Reviewer instances may share the checkout, or name their own validated worktree.
|
|
3806
|
-
The execution directory is separate from the canonical hub project.
|
|
3941
|
+
The execution directory is separate from the canonical hub project. Resolving it
|
|
3942
|
+
(at an extra-instance start and for a turn undo) makes up to four git calls that
|
|
3943
|
+
share one 5 s allowance; when it runs out the start refuses with `Git workspace
|
|
3944
|
+
evidence unavailable`. An Orca
|
|
3807
3945
|
terminal may belong to either that canonical project or the instance's execution
|
|
3808
3946
|
worktree; recovery preserves its exact recorded grouping and live launcher.
|
|
3809
3947
|
Changing or removing the declaration invalidates its saved extra-instance
|
|
3810
|
-
binding
|
|
3948
|
+
binding; the recovery screen and `--verbose` receipt still show what was recorded
|
|
3949
|
+
for it. A roster key left by an undeclared instance (`codex-2`) loads as inert and
|
|
3950
|
+
`ahub roster remove codex-2` deletes it from `.agenthub/config.local.json`; a key
|
|
3951
|
+
held only in the shared `config.json` is refused with a request to edit that file.
|
|
3952
|
+
Any other unknown roster key is still an unsupported peer. Do not infer ownership
|
|
3953
|
+
from a native-kind label alone.
|
|
3811
3954
|
|
|
3812
3955
|
A routing preference for a kind chooses among its instances using current load
|
|
3813
3956
|
and WIP. A manual instance id remains exact. Reviewers from the same kind share
|
|
@@ -3827,3 +3970,11 @@ blocks that undo until the identity and workspace can be reviewed.
|
|
|
3827
3970
|
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.
|
|
3828
3971
|
|
|
3829
3972
|
The per-release "gates run" record is deferred to S6/S7. 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.
|
|
3973
|
+
|
|
3974
|
+
|
|
3975
|
+
## Codex cancellation identity notice
|
|
3976
|
+
|
|
3977
|
+
If Codex is offline with `statusNotice.code = unidentified-cancellation`, the hub
|
|
3978
|
+
could not identify a cancelled native turn. Reattach the native session before
|
|
3979
|
+
further hub delivery. A late completion keeps this hold; verified native
|
|
3980
|
+
reattachment or TUI detachment clears it. The notice contains fixed guidance, without native ids.
|
package/docs/quickstart.md
CHANGED
|
@@ -17,7 +17,7 @@ ahub setup # installs the Claude Code channel plu
|
|
|
17
17
|
Or use the matching GitHub release:
|
|
18
18
|
|
|
19
19
|
```bash
|
|
20
|
-
bun add -g github:STAIxBWLB/agent-hub#v0.12.
|
|
20
|
+
bun add -g github:STAIxBWLB/agent-hub#v0.12.45
|
|
21
21
|
ahub setup
|
|
22
22
|
```
|
|
23
23
|
|
|
@@ -30,6 +30,14 @@ and upgrade or crash recovery, see the [operations guide](operations.md). To
|
|
|
30
30
|
move running projects to a newer release later, run `ahub upgrade` in a
|
|
31
31
|
terminal: it reviews the plan with you, applies it and follows it.
|
|
32
32
|
|
|
33
|
+
## Upgrading to 0.12.45
|
|
34
|
+
|
|
35
|
+
The dashboard is redesigned (#469): from 1024px a top bar and a left sidebar (a row of tabs below that), keys `1`-`7` switch views, `?` lists the keys and `Esc` closes a sheet; a task opens as a side sheet, and the plan, split, releases, delivery evidence and propose forms sit in a Plan sheet opened from the board toolbar. View names, ids and the view cookie are unchanged. A person can load a bundled ACP adapter module declared under `adapter_modules` (a `path` and its `sha256`) in untracked `.agenthub/config.local.json`: only the daemon imports it, at startup, so a changed declaration needs a restart; Settings and hub tools cannot write it, and a declaration in the shared `.agenthub/config.json` is ignored. A project that declares modules can be upgraded or recovered only between releases that advertise `external_adapter_modules`. See [Adding an AI tool](adding-an-ai-tool.md#5-external-adapter-modules-451-a4) (#451). `hub_review` and `ahub review ... --findings '<json>'` take structured findings, `max_nits` in `.agenthub/sdlc.toml` caps the nits per report (default 5), and in enforce an open Important finding refuses approval (#481 S3). Plans take optional `proof` and `risks`. In enforce, approval also needs every changed PR file covered by a literal `plan.paths` entry; otherwise amend the plan through `hub_task_accept` and call done again, or the person runs `ahub sdlc accept-departure <task> <head> <reason...>`, which lapses when the hub restarts (#481 S2). An unresolved review thread now settles only by resolution or a later reply from a GitHub user; any other author kind counts as a pending bot comment (#481). `ahub roster remove <peer>` also deletes the roster key of an instance no longer declared (#225). When `ahub kimi` refuses a `kimi_cmd` with no TUI form, it now says the value is the running hub's, retained until `ahub settings reload` (#654). A Claude session no longer gets `hub_inbox` advice while a delivery hold refuses its pulls (#221). A same-size rewrite of a large file within one filesystem timestamp tick is now told apart by its bytes (#428). Restart Claude sessions so they load the 0.12.45 plugin. Coming from 0.12.43 or older, the notes below apply too.
|
|
36
|
+
|
|
37
|
+
## Upgrading to 0.12.44
|
|
38
|
+
|
|
39
|
+
Run `ahub init` once per project to refresh the hub block in `AGENTS.md`: it tells agents in the agy TUI not to report the hub missing when no `hub_*` tool is listed on the first turn (#607). agy no longer shows Claude's context reading or Claude's transcript usage as its own (#624). When the hub itself starts Kimi or Qwen headless, it also opens `ahub watch <peer>` through its terminal provider; with no provider, or a failing one, the start still succeeds and reports the command to run (#286). When an `implement` or `bulk_edit` owner completes two turns in a row with no recorded writes or task-tool effects, the planner gets a notice, and a done report with no writes since acceptance says so in the review request; set `task_sweep.no_writes_turns` to change the count, or 0 to turn it off (#498). A declared agy extra instance is refused when the config loads, and Qwen's context occupancy shows unknown until a native source is verified (#625). A standard ACP agent can now be declared as a config-only adapter (`adapters.<kind>` in untracked `.agenthub/config.local.json`); restart the hub, then run `ahub roster add <kind>` and `ahub <kind>` (see the operations guide, #451). A project that declares adapters can be upgraded or restarted only to a release that advertises `configured_acp`. A `peers.<id>` block now accepts only `start_mode`: any other field stops the config from loading at hub start, where 0.12.43 ignored it at startup. Restart Claude sessions so they load the 0.12.44 plugin. Coming from 0.12.42 or older, the notes below apply too.
|
|
40
|
+
|
|
33
41
|
## Upgrading to 0.12.43
|
|
34
42
|
|
|
35
43
|
A busy hub no longer stalls when the checkout's HEAD moves under open turns: snapshot Git work in one burst shares a 250 ms budget, and incoming checkouts, merges and fast-forwards are told apart from a peer's own commits through the HEAD reflog, so they no longer raise false concurrent-edit warnings (#576). `ahub task assign` and `hub_task_assign` now refuse with the reason when the named peer cannot take the task (#601). `ahub completion zsh|bash|fish` prints a shell completion script (#603). The console's Peers tab shows each peer's planner WIP, and `w` edits its limit (#532). agy native file reads are recorded as peer facts on 1.4.2 and 1.4.3 (#555); 1.4.3 stays unverified, and in its TUI the hub tools appear from the second turn (#267, #607). Coming from 0.12.41 or older, the notes below apply too.
|
|
@@ -44,7 +52,7 @@ A busy hub no longer stalls when the checkout's HEAD moves under open turns: sna
|
|
|
44
52
|
|
|
45
53
|
## Upgrading to 0.12.40
|
|
46
54
|
|
|
47
|
-
Restart Claude sessions after upgrading: each keeps the plugin server it started with until restarted, so a restart loads the
|
|
55
|
+
Restart Claude sessions after upgrading: each keeps the plugin server it started with until restarted, so a restart loads the current plugin. That plugin asks Claude Code to list the hub tools again when a hub reconnect changes them (#490), and one from an older `ahub claude` launch is refused and stops reconnecting instead of reclaiming the peer (#551).
|
|
48
56
|
|
|
49
57
|
`ahub jcode` opens its TUI again; on 0.12.39 it stopped after the first hub turn (#564). A busy Jcode now takes an `important` message into its running turn (#572), and `ahub doctor` lists the extra MCP servers the hub-owned Jcode loads from its config files. See [Jcode operations](operations.md#optional-jcode-peer).
|
|
50
58
|
|
package/docs/sdlc.md
CHANGED
|
@@ -14,10 +14,10 @@ The built-in lifecycle follows the [AI-Native SDLC Playbook](https://claude.com/
|
|
|
14
14
|
| --- | --- | --- | --- | --- |
|
|
15
15
|
| intent | Plan | person/planner | intent or backlog identity | person spec acceptance required at build entry in enforce |
|
|
16
16
|
| spec | Design | person/planner | issue identity and scope | spec acceptance/body drift checked at build entry |
|
|
17
|
-
| plan | Build | planner/owner | plan, branch, existing predecessor gates | current planner review required at build admission; conformance
|
|
17
|
+
| plan | Build | planner/owner | plan, branch, existing predecessor gates | current planner review required at build admission; literal-path conformance gates approval in enforce |
|
|
18
18
|
| build | Build | owner | PR identity/current head and local check on that head | entering review, when enabled |
|
|
19
19
|
| test | Test | verifier/reviewer | required checks and local gate on the reviewed head | approval, when enabled; integrity/evals pending S4/S5 |
|
|
20
|
-
| review | Deploy | assigned reviewer | current head-bound verdict, no pending change request/open thread; AC checklist under Compliance | approval, when enabled;
|
|
20
|
+
| review | Deploy | assigned reviewer | current head-bound verdict, no pending change request/open thread; AC checklist under Compliance | approval, when enabled; open Important findings block in enforce |
|
|
21
21
|
| merge | Deploy | person | current-head readiness, ordered touching PRs | shown; no mutation |
|
|
22
22
|
| release | Deploy | person | merged commit, version/tag, docs freshness/live receipt checklist | shown; authorization pending S6 |
|
|
23
23
|
| maintain | Maintain | person/planner | follow-up intent and operational evidence | shown; metrics/control bands pending S7 |
|
|
@@ -30,8 +30,8 @@ Stored `implement` and `verify` stage ids read as `build` and `test`. A write th
|
|
|
30
30
|
| --- | --- | --- |
|
|
31
31
|
| S0 | Plan, Design, Build, Test, Deploy, Maintain stage model | Nine built-in bases, canonical ids, stored receipt aliases and playbook labels |
|
|
32
32
|
| S1 | Accepted intent/spec and spec drift | Person-only `ahub sdlc accept`, bound to the issue's body edit time; build entry needs it in enforce (see Spec acceptance below) |
|
|
33
|
-
| S2 | Accepted plan and diff conformance |
|
|
34
|
-
| S3 | Bugs, Security and Compliance review passes |
|
|
33
|
+
| S2 | Accepted plan and diff conformance | Bounded proof/risks, metadata-only plan conformance and person-bound departure acceptance |
|
|
34
|
+
| S3 | Bugs, Security and Compliance review passes | Head-bound findings, configured nit cap, Important approval gate and recurring-finding amendment proposals |
|
|
35
35
|
| S4 | Red-first tests and test integrity | Pending receipts/gates; repository test-integrity rules still apply |
|
|
36
36
|
| S5 | Continuous evals for agent configuration | Pending gate, dependent on #251/#266; no eval pass claimed |
|
|
37
37
|
| S6 | Production approval gate | Pending person-only release authorization; release report stays read-only |
|
|
@@ -64,7 +64,7 @@ The acceptance-criteria checklist belongs to the Compliance pass over the same b
|
|
|
64
64
|
|
|
65
65
|
## Delivery and verification state
|
|
66
66
|
|
|
67
|
-
Head-bound review/approval gates, bounded custom definitions, read-only merge/release reports, S0/S8 vocabulary/checklist migration, S1 spec acceptance and S9 planner admission and S10 cost governance are implemented in source. S11 release-train governance ships in 0.12.37.
|
|
67
|
+
Head-bound review/approval gates, bounded custom definitions, read-only merge/release reports, S0/S8 vocabulary/checklist migration, S1 spec acceptance and S9 planner admission and S10 cost governance are implemented in source. S11 release-train governance ships in 0.12.37. S4-S7 and the native advise-through-merge/release cycle remain pending. S2 plan conformance and S3 findings are implemented in source. Installation, native acceptance and release authorization require separate receipts.
|
|
68
68
|
|
|
69
69
|
## Initial commands
|
|
70
70
|
|
|
@@ -227,13 +227,13 @@ At plan review, batch small related fixes into one area branch and PR; keep rele
|
|
|
227
227
|
|
|
228
228
|
The planner can provide scope with `hub_plan_review`; the person can run `ahub sdlc plan-review <task> <revision> <reason...> --review full --reviewers 1 --ci full`. Tasks records the explicit scope on the plan and its revision-bound receipt. A file-list update retains omitted cost fields; an explicit scope change cannot replace a reviewed scope during enforce acceptance. Old plans and receipts remain unspecified and are never filled by guessing. Advise reports missing scope. In enforce, every newly recorded receipt needs both scopes; an existing scope-less receipt remains valid until a non-release planning revision changes, with the same complete-history exception under S11. Enforce acceptance cannot add owner-supplied cost keys to that legacy receipt; the planner must record an explicit scope first.
|
|
229
229
|
|
|
230
|
-
A review request is premature if its task head does not match the PR head, configured required checks are not green, or a pending bot thread is unanswered. A resolved thread is settled; an unresolved one needs a user reply after its last bot comment. The hub reads identity, timestamps, authorship type and resolution only, never comment bodies. Without explicit required checks the native floor is both OS checks plus `seeded guards`. Advise reports the gap; enforce refuses before the requesting done/cohort writes. A private turn may finish its own PII task in advise with a generic unknown-readiness notice and no remote metadata read; the private-turn refusal for ordinary work remains. Delayed checks revalidate before completing. Background roster/quota role changes remain fact, but enforce holds a new review envelope without an admission readback; refresh it through a manual reviewer assignment after CI and replies are ready. The brief and verdict coverage belong to #520, separate from this admission.
|
|
230
|
+
A review request is premature if its task head does not match the PR head, configured required checks are not green, or a pending bot thread is unanswered. A resolved thread is settled; an unresolved one needs a user reply after its last bot comment. The hub reads identity, timestamps, authorship type and resolution only, never comment bodies. Without explicit required checks the native floor is both OS checks plus `seeded guards`. Advise reports the gap; enforce refuses before the requesting done/cohort writes. If only quiet task-history events arrive during an advise readback, the evidence is discarded and review continues with unknown readiness, without another external read. A reviewer change is allowed because the reviewer is re-derived before the write; it is not an authority or readiness receipt. Mode, owner, privacy, receipt, authority and other history drift still refuse; enforce remains unchanged. A private turn may finish its own PII task in advise with a generic unknown-readiness notice and no remote metadata read; the private-turn refusal for ordinary work remains. Delayed checks revalidate before completing; a refused completion sends a private-aware notice to call `hub_task_done` again only while the task is still in progress with the owner that started the check. Otherwise only the console notice is recorded. Background roster/quota role changes remain fact, but enforce holds a new review envelope without an admission readback; refresh it through a manual reviewer assignment after CI and replies are ready. The brief and verdict coverage belong to #520, separate from this admission.
|
|
231
231
|
|
|
232
|
-
CI controllers are serialized per pull request (`check-pr-<number>`) and for main pushes and dispatches (`check-main`), for the entire workflow and without canceling an admitted controller (#609). Within a PR `queue: single` lets a newer pending run replace the older pending one; main keeps `queue: max`, which preserves up to 100 waiting controllers ([GitHub concurrency documentation](https://docs.github.com/en/actions/how-tos/write-workflows/choose-when-workflows-run/control-workflow-concurrency)). Different PRs run side by side: a repository-wide lock serialized every PR (17-18 min alone, 40-54 min with 4-5 PRs in flight on 2026-10-20). Two groups admitting the same tree at the same instant can run it twice; that duplicate costs one run and never a false green. Required job names and read-only token permissions are unchanged. Before PR or push gates, `scripts/ci-reuse.mjs` reuses successful full proof for the identical Git tree, including its workflow. On reuse the two OS matrix jobs still expand and report their exact required names, while the seeded aggregate reports its exact name. They run only a `Reuse verified identical-tree full gate` marker step naming the original proof; full setup and gates are skipped. Skipped jobs and successful reuse-reporting jobs never count as fresh full proof. A running identical-tree gate refuses duplicate admission; unknown active coverage refuses safely. Metadata reads are bounded to 100 recent successes/active runs and 240 calls within 60 seconds. Pending serialized controllers are not admitted gates. Run focused tests
|
|
232
|
+
CI controllers are serialized per pull request (`check-pr-<number>`) and for main pushes and ordinary dispatches (`check-main`), for the entire workflow and without canceling an admitted controller (#609); macOS timing dispatches use their own macos-timing group. Within a PR `queue: single` lets a newer pending run replace the older pending one; main keeps `queue: max`, which preserves up to 100 waiting controllers ([GitHub concurrency documentation](https://docs.github.com/en/actions/how-tos/write-workflows/choose-when-workflows-run/control-workflow-concurrency)). Different PRs run side by side: a repository-wide lock serialized every PR (17-18 min alone, 40-54 min with 4-5 PRs in flight on 2026-10-20). Two groups admitting the same tree at the same instant can run it twice; that duplicate costs one run and never a false green. Required job names and read-only token permissions are unchanged. Before PR or push gates, `scripts/ci-reuse.mjs` reuses successful full proof for the identical Git tree, including its workflow. On reuse the two OS matrix jobs still expand and report their exact required names, while the seeded aggregate reports its exact name. They run only a `Reuse verified identical-tree full gate` marker step naming the original proof; full setup and gates are skipped. Skipped jobs and successful reuse-reporting jobs never count as fresh full proof. A running identical-tree gate refuses duplicate admission; unknown active coverage refuses safely. Only a 404, a timeout on the individual read limit, or malformed successful proof is dropped: a failed successful-run list drops that proof phase, while a failed commit or jobs read drops only that candidate and continues the bounded search. Other failures (5xx, 403, 429, response size bounds, authentication and unclassified errors) remain unknown. Exhausting the overall deadline or request budget stays unknown even when a request reports a timeout. Without verified proof, a complete empty active scan requires a fresh gate. An unreadable, truncated or unidentifiable active inventory, or exhaustion of the shared request budget or deadline, stays unknown and refuses admission. The release reader (`--require`) still refuses without verified identical-tree full proof. Metadata reads are bounded to 100 recent successes/active runs and 240 calls within 60 seconds. Pending serialized controllers are not admitted gates. Run focused tests and `scripts/check.sh --changed` locally; CI carries the full-suite gate; a known flake warrants only one rerun of its failed job with recorded evidence.
|
|
233
233
|
|
|
234
234
|
`hub_sdlc_costs` and the person's `ahub sdlc costs [owner/repo]` report linked merged PRs without changing the board. A review round is a board done-to-verdict handoff, not each reviewer in a multi-reviewer round. An observed handoff still counts when its declared scope was none. The report counts distinct check-workflow run ids, extra attempts (`run_attempt - 1`), full re-reviews, delta rounds and summed review intervals, with GitHub review counts alongside them. Unspecified scope and unavailable evidence stay unknown/incomplete. Only current public links are projected; a private counterpart withholds the whole PR. Up to 16 linked PR reads share 20 seconds; each PR covers at most 100 commits, reviews and recent check-workflow runs, and the pure aggregator bounds 500 tasks, 200 PRs and 10,000 history entries. Truncation is explicit. S10 exposes pure band-breach proposal data; S7 owns rolling baselines, band configuration, automatic scheduling and board-card creation.
|
|
235
235
|
|
|
236
|
-
S10 policy ceilings are
|
|
236
|
+
The S10 policy ceilings are settled in the spec: hub task #284 implements the lost-proof admission rule, #285 owns thread-author classification, and #286 implements quiet-only advise drift and delayed refusal notices to the same check owner while the task remains in progress. The per-PR queue is already in place (#609).
|
|
237
237
|
|
|
238
238
|
# Planner release trains (S11)
|
|
239
239
|
|
|
@@ -260,3 +260,21 @@ Configured cut dates must be positive safe UTC millisecond timestamps; the train
|
|
|
260
260
|
Release-cut refusals name the applicable exits: stop or resume a paused peer, adjust `ahub budget set`, or drop the train with `ahub release drop`. Install the resource controller or fix PATH when `dot` is unavailable or shadowed; retry the cut outside an outer admission lease when nested admission is refused.
|
|
261
261
|
|
|
262
262
|
Train history retains at most 256 events. Configuration no-ops retain the revision and history; meaningful configuration reserves the final two slots for cut and shipment. Capacity refusals occur before effects; an already exhausted legacy train requires a person-selected replacement.
|
|
263
|
+
|
|
264
|
+
## Structured review findings (#481 S3)
|
|
265
|
+
|
|
266
|
+
`hub_review` accepts optional `findings`, or the person can pass JSON with `ahub review <id> <verdict> --findings '<json>'`. Each item has a stable `id`, `pass` (`bugs`, `security`, `compliance`), `severity` (`important`, `nit`) and `disposition` (`open`, `fixed`, `false_positive`, `accepted`), with optional `path` and `rationale`. Only the person can use `accepted`, with a non-empty rationale. Reports have at most 64 findings and 64 KiB; ids, paths and rationale have bounded lengths. `max_nits = 5` in `.agenthub/sdlc.toml` sets the report's nit cap (0..64), including in off mode.
|
|
267
|
+
|
|
268
|
+
Verdict history retains each report and its reviewed head. Within a task, the semantic id remains the finding identity when its pass is reclassified; a disposition for that id settles the same finding across passes. Open Important findings carry forward across heads until a reviewer explicitly sets fixed or false_positive, or the person sets accepted with a rationale. Omitting an id never settles it; nits stay on their original head. Review requests list carried public ids, pass, severity and optional path; private finding details stay withheld. Head selection uses the first valid full SHA from the PR head, reviewed head, local head, then commit metadata; unresolved non-empty reports refuse in enforce. A supplied head must match the resolved task head when one exists, and that resolution is rechecked after asynchronous screening. Status and the merge queue show counts for the current head. In enforce, approval refuses while any Important finding remains open; advise exposes the count without blocking. Existing verdicts without structured findings stay compatible.
|
|
269
|
+
|
|
270
|
+
Finding text passes through the task privacy boundary. PII tasks remain stubs; withheld reports expose only counts on ordinary tasks. When the same public, non-withheld semantic pass/id pair appears on a second distinct task, Tasks proposes one durable, marker-deduplicated backlog plan (excluding false positives) to amend AGENTS.md or an area note. Only hub-proposed plan cards supply dedupe markers. The proposal grants no editing or acceptance authority.
|
|
271
|
+
|
|
272
|
+
## Plan conformance (#481 S2)
|
|
273
|
+
|
|
274
|
+
Plans accept optional `proof` (test paths or check names) and `risks`, each at most 32 strings of 512 characters. They share the task-text privacy screen; enabled SDLC schemas advertise them while ordinary off-mode schemas stay unchanged. File coverage compares repository-relative identities for literal file or directory prefixes in `plan.paths`, with no glob expansion. Repository-root entries (including normalized `.` or `./`) never cover the inventory and leave conformance unknown. Absolute entries map only through verified same-repository aliases; an unverified mapping remains unknown. Symbols, proof and risks do not cover files. A planned path left untouched is advice; a changed path outside the plan is a departure. Missing plans, unsafe or unmappable paths, truncated inventories, renamed/copied files without both path identities, and failed metadata reads remain unknown.
|
|
275
|
+
|
|
276
|
+
Review/status use the same bounded PR file metadata reader as the merge queue; no patch or body is fetched. In enforce, approval requires known coverage of every changed path or a current person acceptance of the departure. Request changes, amend the plan through `hub_task_accept`, and call done again before approval. Off adds no conformance gate; advise reports departures without blocking.
|
|
277
|
+
|
|
278
|
+
The person may instead run `ahub sdlc accept-departure <task> <head> <reason...>`. Acceptance requires a currently known departure, never unknown or already covered conformance. The hub reads and captures the complete current repo/PR/head inventory, verified task repository scope and the plan fingerprint/revision; changing the plan, repository refs/scope, link, head or inventory invalidates that acceptance. Departure consent also expires on hub restart: amend the plan or accept again in the new host lifetime. This fail-closed ceiling prevents a rejected, unpersisted readback from reviving old consent; a durable host observation journal is the upgrade path. The reason is bounded to 2048 characters, sanitized and privacy-screened. There is no peer tool or live-test delegation for this person-only operation. Private PR inventories are unavailable to this remote readback; amend the private plan instead. Public private-task projections show only whether a departure acceptance exists.
|
|
279
|
+
|
|
280
|
+
The report names "plan revisions after the first observed push". Host-authored content revisions live in task history, independently of backlog `planningRevision`. The first verified remote head records the current revision/fingerprint; earlier pushes are explicitly unknown. Pending observations persist only with successful owner/reviewer transitions; status and queue reads do not append board history. Off mode captures no S2 observation or content revision. Plan edits made while off therefore do not count in revisions after the first observed push when reporting resumes. Revoking an existing person acceptance on a plan change still persists its invalidation, including while reporting is off. If startup loses a pending observation, the earlier interval remains unknown. This observation boundary is a ceiling until a webhook or Actions push receipt provides a true push-event source.
|