@staix/agent-hub 0.29.0 → 0.30.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 +12 -0
- package/README.md +5 -3
- package/docs/agent-notes/README.md +12 -0
- package/docs/agent-notes/adapters.md +2 -1
- package/docs/agent-notes/bus.md +1 -0
- package/docs/agent-notes/daemon.md +2 -1
- package/docs/agent-notes/tasks.md +4 -2
- package/docs/agent-notes/tests.md +2 -0
- package/docs/design/adapters.md +56 -0
- package/docs/design/agent-hub-architecture.md +12 -0
- package/docs/design/bus.md +8 -0
- package/docs/design/task-board.md +18 -0
- package/docs/dogfood.md +7 -4
- package/docs/operations.md +106 -11
- package/docs/quickstart.md +10 -3
- package/docs/sdlc.md +1 -1
- package/docs/security.md +28 -2
- package/docs/smoke.md +13 -2
- package/docs/specs/2026-10-12-office-view-design.md +86 -3
- package/docs/verification/2026-11-03-0.30.0.md +54 -0
- package/docs/verification/2026-11-03-agy-1.4.12-launcher-nonce.md +42 -0
- package/docs/verification/2026-11-03-agy-1.4.12.md +90 -0
- package/docs/verification/2026-11-03-agy-tui-close.md +49 -0
- package/docs/verification/2026-11-03-grok-1.0.62-t0.md +150 -0
- package/docs/verification/2026-11-03-pi-150-compat.md +40 -0
- package/docs/verification/2026-11-03-pi-title-api.md +26 -0
- package/docs/verification/2026-11-03-qwen-0265-acp.md +55 -0
- package/docs/verification/2026-11-03-qwen-0265-dual-output.md +40 -0
- package/docs/verification/dogfood/2026-11-03.md +3095 -0
- package/docs/verification/pi-150-model-free.ts +57 -0
- package/docs/verification/pi-150-native.mjs +21 -0
- package/docs/verification/pi-150-title-api.mjs +31 -0
- package/docs/verified.json +14 -14
- package/package.json +2 -2
- package/plugins/agent-hub/.claude-plugin/plugin.json +1 -1
- package/plugins/agent-hub/server.js +193 -29
- package/src/adapters/compat.ts +10 -10
- package/src/adapters/definitions/builtin-metadata.ts +2 -2
- package/src/adapters/local-worker.ts +22 -6
- package/src/adapters/pi.ts +92 -10
- package/src/adapters/registry-types.ts +4 -0
- package/src/adapters/registry.ts +2 -0
- package/src/cli/autonomy.ts +66 -1
- package/src/cli/console-state.ts +6 -2
- package/src/cli/help.ts +2 -0
- package/src/cli/identity.ts +8 -1
- package/src/cli/main.ts +39 -0
- package/src/cli/worktree-tidy.ts +158 -0
- package/src/cli/worktrees-output.ts +43 -0
- package/src/hub/board.ts +28 -3
- package/src/hub/bus.ts +4 -4
- package/src/hub/conductor.ts +4 -3
- package/src/hub/config-apply.ts +2 -1
- package/src/hub/daemon.ts +237 -19
- package/src/hub/dogfood-read.ts +62 -9
- package/src/hub/hub-tools.ts +11 -5
- package/src/hub/manager.ts +9 -1
- package/src/hub/peer-doing.ts +33 -3
- package/src/hub/peers.ts +7 -0
- package/src/hub/settings.ts +3 -0
- package/src/hub/tasks.ts +30 -8
- package/src/hub/ui.ts +2 -0
- package/src/hub/worktree-prs.ts +100 -0
- package/src/hub/worktree-shelf.ts +246 -0
- package/src/memory/capture.ts +1 -1
- package/src/pi/extension.ts +43 -3
- package/src/ui/index.html +133 -10
- package/src/ui/office-model.ts +64 -8
- package/src/ui/office-space.ts +1 -0
- package/src/ui/plugin.ts +14 -3
package/CHANGELOG.md
CHANGED
|
@@ -4,6 +4,18 @@ Issue and pull request numbers in the entries for 0.7.7 and earlier refer to the
|
|
|
4
4
|
|
|
5
5
|
## Unreleased
|
|
6
6
|
|
|
7
|
+
## 0.30.0
|
|
8
|
+
|
|
9
|
+
- Worktree shelf (#1057: #1156, #1158, #1159, #1160). The hub derives at read time which worktrees are live and which are settled (its PR merged or closed with no newer work, or quiet for `worktrees.quiet_days`, default 7), and the person can Park or Pin a row. `ahub worktrees [--json]` lists both; `ahub worktrees tidy` previews what is safe to remove, with every refused row's reasons, and `ahub worktrees tidy --confirm <fingerprint>` removes only those, in the person's own CLI process, with `git worktree remove` and `git branch -d` and never a force flag. Dirty, locked, unpushed, unmerged, bound-to-open-task, main and instance worktrees are refused. The dashboard's Agents panel gets a Worktrees section with Park, Pin and an explicit "Refresh PR states" (at most 8 PRs); no read makes a GitHub call, and an inbox alert fires above `worktrees.alert_over` (default 10). The shelf adds no removal to the daemon: it still removes only an empty hub-made task worktree on decline or cancel (0.29.0).
|
|
10
|
+
- Pi and the local worker name their own session with `hub_set_title` (#1157, #1055). The first name is final; the person replaces it with `ahub title <peer> <text...>` or the console's `t` editor. An unnamed session shows a screened label from its first incoming message to the console and the conductor only, `unnamed` to everyone else. Claude, Codex, Kimi and ACP tool schemas are unchanged.
|
|
11
|
+
- Office view phase 5 (#264: #1142, #1152): a Backlog wall before To do (rank order, at most 8 cards, reserved, pinned and waiting marks), up to three release docks with approved/scope and cut date, and the unplanned count on each planner's desk. Everything comes from the existing snapshot; no daemon change.
|
|
12
|
+
- Plan sources are validated (#1144, #1151): a plan's `sources` must name registered `src:<16 hex>` ids (`ahub source list`), or the proposal or acceptance is refused before any effect. The dashboard Plan sheet lists source metadata and stages natural-language requests through the same held-draft intake as the CLI.
|
|
13
|
+
- `ahub autonomy status` is readable (#1137, #1138): a peer table first, identical floor verdicts and unknowns once with their peer lists, and class enforcement counts. `--json` is unchanged.
|
|
14
|
+
- `hub_backlog_return_untouched` takes `fields.reserved` (a peer id or null), applied in the same write as the return, so a parked card is not offered straight back under auto-pull (#1154, #1155).
|
|
15
|
+
- The weekly dogfood scorecard reads PR flow on a board over 500 tasks (#1147, #1153): a separate, text-free PII inventory decides privacy, and task rows are read for the scorecard window only. The 2026-11-03 scorecard and the #788 progression measures are recorded (#1149).
|
|
16
|
+
- Compatibility receipts (#888): Pi 1.5.0 (#1148), qwen ACP 0.26.5 (#1150; qwen-dual stays at 0.26.1) and agy 1.4.12 (#1141, #1146) join `verified`. Run agy with `AGY_CLI_DISABLE_AUTO_UPDATE=1`: without it agy replaced its own binary during a launch (#1140). The `ahub agy` launcher close leg (#1140) and an isolated Grok 1.0.62 T0 (#1139, #635) are recorded.
|
|
17
|
+
- Pin `@staix/agent-mem` 0.20.1 (was 0.20.0, #1136). A setup receipt written by 0.20.1 can be v2, which 0.20.0 refuses to apply or undo; undo with the release that wrote it.
|
|
18
|
+
|
|
7
19
|
## 0.29.0
|
|
8
20
|
|
|
9
21
|
- The hub makes the task worktree for a hub-sandboxed owner (Pi, the local worker) and commits and pushes its work at `hub_task_done` (#1115, #1127). The commit goes through a private index and a compare-and-swap ref update, only on top of the recorded base or the hub's own last commit, after the changed paths pass the deny and grant policy; a moved branch or HEAD, a refused path or a changed gitlink refuses the commit with its cause. Decline and cancel remove an empty worktree and keep one with uncommitted work.
|
package/README.md
CHANGED
|
@@ -5,7 +5,7 @@ Code, Codex, Kimi Code, Pi and a hub-owned local worker (plus Qwen Code, Jcode a
|
|
|
5
5
|
Antigravity if you want them) share a message bus, a task board and a memory, each
|
|
6
6
|
connected through its own native interface.
|
|
7
7
|
|
|
8
|
-
Status: 0.
|
|
8
|
+
Status: 0.30.0, control protocol 17 (accepts 16). Bun + TypeScript, one loopback daemon per
|
|
9
9
|
project. Site: [agent-hub.staix.net](https://agent-hub.staix.net).
|
|
10
10
|
|
|
11
11
|
## Quick start
|
|
@@ -100,7 +100,9 @@ defaults. The
|
|
|
100
100
|
worktree with a machine-wide view, live settings reload, machine-local schedules
|
|
101
101
|
that propose tasks by interval or by a `cron` expression in a time zone
|
|
102
102
|
(`ahub schedule list`), a one-confirmation autonomy
|
|
103
|
-
preset (`ahub autonomy on|off|status`),
|
|
103
|
+
preset (`ahub autonomy on|off|status`), a worktree shelf that lists live and
|
|
104
|
+
settled worktrees and removes the safe ones only on your confirmation
|
|
105
|
+
(`ahub worktrees`), structural config
|
|
104
106
|
(instances, review, lifecycle) edited with a validated diff and a backup from the
|
|
105
107
|
console or a dashboard settings session, token usage and quota
|
|
106
108
|
history (`ahub usage` and the dashboard's Usage view), and `ahub upgrade`, which
|
|
@@ -135,7 +137,7 @@ uses the home must run a version that understands the new layout.
|
|
|
135
137
|
[#258](https://github.com/STAIxBWLB/agent-hub/issues/258)).
|
|
136
138
|
|
|
137
139
|
Live checks that still need a real account or a person are listed in the
|
|
138
|
-
[release verification report](docs/verification/2026-11-03-0.
|
|
140
|
+
[release verification report](docs/verification/2026-11-03-0.30.0.md) and the
|
|
139
141
|
[smoke checklist](docs/smoke.md).
|
|
140
142
|
|
|
141
143
|
## Documentation
|
|
@@ -2,6 +2,18 @@
|
|
|
2
2
|
|
|
3
3
|
Human explanation. Agent instructions: the area notes in this directory.
|
|
4
4
|
|
|
5
|
+
In the #1055 review, the new title-binding lookup preceded invocation,
|
|
6
|
+
pending-delivery and supervision bookkeeping in `Bus.onDelivered`. The bus's
|
|
7
|
+
outer observer catch protected settlement but could not finish bookkeeping
|
|
8
|
+
after a journal lookup threw. This is the background for isolating that
|
|
9
|
+
fallible observer work in the bus area note.
|
|
10
|
+
|
|
11
|
+
Two #1055 refusal tests exposed Bun 1.4.2 replacing a received `error` string
|
|
12
|
+
with the `StringContaining` object supplied to `toMatchObject`. A minimal
|
|
13
|
+
same-version reproduction showed `typeof error` changing from `string` to
|
|
14
|
+
`object`. Preserving the original value keeps later error-text and canary
|
|
15
|
+
checks meaningful; changing production messages would not address this cause.
|
|
16
|
+
|
|
5
17
|
On 2026-10-13, two PR #368 reviews found that a missing delivery id or a missing visible row could select another Queue row. The console navigation rule in daemon.md records the exact-row contract. The missing-row and ID-less secondary-delivery regression tests demonstrate both cases.
|
|
6
18
|
|
|
7
19
|
Background behind some of the rules, moved from the top-level README on 2026-10-16:
|
|
@@ -51,6 +51,7 @@ Scope: the Codex, ACP and Pi adapters and the Pi extension (turn `generation` an
|
|
|
51
51
|
- A durable Codex steer stays pending while its native turn runs. Its RPC may answer after `turn/completed`: bound unanswered replies from that turn's end, requeue a definitive late refusal, and hold a late acceptance as uncertain without attaching it to a later turn's audience (#296).
|
|
52
52
|
- Codex reports each thread it adopts, started or resumed (`onThread`, the `native_thread` event), before any turn on it starts: the upgrade planner (Codex's `session.zeroTurn` in `src/adapters/definitions/native-recovery.ts`, read through `nativeZeroTurn` in `src/cli/upgrade-runtime.ts`, #215) gives each codex `turn_start` to the thread adopted last before it and plans a thread it saw start with no turn (and no rollout) as a fresh start.
|
|
53
53
|
- Peer DOING (#878): an adapter reports a native session title or its plan's in-progress item only through `onDoing` (set by `Bus.add`), with `reset` when it binds a new session or thread; never put that text in a log line, event, message, status notice or memory capture. Codex reads only `thread/name/updated` and `turn/plan/updated` of the bound thread, ACP only a `plan` update inside the current turn; never guess a value for a peer without such a source.
|
|
54
|
+
- Session titles (#1055, spec `docs/design/adapters.md` "Session title slots"): only a fixed builtin may declare `title` or `titlePersistence` (`assertNativeCapabilityScope`), and only a `title: "hub"` definition (Pi, local) is offered `hub_set_title` (`sessionTitleTools`); keep it off Claude, Codex, Kimi and ACP tool lists and native auto-approval names. Write a title only through `setSessionTitle` (unnamed-only for an agent, `current()` rechecked right before the effect) and report it only through `onDoing("title", ...)`; never pass a title call to `observeTool` or capture, and never store a derived unnamed label.
|
|
54
55
|
- Codex 0.154.0 `agentMessage` items carry `text` and `phase` (not `content[]`); only the last non-`commentary` message of a turn is shared.
|
|
55
56
|
- Hide `hub_delivery_retry` from delivery-only ACP/local tool lists and omit its retry hint. Treat `AGENTHUB_DELIVERY_ONLY` only as an exposure hint; the daemon must refuse by the actual attached adapter, and native children must not inherit the marker. A Claude push that cannot reach its model reports `failed_safe`; if the daemon refuses that optional receipt state, try `needs_review` and retain uncertainty when neither receipt is confirmed. Keep `hub_delivery_resolve` on every list, delivery-only included: settling one's own `needs_review` row is not a retry (#1112).
|
|
56
57
|
- ACP `onActivity` is a closed, transient person-only projection. Never publish it through the bus, log/event writers, UI, tools or memory. Emit only for an active turn; latch private envelopes and associated PII work, including unreadable privacy state, and discard pending chunks on turn teardown. Native terminal references do not authorize filesystem/terminal RPC reads.
|
|
@@ -106,7 +107,7 @@ Scope: the Codex, ACP and Pi adapters and the Pi extension (turn `generation` an
|
|
|
106
107
|
- Jcode MCP set (#572): `jcode acp` and `jcode serve` start every server named in `<JCODE_HOME>/mcp.json` and the project's `.jcode/mcp.json`, `.mcp.json` and `.claude/mcp.json`, and no flag or variable selects them (`--mcp-tools`, `JCODE_MCP_TOOLS`, `JCODE_DISABLED_TOOLS` and `JCODE_DISABLE_CLAUDE_MCP` do not). Do not paper over it: `ahub doctor` lists the extra names through `jcodeExtraMcpServers` (`src/adapters/definitions/native-cli-jcode-mcp.ts`), names only, those four paths only, no home search, no link followed. Below the project root or home, `.jcode` and `.claude` must be real directories and the file a regular file (per-component `lstat`, an `O_NOFOLLOW` open and an `fstat` identity recheck); a system alias at or above the configured root stays allowed. Revisit if upstream adds a server selector.
|
|
107
108
|
- Extra Jcode instances (#685, #368): one `JcodePeer` per declared id (at most 8), each with its own `jcode_home`, socket and state. The person authenticates inside Jcode; the hub passes `-p`, `--model` and `--provider-profile` exactly as declared, handles no model credential or URL, and only checks that the home is a directory the person owns (`jcodeHomeRefusal`: no read or write under it). Never put `AGENTHUB_PEER_ID` in the shared `.jcode/mcp.json` entry (`jcodeMcpEntry`): every owned launch supplies it through `peerChildEnv(<id>)` or the TUI launch env, together with its `AGENTHUB_LAUNCH_ID`. The MCP server with no `AGENTHUB_PEER_ID` in tools mode refuses every tool and never defaults to a peer id. The daemon admits a `tools` hello for a peer whose definition is `owned-acp` only when it carries the launch id of that peer's current owned launch (`JcodePeer.ownsLaunch`, from `jcodeLaunches`); never trust the env alone. `jcode --as <id>` never opens a TUI. Declare the semantic `instances.nativeHome` on a definition, never compare the kind name (`registry-guard`); it is a fixed-builtin capability that `assertNativeCapabilityScope` refuses elsewhere. `checkJcodeInstanceHomes` (`src/hub/instances.ts`, run once at daemon startup) refuses a home equal to or nested in the base Jcode's or another instance's; compare homes only through its `canonicalHome` (`realPath`, case-folded on a volume that folds case, inode), never `resolve()` text. The preview of `--as <id>` passes the instance to `previewJcode` so it shows the daemon's real flags.
|
|
108
109
|
- Jcode defers MCP tools (#912, `JCODE_DEFERRED_TOOLS_INSTRUCTION` in `src/hub/envelope.ts`): a Jcode peer lists only its built-in tools until the model searches for an MCP tool, so without a hint it reports the hub tools missing and answers in its final message. The guidance is written in exactly one place, that constant, and `JcodePeer` sends it through `jcodePreamble` (`src/adapters/jcode.ts`) ahead of the role contract, once with a session's first prompt, for the base peer and every extra instance. Never copy it into `STANDING_INSTRUCTION`, `templates/AGENTS.block.md` or a profile template (the Jcode bullet there is the same text in every template, and `test/jcode-deferred-tools.test.ts` pins both), and never change Jcode's own deferral or cache settings from the hub. The tools it names must exist, and the server name must be `JCODE_MCP_NAME`.
|
|
109
|
-
- Antigravity P1 (`src/adapters/agy/`): keep it optional, cloud, off roster by default, implementer/verifier only and pull-only. Until per-mode native prompt behavior is verified, reject hub permission/unattended mappings, headless starts and resume selectors; do not infer a quota source. The launcher refuses extra instances, any `agy --version` that compat `agy` refuses (1.4.2 and 1.4.
|
|
110
|
+
- Antigravity P1 (`src/adapters/agy/`): keep it optional, cloud, off roster by default, implementer/verifier only and pull-only. Until per-mode native prompt behavior is verified, reject hub permission/unattended mappings, headless starts and resume selectors; do not infer a quota source. The launcher refuses extra instances, any `agy --version` that compat `agy` refuses (1.4.2, 1.4.7 and 1.4.12 are T0-verified; other 1.4.x patches launch with a stderr warning, #589; another version needs the person's trust, #691), and a hub without the `native_hook_launch` feature.
|
|
110
111
|
- Only the person's `ahub agy` launcher manages `.agents/plugins/agent-hub/{plugin.json,mcp_config.json,hooks.json}`. Refuse linked/foreign entries before any write; cleanup only the matching launch. Never write project-level `.agents/hooks.json` or personal Gemini settings. The plugin MCP server is named `agent-hub_agent-hub` by the pinned native client.
|
|
111
112
|
- Managed native facts use the definition's typed `NativeFactsPort` and the daemon's ephemeral `native_hook_launch` authority. Bind every observation to the daemon, launch, native session, project and current socket generation. Stop requires `fullyIdle`, normal termination and a fresh validated completion row; a missing readback keeps the peer busy. Native fact acknowledgement accepts only SDK ephemeral-message rows in the exact session transcript path. `decodeAgyHook` classifies as `Read` only the observed native read (`view_file` `AbsolutePath`, or 1.4.3 `read_file` `Path`); a `StartLine`/`EndLine` range becomes `offset`/`limit`, so a partial read never counts as a whole-file view. Unobserved edits get no scoped grant or effect attribution. The daemon records Pre/PostToolUse through `Facts.preTool`/`postTool` and offers nothing on them (#555).
|
|
112
113
|
- `terminalOwner(launchId)` (`src/hub/peers.ts`, #482) answers only for the adapter's current native TUI owner bound to that launch (Codex: the claimed link with that `launchId`, both sockets open; the launcher sends the id as the upgrade's bearer token through `--remote-auth-token-env`, because `codex --remote` refuses any path or query (#586); Pi: a started, verified TUI owner on its requested session); a start acknowledgement is never ownership. A Codex link that is not the claimed one or has a closed socket never adopts a thread.
|
package/docs/agent-notes/bus.md
CHANGED
|
@@ -24,6 +24,7 @@ Scope: delivery, digests and condensation, reply addressing, priority and limits
|
|
|
24
24
|
- The `onImportantQueued` nudge for a pull-only peer (#398) is advisory: it never dequeues, steers, journals or settles, and a throw from it never stops an enqueue.
|
|
25
25
|
- Merge recall and restored prefaces through `mergePrefaces`/`uniqueMemoryLines`; preserve distinct context and new notes, deduplicate memory identities, and keep colliding short prefixes distinct. Use `recallDisplayText`/`recallMemoryLines` before display caps: suppress only matching automatic approval outcomes, retain nonempty rejection details, preserve id headers and complete-word excerpts, and keep token trimming at line boundaries. Keep these changes display-only.
|
|
26
26
|
- Observe memory rendering from final `out`, not `originals`, and suppress telemetry when either contains private envelopes. A pull emits rendered/delivered observations only after its journal transaction commits; observer exceptions must never change delivery settlement.
|
|
27
|
+
- Read `Bus.onDelivered`'s optional persisted delivery ID only at its positive-ACK observation site; isolate fallible observers so they cannot skip invocation, pending-delivery or supervision bookkeeping.
|
|
27
28
|
- Classify automatic held-row completion only through `supersededDeliveryReason`: every original must be a hub presence or a provably moot hub task/review notice. An owner offer (`refs.taskOffer`) is moot only while that recipient owns the task in `in_progress`, `in_review` or `changes_requested` and its own `accepted` entry follows the latest ownership event within the 4096-entry bound; a person's acceptance, an unmarked reminder or missing proof keeps the hold (#434). Unknown/live/mixed rows stay held; keep `deliveryTaskContext` display-only. Tag only current-owner notices as owner audience; leave former-owner withdrawals untagged until the task closes. For cancelled tasks, settle earlier workflow notices but retain the stop notice whose `refs.taskCancelled` matches the cancellation revision. Preserve the requested-stop exclusion in `daemon.md`. Record the hub actor through `resolveDelivery`, restoring presence originals through `restorePreface` only for hub completion (person completion stays consumed); run on hold entry, restore, recovery release and board changes, never replay uncertain work. List every hold once in status, console notices and route explanations (#434).
|
|
28
29
|
- `resolveDelivery` mutates queues in place; a refused or failed resolution puts back only that peer's queue and preface and keeps every pause. Never reload a snapshot on a live bus. Keep the selected delivery id and journal revision fixed in console row actions; retry/discard/completed never change a task role or cancel a native turn.
|
|
29
30
|
- Recovery-deferred hub notices (`deferredHub`, #478) are accepted work kept outside the queues and the carried-queue integrity projection: never put them in `queues` or the integrity view while held. Every read of pending work includes them (`pendingEnvelopes`, `withdraw`, `revokeSupervision`, `queuedBacklogIds`, `get`, reset). Past `MAX_DEFERRED_HUB` refuse the publish; never drop a kept notice.
|
|
@@ -12,7 +12,7 @@
|
|
|
12
12
|
- For conductor recovery (#900), keep source-issued private continuity separate from public actor/audit metadata; recheck the same initiator and every affected project at effect boundaries. Compare candidate config and routing with the running accepted policy. Verify and use the same captured startup inputs before any adapter import; never publish a later raw-file reread. Keep captured TUI restoration and unknown source/target evidence person-only. Derive target feature floors from verified target bytes, never a request or receipt claim (`docs/operations.md`, "Conductor-bound restart and recovery continuation").
|
|
13
13
|
- Conductor operational refusals (#812, #839): throw `OperationalAuthorityError` (`src/hub/operational-authority.ts`) for an authority refusal, never a plain `Error`. Only it sets `authorityRefused: true` and its scope on the reply, which the control `reply` wrapper audits once as a refused `agent_cli` event; any other failure of a labelled request keeps its own error with `authorityRefused: false` and is never audited as refused, and the CLI's `fail` audits only when `authorityRefused` is not `false`. An unreachable or dead hub is "authorization unavailable", never a missing conductor role. `publicOperationalView` is cached on the board status revision, config revision, routing and the private-path policy: a new input to that projection joins the cache key.
|
|
14
14
|
|
|
15
|
-
- Peer DOING (#878): `observeDoing` is the only writer and `doingRows` the only reader (`src/hub/daemon.ts`, helpers in `src/hub/peer-doing.ts`). Decide privacy at observation (`contextPii` or a refusing or throwing `tasks.publicMetadataText` latches a stub) and again at display (an open PII task owned or reviewed, or a private turn); reuse those screens, never add one. `status()` adds it only with `includeDoing` (the `status` reply and the dashboard snapshot), so `writeStatus` never writes it to `status.json`; the conductor's `hub_status` gets the same rows, each non-stub value framed by `publicConductorStatus` as untrusted (`sanitize`d text, `
|
|
15
|
+
- Peer DOING (#878): `observeDoing` is the only writer and `doingRows` the only reader (`src/hub/daemon.ts`, helpers in `src/hub/peer-doing.ts`). Decide privacy at observation (`contextPii` or a refusing or throwing `tasks.publicMetadataText` latches a stub) and again at display (an open PII task owned or reviewed, or a private turn); reuse those screens, never add one. `status()` adds it only with `includeDoing` (the `status` reply and the dashboard snapshot), so `writeStatus` never writes it to `status.json`; the conductor's `hub_status` gets the same rows, each non-stub value framed by `publicConductorStatus` as untrusted (`sanitize`d text, `untrusted: true`, and `from` with `doingNote`, or `note: INCOMING_LABEL_NOTE` for a `label` row); never give an agent DOING text without that frame. A `label` row (`unnamedSessionLabel`: an unnamed `title: "hub"` session's first acknowledged incoming delivery, #1055) reaches only the console, the dashboard and the conductor (`includeIncomingLabels`); every other status reader gets `unnamed`. The console `title` op and `hub_set_title` both go through `setSessionTitle` (writer rule: `adapters.md`), and `session-title-inputs.json` keeps only session, delivery and envelope ids (`sessionTitleBindings`, `titlePersistence: "native"` peers only), never a name, body or label. Claude's title comes from the transcript path the facts hook reported and `claudeTranscript` validated, read through `readAiTitle` (1 MiB per read, lines over 64 KiB skipped); its current item only from PreToolUse input, never the transcript; every cached TaskUpdate subject, current or not, is screened when it is learned (`contextPii` or `tasks.publicMetadataText` refusing or throwing, via `ClaudeTodoSource`), keeps that verdict and passes it to `observeDoing` as `learnedPrivate`. A doing failure never changes a facts reply.
|
|
16
16
|
- Crash-recovery lines (#1111): `recoverAfterCrash` adds a line only through its `report(line, notice)`. Tag a line that asks something of a peer's owner with `peer`; it clears when that peer attaches. Tag the headline with `inFlight`; its count is read live from `crashLostIds`. A line about what recovery found or did gets neither and stays. Status shows only `crashView()` (`openCrashNotices`), never the raw list. `crashReported`, which the Jcode TUI hint reads, never clears.
|
|
17
17
|
|
|
18
18
|
- Conductor console ops (#933): a tools caller's `CONDUCTOR_TASK_OPS` request (`github_import`, `intake_approve`, `schedule_run|pause|resume`, `task_unassign`) and `goal_link` are admitted by `admitOperational` in `onMessage` before Tasks sees them, and the task op runs inside `operationalContext`, which `TasksDeps.operational` reads; never let `taskOpBody` admit a non-person caller to a console op any other way. `conductor_operational` `admit` audits a `CONDUCTOR_LOCAL_OPERATIONS` form once; `authorize` only rechecks the role. `task_catalog_role` from a tools caller passes the peer to `Tasks.setRole` (never `USER`), and is refused for a child, external, read-only or PII-context caller.
|
|
@@ -34,6 +34,7 @@ Scope: the operator console, control WS and its protocol, state files, the dashb
|
|
|
34
34
|
- acon plugin (#935): `src/ui/plugin.ts` never bypasses `uiAction`'s whitelist or `DASHBOARD_VIEWS`. It reaches project hubs only through `projectConsole` (`src/hub/manager.ts`), the same listing and forwarding `ahub ui --all` uses; widen neither for the plugin. Its inbox verbs map to the hub's own `permit` with an option the request offers. Its panel is built from `renderDashboard()` by exact rewrites that throw when a target line moves: when you edit a rewritten line in `src/ui/index.html` (post, the cookie helper, `$`, the theme `apply`, the view key listener, the title update or the landmark tags), update `panelAssets()` in the same PR. `lifecycle.ts` is loaded lazily there, because it reaches the daemon and CLI files acon's import scan reads. `ahub setup` registers it only through `registerAconPlugin` (`src/cli/acon-register.ts`): `console/plugins/ahub.json` under the hub home names the module and its sha256 (directories 0700, written to a temp file and renamed); the package export `./ui-plugin` is pinned by `scripts/check-package.mjs`. The Space panel (#1012, `src/ui/office-space.ts`) maps only the #264-allowlisted `officeModel` scene, pinned by the #264 marker fixture in `test/acon-plugin.test.ts`; never feed it an envelope body, task detail, path or symbol. A Space snapshot answers within acon's 5 s: the listing gets half of `SPACE_BUDGET_MS` and falls back to the last listing (a late one still replaces it), projects are read concurrently and a late one is left out of that poll, the cached office model is keyed by the daemon's `instanceId` (a restarted daemon restarts its ui sequence at zero), and `pollMs` stays under acon's 3000 ms link gap.
|
|
35
35
|
- Dashboard Usage view (#683 U4): `uiSnapshot` builds `usage` only when the request's view is `agents`, never on a hidden view's poll. It folds with `catchUp(100)` and sets `behind: true` when that is not done, so a partial figure is never shown as the whole. A failed read answers `{ unavailable: true }`, never `undefined`, which the page reads as a hidden view and keeps the old figures. The snapshot carries task ids, a `pii` flag and numbers only, never a title; the page shows a private task as `#id [pii]`. Daily figures come only from `UsageRollup.daily` (rule: `budget.md`).
|
|
36
36
|
- Dashboard Code group (#684 2c): `codeGroup` (`src/hub/task-code-group.ts`) is the only dashboard classifier of changed symbols against `plan.symbols`, called in `uiSnapshot` with the public `codeSymbols`, `tasks.publicMetadataText` and `tasks.isPii`; the page (`codeGroupSection`) draws it and never matches names. A private task gets no `codeGroup`. A stored receipt (one with `repositoryKey`), a missing or an unknown one is `state: "unknown"`. A malformed or screened-out plan name is counted in `planWithheld` and marks the group incomplete, never dropped. Lists stop at `CODE_GROUP_MAX` (40) with the rest in `more`, and `counts` are totals. The memory view reads a `node` of up to 512 characters only when it starts with `symbol:` (200 otherwise).
|
|
37
|
+
- Worktree shelf (#1057, `worktreeShelf` and `worktreesView` in `src/hub/daemon.ts`; spec `docs/design/task-board.md`): never add a worktree or branch removal to the daemon (its only one stays the empty hub-made task worktree in `src/hub/task-worktree.ts`); shelf removal exists only in the person's `ahub worktrees tidy --confirm` (`src/cli/worktree-tidy.ts`), which `daemon.ts`, `worktree-shelf.ts`, `board.ts`, `ui.ts` and `plugin.ts` never import. GitHub is read only by `worktrees_refresh_prs` (`refreshPrStates`); a snapshot, `ahub worktrees` and the inbox alert never reach it, and `worktreesView` answers from its 30 s cache without awaiting git. The `worktrees` read is open to a tools caller outside a private turn; override set/clear and the refresh are console-only and refused for a live-test dashboard session. After the awaited git reads, ask the recovery/stopping fence again, then read and write the board with nothing awaited between. The board keeps only the person's Park/Pin (`worktree_override`), never a derived settled state, and accepts one only for a path the shelf just listed, never the main checkout or an instance worktree.
|
|
37
38
|
- Agent CLI drift (#843 AC3): the daemon's `sweepAgentDrift` (once after start, then `agentDriftEveryMs`, default one day, on its own timer apart from the task sweep) is the only caller of `noticeDrift` (`src/hub/agent-drift.ts`). It reads versions only through `agentVersionsAsync` (`src/cli/agents.ts`), which shares `probeTarget` and `probeRow` with the `ahub agents` probe: never add a second probe path, a network read or an install. The first probe is owed until one has run (`agentDriftOwed`): a daemon that starts inside a controlled recovery skips it and the 1 s tick pays it once `recoveryActive()` ends, so a restart or upgrade never waits a day for the first notice. `startDaemon` probes only when `agentDrift` is set (`lifecycle.ts` sets `true`; tests inject a function), so a test daemon never spawns a real CLI. Drift is an installed executable with a readable version whose state is not `verified`; a missing or unreadable one is silent. The notice carries id, version, state and the receipt path only, is marked per (id, version, planner) in `agent_drift_notices` before it is published to that planner alone (and forgotten if it cannot be queued, so only that planner is retried), and goes to `plannerPeers()` that are rostered; with none, nothing is recorded. `agentVersionsAsync` reads stdout as a stream with the 64 KiB cap enforced as it arrives (kill and cancel on overflow) and cancels the reader at its deadline and a short grace after the probe exits, so a descendant holding the pipe cannot hang it. Never put CLI output in it.
|
|
38
39
|
- SDLC band timer (#481 S7): the daemon's band interval (`bandsEveryMs`, default 5 min) is the only caller of `tasks.proposeFromBands`. It skips while stopping or in recovery, when `tasks.bandsState()` is not ready or `bandsHeld` holds, and when `board.statusRevision()` with the band signature is unchanged since the last pass that ran; it records that key after the write. A failure is one sanitized `hub.log` line. Writer rules: `tasks.md`.
|
|
39
40
|
- `ahub board` (#724): `renderBoard` prints one line per task only with a terminal width and without `--full`; `--full`, `ahub task show` and output without a width keep the full table and SDLC advice. `STAGE_CODES` (`src/cli/output.ts`) keys on blocker-text prefixes from `src/hub/sdlc.ts`: rewording a blocker there updates its code in the same PR. TITLE is cut, never wrapped; STAGE is never dropped (it falls back to the meter and the code); a PII row keeps its `(ahub task show N)` hint while 8 title columns remain.
|
|
@@ -55,6 +55,8 @@ Scope: the task flow, assignment, the state machine and the hub's MCP tools. Rea
|
|
|
55
55
|
|
|
56
56
|
- Resolve any id that may be an extra instance or a configured kind through the startup-bound project catalog: `this.d.adapterCatalog` with `this.d.instances?.()` in Tasks, `opts.adapterCatalog` in `assign()` and `relayCandidates`, and the `adapterCatalog` hook in `Conductor`/`conductorStart`; a bare `definitionOf(id)` answers builtins only. Only a kind's routing preference expands to its instances; manual ids, reservations and nominations stay exact. Reviewer independence compares families: take the own family only when no other is eligible, record it through `assignmentNote` (`sameFamilyFallback=true`), explicit nominations included; persist the `instance`/`family` refusal codes as `policy`.
|
|
57
57
|
- Tool publication (`projectToolList`, `projectConductorTools`, `configuredToolContext` in `src/hub/hub-tools.ts`) only narrows the list or adds start targets, using authenticated status metadata (`configuredAdapters`, `startablePeers`, `readOnlyPeers`); in a configured project, malformed or incomplete metadata publishes the read-only set and adds no start target. Publication is never authority: `taskOp` refuses read-only mutations independently, and MCP task and conductor refusals keep their text and set `isError: true`.
|
|
58
|
+
- `hub_set_title` (`SESSION_TITLE_TOOL`, #1055) is in `TASK_TOOL_NAMES` but never in `TASK_TOOLS`; its publication rule is in `adapters.md`, and its `taskOpBody` case accepts only `title`.
|
|
59
|
+
- Plan sources (#1144): `propose`, `acceptReviewed` and `accept` call `validatePlanSources` on the plan they will keep (the new one, else the stored one) before any board write or notice; an id the source registry (`TasksDeps.intake.sourceKnown`) does not know is a `PreEffectToolRefusal` naming `ahub source list`. A new path that admits a plan calls it too.
|
|
58
60
|
- SDLC metrics and bands (#481 S7): `sdlc-metrics.ts` is pure (history in, numbers out, no clock, no I/O); every metric a task cannot derive is `null`/`unknown`, never 0, and a private task is only counted as withheld. Stale-head re-reviews need both heads on a round (`reviewCost.head` on the done, `reviewFindings.head` on the verdict): never derive them from event order. `Tasks.sdlcMetrics` is the one read (planner or person, refused during a private turn) and `Tasks.proposeFromBands` the one writer. It is held like every host writer (`sweepHeld`, `bus.isRecoveryHeld`, `bus.storageError`), keys a breach by its episode (`BandResult.episode`, derived from history so a restart finds the same key), writes nothing while the metric has an open band card, and records a log notice in `band_notices` so it is announced once. Card text is fixed words, numbers and task ids, screened through `signalsFor` and `candidateTextAllowed` like the recurring-finding card; never put task titles, notes or PR text in it, and never open a GitHub issue. Drift honours `min_delta` like every tier. `[bands]` is validated by `bandsConfig` with field-level paths; do not add a tier or a metric without a test that fires it. Merge-time figures (#442) come from `MergeRecord.cost`, written once by `guardedMerge` after it observes the merge (`mergeCostFrom`, `cleanMergeCost` in `cost-governance.ts`): re-validate a stored record with `cleanMergeCost` before use, never let that read touch the merge result, and keep a missing or partial read `null`, never 0. `hotfix_rate` and `security_open_alerts` are external series (`EXTERNAL_METRICS`, `Tasks.externalSeries`) whose points are releases and scans, so a card names its `subject`; a legacy release with no train history is left out of the series, never a 0. The security scan (`security-baseline.ts`, `Tasks.runSecurityScan`) reads GitHub's own open-alert counts, runs no scanner, schedules nothing without `[security_scan]`, is held like every host writer, re-judges its schedule after the awaited read and records counts only; a source it cannot read is an unknown total, never zero.
|
|
59
61
|
- Admit a declared extra (a person-started `ahub claude --as` instance and a headless Claude implementer included, #742, #747) as owner of any class whose owner role is implementer (`ownerRoleOfClass`) only with the current implementer role and `ownerIsolation[id] === true`, the daemon's proof that the live adapter generation started in `executionRoots[id]` outside the canonical cwd. Never derive it from the declaration's `worktree`, its roles or a reload. Only the status path passes a display `ownerIsolation` projection (`stalled(projection)`); every admission and effect caller uses fresh `TasksDeps.ownerIsolation`.
|
|
60
62
|
|
|
@@ -82,13 +84,13 @@ Scope: the task flow, assignment, the state machine and the hub's MCP tools. Rea
|
|
|
82
84
|
- `Tasks.retryReviewer` is host-only (the daemon's reviewer `onUnavailable`), never a tool or CLI path. It acts only while the failed peer is still the reviewer of an `in_review`, non-PII task, with the same review head, the request's `reviewRequestRevision` and a current child generation; `valid()` rechecks all of it after every await and right before the write, which also passes the flow revision. A manually named reviewer's `no_verdict` and a no-verdict with no declared pool go only to the person; other manually named failures keep explicit person control. Confirm fallback only after accepted reviewer publication or a person offer and a fresh binding/generation check; failed publication or notification never confirms settlement. Each failed peer is tried once per head and round (`reviewerFailure` history). `reviewRequestRevision` counts only role events (`done`, `reviewer changed`, `reviewer unavailable`, ownership), so bookkeeping never fences a request; keep `refs.reviewRevision` and `refs.commit` on the review envelope.
|
|
83
85
|
- Release trains (#481 S11): change trains only through `Tasks.newRelease`, `configureRelease`, `assignRelease` and `cutRelease` (planner or person), and refuse every `hub_release_*` in a PII turn. `assignRelease` writes only `release` and refuses a frozen, shipped or dropped release on either side; private or pinned scope is person-only. One active or frozen train at a time. A cut freezes only through `Board.freezeRelease` after a fresh `releaseWindow` (host lease from `readReleaseHostAdmission`, quota, `current()`, at most 5 s old); unknown evidence defers, and the window is closed in `finally`. A cut never publishes, tags or upgrades, and upgrade and recovery never consult `releaseWindow`. Keep two of `RELEASE_TRAIN_HISTORY_LIMIT` slots for cut and ship; a configuration no-op appends nothing. An S9 plan receipt survives only contiguous planning revisions that changed exactly `["release"]`.
|
|
84
86
|
- Release authorization (#481 S6, `src/hub/sdlc-release-authorization.ts`): the console user or the one configured conductor through `sdlc authorize-release` creates one under the same admission as `sdlc merge` (tools role, matching configured conductor, no `toolsOwner`, private turn or caller PII context). Keep the actual asker in receipt `by` and the audit, and the cooperative explicit-person-instruction rule in AGENTS.md Releasing. Keep consent bound to the current HEAD commit, its committed package version, the origin identity and a fresh, error-free docs verdict, all reread after the docs check. It lives in daemon memory only: a restart revokes it and the event is audit, never restored. `release-guard` and `release-guard-hook` are refused in a private turn; a tag push needs a matching receipt, and npm publish also a clean tracked worktree and the same docs digest. Never accept authorization from a tool, a task receipt or a file.
|
|
85
|
-
- `Board.update`'s untouched-return exception is the only way owned or accepted work returns to backlog: keep every condition (exact event, owner and reviewer null, state, `expectedFlowRevision`, non-empty note, no work evidence) and reach it only through `Tasks.returnUntouchedToBacklog` (#481 S9).
|
|
87
|
+
- `Board.update`'s untouched-return exception is the only way owned or accepted work returns to backlog: keep every condition (exact event, owner and reviewer null, state, `expectedFlowRevision`, non-empty note, no work evidence) and reach it only through `Tasks.returnUntouchedToBacklog` (#481 S9). Optional return `fields` accepts only `reserved`, follows the existing reservation authority and target-admission rules before and after screening, and saves the reservation with Board-derived planning history and revision in the same state write before capacity notices or auto-pull; omitted fields retain the plan (#1154).
|
|
86
88
|
- The main-checkout note (`mainCheckoutWarning`, `src/hub/main-checkout.ts`, #572) is advisory text in the `hub_task_done` result, the review request and status, never a refusal. A branch name or other metadata the hub reads from outside the board reaches task text only after `Tasks.publicMetadataText` (PII patterns, secret shapes, private paths) and a plain ref-name check; otherwise use the generic sentence that carries none of it.
|
|
87
89
|
|
|
88
90
|
- No-write advisories (#498): count only host-captured, completed owner turns bound to the current acceptance of an `implement` or `bulk_edit` task. Require a known empty snapshot diff, unchanged HEAD/shared branch refs and no task-tool effect; changes, effects, pending/unknown effects or unknown captures break the streak. Treat empty canonical-root captures as unknown when an owned task records branch/commit, worktree paths or SDLC PR/head evidence. Require exclusive Git worktree scope at both boundaries and no `.worktrees` directory before trusting negative canonical evidence; uncommitted and detached worktrees otherwise remain unknown. Keep builtin-owner D1 inert while `.worktrees` exists; narrow this deliberate false-negative ceiling only after binding each owner to its real execution worktree. Keep `task_sweep.no_writes_turns` independent of the time sweep (default 2, 0 disables), and route structured `no-writes` stalls through the existing public-title planner notice and grooming paths.
|
|
89
91
|
- At done, add `no writes recorded since acceptance` to review only when the daemon proves no owner touches since the first acceptance after the current ownership boundary and an empty acceptance-to-now diff, with no branch, review head or PR evidence. Preserve that baseline and earlier touches across plan-revising re-acceptance. Require a canonical-root D2 result to pass the same elsewhere-hint and exclusive-current-worktree checks before treating it as negative evidence. Bind extra-instance evidence to its current verified execution owner/root; missing, private, disabled, expired-budget or restart-lost baselines stay unknown. Capture through the guarded `TasksDeps.ownerAccepted` callback for explicit accept/self-claim only; an implicit done acceptance has no pre-work baseline and remains unknown. Do not capture private work for this advisory, block done, or accept evidence flags from models.
|
|
90
92
|
|
|
91
|
-
- `ahub report --dogfood` (#687, `src/hub/dogfood.ts`, `src/hub/dogfood-read.ts`) opens `hub.db` read-only with byte and row caps; an unavailable or incomplete source reports `null`, never 0, and per-PR GitHub reads run only when the
|
|
93
|
+
- `ahub report --dogfood` (#687, `src/hub/dogfood.ts`, `src/hub/dogfood-read.ts`) opens `hub.db` read-only with byte and row caps; an unavailable or incomplete source reports `null`, never 0, and per-PR GitHub reads run only when the PII inventory is complete. That inventory (`dogfoodPiiInventory`, #1147: ids and PR refs of PII tasks, no text, 2000-task cap) is read from the whole board, while `dogfoodTasks` takes a `{since, until}` window and the PR numbers of the merged PRs, so a board over 500 tasks still reads complete and every sibling task of an in-window PR is read whatever its update time (a partial sibling set would give wrong lead times, so overflow makes the source incomplete); `dogfoodPrs` without an inventory argument still uses the task list as the inventory.
|
|
92
94
|
- Qualified titles (#723): `planIssueIdentity` links a sibling `repo#N` only when `refs.repo` names that repository (`siblingTitleRefs`, `spec-read.ts`), and an `owner/repo#N` only when it is `refs.repo`, else this project's repository; a sibling shorthand beside a bare `#N` is two identities and links neither. A title that names another repository with no `refs.repo` does not link, and `refsRepoHint` tells the proposer to set it (advice in the `hub_task_propose` result, never a refusal; none in mode off, for a PII task or once `refs.repo` or an issue is set). There is no retitle path on the board: a title is fixed at propose.
|
|
93
95
|
|
|
94
96
|
- Planner walk and reviewer independence (#525-3): `plannerWalk` and `implementers` in `src/hub/routing.ts` are pure; the planner is chosen only inside `assign()` (`planners`, `namedPlanner`, `keptPlanner` in, `Assignment.planner`/`plannerReason` out), and `Tasks` never picks one outside it. `Tasks.weights()` is the one place `planners` is built (rostered holders of `d.roles`, sorted), so every `assign()` path and `explain` agree. Pass a bound task as `keptPlanner` to that same walk: its explanation names the binding as `kept`; drafts and legacy null bindings still rotate, and explanation never writes a binding or history. Rotation is `task.id mod n` over every holder and is never renumbered by availability; a named planner is exact and never replaced. I-2 (`implemented this task`) lives in `blocked()` for the reviewer role as its last capability/independence check before queue admission, so the legacy, named and pool paths share it and it only ever removes an otherwise eligible candidate; when I-2 removed every otherwise eligible reviewer, the person reviews with `reviewBlocked`, never an implementer.
|
|
@@ -4,6 +4,7 @@ Scope: tests, fakes and the test gate. Read before adding or changing a test or
|
|
|
4
4
|
|
|
5
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
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.
|
|
7
|
+
- On Bun 1.4.2, preserve original fields before `toMatchObject` with asymmetric matchers, or assert primitive fields and strings separately; never use a matcher-mutated value for a later content/canary assertion. Recheck this restriction when upgrading Bun.
|
|
7
8
|
- A daemon fixture that routes write-class work (`implement`, `bulk_edit`) to a hub-sandboxed peer (Pi, `local`) runs in a throwaway repository made with `gitProject()` (`test/fakes/git-project.ts`), never in `ROOT`: the agent-hub checkout carries the machine's git config (a hosted runner's checkout has an `includeIf` credentials entry the hub refuses), so such a test passes on one machine and fails on another, and it would make a hub worktree and `task/*` branch inside the checkout under test (#1129). Before calling a hosted-only failure a flake, reproduce the runner's condition in a scratch clone.
|
|
8
9
|
- A fixture that walks a policy table (a `COMPAT` entry list, a registry) derives its per-entry values from the table, never a fixed list of keys or versions a planned entry is not in: the compat watch test builds every served version and the ahead/unknown row from `COMPAT[id]`, so an added entry (`pi`, #843 AC2) is covered without editing the test.
|
|
9
10
|
- 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.
|
|
@@ -29,6 +30,7 @@ Scope: tests, fakes and the test gate. Read before adding or changing a test or
|
|
|
29
30
|
- 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. `scripts/check.sh`, `--changed` included, never runs the seeds, so rot shows only in CI's `seeded guards` job (the `profile-cli-dry-flags` anchor needed a search-text update in both #1100 and #1110, 2026-11-02). Before pushing a change to a file `scripts/seeds.json` names, confirm every anchor still matches once: `bun -e 'const f=require("fs");for(const x of JSON.parse(f.readFileSync("scripts/seeds.json","utf8")).seeds){const n=f.readFileSync(x.file,"utf8").split(x.search).length-1;if(n!==1)console.log("seed rot:",x.id,n)}'` prints nothing when every anchor is intact. 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). On seed rot the checker names the anchor's symbol at a base commit and where it is now (`seedRotContext`): `--base <commit>` sets that base (CI passes the PR base from a `fetch-depth: 0` checkout; keep both), and without it the base is `merge-base origin/main HEAD`. The context is evidence only: it never relaxes the one exact match, and an unavailable base says so instead of guessing.
|
|
30
31
|
- `scripts/lib/ts-ast.ts` is the one TypeScript AST helper for scripts and source-scanning tests (`parseSource`, `callSites`, `symbolSpans`, `enclosingSymbol`, `findSymbol`; inline HTML scripts keep their offsets). It gives lexical locations only, never resolved call edges, and stays dev tooling: never import it from `src/`.
|
|
31
32
|
- The native branch ratchet (`test/daemon-native-branches.json`) is reviewed data, never regenerated by a test (rule: `adapters.md`). `test/daemon-native-proof-gates.test.ts` runs daemon code cut out of `src/hub/daemon.ts` by its text, from ` function frontendProxyState(` to ` const manualPaused =` and from ` async function changePermission(` to ` async function confirmPermissionDefault(`, and the ` const isSupportedPeer =` and ` const readOnlyPeer =` bindings: renaming, reordering or reindenting them updates the test in the same PR. Test Claude launch permission proof through `createClaudeLaunchProof` in `src/hub/claude-launch-proof.ts`; never restore the removed daemon wrapper just to slice it in a fixture.
|
|
33
|
+
- The #1057 worktree tests cut source by its text: `test/worktree-shelf.test.ts` reads `src/hub/daemon.ts` from `case "worktrees":` to `// #604 (agent-hub#1030)`; `test/worktree-panel.test.ts` reads it from `async function worktreeShelf(` and `function worktreesView(` to `async function uiMemorySnapshot(`, `src/ui/index.html` from `function renderWorktrees(shelf)` to `function renderBench(bench)` (plus the helper lines it names) and `src/hub/sdlc.ts` from `export async function readSdlcPr(` to `export async function readSdlcPrHead(`. Moving, renaming or reordering those lines updates the test in the same PR. The removal-module lists pinned there and in `test/worktree-tidy.test.ts` (`src/hub/code-facts.ts`, `src/hub/task-worktree.ts`, `src/cli/worktree-tidy.ts`) are never extended to let a new removal pass (rule: `daemon.md`).
|
|
32
34
|
|
|
33
35
|
- `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.
|
|
34
36
|
- `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.
|
package/docs/design/adapters.md
CHANGED
|
@@ -71,6 +71,62 @@ Native CLI compatibility is centralized in `compat.ts`. Each gated operation ask
|
|
|
71
71
|
|
|
72
72
|
External adapter modules are an explicit, person-owned, machine-local opt-in. The daemon verifies and pins the captured bundle at startup, then admits only the bounded headless ACP instance contract through host-managed launch and current-owner checks. Module code runs in the daemon trust domain; pinning is integrity evidence, not a sandbox. Modules cannot supply native reviewer engines, proof ports, permission callbacks, provider selectors, or arbitrary recovery authority. This ceiling limits what imported code can claim and prevents live reload from replacing the executable catalog ([#754](https://github.com/STAIxBWLB/agent-hub/issues/754)).
|
|
73
73
|
|
|
74
|
+
## Session title slots
|
|
75
|
+
|
|
76
|
+
Only fixed Pi and local definitions opt into `title: "hub"` (#1055). Their
|
|
77
|
+
standalone `hub_set_title` schema is excluded from native-peer tool lists, and
|
|
78
|
+
the daemon independently checks the resolved definition and current owner.
|
|
79
|
+
An agent fills only an unnamed slot; a person's console rename replaces it.
|
|
80
|
+
Pi's native session name is canonical and its bridge rechecks the native name,
|
|
81
|
+
owner, session and generation before writing. Local keeps its name in the
|
|
82
|
+
worker's existing in-memory session. Both expose a separate `titleSession`
|
|
83
|
+
identity for display continuity; Local's native-session/provenance authority
|
|
84
|
+
does not change. Titles pass the current metadata/privacy screen at write and
|
|
85
|
+
display. No helper inference or task/Git/instance naming path is involved.
|
|
86
|
+
|
|
87
|
+
Refusals keep the `already_named`, `private` and `invalid` reason codes and
|
|
88
|
+
include a public `error` explaining the cause and next step. Errors identify
|
|
89
|
+
length, offline/owner, session/generation and confirmation failures without
|
|
90
|
+
copying the supplied title or private details. If a write might have landed,
|
|
91
|
+
the result says it may have been applied and directs the caller to inspect
|
|
92
|
+
`ahub status` before retrying.
|
|
93
|
+
|
|
94
|
+
An unnamed label is derived from the first positively acknowledged original,
|
|
95
|
+
never stored as a name. The #1055 restart-recomputation design is amended with
|
|
96
|
+
`session-title-inputs.json` under the daemon state directory: the journal does
|
|
97
|
+
not itself bind an original delivery to the recipient's native session.
|
|
98
|
+
Only fixed definitions declaring `titlePersistence: "native"` retain restart
|
|
99
|
+
bindings. This private, atomically replaced file maps each retained peer ID
|
|
100
|
+
to exactly three opaque IDs, `session`, `delivery` and `envelope`. It contains no source body,
|
|
101
|
+
session title or derived label. Only the first positive delivery acknowledgement
|
|
102
|
+
for a peer's current title-session can create or replace its entry;
|
|
103
|
+
`hub_set_title` and person rename never write it.
|
|
104
|
+
|
|
105
|
+
The file is limited to 128 peer entries and 64 KiB of serialized UTF-8,
|
|
106
|
+
including JSON formatting and the final newline. Each stored ID is nonempty,
|
|
107
|
+
at most 512 UTF-16 code units, and excludes control and format characters.
|
|
108
|
+
An unreadable, malformed, oversized, symlinked or multiply linked input is
|
|
109
|
+
ignored. Capacity or write failure leaves delivery successful and makes that
|
|
110
|
+
new restart binding unavailable.
|
|
111
|
+
|
|
112
|
+
Native-session bindings survive daemon stop/restart until replaced by the
|
|
113
|
+
first acknowledged delivery of a later session for that peer, or until the
|
|
114
|
+
state directory is removed. Entries for removed instances are dropped when
|
|
115
|
+
their removal applies. Local's session exists only in daemon memory and gets
|
|
116
|
+
a fresh ID each start, so its binding is not retained on disk; startup removes
|
|
117
|
+
legacy Local entries. A stale native-session entry grants no display value: restore
|
|
118
|
+
requires the same title-session ID and the original still retained in that
|
|
119
|
+
project's journal. A different session or pruned original yields no inherited
|
|
120
|
+
label. There is no separate retention of message text and no timer or automatic
|
|
121
|
+
replay. The derived label remains absent from disk status, events, logs,
|
|
122
|
+
capture and rooms.
|
|
123
|
+
|
|
124
|
+
Incoming-message labels are visible only to the console and the configured
|
|
125
|
+
conductor. Other status readers receive `unnamed`. A label has its own source
|
|
126
|
+
tag and explanation: its text comes from an incoming message and is never
|
|
127
|
+
attributed as speech written by the session's peer. Public-metadata screening
|
|
128
|
+
and private stubs still apply to authorized label viewers.
|
|
129
|
+
|
|
74
130
|
## Evidence boundary
|
|
75
131
|
|
|
76
132
|
Compatibility probes and fake transports verify source contracts, not an account login, live model turn, native permission prompt, or operating-system isolation. Those receipts remain separate because protocol inventory alone cannot prove runtime behavior ([#625](https://github.com/STAIxBWLB/agent-hub/issues/625), [#754](https://github.com/STAIxBWLB/agent-hub/issues/754)).
|
|
@@ -60,6 +60,18 @@ The hub's portable memory storage, sync, indexing, and project helpers come from
|
|
|
60
60
|
|
|
61
61
|
## Privacy and recovery
|
|
62
62
|
|
|
63
|
+
Session titles for the two hub-owned title slots (#1055) are conditional writes
|
|
64
|
+
to the current adapter session, under the same open-PII-task/private-turn and
|
|
65
|
+
public-metadata screens as DOING. Person rename remains console-only. Live
|
|
66
|
+
unnamed labels are computed from acknowledged source messages and shown only
|
|
67
|
+
to the console and configured conductor; other readers see `unnamed`.
|
|
68
|
+
Their source note identifies incoming text, not speech by the recipient.
|
|
69
|
+
Only opaque
|
|
70
|
+
session/delivery/envelope bindings persist in `session-title-inputs.json`, never
|
|
71
|
+
copied labels. Its fields, bounds and lifetime are defined by the
|
|
72
|
+
[session-title storage contract](adapters.md#session-title-slots). Native title,
|
|
73
|
+
task title, Git branch and instance identity remain independent.
|
|
74
|
+
|
|
63
75
|
Privacy decisions are made at hub boundaries, not delegated to model routing. A private task or turn is represented by a private envelope and a restricted public view; task text is screened before it enters memory, shared notices, or model calls. The PII screen can run on-device; its gateway path requires positive campus admission, so an unknown network result is not treated as safe. This makes each downstream observer consume the same host decision rather than infer privacy from a label or vendor ([#198](https://github.com/STAIxBWLB/agent-hub/issues/198), [#752](https://github.com/STAIxBWLB/agent-hub/issues/752)).
|
|
64
76
|
|
|
65
77
|
Recovery is a coordinated daemon operation over separately persisted board, delivery, and native-session evidence. A restart or upgrade can continue only with compatible feature evidence from the verified target and host-held proofs that still match the project and actor. Adapters report native evidence; they do not certify their own recovery authority. This avoids treating a stale status file or an unverified receipt as proof that a native process is safe to resume ([#37](https://github.com/STAIxBWLB/agent-hub/issues/37), [#625](https://github.com/STAIxBWLB/agent-hub/issues/625), [#900](https://github.com/STAIxBWLB/agent-hub/issues/900)).
|
package/docs/design/bus.md
CHANGED
|
@@ -33,6 +33,14 @@ The bus applies limits to a sender, sender-recipient pair, important messages, a
|
|
|
33
33
|
|
|
34
34
|
The in-memory bus schedules messages; `DeliveryJournal` durably records an individual transport attempt and the original envelopes behind any rendered output. A peer receipt such as `accepted` or `completed` describes transport, not the corresponding task's acceptance, completion, or approval. That separation lets a timeout or restart remain explicitly uncertain without inventing a task outcome ([#37](https://github.com/STAIxBWLB/agent-hub/issues/37)).
|
|
35
35
|
|
|
36
|
+
For #1055, `Bus.onDelivered(peer, originals, deliveryId?)` exposes the existing
|
|
37
|
+
journal delivery ID to the observer at the first positive transport ACK. The
|
|
38
|
+
optional argument accompanies the original envelopes, including when the
|
|
39
|
+
rendered delivery was condensed. Queueing supplies no positive-ACK evidence,
|
|
40
|
+
and an observer exception cannot alter delivery settlement. This supports the
|
|
41
|
+
[ID-only session-title restart binding](adapters.md#session-title-slots) without
|
|
42
|
+
changing dispatch, retry, task acceptance or task-completion authority.
|
|
43
|
+
|
|
36
44
|
Push peers receive bounded batches. Condensation is optional, excludes important, remote, and invocation messages, and keeps `originals` separate from `out` so replies, safe failures, and recovery still refer to what the peer was actually given. On an eligible safe retry, a failed batch falls back to individual envelopes, and a previously failed envelope goes alone so a poison item cannot block its neighbors. These bounds preserve queue progress when summarization or one recipient fails ([#713](https://github.com/STAIxBWLB/agent-hub/issues/713), [#553](https://github.com/STAIxBWLB/agent-hub/issues/553)).
|
|
37
45
|
|
|
38
46
|
Pull-only peers catch up oldest-first under a byte budget, but the first envelope always goes even when it exceeds that budget so one oversized item cannot starve the queue; push peers use the digest batch limit. Pull records returned envelopes as completed at handout because the tool result is the readback, not a later native receipt. A busy Claude channel can also pull its own messages through `hub_inbox`; that avoids making a long-running turn an unbounded queue lock ([#493](https://github.com/STAIxBWLB/agent-hub/issues/493), [#713](https://github.com/STAIxBWLB/agent-hub/issues/713)).
|
|
@@ -47,6 +47,8 @@ paths:
|
|
|
47
47
|
- src/cli/handoff-args.ts
|
|
48
48
|
- src/cli/handoff-output.ts
|
|
49
49
|
- src/hub/task-list-page.ts
|
|
50
|
+
- src/hub/worktree-shelf.ts
|
|
51
|
+
- src/hub/worktree-prs.ts
|
|
50
52
|
---
|
|
51
53
|
|
|
52
54
|
# Task Board
|
|
@@ -69,6 +71,14 @@ Automatic offer and backlog paths respect `plannedStart`; a future card becomes
|
|
|
69
71
|
|
|
70
72
|
Urgent acceptance can reclaim only enough unaccepted, unstarted, non-pinned and non-urgent offers to fit the limit. It does so after other admission checks and immediately before the accept write; if insufficient, it reclaims none. Accepted work is never displaced merely to satisfy a new urgent request, because an offer and an active commitment have different costs ([#1080](https://github.com/STAIxBWLB/agent-hub/issues/1080)).
|
|
71
73
|
|
|
74
|
+
Untouched returns may atomically update `reserved` while moving eligible proposed or accepted work back to Backlog. `Tasks` retains the existing authority, execution-evidence, pin, release and reservation gates, including post-screen rechecks. An explicit reservation patch advances planning revision and records its prior value in the same state/history write, before capacity publication can trigger auto-pull. Clearing it therefore parks the card; omitting fields preserves its planning. Assignment and auto-pull selection remain the existing rules, with no time-based suppression ([#1154](https://github.com/STAIxBWLB/agent-hub/issues/1154)).
|
|
75
|
+
|
|
76
|
+
## Source-backed plans and request intake
|
|
77
|
+
|
|
78
|
+
For a profile that supports `plan.sources`, `Tasks` validates every retained source ID against the source registry at proposal and acceptance, before board writes, admission effects or peer notices. Unknown IDs are refused with the ID and `ahub source list` guidance. Overlap notices compare the registry IDs; private task plans remain withheld by the shared public view.
|
|
79
|
+
|
|
80
|
+
Natural-language intake from the CLI or dashboard enters the same `Tasks.intake` path and creates held draft Backlog cards, not offers. Dashboard task replies use the public view, and a ticket-bound live-test session cannot create these person-attributed drafts. Approval and discard retain their existing authority. Read-only source snapshots carry only ID, kind, byte size, hash prefix and registration time, with no content or origin paths ([#1144](https://github.com/STAIxBWLB/agent-hub/issues/1144)).
|
|
81
|
+
|
|
72
82
|
## Completion, review and evidence
|
|
73
83
|
|
|
74
84
|
Completion binds the submitted full head and any verified deliverable records. It may run a configured check before requesting review. A review records a verdict against that evidence; it does not itself merge a PR. An unmerged approved task can receive a new owner submission, superseding the earlier review binding rather than treating old approval as authority for changed code ([#1047](https://github.com/STAIxBWLB/agent-hub/issues/1047)). A re-file cannot silently inherit an old head after the linked PR moves, and an approval with no supplied head refuses known-stale evidence. Refusals name the recorded and supplied or read heads and direct the owner to re-file `hub_task_done` with the new full SHA in `refs.commit`; summary text is not head evidence. This prevents stale approval from being presented as review of changed code ([#1121](https://github.com/STAIxBWLB/agent-hub/issues/1121)).
|
|
@@ -84,3 +94,11 @@ Agent list reads return bounded, newest-updated compact pages built from public
|
|
|
84
94
|
Lifecycle policy can be off, advisory or enforced; this is separate from always-applicable role and privacy invariants. Plans, cost/check scope, review findings and external evidence retain their own typed provenance. Research campaigns and experiments likewise distinguish declared, host-observed and runner-attested facts instead of promoting prose to a signed result ([#481](https://github.com/STAIxBWLB/agent-hub/issues/481), [#932](https://github.com/STAIxBWLB/agent-hub/issues/932), [#1018](https://github.com/STAIxBWLB/agent-hub/issues/1018)). Detailed research contracts remain in the [campaign design](../specs/2026-11-01-research-campaigns-design.md).
|
|
85
95
|
|
|
86
96
|
When the specs policy is enabled, covering design specs participate in proposal/accept notices and completion/review reports. Report mode informs; require mode needs an updated spec or a recorded acknowledgement for each applicable unchanged spec. Commitless and verified no-change work remains quiet, and private evidence is withheld rather than exposed to satisfy a report. These modes preserve owner choice while making design drift visible ([#1064](https://github.com/STAIxBWLB/agent-hub/issues/1064)).
|
|
97
|
+
|
|
98
|
+
## Worktree shelf (#1057)
|
|
99
|
+
|
|
100
|
+
The board stores facts about worktrees and never a verdict. `worktree_override` holds only a person's Park (`settled`) or Pin (`active`) per absolute path and its time; whether a worktree is settled is derived on every read by the pure `partition` from the git facts, the bound task (the open or latest task whose `refs.branch` is the worktree branch, never a PII task), the PR state and those overrides. An override counts only while no newer activity exists, and a later write removes an inert one.
|
|
101
|
+
|
|
102
|
+
The PR state has two sources and no others. The board's own evidence says `merged` only through the guarded merge's record (`sdlc.merge`); a cancelled task or a task with no record says nothing about its PR and reads `unknown`. The person's explicit refresh (`worktrees_refresh_prs`, `src/hub/worktree-prs.ts`) reads the PR through `readSdlcPr`, bounded and one at a time, and keeps `open`, `merged` or `closed` with GitHub's own change time in the daemon's memory. `worktree-shelf.ts` holds no GitHub access, so a panel read, `ahub worktrees` and the inbox alert make no GitHub call.
|
|
103
|
+
|
|
104
|
+
A worktree whose activity could not be read is live and never settled. The shelf reads git asynchronously under a total budget and answers `complete: false` when it is cut; the daemon's event loop is never held. The daemon deletes no worktree and no local branch: its git port allows three read argv shapes, and removal exists only in the person's own `ahub worktrees tidy --confirm`, which is console class and issues `git worktree remove` and `git branch -d`, never `--force` or `-D`. Operations: `docs/operations.md`, "The worktree shelf (#1057)".
|
package/docs/dogfood.md
CHANGED
|
@@ -19,6 +19,7 @@ agent-hub is developed with agent-hub. This page lists every feature, whether it
|
|
|
19
19
|
| Encrypted rooms (#446 X1) | off; source-only crypto/wire, with no broker, room link, invitation CLI or delivery enabled | real WebCrypto vectors and negative wire fixtures; atomic memory fake ports establish ordering only; durable X3 state and the X7 two-machine person receipt remain unverified |
|
|
20
20
|
| Blind room broker (#446 X2) | off; standalone source/fixture work, no public TLS service or hub room link configured here | fake-client auth/fan-out/revocation/restart, ciphertext-only child-process canary, owner admission/offline room reset, queue caps/TTL/FIFO/migration, safe directory modes, and person-only CLI tests; live proxy/account/X7 receipts remain unverified |
|
|
21
21
|
| Room membership and presence (#446 X3) | off; no person room enrollment or exposure configured here | isolated fake-hub/broker tests for OOB enrollment, MITM refusal, durable sequence/replay/grace, explicit fingerprint confirmation/abort, refused-frame retirement, exposure revocation and ids-only presence; room list/status for ids and link state; X4 chat and X7 two-machine live receipt remain separate and unverified |
|
|
22
|
+
| Worktree shelf (#1057): `ahub worktrees`, `tidy`, `tidy --confirm <fingerprint>`, the Agents panel section, Park/Pin, the explicit PR refresh and the inbox alert | fixture-only until a person uses it, and only after release and upgrade: this repository keeps every task in `.worktrees/<name>`, so it is the first real use, but nothing here has run `tidy --confirm` yet, and `worktrees.quiet_days` stays 7 until the owner has used it for a week | `ahub worktrees` live and settled counts over a week, and how many settled rows a person then Pins (a Pin means a rule was wrong); `tidy` preview refusals by reason (`task not approved or cancelled`, `unpushed commits`, `branch changed since preview`); after a confirm, `git worktree list` against the preview; test/worktree-shelf.test.ts, test/worktree-tidy.test.ts and test/worktree-panel.test.ts (a PII row stays live and redacted, a branch switch refuses, no GitHub call on a read, the daemon removes nothing) |
|
|
22
23
|
| Message bus, digests, priorities | on | `ahub report`: messages, digests, overflow, undeliverable; delivery latency from events |
|
|
23
24
|
| Control-send recipient normalization (#963) | active after release and hub upgrade; this PR does not change the running hub | string recipients reach only the named peer, invalid shapes publish nothing, omitted/empty arrays preserve broadcast; real native recipient behavior remains unverified |
|
|
24
25
|
| Compact board (#724) | on | `ahub board` at 80, 120 and 200 columns: one line per task (test/board-compact.test.ts) |
|
|
@@ -46,7 +47,7 @@ agent-hub is developed with agent-hub. This page lists every feature, whether it
|
|
|
46
47
|
| Codex planner sandbox and role visibility (#525-6) | source/fixture coverage after merge and release; six declared planner/implementer/reviewer instances are not configured here, and the person live leg is unverified | fake thread-start/resume read-only confirmation and refusal before adoption; planner-only Claude mutating-tool grants withheld; status form/capable roles/isolation and task planner/holder-reason parity across CLI, console, dashboard and explain; the native six-instance receipt in `docs/smoke.md` is recorded separately when run |
|
|
47
48
|
| Floor policy (#999 T3, `action_policy.floor`) | source and test after merge and release; off here (a person turns it on in the untracked `config.local.json`), and the running hub takes it after upgrade | `test/action-floor.test.ts` (every allowed, asked and opaque case, status guard rows) and `test/daemon-action-floor.test.ts` (the Pi permit path through the real daemon: allowed calls run with no prompt, force and default-branch pushes, tags, `gh release`, `--admin` and other repositories ask); T4's live receipt records it under Orca |
|
|
48
49
|
| Confirmation-point inventory (#999 T0) | source and test coverage; `ahub autonomy status` reads the inventory after this feature is released, with no default prompt change from the data alone | `test/confirmation-matrix.test.ts`: every adapter-catalog kind has a tool-approval and a classed-action row per environment, kinds facts equal the definitions, cited source lines exist; the `unknown` rows are the list T4's live receipt must close |
|
|
49
|
-
| Project autonomy preset (#999 T1) | off on this repository until released and explicitly enabled by the person; project scope only | focused person-only, retained-consent, metadata-independent off and authenticated/fallback preset undo tests; the preset writes `action_policy.floor` (#999 T3) and off removes it; status
|
|
50
|
+
| Project autonomy preset (#999 T1) | off on this repository until released and explicitly enabled by the person; project scope only | focused person-only, retained-consent, metadata-independent off and authenticated/fallback preset undo tests; the preset writes `action_policy.floor` (#999 T3) and off removes it; status leads with a peer table separating current modes from desired defaults, groups identical floor verdicts and unknowns once with peer lists (#1137), and reads each floor class from the host verdict (`floorEnforcement`), naming the unverified guard where the hub does not see the call first; the Orca zero-prompt native task remains T4 |
|
|
50
51
|
| Machine autonomy and used Claude homes (#999 T2) | off until released and explicitly opted in by the person; no real home is changed by fixture tests | trusted-default/unsafe-default startup consent, all-project preview/apply, init inheritance with unchanged absent-file ask defaults, and isolated per-home plugin/hook setup/status tests; #1069 covers preserving project/local plugin rows while configuring only user scope, plus recording `start.agents` after a failed home step; metric: applied/refused registered targets and missing/unknown discovered-home states; native Orca receipt remains T4 |
|
|
51
52
|
| Read-time memory code neighbours (#684 phase 4) | off until released; dashboard toggle is opt-in | derived call edges with current SHA; attached fail/decision brief coverage; explicit unknown/truncated results; unchanged authoritative memory log bytes |
|
|
52
53
|
| Canonical memory symbol anchors (#684 phase 3) | off until released; optional `doctor --stale-anchors` report only | actual-symbol completion anchors versus plan guesses; symbol-related earlier notes at memory hop two; stale/unknown/truncated report counts; zero note mutations during inspection |
|
|
@@ -99,8 +100,8 @@ agent-hub is developed with agent-hub. This page lists every feature, whether it
|
|
|
99
100
|
| amem runtime integration (agent-mem#13 P4) | on after release; no claude-mem worker dependency | no-worker tripwire requests (must be 0), screened capture rows, profile refusals, all-author short-term recall, bounded-view truncation notices and curated-only ask citations |
|
|
100
101
|
| Shared memory through amem, briefs, recall preface | on; library intent briefs and plan guard (#436) take effect after release and hub upgrade; legacy index/timeline reads and worker notices retire in intent mode | notes saved per week, brief hits, fails that prevented a retry; same-input hub/library brief equality, host privacy refusals and front-of-brief plan fails despite the library byte cap or standalone screen |
|
|
101
102
|
| `ahub ask` | on | answers with cited ids |
|
|
102
|
-
| Source registry, `ahub source add\|list` (#508 AC1, AC2, AC5 CLI) | on after release; local (no daemon needed for the person); `add` is the person's or the configured conductor's (#933, admitted and audited by the hub); `hub_source_read` adds bounded, screened snapshots and private stubs; plan
|
|
103
|
-
| Intake drafts, `ahub intake` (#511 AC1 CLI, AC2 to AC4) | on after release; person-only except `approve`, which the configured conductor may run too (#933); a draft is held until `ahub intake approve`, so nothing changes for work that does not use it; the dashboard
|
|
103
|
+
| Source registry, `ahub source add\|list` (#508 AC1, AC2, AC5 CLI) | on after release; local (no daemon needed for the person); `add` is the person's or the configured conductor's (#933, admitted and audited by the hub); `hub_source_read` adds bounded, screened snapshots and private stubs; plan sources are registry-validated on proposal/acceptance, and the dashboard Plan sheet lists allowlisted source metadata (#1144) | `ahub source list` rows (id, size, private, origins); `sources/` under the hub state dir; a refusal in the CLI audit for an agent shell; unknown-plan-source refusals and metadata-only dashboard snapshot tests (#1144) |
|
|
104
|
+
| Intake drafts, `ahub intake` (#511 AC1 CLI, AC2 to AC4) | on after release; person-only except `approve`, which the configured conductor may run too (#933); a draft is held until `ahub intake approve`, so nothing changes for work that does not use it; the dashboard Plan sheet stages the same held drafts and refuses delegated live-test intake (#1144) | drafts opened, approved and discarded in `ahub backlog list` and the board history (`draft approved`, `intake unsplit` with its reason); the share of drafts the planner or hub model split without a reason on file |
|
|
104
105
|
| Machine-local schedules (#512 AC1, AC3) | on after release for a person who adds `schedules` to `config.local.json` (`every` `<n>m\|h\|d`, 15 minute minimum); nothing runs without it, and a committed config cannot set it | `ahub status` shows one `schedule <id>` line (next run, last run, task, skipped runs); the backlog cards the hub proposed; no second card while the first is open |
|
|
105
106
|
| Calendar-anchored schedules, `cron` and `tz` (#1017) | on after release for a person who writes `cron` (five fields) with an optional `tz` in `schedules`; `every` is unchanged and a project without `cron` sees no change | `ahub schedule list` and `ahub status` show the expression and the next run in the schedule's zone; one task per fire, not one per missed fire after downtime; a skipped or repeated local time on a change day runs once; a too-frequent expression refused at load with the entry named |
|
|
106
107
|
| Schedule commands and dashboard next run, `ahub schedule list\|run\|pause\|resume` (#512 AC4, AC3 UI) | on after release for a person who configured `schedules`; `run`, `pause` and `resume` are the person's or the configured conductor's (#933), nothing changes for a project with none; the dashboard table is read-only | `ahub schedule list` rows and the dashboard Schedules table (state, next run, last run, task); `paused` and manual runs in the board history of the task a run makes; a pause that is never replayed as a run on resume |
|
|
@@ -180,6 +181,7 @@ agent-hub is developed with agent-hub. This page lists every feature, whether it
|
|
|
180
181
|
| Verdict mirror (#786 T3) | `auto_merge = "dry-run"` first (same setting as the guarded merge): the dry-run notice names the one comment review it would post; `on` after one dry-run notice matches a verdict | one `agent-hub verdict:` comment review per (task, head, verdict) on each PR; none at a stale head; the headline alone for a private task |
|
|
181
182
|
| Guarded merge and result recording (#786 T1/T2, #899) | `auto_merge = "dry-run"` first in this repository's `.agenthub/sdlc.toml`; `on` after one dry-run report matches a manual merge; the report prints for a person by default and as a document with `--json` | `ahub sdlc merge <task> --dry-run` reports the mode, the decision, each refusing gate and the `gh` command it would run, readable without `--json`; `--json` is byte for byte the view printed before #899 (`test/sdlc-output.test.ts`); merges the hub made against merges a person or reviewer made by hand; host merge/cleanup records and owner-notice status; fake-gh ordering/idempotency proof, no live cleanup claim |
|
|
182
183
|
| Peer `DOING` column (#878) | on after the release for Claude sessions started with `ahub claude` and the Codex TUI; ACP peers show it only when their agent sends a plan | `ahub status` shows a title or current item for an active Claude and Codex peer without a hub tool call; `[private]` during PII work; the column disappears when no peer has a value |
|
|
184
|
+
| Pi/local session titles (#1055) | available after release for hub-owned Pi/local sessions; native Pi setter/readback is probed, installed use and the live restart receipt remain unverified (#1143) | first `hub_set_title` succeeds once, later agent writes return `already_named`, and person rename replaces it; live status shows the title or a screened unnamed label while `status.json` omits both |
|
|
183
185
|
| 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 |
|
|
184
186
|
| Usage view (#683) | CLI (`ahub usage`, U2), console Usage tab `7` (U3) and dashboard Usage view (U4) shipped | its own panels |
|
|
185
187
|
| 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) stays opens |
|
|
@@ -188,6 +190,7 @@ agent-hub is developed with agent-hub. This page lists every feature, whether it
|
|
|
188
190
|
| Settings reload | on | reloads applied, pending restarts |
|
|
189
191
|
| 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 |
|
|
190
192
|
| Upgrade and recovery | on, per release | upgrade receipts in `docs/verification/` |
|
|
193
|
+
| Office planning view (#264 phase 5) | source/fixture implementation; available after release when the person selects Flat or Isometric (Off by default); the live two-agent, both-theme AC9 browser look remains unverified | `test/office-model.test.ts` pins Backlog count/order/cap, reserved/pin/waits and release/planner metadata; `test/office-view.test.ts` pins dock and overflow navigation, keyboard access, stable wall-to-To do card nodes, geometry, reduced motion and light/dark token contrast; served-page raw/gzip deltas are measured against the PR merge base |
|
|
191
194
|
| acon console plugin (#935) | off here until a person runs `ahub setup` after the release and `acon ui` on this machine; the plugin registers per agenthub home, not per repository | `acon plugins` shows `ahub verified`; the console's agent-hub panel shows this repository's live board, and an approval answered from the console inbox appears in hub.log as `answered option allow_once by dashboard`. Space (#1012): adopted with the plugin, no separate switch; acon's Space view (Flat, Room, 3D) shows this repository's peers as actors (state and marks matching the dashboard's office), the board columns as queue/work/review/done zones with the same counts, and overlap links plus message arcs (broadcasts to every other desk) as links; clicking an actor or card opens this project's agents or board panel |
|
|
192
195
|
| 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) and 0.164.0 by the [2026-10-24 receipt](verification/2026-10-24-codex-0164-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), Kimi 2.3.1 by the [2026-10-25 model-free receipt](verification/2026-10-25-kimi-231-reviewer-compat.md) and 2.3.2 by the [2026-10-26 one](verification/2026-10-26-kimi-232-reviewer-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 |
|
|
193
196
|
| Peer child environment isolation (#1077) | source and fixture coverage; no native account or Orca session used | daemon peer children, version probes and process-table helpers contain no `ORCA_*`; person-owned native launches keep their fixture pane's `ORCA_*` context while replacing agent identity; explicit Claude/Codex selectors pass through where the adapter inherits them; preload clears ambient selectors without changing fixture home bytes |
|
|
@@ -208,7 +211,7 @@ Run `ahub report --dogfood --since 7d --json` against this repository's own hub
|
|
|
208
211
|
- `null` in JSON means unknown; the text format prints `unknown`. Source availability and completeness are separate. Partial event data is an observed subset, not a complete token bill; event, usage and attribution coverage are separate.
|
|
209
212
|
- Flow is scoped to at most 16 merged PRs found by a capped GitHub read. PR lead time runs from the earliest linked task proposal to the last linked task approval. Missing task links remain unknown. Review rounds reuse `costMetrics`' board handoff definition. CI reads use `head_sha=<recorded-head>&created=<since>..<until>` so unrelated weekly or lifetime runs cannot consume a PR's 100-run page. Only a fully enumerated commit/review/head SHA set is used, including when REST omits PR associations. At most eight heads per PR and 32 CI requests across the 16 selected PRs run under the shared 20 s deadline and 128 KiB response cap. Missing/truncated metadata, a failed or truncated head response, duplicate run ids, more than 100 combined PR runs or an exceeded request/deadline budget keeps that PR's CI unknown. Recent merged PRs are read first. First-pass CI needs the first run's original conclusion and a PR opened within the window; older or unknown PR opening times stay unknown because earlier runs may be outside the window. A later rerun's success does not certify the first result. Same-head reruns count actual `run_attempt - 1`, not duplicate workflows.
|
|
210
213
|
- Connector counts are review threads first opened by that bot after all linked tasks were approved, not a severity classification. Bug counts use explicit cross-references and the `bug` label; they show association, not proven causality, and include the observation age up to 14 days.
|
|
211
|
-
- Each input event/log file has a 16 MiB read cap. The board
|
|
214
|
+
- Each input event/log file has a 16 MiB read cap. The board is read in two bounded parts (#1147). The PII inventory is read completely from the whole board, ids and PR refs only, with no text, under a 2000-task cap and independent of the window; a task whose signals or receipt cannot be read, or an overflow of that cap, makes it incomplete and forbids every per-PR GitHub read. The other task rows are limited to tasks overlapping `[since, until]` (updated at or after `since`, created at or before `until`), at most 500 rows, with per-cell and total byte bounds; their overflow marks the board source incomplete but no longer blocks the PR reads. Every task linked to a PR merged in the window is also read, whatever its update time, under the same 500-row bound, so a PR's lead time, review rounds and findings never come from a partial sibling set; if that read overflows, the source is incomplete and those PR metrics are unknown. GitHub has a 20 s deadline, a 16-PR cap, 100-node pages and 128 KiB responses. Missing, malformed or truncated evidence remains incomplete; the report does not silently call it zero.
|
|
212
215
|
- Digest and held counts describe retained journal rows created in the window. The journal prunes terminal rows, so these are not historical totals. It does not record the first delivery timestamp: `updatedAt` must not be used as delivery latency.
|
|
213
216
|
- Control latency, connection timeouts, CPU/RSS history, actual snapshot exhaustion totals and the adjudicated real-blocker rate currently have no durable source and remain unknown. Snapshot budget notices count one log diagnostic per affected burst. Error classes and advise-gate matches are fixed-pattern log counts; no raw line is exported.
|
|
214
217
|
- Adoption row ids are the 1-based table-row order in this document at the feature-table source commit recorded in the receipt. A matching event is evidence of that leg's use, not proof that the entire feature was enabled or correct. Rows without a dedicated observable leg stay unknown.
|