@staix/agent-hub 0.16.0 → 0.17.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 +18 -0
- package/README.md +2 -2
- package/docs/agent-notes/adapters.md +18 -5
- package/docs/agent-notes/daemon.md +4 -3
- package/docs/agent-notes/tasks.md +2 -2
- package/docs/agent-notes/tests.md +5 -3
- package/docs/dogfood.md +6 -3
- package/docs/operations.md +45 -18
- package/docs/quickstart.md +6 -2
- package/docs/roles.md +1 -0
- package/docs/sdlc.md +11 -11
- package/docs/security.md +15 -5
- package/docs/smoke.md +51 -3
- package/docs/specs/2026-09-19-agent-hub-design.md +40 -4
- package/docs/specs/2026-09-20-upgrade-recovery-design.md +3 -1
- package/docs/verification/2026-10-25-0.17.0.md +74 -0
- package/docs/verification/2026-10-25-codex-0164-reviewer-tool-call.md +52 -0
- package/docs/verification/2026-10-25-kimi-231-reviewer-compat.md +48 -0
- package/docs/verification/2026-10-25-kimi-usage-ac11.md +48 -0
- package/docs/verification/2026-10-25-qwen-dual-output-hub-e2e.md +23 -0
- package/docs/verification/2026-10-25-qwen-dual-output-t0.md +20 -0
- package/docs/verification/2026-10-25-reviewer-no-verdict-diagnostics.md +35 -0
- package/docs/verified.json +20 -17
- package/package.json +1 -1
- package/plugins/agent-hub/.claude-plugin/plugin.json +1 -1
- package/plugins/agent-hub/server.js +1022 -160
- package/src/adapters/codex-review-isolation.ts +10 -1
- package/src/adapters/compat.ts +7 -2
- package/src/adapters/conformance/enrollment.ts +18 -4
- package/src/adapters/conformance/factories.ts +4 -1
- package/src/adapters/conformance/kit.ts +11 -0
- package/src/adapters/definitions/native-cli-entry-qwen.ts +32 -4
- package/src/adapters/definitions/native-cli-preview-qwen.ts +9 -1
- package/src/adapters/definitions/native-factories.ts +8 -0
- package/src/adapters/definitions/native-recovery.ts +8 -0
- package/src/adapters/definitions/qwen.ts +7 -4
- package/src/adapters/qwen-dual.ts +578 -0
- package/src/adapters/registry-types.ts +5 -1
- package/src/adapters/registry.ts +9 -1
- package/src/adapters/reviewer-mcp.ts +12 -6
- package/src/adapters/reviewer-print.ts +100 -17
- package/src/cli/upgrade.ts +4 -3
- package/src/hub/daemon.ts +134 -22
- package/src/hub/memory-plan-guard.ts +8 -33
- package/src/hub/tasks.ts +25 -12
- package/src/memory/brief.ts +45 -5
package/docs/quickstart.md
CHANGED
|
@@ -17,7 +17,7 @@ ahub setup # installs the Claude Code channel plu
|
|
|
17
17
|
Or use the matching GitHub release:
|
|
18
18
|
|
|
19
19
|
```bash
|
|
20
|
-
bun add -g github:STAIxBWLB/agent-hub#v0.
|
|
20
|
+
bun add -g github:STAIxBWLB/agent-hub#v0.17.0
|
|
21
21
|
ahub setup
|
|
22
22
|
```
|
|
23
23
|
|
|
@@ -30,6 +30,10 @@ and upgrade or crash recovery, see the [operations guide](operations.md). To
|
|
|
30
30
|
move running projects to a newer release later, run `ahub upgrade` in a
|
|
31
31
|
terminal: it reviews the plan with you, applies it and follows it.
|
|
32
32
|
|
|
33
|
+
## Upgrading to 0.17.0
|
|
34
|
+
|
|
35
|
+
`ahub qwen --mode tui` runs Qwen Code's own TUI in your terminal, driven by the hub through its dual-output files (#286). It is opt-in: the hub never opens it by itself, `roster.qwen` stays off by default, and headless Qwen works as before. It needs Qwen Code 0.26.1 (a new `qwen-dual` compat entry; the headless entry is not widened), refuses an extra instance (`--as`) and refuses while a headless Qwen runs (`ahub stop qwen` first). It has two limits: a hub deny ends the turn without an answer (the delivery completes, so later messages still reach Qwen), and a stalled turn is never cancelled; the hub reports it and you press Esc in the Qwen terminal. `ahub stop qwen` does not end the TUI, and `ahub upgrade` and recovery do not restore it: end it in its terminal before you upgrade, then run `ahub qwen --mode tui` again, which resumes its session. The `codex-review` reviewer now approves exactly `hub_review` and `hub_task_show`, per tool: on Codex 0.164.0 `exec` refused every MCP tool call, so its reviews ended without a verdict (#783). A reviewer turn that still ends without a verdict leaves one screened `hub.log` line (exit code and the hub tools it called) and is offered on, or to you when you named that reviewer (#453). A stale review request that the hub refuses at admission no longer marks the reviewer unavailable: a request whose binding changed is skipped, and one whose policy changed is rerouted as no verdict (#781). An unavailable reviewer slot no longer stops the agy TUI from reading its own task (#794). An `ahub recovery dispose --stop-and-archive` audit entry now keeps the error that blocked the upgrade as `cause` (#776). A task counts toward a recurring finding only where that finding was recorded open or accepted, so a fix-only record no longer files a "Review policy amendment" card (#328), and a missing generation sync remote no longer logs a failure every sync interval (#401). Upgrade running hubs with `ahub upgrade` and restart Claude sessions so they load the 0.17.0 plugin. Coming from 0.15.0 or older, the notes below apply too.
|
|
36
|
+
|
|
33
37
|
## Upgrading to 0.16.0
|
|
34
38
|
|
|
35
39
|
Always-ask now covers the local worker, Pi and Codex too (#636 slice 3): a local or Pi model tool call is classed before its permission mode, a `never-ask` grant, Pi's `Always allow` or `--unattended`, and a classed command approval request that Codex sends offers only allow-once, deny and the class budget. Unattended local or Pi work that publishes, installs or deletes now waits for you: answer it in the console, or change `action_policy` as in the 0.15.0 notes below. `ahub status` still lists Codex as not enforceable, because its native policy can run a command without sending a request, and Jcode as having no hub approval path. An approval whose command is cut or withheld (an ACP peer's private turn, for one) can only be denied, and a local `action_policy` that weakens the defaults is flagged on every status policy row. The classifier now reads the literal words around a shell expansion (`git push origin $B` asks) and takes the strictest class where the expansion can change it, while a `gh api graphql` call with one literal query no longer asks (#762, #756; see [security notes](security.md)). The hub no longer calls a claude-mem worker (agent-mem#13 P4): recall, briefs, `ahub ask` and the local worker's capture run in process on the exact-pinned `@staix/agent-mem` 0.7.0, `memory.worker_url` is ignored, `ahub doctor` reports the library, and `ahub ask --remember` and context checkpoints save pending candidates that only a person accepts; `ahub memory import claude-mem` stays the explicit way to copy old notes. The planner setting takes up to eight peers (`ahub settings set planner claude,codex`): each new task records one of them, only that planner or you change its planning, an agent's own plan is reviewed by another holder whenever one is online, and `ahub task assign <id> <peer> --role planner` moves it (#525-2; see [the planner](operations.md#the-planner-455)). Once a planner list is set or any task records a planner, `ahub upgrade` and recovery refuse a target release without `task_planners`, so the project cannot be moved back to 0.15.0 that way. `ahub deliverables [--task <id>] [--approved] [--check]` lists what each task delivered and a reviewer approved, and a dashboard card shows a `Deliverables n` chip (#509b). `ahub budget` prints each window's kind and source and says why a peer has no number (#285). The dashboard Memory view gains a Code neighbours toggle for a selected symbol, and `ahub doctor --stale-anchors` is a read-only report of memory symbol anchors at the current head (#684). The `codex-review` reviewer on Codex 0.164.0 no longer needs `ahub agents trust` (#757). Upgrade running hubs with `ahub upgrade` and restart Claude sessions so they load the 0.16.0 plugin. Coming from 0.14.0 or older, the notes below apply too.
|
|
@@ -92,7 +96,7 @@ The 0.12.38 plugin reports a reply stopped by the hop cap as not delivered inste
|
|
|
92
96
|
|
|
93
97
|
Antigravity (`agy`) can join as an optional pull-only implementer (#485). It is off roster by default: install the Antigravity CLI 1.4.x (1.4.2 verified; other 1.4.x patches launch with a warning; on 1.4.3 the hub tools appear from the second TUI turn), run `ahub roster add agy`, then run `ahub agy` in a terminal. Its native permissions are unmanaged and it has no hub sandbox. See [Antigravity operations](operations.md#optional-antigravity-agy-peer).
|
|
94
98
|
|
|
95
|
-
Reviews can rotate across fresh Claude, Codex and Kimi reviewer sessions (#519, #533). Declare headless reviewer-only instances in `.agenthub/config.local.json`; Codex and Kimi join only with `review_mode: "fresh"`. Codex also needs a compat-admitted Codex (receipt-verified 0.146.0 and 0.163.0; select it with `codex_bin` when needed), and Kimi needs a compat-admitted Kimi Code on PATH (receipt-verified 2.2.0 and 2.3.
|
|
99
|
+
Reviews can rotate across fresh Claude, Codex and Kimi reviewer sessions (#519, #533). Declare headless reviewer-only instances in `.agenthub/config.local.json`; Codex and Kimi join only with `review_mode: "fresh"`. Codex also needs a compat-admitted Codex (receipt-verified 0.146.0 and 0.163.0; select it with `codex_bin` when needed), and Kimi needs a compat-admitted Kimi Code on PATH (receipt-verified 2.2.0, 2.3.0 and 2.3.1), signed in with its own OAuth login. Run `ahub agents` for the current version policy; every fresh reviewer still needs a passing runtime preparation. The [2.3.0](verification/2026-10-22-kimi-qwen-compat.md) and [2.3.1](verification/2026-10-25-kimi-231-reviewer-compat.md) receipts are model-free and do not establish real login or model readiness. A headless reviewer instance without `review_mode` keeps its native behavior and stays out of the pool. agy is not a reviewer: naming it in `review.pool` shows an unavailable skip in `ahub route explain`, never a review. Once a train is active or frozen, or `review.pool` is set or fresh reviewer instances are declared, `ahub upgrade` refuses a target release without that support, so an older release cannot take the project over. See [fresh reviewer pool](operations.md#fresh-reviewer-pool-519).
|
|
96
100
|
|
|
97
101
|
A guarded config edit that the hub keeps at its running value now shows as `config retained: <keys>; run ahub settings reload` in status, the console and the dashboard's Settings, and starting a peer whose launch keys are retained says it uses the running value (#542). See [settings](operations.md#settings-from-the-dashboard-and-the-terminal).
|
|
98
102
|
|
package/docs/roles.md
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
# Role catalog (#295)
|
|
2
2
|
|
|
3
3
|
- Flow roles remain conductor, planner, implementer, verifier and reviewer. Only the existing `roles` block controls their authority and routing. Catalog roles are bounded guidance selected through `catalog_roles`; they add no tool, capability, permission mode, sandbox reach or routing preference.
|
|
4
|
+
- Reviewer completion (#784, this repository): the reviewer that approves a PR at its head merges it once exact-head CI is green, no review thread is unresolved and no change request is pending, then removes the branch and worktree, or names the open gate. A release PR waits for the person's release consent. This is this repository's convention (its `AGENTS.md`, outside the managed block), not a shipped default: the AGENTS block template, the catalog roles and the flow role contracts are unchanged, so another project keeps merge with the person unless its own AGENTS.md delegates it.
|
|
4
5
|
- Default catalog assignments are absent. Existing flow contracts are byte-identical to main d83cb8dd when no catalog role is selected.
|
|
5
6
|
|
|
6
7
|
# Definitions and layers
|
package/docs/sdlc.md
CHANGED
|
@@ -10,17 +10,17 @@ A reviewed head is a full lowercase 40-character SHA observed through a successf
|
|
|
10
10
|
|
|
11
11
|
The built-in lifecycle follows the [AI-Native SDLC Playbook](https://claude.com/resources/articles/the-ai-native-sdlc-playbook). Stage ids describe the hub's artifacts and handoffs; the playbook labels group them into Plan, Design, Build, Test, Deploy and Maintain.
|
|
12
12
|
|
|
13
|
-
| Stage id | Playbook stage | Acting role | Artifact or exit gate | Current enforcement |
|
|
14
|
-
| --- | --- | --- | --- | --- |
|
|
15
|
-
| intent | Plan | person/planner | intent or backlog identity | person spec acceptance required at build entry in enforce |
|
|
16
|
-
| spec | Design | person/planner | issue identity and scope | spec acceptance/body drift checked at build entry |
|
|
17
|
-
| plan | Build | planner/owner | plan, branch, existing predecessor gates | current planner review required at build admission; literal-path conformance gates approval in enforce |
|
|
18
|
-
| build | Build | owner | PR identity/current head and local check on that head | entering review, when enabled |
|
|
19
|
-
| test | Test | verifier/reviewer | required checks and local gate on the reviewed head | approval, when enabled; bug test integrity below, evals pending S5 |
|
|
20
|
-
| review | Deploy | assigned reviewer | current head-bound verdict, no pending change request/open thread; AC checklist under Compliance | approval, when enabled; open Important findings block in enforce |
|
|
21
|
-
| merge | Deploy | person | current-head readiness, ordered touching PRs | shown; no mutation |
|
|
22
|
-
| release | Deploy | person | merged commit, version/tag, docs freshness/live receipt checklist | person authorization and optional tag hook below |
|
|
23
|
-
| maintain | Maintain | person/planner | follow-up intent and operational evidence | shown; metrics and control bands from S7 (`ahub sdlc metrics`) |
|
|
13
|
+
| Stage id | Playbook stage | Acting role | Artifact or exit gate | Current enforcement | Class (#784) |
|
|
14
|
+
| --- | --- | --- | --- | --- | --- |
|
|
15
|
+
| intent | Plan | person/planner | intent or backlog identity | person spec acceptance required at build entry in enforce | person-only (acceptance) |
|
|
16
|
+
| spec | Design | person/planner | issue identity and scope | spec acceptance/body drift checked at build entry | person-only (acceptance) |
|
|
17
|
+
| plan | Build | planner/owner | plan, branch, existing predecessor gates | current planner review required at build admission; literal-path conformance gates approval in enforce | agent-executable |
|
|
18
|
+
| build | Build | owner | PR identity/current head and local check on that head | entering review, when enabled | agent-executable |
|
|
19
|
+
| test | Test | verifier/reviewer | required checks and local gate on the reviewed head | approval, when enabled; bug test integrity below, evals pending S5 | agent-executable |
|
|
20
|
+
| review | Deploy | assigned reviewer | current head-bound verdict, no pending change request/open thread; AC checklist under Compliance | approval, when enabled; open Important findings block in enforce | agent-executable |
|
|
21
|
+
| merge | Deploy | person | current-head readiness, ordered touching PRs | shown; no mutation | agent-executable where the project's AGENTS.md delegates it to the approving reviewer (this repository does, #784); the hub still shows the person as actor |
|
|
22
|
+
| release | Deploy | person | merged commit, version/tag, docs freshness/live receipt checklist | person authorization and optional tag hook below | agent-evidence / person-verdict (consent) |
|
|
23
|
+
| maintain | Maintain | person/planner | follow-up intent and operational evidence | shown; metrics and control bands from S7 (`ahub sdlc metrics`) | agent-executable |
|
|
24
24
|
|
|
25
25
|
Stored `implement` and `verify` stage ids read as `build` and `test`. A write that names the old id stores the id the configuration lists, so new enabled-lifecycle receipts use the canonical ids. The default remains off, preserving existing off-mode output, receipt and schema shapes. Status, explain, console and dashboard name the playbook stage beside enabled lifecycle ids.
|
|
26
26
|
|
package/docs/security.md
CHANGED
|
@@ -7,7 +7,7 @@ agent-hub connects agents that can each run commands. This page says what the hu
|
|
|
7
7
|
- **Other agents' text is untrusted.** Every message that crosses from one peer to another is framed as untrusted input: a channel tag with `meta.source` for Claude started with `ahub claude`; otherwise a fixed header line (`frame()`, `[agent-hub message from ...]`) with an instruction that says it is untrusted, the plugin's server instructions for a tools-only Claude reading `hub_inbox` and a standing instruction for the others. A message body cannot forge the hub's own headers: such lines are quoted (`sanitize`). Replies inherit a hop count capped at 3, so agents cannot ping-pong forever; neither a digest nor a steer can reset it.
|
|
8
8
|
- **The control link is loopback plus a secret.** The daemon and the Codex proxy bind 127.0.0.1 only. The control WebSocket requires a per-run token (`.agenthub/state/control-token`, mode 600), and both servers refuse any request that carries an `Origin` header: any web page can open a WebSocket to localhost, and browsers always send `Origin`. The bearer header a Codex TUI sends to the proxy carries only the terminal launch id (#586): it binds terminal ownership and is not a credential. External clients cannot claim the console user's id or a hub-managed peer's id.
|
|
9
9
|
- **Permission prompts stay on unless an operator selects a permission mode.** `ask` leaves native decisions unchanged. `ask-when-needed` grants scoped Claude file tools or Pi/local writes and edits; Kimi and Codex use their own native policies. `never-ask` requires explicit runtime confirmation or a person's console `y` for a config default at hub start. Until that config confirmation the peer runs in `ask`. Kimi's and `local`'s permission requests are relayed to the console and cancelled after `approvals.timeout_s` (default 120 s) of silence; the macOS notification for a waiting request carries the peer and the tool name only. The one exception is the hub's own tools (`hub_send` and the task tools, matched by exact name): Kimi's requests for them are approved once without a prompt and logged by name, the same trust Codex gets through `approval_mode` in the hub's config. They invoke hub-owned operations rather than arbitrary file or shell tools, and every call passes the hub's own checks. A call the always-ask policy classes (below) is never approved this way, whatever its title. Identity comes from the exact permission title, or from the earlier tool-call title bound to the same call id and resolved against the configured MCP servers when the permission title contains argument JSON (Qwen). Payload text and unrelated display titles never establish identity. Configured ACP kinds (#451) never get this exception: a request whose title spells a hub tool exactly still goes to the person approval path, and only a mapped native mode changes what the agent itself asks. For ACP agents, a payload longer than the console shows is marked as cut and never offers a session-wide grant. Pi's write, edit, shell and git write requests are relayed the same way and can be answered `Always allow <tool> until Pi restarts`: later calls of that tool by the same Pi start skip the prompt but stay inside the path guard and sandbox. The grant covers every later call of that tool whatever its payload, is kept in memory only, cannot be given from the dashboard, and each call it allows is logged by tool name. A Pi or `local` request whose call was cancelled or whose turn ended is withdrawn at once: a later answer is refused and grants nothing, an `Always allow` included. One shell command runs without a hub approval: the `!command` a person types in the managed Pi TUI. It stays inside the same sandbox and denylist, and the hub takes it only from the verified owner process of a Pi in TUI mode, while Pi is idle, on a reservation that is used once; a model's own bash call never takes that path (issue #253). `--unattended` turns prompts off, says so loudly, and is never the default; a classed call from a hub-owned ACP peer, the local worker or Pi still waits for a person under it.
|
|
10
|
-
- **A committed config cannot choose launch commands, credential files, data endpoints, permission modes or a wider sandbox.** The machine-local fields (`kimi_cmd`, `permission_modes`, `codex_bin`, `pi.cmd`, `checks`, `mlx.bin`, `mlx.runtimeDir`, `mlx.modelPath`, `omniroute.urls`, `omniroute.access_hosts`, the `omniroute` key files, `memory.worker_url
|
|
10
|
+
- **A committed config cannot choose launch commands, credential files, data endpoints, permission modes or a wider sandbox.** The machine-local fields (`kimi_cmd`, `permission_modes`, `codex_bin`, `pi.cmd`, `checks`, `mlx.bin`, `mlx.runtimeDir`, `mlx.modelPath`, `omniroute.urls`, `omniroute.access_hosts`, the `omniroute` key files, `memory.worker_url` (still accepted, ignored since 0.16.0), `local.read_allow`, `local.bash_network`, `local.network_allow`, `terminal.open`, `instances`, `action_policy`) apply only from a config file git confirms nobody committed (`instances` and `action_policy` are read only from an untracked `.agenthub/config.local.json`; in `config.json` they are ignored even when untracked): `.agenthub/config.json` or `.agenthub/config.local.json`, matched by file identity so no other spelling the file system accepts slips past, and `.agenthub` itself not a committed symlink or submodule. Without a repository, or when git fails, they keep their defaults; an empty value always means the default. "Untracked" is answered by the repository that contains the project: a checkout copied or extracted into an unrelated repository, or into an ignored directory of one, is trusted like your own files. So a cloned repository cannot choose a launch command, a completion check, a gateway to send a key file to, a memory endpoint, a permission mode, the command that opens a terminal, or a wider sandbox. A command in `checks` runs as you, outside the local worker's sandbox, like a git hook. So do the `setup` and `verify` commands of a benchmark suite (issue #251): `ahub bench run <suite.json>` is a person's command, refused from an agent session, and it resets the work tree for each attempt, so it runs only with `bench.enabled`, at the root of a clean repository that tracks nothing under `.agenthub/`. Nothing in `routing.toml` or in task text is ever run. `routing.toml` and the other shared fields still come from the checkout, and they matter: `routing.toml` picks the models the local worker and the hub's inference use at your gateway and can turn the PII constraint off, and roles and budget shape who does what. Review them in a repository you do not trust. The hub's own git calls outside the sandbox (snapshots, facts, config trust, project discovery, memory recall, the bench runner) all use the single hardened implementation in the exact-pinned first-party `@staix/agent-mem/platform` package, re-exported by `src/hub/git.ts`, which disables the programs a repository's configuration can name — the file-system monitor, hooks, external diff, the pager, and the credential and askpass helpers, with prompts failing closed — so a config that ever does get written cannot run code with the hub's rights. Of those, the monitor, hooks and external diff are reachable by commands the hub issues (the planted-program test exercises all three; a `diff --no-index` reads the repo's config, so that call also passes `--no-ext-diff` and `--no-textconv`); the pager is not (every call pipes stdout). Credential and askpass helpers are disabled for project repositories; memory sync alone may use credentials for its own memory repository, without prompts. The cost: `core.fsmonitor=false` also turns off a person's enabled built-in monitor for the hub's own calls, so the per-turn snapshot's `add -A` scans the whole tree (the snapshot Git calls of one burst share a 250 ms budget, each call at most 10 s; a boundary that runs out is recorded as unknown, never as a stale baseline) (#576). git has no global switch for filter drivers, and of the hub's commands only the snapshot's `add`/`restore` would run one (its driver command lives in the protected `.git/config`).
|
|
11
11
|
- **Telemetry holds no bodies.** `.agenthub/state/events.jsonl` (issue #40) records envelope ids, routing and sizes, task ids and states, overlapping paths and token counts, and the task id (with a PII flag) that each usage and token record is attributed to. It never records a message body, a task title or detail, and marks private (PII) envelopes and tasks as such. Budget pauses, resumes and quota resets carry the peer, window, times and the hub's own reason text, and a person's release authorization its commit, version and docs digest. The usage roll-up (#683) folds the usage, token and limit records into `hub.db` and joins issue, PR and release from the board when read; `ahub usage` and the console's Usage panel answer only the console and a configured planner, and a planner not during a private turn. Kimi's token records (#285) are counted from its own session wire log under its home: only the four token integers, the model label and the row time are copied, and the record id is an opaque hash, never a path, session id or text. The log stays on the machine; `ahub export` only prints it. Two opt-in stores copy numbers from it into your home directory, shared by every project on the machine (directories mode 700, files 600): research records (issue #247, `research.enabled`) in `~/.agenthub/research/` hold per approved task the ids, states, counts, tokens, times and PII flag the events hold, a person's label from a closed list, and a hash in place of the project id; benchmark runs (issue #251) in `~/.agenthub/bench/` hold suite name and hash, arm label, the attached peers' ids, models and permission modes, task ids, outcomes, exit codes, counts, tokens and times, never suite text or a command's output.
|
|
12
12
|
- **Snapshots stay in your repository.** Per-turn snapshots (issue #33) are git objects in the project's own object store, written through a temporary index; nothing is referenced, pushed or copied elsewhere, and `git gc` prunes them. They hold what the work tree held, including untracked files that are not ignored, so keep secrets in ignored files. They carry the repository's own permissions, and nothing caps their disk use but `git gc`. A turn of a peer holding an open PII task is not snapshotted; a PII file left in the project is snapshotted by later turns like any other file. `ahub undo` restores only files whose current content is exactly what the turn left.
|
|
13
13
|
- **The edit hook reads, never decides.** `ahub check-path --hook` (issue #32) reads hub.db and returns context for Claude and a line for you; it sets no permission decision, so your permission rules stay in charge. It names other owners' task ids, titles and states, which then reach Claude's model; PII tasks are left out.
|
|
@@ -346,13 +346,23 @@ Antigravity (`agy`, #485) is optional, cloud and off roster by default. The laun
|
|
|
346
346
|
|
|
347
347
|
Configured ACP adapters (#451 A3) are declared only by the person, in a Git-confirmed untracked `.agenthub/config.local.json`. The whole declaration (command, argv, identity, roles and mode maps) is machine-local authority: a shared or tracked declaration is ignored with a notice, settings never edit it, and changes need a hub restart. The hub runs only the declared argv, never a module or callback. Argv that selects a leader, attach, connect or remote mode is refused, and a Grok declaration needs `--no-leader`. Each start requires the pinned native identity, an exact `session/load` echo on resume, and positive ownership of the spawned process and its original process group; unknown ownership refuses, and stop signals only that verified group. An agent that hands its work to a process outside that group is outside this boundary. Without a mode map native policy is unmanaged, and there is no hub sandbox either way. Configured kinds are cloud peers (never PII), headless only and off roster by default, and the hub makes no quota, context or controlled-recovery claims for them. A read-only instance needs the native read-only mode acknowledged before a prompt and is limited to read and review hub tools, both in the published MCP list and at the host; its native non-read tool requests are rejected, and no setting or runtime change can raise it. These agents' stderr and error text are withheld from logs, and previews redact the executable, argv and session id.
|
|
348
348
|
|
|
349
|
-
External adapter modules (#451 A4) are the one way to run adapter code that ships outside the hub, and the pin is code authorization, not a sandbox. `adapter_modules` is read only from a Git-confirmed untracked `.agenthub/config.local.json` (never from a shared, tracked or cloned config, and never written by settings or a hub tool). Only daemon startup imports a module, from the captured bytes that matched the `sha256` pin, not by reopening the path; the file must sit inside the project with no symlinked component and not be world-writable. Its imports are bound to the running hub's own `@staix/agent-hub/adapter` and builtins (`bun:ffi` and `bun:test` excluded), and dynamic imports, `require`, `eval` and `Function` are refused by a conservative scan. Once imported, the module runs with the daemon's privileges: the 2 s import deadline stops waiting but does not cancel its code or undo side effects, and the host's default against hub-tool title auto-approval holds only while the module keeps the factory options it is given. Inspect the whole bundle before recording its hash. Refusal reasons withhold source and module error text. A loaded kind keeps its own family, cloud trust (never PII), headless start and off-roster default, and fixed-builtin capabilities are refused.
|
|
349
|
+
External adapter modules (#451 A4) are the one way to run adapter code that ships outside the hub, and the pin is code authorization, not a sandbox. `adapter_modules` is read only from a Git-confirmed untracked `.agenthub/config.local.json` (never from a shared, tracked or cloned config, and never written by settings or a hub tool). Only daemon startup imports a module, from the captured bytes that matched the `sha256` pin, not by reopening the path; the file must sit inside the project with no symlinked component and not be world-writable. Its imports are bound to the running hub's own `@staix/agent-hub/adapter` and builtins (`bun:ffi` and `bun:test` excluded), and dynamic imports, `require`, `eval` and `Function` are refused by a conservative scan. Once imported, the module runs with the daemon's privileges: the 2 s import deadline stops waiting but does not cancel its code or undo side effects, and the host's default against hub-tool title auto-approval holds only while the module keeps the factory options it is given. Inspect the whole bundle before recording its hash. Refusal reasons withhold source and module error text. A loaded kind keeps its own family, cloud trust (never PII), headless start and off-roster default, and fixed-builtin capabilities are refused. The daemon reads a native proof port (a status notice, the Codex frontend proxy, a native turn revert, a Claude launch proof) only from a fixed builtin definition, so a module product that carries one gains nothing (#790).
|
|
350
350
|
|
|
351
351
|
# Transient ACP activity (#286)
|
|
352
352
|
|
|
353
353
|
- Watch activity can contain file contents, thoughts and command output. It is console-role only, kept in a bounded in-memory ring and exposed only through the `watch` control request. Agent shells, conductor tools, dashboard snapshots, hub tools and memory observers cannot read it. Terminal bodies pass through the console's sanitization and wrapping helpers. Slow sockets close instead of accumulating unbounded output.
|
|
354
354
|
- Private/PII turns show a single stub; the adapter latches privacy for that turn. The existing final-answer bus and native approval paths (console and dashboard) remain independent, with their existing privacy rules. Watch never adds activity to either path or any disk log.
|
|
355
|
-
-
|
|
355
|
+
- Qwen's dual-output TUI (#286 Part 3, [receipt](verification/2026-10-25-qwen-dual-output-t0.md)) is a separate transport, covered next.
|
|
356
|
+
|
|
357
|
+
# Qwen dual-output input file (#286 Part 3)
|
|
358
|
+
|
|
359
|
+
- **The input file is a prompt channel.** Whatever is appended to `<state dir>/qwen/qwen-dual/input.jsonl` is read by Qwen as typed input: a `submit` line is a prompt, a `confirmation_response` line answers a tool approval. The files are created 0600 in a 0700 directory owned by the person running the hub, opened without following links, refused if they are not regular files owned by that person with no group or other access, and written only by the daemon's adapter. No other peer can write them through a hub tool.
|
|
360
|
+
- Qwen's own write and shell tools can reach the file, but only through approval. The hub's approval path therefore refuses, without asking anyone, any Qwen tool request other than the hub's own tools whose input names the hub state directory, its real path, `.agenthub/state`, the `qwen-dual` directory or `input.jsonl`. That check is a lexical one on the request, as the managed local and Pi tools' state-dir guards are: it is not a sandbox, and a Qwen started with `--approval-mode yolo` by a person in their own terminal is outside it.
|
|
361
|
+
- Text another agent wrote reaches Qwen only inside a hub-framed digest. A digest that would begin with `/`, `!`, `@` or `?` (a slash command, a shell line, a file include or a help key in Qwen's prompt) is prefixed with a hub line first.
|
|
362
|
+
- Hub tools are auto-approved by the exact `mcp__agent-hub__<tool>` name Qwen reports for the call and never by prefix, title or argument text. Everything else asks the console, and an always-ask class call (#636) is never auto-approved. A prompt answered in the Qwen terminal withdraws the hub's pending approval: a late answer finds nothing to grant.
|
|
363
|
+
- Identity: the adapter attaches only a Qwen whose `session_start` states a verified version, protocol 2 and the events the hub reads, and it binds the running owner from the host's process table (the root of the process tree whose argv names this adapter's input file: Qwen 0.26.1 runs as a wrapper, a node launcher and a `cli.js` child that all name it; two separate roots is no owner), never from a message the launcher sends. The hub never signals that process and never types into its terminal; the person's terminal owns it.
|
|
364
|
+
- Environment: `ahub qwen --mode tui` execs Qwen in the person's own environment (Qwen needs their PATH, HOME and provider key, which the daemon never copies) with the hub's peer marker over it. Every other `AGENTHUB_*` variable but `AGENTHUB_HOME`, the other agents' vendor markers, and recovery and unattended authority are dropped, so a Qwen started from another agent's shell never carries that agent's identity, launch id or mode.
|
|
365
|
+
- Not defended: a person who gives Qwen yolo mode, an agent that Qwen runs and that writes the file through a path this lexical check does not recognise, and another process of the same user (the usual same-user boundary of the state directory).
|
|
356
366
|
|
|
357
367
|
## Global attention metadata (#280, phase 3)
|
|
358
368
|
|
|
@@ -455,9 +465,9 @@ A terminal Claude implementer or verifier instance (#525-5) owns write-class tas
|
|
|
455
465
|
|
|
456
466
|
The headless Claude implementer (#525-4) runs one owned `claude -p` child per turn in its declared worktree, with `--setting-sources ""`, `--strict-mcp-config`, native permission mode `default`, the hub's managed PreToolUse hook and `--permission-prompt-tool mcp__agent-hub__hub_permission_request`; never `--bare`, `acceptEdits`, `dontAsk` or `bypassPermissions`, and `never-ask` is refused. Each task keeps its own persisted session; unscoped chat reuses one only while exactly one task is in progress, and a `hub_inbox` read refuses a batch attributed to another task. The approval path fails closed: `hub_permission_request` is accepted only from the current child's launch and generation, goes to the console's existing approval queue and `approvals.timeout_s`, and offers allow-once and deny only, with no allow-always or session grant. A timeout, a prompt failure, a disconnect, a hub that is down or refusing, or a stale child binding answers deny; a failed hook decides nothing, so the call still waits for a person. The approval title is the tool name and a JSON rendering of the arguments passed through `sanitize()` and the public-text screen (any PII, secret or private-path match withholds the whole value as `[withheld]`, before any cut), never task text, cut at 600 characters with a `[cut, N chars]` marker; the original arguments stay with the child and are not logged. The MCP wire cannot tell a native permission dispatch from a call the model makes itself; either way it needs a fresh allow-once for its own arguments and grants nothing later. A stop holds an uncertain delivery until the owned process group is confirmed gone. The [native permission receipt](verification/2026-10-23-headless-claude-permissions.md) is model-free on Claude Code 2.1.309; it is not sandbox proof, and a real account, model and person interaction remains a live leg.
|
|
457
467
|
|
|
458
|
-
The fresh Codex reviewer (#519) needs `review_mode: "fresh"` and a compat-admitted Codex (receipt-verified 0.146.0, 0.163.0 and 0.164.0; `ahub agents` shows the current policy); an unverified/untrusted version is unavailable. Before each review it requires a model-free isolation receipt: binary and config unchanged, only the two hub tools in the configured MCP inventory, plugins, hooks and Apps disabled (Apps by a separate feature readback), and a named filesystem profile whose fixtures prove public reads, private-path denial (exact, glob and symlink), standard host credential denial and write denial. Credential and tool-home roots are denied even with an empty `local.deny`; that list does not cover every personal file. A dangling or unreadable credential root refuses the reviewer, and the reason names only its path class (`~/.docker`, `$DOCKER_CONFIG`), never the path or its contents. A failed per-review start names one class from a closed list (`version`, `binary`, `mcp_inventory`, `plugins_hooks_apps`, `sandbox_proof`, `credential_root`, `timeout`, `unknown`) with a fixed sentence in the unavailable reason, `hub.log` and the `reviewer` line of `ahub status`; it never carries a path, native CLI text or a token (#722). Its shell is sandboxed read-only, not Claude's Read/Grep/Glob whitelist. Network and web search are configured disabled but not observed: managed requirements can still select cached web search, and no run has shown the review selecting the profile through `default_permissions`. A failed receipt or start never falls back to a continuing native session, and a child that ends without a saved verdict is never approval. The agy reviewer slot is an unavailable placeholder and never runs.
|
|
468
|
+
The fresh Codex reviewer (#519) needs `review_mode: "fresh"` and a compat-admitted Codex (receipt-verified 0.146.0, 0.163.0 and 0.164.0; `ahub agents` shows the current policy); an unverified/untrusted version is unavailable. Before each review it requires a model-free isolation receipt: binary and config unchanged, only the two hub tools in the configured MCP inventory (each approved by its own name, never by a server-wide default, #783), plugins, hooks and Apps disabled (Apps by a separate feature readback), and a named filesystem profile whose fixtures prove public reads, private-path denial (exact, glob and symlink), standard host credential denial and write denial. Credential and tool-home roots are denied even with an empty `local.deny`; that list does not cover every personal file. A dangling or unreadable credential root refuses the reviewer, and the reason names only its path class (`~/.docker`, `$DOCKER_CONFIG`), never the path or its contents. A failed per-review start names one class from a closed list (`version`, `binary`, `mcp_inventory`, `plugins_hooks_apps`, `sandbox_proof`, `credential_root`, `timeout`, `unknown`) with a fixed sentence in the unavailable reason, `hub.log` and the `reviewer` line of `ahub status`; it never carries a path, native CLI text or a token (#722). Its shell is sandboxed read-only, not Claude's Read/Grep/Glob whitelist. Network and web search are configured disabled but not observed: managed requirements can still select cached web search, and no run has shown the review selecting the profile through `default_permissions`. A failed receipt or start never falls back to a continuing native session, and a child that ends without a saved verdict is never approval. The agy reviewer slot is an unavailable placeholder and never runs; it binds nothing, so agy's own `hub_task_show` reads are not fenced as a review (#792).
|
|
459
469
|
|
|
460
|
-
The fresh Kimi reviewer (#533) needs `review_mode: "fresh"` and a compat-admitted Kimi Code (receipt-verified 2.2.0 and 2.3.
|
|
470
|
+
The fresh Kimi reviewer (#533) needs `review_mode: "fresh"` and a compat-admitted Kimi Code (receipt-verified 2.2.0, 2.3.0 and 2.3.1; `ahub agents` shows the current policy) with its native OAuth login; an unverified/untrusted version, or a failed preparation, is unavailable. The [2.3.0](verification/2026-10-22-kimi-qwen-compat.md) and [2.3.1](verification/2026-10-25-kimi-231-reviewer-compat.md) receipts are model-free and do not prove real login or a model review. It is a native tool-authority boundary, not a sandbox: no Seatbelt wraps it, and same-user native code is not isolated from the filesystem or network. Each review starts a new child in a private `KIMI_CODE_HOME` with managed-provider-only config, a clean temporary working directory and an empty skills directory. Its agent may use only `hub_review` and `hub_task_show`, served by a loopback MCP session bound to the task, head and review revision that never carries the hub's control token; it has no native read, file or shell tool and sees only the screened review brief and host facts. Its `credentials/` and `oauth/` directories and `device_id` link to the person's native ones, so refresh keeps Kimi's own lock and atomic rename; no token is copied and `KIMI_DISABLE_OAUTH_LOCK` is never set. A stop revokes its verdict authority before termination; killing it mid-refresh may require a native re-login. Private tasks are never given to it, and a child that ends without a saved verdict is never approval.
|
|
461
471
|
|
|
462
472
|
The review brief (#520) withholds private changes whole (both sides of a move) and counts them; a rule file that fails the private-data screen is named, not shown. Attributes and rules are read at the merge base, so a change cannot loosen its own review. Optional changed symbols (#684) come from ripwire 0.6.7 on the daemon's host PATH, run as an owned process on a disposable snapshot of the task's commit; each path, name and kind passes the hub's public metadata screen before the brief, the console's task detail or the dashboard shows it, a private task stores and shows none, and a failed or missing read is `unknown`, never a gate. Spec acceptance, plan review, plan conformance, premature-review, cost, test-integrity (#481 S4), issue and PR identity linking (#694) and dogfood scorecard (#687) reads fetch GitHub metadata only (edit times, check states, thread authorship and resolution, changed file names, open PR heads, workflow run conclusions), never issue bodies, comments or patches. Issue identity linking at propose and accept (#723) parses the task's title and refs locally, reads nothing from GitHub and skips PII tasks. The CI workflow keeps `contents: read` and `actions: read`.
|
|
463
473
|
|
package/docs/smoke.md
CHANGED
|
@@ -1,6 +1,44 @@
|
|
|
1
1
|
# Live smoke checklist
|
|
2
2
|
|
|
3
|
-
`scripts/check.sh` covers everything against fakes. The legs below need real accounts
|
|
3
|
+
`scripts/check.sh` covers everything against fakes. The legs below need real accounts, a real terminal or a person's judgement, and their results are recorded here. An explicitly authorized agent runs most of them itself with `ahub --live-test` in a disposable project (see [Agent-operated verification](#agent-operated-verification-408)); each open leg carries one class (#784):
|
|
4
|
+
|
|
5
|
+
- `agent-executable`: an authorized agent runs it and records the result.
|
|
6
|
+
- `agent-evidence / person-verdict`: an agent runs it and collects the evidence; the person gives the verdict named in the row.
|
|
7
|
+
- `person-only`: only the person can do it (a login, a native choice, a consent).
|
|
8
|
+
- `external-blocked`: waiting on something outside the hub; the row names it.
|
|
9
|
+
|
|
10
|
+
## Open live legs by class
|
|
11
|
+
|
|
12
|
+
| Leg | Section | Class | Person verdict or blocker |
|
|
13
|
+
| --- | --- | --- | --- |
|
|
14
|
+
| Off-campus Cloudflare Access headers (#1) | Issue #7 verification ledger | external-blocked | Access credential files; then agent-executable |
|
|
15
|
+
| Natural Codex budget pause with a TUI attached (#1) | Issue #7 verification ledger | external-blocked | a natural weekly reading above the gate |
|
|
16
|
+
| Native Pi restoration and the operator plan (#273) | Issue #273 operator execution plan | agent-executable | |
|
|
17
|
+
| Hub memory live run (#258) | Hub memory | agent-executable | |
|
|
18
|
+
| Office view look (#264) | Office view | agent-evidence / person-verdict | the look in a browser, light and dark |
|
|
19
|
+
| Add-on arms and fingerprints (#248) | Add-on arms and fingerprints | agent-executable | |
|
|
20
|
+
| Settings from the dashboard and the terminal (#269) | Settings from the dashboard and the terminal | agent-evidence / person-verdict | the dashboard controls as a person sees them |
|
|
21
|
+
| Codex resumed approval policy stickiness (#270) | Codex resumed approval policy | person-only | the explicit native choice in the person's TUI |
|
|
22
|
+
| Interactive upgrade screens (#272) | Interactive upgrade | agent-executable | |
|
|
23
|
+
| `ahub reset` on a scratch project (#214) | ahub reset on a scratch project | agent-executable | |
|
|
24
|
+
| Claude without channel evidence (#205) | Claude without channel evidence | agent-executable | |
|
|
25
|
+
| Console layout and readability (#213, #201) | Console layout and readability | agent-evidence / person-verdict | readability at 80 and 120 columns |
|
|
26
|
+
| `ahub bench` live run (#251) | `ahub bench` live run | agent-executable | |
|
|
27
|
+
| Codex and Pi permission-mode legs (#240, #242) | Permission modes | agent-executable | |
|
|
28
|
+
| Dashboard themes (#246) | Whole-board task progress and dashboard themes | agent-evidence / person-verdict | light and dark legibility |
|
|
29
|
+
| Command output (#284) | One-shot command output; Remaining command output | agent-evidence / person-verdict | terminal rendering at 80 and 120 columns |
|
|
30
|
+
| Qwen and Kimi cancellation timing (#304) | ACP cancellation settlement | agent-executable | |
|
|
31
|
+
| Kimi TUI pull mode (#316) | Kimi TUI pull mode | agent-executable | |
|
|
32
|
+
| One issue through merge and release in advise mode (#257) | SDLC gates | agent-evidence / person-verdict | release consent |
|
|
33
|
+
| Qwen native turn and recovery (#256) | Qwen Code production peer | agent-executable | |
|
|
34
|
+
| Jcode instance sign-in per `jcode_home` (#332, #529) | Jcode peer | person-only | signing in to each instance's account |
|
|
35
|
+
| Planner grooms a backlog into two releases (#263) | Project board; Planning and release controls | agent-executable | |
|
|
36
|
+
| Character glyphs in real terminals and tmux (#280) | Console attention; Dashboard character | agent-evidence / person-verdict | glyph rendering in physical terminals |
|
|
37
|
+
| Console Settings panel in a native terminal (#439) | Settings reload and console fixture | agent-evidence / person-verdict | the panel as a person sees it |
|
|
38
|
+
| Reviewer pool native legs (#519) | Reviewer pool native legs | agent-executable | after #783 ships |
|
|
39
|
+
| Antigravity TUI close (#485) | Antigravity P1 | agent-executable | |
|
|
40
|
+
| Managed Pi live receipts (#418 AC8) | none yet (issue #418) | person-only, then agent-executable | the person opts in to a check runner and answers the approvals |
|
|
41
|
+
| Kimi credential probe (#285 AC9) | Kimi usage counting | person-only | the owner's approval of the probe |
|
|
4
42
|
|
|
5
43
|
## Issue #273 operator execution plan (2026-10-16)
|
|
6
44
|
|
|
@@ -56,6 +94,16 @@ operationId, console hello and initiating-peer audit (225 related tests passed).
|
|
|
56
94
|
This is fixture/CLI proof, not an actual native Pi restoration; that live leg
|
|
57
95
|
remains owed in #273.
|
|
58
96
|
|
|
97
|
+
## Kimi usage counting (#285 AC11)
|
|
98
|
+
|
|
99
|
+
The [2026-10-25 Kimi 2.3.1 receipt](verification/2026-10-25-kimi-usage-ac11.md)
|
|
100
|
+
records an isolated-HOME ACP session. Native `/usage` exactly matched the
|
|
101
|
+
main-agent four-counter wire total but excluded subagent usage; neither
|
|
102
|
+
`/usage` call added a `usage.record`. The ordinary main row appeared after the
|
|
103
|
+
prompt-result read and within 1,000 ms, while the subagent turn's main and
|
|
104
|
+
subagent rows were present at its result. See the receipt for the counts,
|
|
105
|
+
occupancy timing and isolation boundary.
|
|
106
|
+
|
|
59
107
|
## Console task and queue operations (#424)
|
|
60
108
|
|
|
61
109
|
2026-10-16, macOS, source `691f00ac`: a disposable Git project and fake ACP
|
|
@@ -1953,13 +2001,13 @@ Complete these legs in a disposable project with authorized accounts after sourc
|
|
|
1953
2001
|
- Request a delta review and confirm the same eligible independent reviewing family receives a new session with prior findings. After an earlier same-family fallback, a newly available independent family still takes precedence. Exercise configured re-rotation separately. Stop or exhaust one reviewer and confirm the pure routing walk chooses the next eligible candidate; no failed start or child ending without a saved verdict becomes approval. Check the next candidate even when the missing verdict followed a vendor quota error; no text-based quota classifier is assumed.
|
|
1954
2002
|
- Make all pool candidates unavailable and confirm a blocked review awaiting planner/person action. Then test a different fresh session in same-family fallback only when no other family can run. Verify stale-child, changed-head and private-review verdict refusals.
|
|
1955
2003
|
- Verify compatibility separately: a Codex/Kimi headless reviewer-role declaration without `review_mode` keeps its native app-server/ACP behavior and existing worktree/permission options, and stays outside the fresh pool. An explicit fresh declaration must refuse an unsupported backend without downgrading.
|
|
1956
|
-
- The default logical pool is Claude, Codex, Kimi. Kimi is runnable for a compat-admitted version when runtime preparation passes (receipt-verified 2.2.0 and 2.3.
|
|
2004
|
+
- The default logical pool is Claude, Codex, Kimi. Kimi is runnable for a compat-admitted version when runtime preparation passes (receipt-verified 2.2.0, 2.3.0 and 2.3.1; inspect `ahub agents`); an unverified/untrusted version or failed preparation is unavailable with its reason. Seed modulo includes only kinds with a declared candidate, even when blocked; undeclared kinds remain trailing trace entries. Verify seeds 0..5 with only Claude/Codex declared alternate 3/3, and an offline declared kind still retains its slot. Agy stays unavailable as a reviewer with exact reason `agy is an implementer peer (#485)`, including explicit pool requests. #234 adds no agy reviewer launch or live leg.
|
|
1957
2005
|
|
|
1958
2006
|
Record source head, exact executable version/hash, isolation receipt, task/head identities, route walk, distinct native session evidence and persisted verdict separately. Do not record credentials or raw transport environments. Codex sandboxed shell must not be described as Claude's Read/Grep/Glob-only whitelist. Network policy is configured disabled; no network-denial probe or native LLM isolation result has been recorded by this investigation.
|
|
1959
2007
|
|
|
1960
2008
|
## Kimi reviewer and task-bound MCP (#533)
|
|
1961
2009
|
|
|
1962
|
-
Kimi receipt-verified 2.2.0 and 2.3.
|
|
2010
|
+
Kimi receipt-verified 2.2.0, 2.3.0 and 2.3.1 are available when runtime preparation passes; a failed or unsupported preparation remains unavailable. The [2.3.0](verification/2026-10-22-kimi-qwen-compat.md) and [2.3.1](verification/2026-10-25-kimi-231-reviewer-compat.md) receipts are model-free; the following authenticated evidence belongs to 2.2.0. Completed evidence includes account-free two-tool/native OAuth race checks, host/fake lifecycle refusals and authenticated fresh `changes_requested` verdicts. The complete-collector live run observed project parity/canary absence, authenticated exact-two hub inventory/calls, zero decoy requests, cleanup and unchanged real config hashes. Its original aggregate receipt remains failed because of an unattributed extra observer error; the later account-free GET reproduction/correction is not a new live pass. These separate outcomes are preserved in the R0/live report. The remaining actual interactive-TUI/coexistence and usage-observation checks are **UNVERIFIED**; they do not turn completed live/fake evidence into an unrun test. Person ruling 2026-10-19 (8281ec35) limits #234 to Kimi and task-bound MCP and supersedes all earlier agy reviewer implementation/admission/live-leg rulings. Agy is an implementer under #485; its retained reviewer slot remains unavailable with exact reason `agy is an implementer peer (#485)`. Earlier agy probes and unresolved meta-tool/resource findings remain historical in the [R0 report](verification/2026-10-19-kimi-agy-reviewer-isolation.md), not active smoke requirements here. Kimi's native-permission/shared-account ruling supersedes its earlier R0-only hold and Seatbelt requirement. The [spec amendment](specs/2026-09-19-agent-hub-design.md#kimi-reviewer-and-task-bound-mcp-amendment-533-hub-task-234) defines the active contract and residuals.
|
|
1963
2011
|
|
|
1964
2012
|
- For Kimi regression smoke, confirm a fresh child per review, no resume, source evidence supplied only by screened `hub_task_show`/review brief and host facts, and no native Read/file/shell tool. Confirm the in-process host MCP exposes only the task-bound `hub_review` and `hub_task_show` capability, without the full hub control token. Exercise wrong task/head/review revision, stale/replaced child, private review, malformed arguments, unknown tool and stopped-child refusals; verify no saved approval on any failure.
|
|
1965
2013
|
- For each compat-admitted Kimi version, confirm no Seatbelt wrapper, a private `KIMI_CODE_HOME` with managed-provider-only config, clean temporary cwd and explicit empty skills directory. Verify exactly the two `mcp__agent-hub__...` tools in model disclosure and invocation; reject native read/write/command and extra MCP calls. Confirm `credentials/` and `oauth/` are directory symlinks to the native locations, `device_id` links the native identity, no token copy/extraction/static provider is used, and `KIMI_DISABLE_OAUTH_LOCK` is absent. The concurrent account-free OAuth print stand-in proved two expired generations serialize refresh through shared directory links, but it was not an attached interactive TUI. Actual TUI refresh/atomic rename/lock coordination and shared-account quota/pause fanout remain separate live legs. Existing project MCP/config byte-parity observations remain recorded in the R0/live report. Record identities/metadata without token values.
|
|
@@ -2649,6 +2649,28 @@ the queue.
|
|
|
2649
2649
|
- Activity contains delivery ids/senders, message/thought chunks, bounded tool summaries, plans, permission request/outcome ids and turn end. It never enters persistent events/logs, UI, tools or cloud memory. Existing final answers still travel through the answer bus.
|
|
2650
2650
|
- Kimi TUI defaults remain pull-only. Qwen TUI and native visualizer support remain gated by unverified T0 checks. See [verification](../verification/2026-10-11-acp-watch-t0.md).
|
|
2651
2651
|
|
|
2652
|
+
# Qwen dual-output TUI amendment, 2026-10-25 (#286 Part 3)
|
|
2653
|
+
|
|
2654
|
+
- T0 ran live on Qwen Code 0.26.1 under an isolated HOME ([verification](../verification/2026-10-25-qwen-dual-output-t0.md)): Q2, Q3, Q5 and Q6 pass, Q4 fails. The owner decided on 2026-10-25 to build Part 3 with the two limits below stated, so AC9's condition reads "Q2, Q3, Q5 and Q6 pass; Q4's limit is stated".
|
|
2655
|
+
- Launch: `ahub qwen --mode tui` runs `qwen --input-file <in> --json-file <out> --mcp-config <hub server> --approval-mode default` in the person's terminal, as `ahub pi --mode tui` does. Both files live under the project state dir, mode 0600. The event file is a regular file the adapter tails from a saved offset.
|
|
2656
|
+
- Version gate: a separate compat entry for the dual-output contract, verified 0.26.1 only. It does not widen the headless `qwen` entry, whose ACP receipts are separate.
|
|
2657
|
+
- Delivery: `deliver()` sets busy synchronously and appends one `{"type":"submit","text"}` line with the digest. Qwen queues a submit that arrives during a turn and runs it after that turn ends.
|
|
2658
|
+
- Turn end: Qwen emits no `result` event in this mode. A turn ends at an `assistant` event whose `stop_reason` is not `tool_use`, followed by `message_stop`; that assistant text is the turn's shared answer. A `result` event, if a later build emits one, also ends the turn.
|
|
2659
|
+
- Approvals: `control_request` with `subtype: can_use_tool` goes to the hub approval path, and the answer is `{"type":"confirmation_response","request_id","allowed"}`. Allow or deny only. The hub's own tools are approved by the exact `tool_name` (`mcp__<server>__<tool>`), never by prefix. A `control_response` for a pending request that the hub did not answer means the person answered in the TUI: withdraw the hub's pending approval. A `control_response` error for a stale id is logged, never retried.
|
|
2660
|
+
- Limit 1, deny: a hub deny cancels Qwen's whole turn and Qwen emits nothing for it. After sending `allowed: false` the adapter ends the turn itself (idle, no shared answer, a notice that the turn was denied).
|
|
2661
|
+
- Limit 2, no external stop: the input protocol has only `submit` and `confirmation_response`, and SIGINT does not stop a turn. The inactivity watchdog never cancels a Qwen TUI turn. It reports the stalled turn to the person (status, console and dashboard: "stalled; press Esc in the Qwen terminal") and keeps the peer busy. The hub never types into the terminal or signals the process to stop a turn.
|
|
2662
|
+
- Prompt channel: the input file is a prompt channel. Qwen's own write and shell tools reach it only through approval, and the hub's approval path refuses any write or command that names the state dir. No other peer can write it.
|
|
2663
|
+
- Detached: the TUI keeps working when nobody reads the event file (T0 Q5). A daemon that cannot read the file treats the peer as detached, not idle; on reattach the adapter resumes from its saved offset.
|
|
2664
|
+
- Recovery: `qwen -r <session_id>` with the same two files resumes the conversation, does not replay earlier submits, and appends to the event file. The adapter records `session_id` from `system/session_start` and resumes from its saved offset, so events from before the restart are not processed twice.
|
|
2665
|
+
- Implementation notes (#449, measured on 0.26.1 where stated):
|
|
2666
|
+
- Files and state: `<state dir>/qwen/qwen-dual/{input.jsonl,events.jsonl,mcp.json,state.json}`; `state.json` holds `session_id`, the read offset, a generation counter and the bound owner (pid with its `processSignature`). `ahub qwen --mode tui` is explicit only: Qwen's `start.forms` stays `["headless"]`, so Qwen is not a start-mode peer and the hub never opens this terminal itself. The TUI driver is the definition's `start.createTui` on the `qwen-dual` daemon port, a fixed-builtin capability.
|
|
2667
|
+
- Gate: checked twice, by `qwen --version` against `COMPAT["qwen-dual"]` before any file is made, and by the TUI's own `session_start` (version, `protocol_version` 2, the events the adapter reads). An unverified TUI is not attached and the person gets a notice.
|
|
2668
|
+
- Event file: Qwen opens an existing `--json-file` at byte 0 without truncating it (it overwrote the head of a 3 KB file in place), so appending after a restart is not what it does. A fresh or resumed launch therefore starts from empty files (the old event file is kept as `events.prev.jsonl`, the saved offset restarts at 0 for the new file), and a hub restart while the person's Qwen keeps running adopts it and reads on from the saved offset with no launch. Qwen starts reading the input file at its size at start, so earlier submits are not replayed either way.
|
|
2669
|
+
- Owner: bound from the host's process table (the one process whose argv contains `--input-file <this adapter's file>`; two matches is no owner), at `session_start` and then every second. No launcher message is trusted for it. When the owner is gone the peer goes offline, an open hub turn settles `needs_review` and a notice says the terminal ended. The hub never signals it.
|
|
2670
|
+
- A prompt in the event stream that is not the hub's submit (matched by its text) is the person typing: the peer is busy, nothing is shared, and a `can_use_tool` request in that turn is the person's own (the console is not asked). A hub turn that a person's prompt cuts off (Esc, then a new prompt) settles `needs_review`.
|
|
2671
|
+
- `ahub stop qwen` refuses for the TUI (end it in its terminal); a headless Qwen cannot be started while the TUI runs, and the reverse. Crash and upgrade recovery refuse to restore this terminal as a headless worker (`recoveryRefusal`), as for Kimi's own TUI.
|
|
2672
|
+
- Not verified live: the T0 receipt covers Q2 to Q7 on 0.26.1; this PR's tests use a fake dual-output Qwen (`test/fakes/qwen-dual.ts`) and no live model, so end-to-end use with a real account remains the person's check (`docs/dogfood.md`).
|
|
2673
|
+
|
|
2652
2674
|
# Historical workflow amendment, 2026-10-11 (#257, superseded by #481)
|
|
2653
2675
|
|
|
2654
2676
|
- The original optional workflow selector used existing SDLC receipts and Tasks authority; its current replacement is the #481 amendment below. A reviewer/person-owned checklist stores bounded criterion ids, hashed evidence refs and verified/failed/unverified claims bound to the reviewed head.
|
|
@@ -4716,7 +4738,21 @@ export interface ClaudeLaunchProof {
|
|
|
4716
4738
|
|
|
4717
4739
|
### Call-site inventory
|
|
4718
4740
|
|
|
4719
|
-
Counts are call occurrences in `src/hub/daemon.ts` (`grep -o`). `ripwire . --callers=<predicate>` lists the same functions and lines.
|
|
4741
|
+
The following inventory is historical, audited at `bab87cb2` (2026-10-22). Counts are call occurrences in `src/hub/daemon.ts` (`grep -o`). `ripwire . --callers=<predicate>` lists the same functions and lines.
|
|
4742
|
+
|
|
4743
|
+
Slice 2a freezes the current baseline at `f11a2a1c3e24d1c431937941ecd40b07ca00a5cc` in `test/daemon-native-branches.json`, with executable checking in `test/daemon-native-branches.test.ts` using the shared `scripts/lib/ts-ast.ts` helper. The JSON had 61 predicate/function rows and 85 calls at that baseline; #449 (the Qwen dual-output TUI, #286 Part 3) added three reviewed `isQwenDualPeer` rows (`startPeerBody` 2, `status` 1, `stopPeer` 1: 64 rows, 89 calls) across `src/hub/daemon.ts` and `src/hub/claude-launch-proof.ts` (the launch-proof reader currently contributes zero native predicate calls). Each row carries its current reason; additions and removals both require an explicit reviewed table edit. Tests never regenerate the table.
|
|
4744
|
+
|
|
4745
|
+
| Predicate | Historical count | Current count | Delta |
|
|
4746
|
+
|---|---|---|---|
|
|
4747
|
+
| `isCodexPeer` | 12 | 13 | +1 |
|
|
4748
|
+
| `isPiPeer` | 20 | 21 | +1 |
|
|
4749
|
+
| `isWsPeer` | 25 | 27 | +2 |
|
|
4750
|
+
| `isAcpPeer` | 12 | 13 | +1 |
|
|
4751
|
+
| `isLocalPeer` | 6 | 8 | +2 |
|
|
4752
|
+
| `isJcodePeer` | 1 | 1 | 0 |
|
|
4753
|
+
| `isReviewerPrintPeer` | 2 | 2 | 0 |
|
|
4754
|
+
|
|
4755
|
+
The seven additional calls retain later native proof behavior: `actionEnforcement` adds one call each for Codex, Pi, Ws, ACP and Local; `actionSession` adds Local's intentionally separate action-allowance session; `currentExecutionOwner` adds the terminal Claude socket-generation/worktree proof. Slice 2a removes none of these calls to match the historical plan. The detailed historical assignments below remain context for later slices; the committed table is the current per-function ratchet.
|
|
4720
4756
|
|
|
4721
4757
|
| Predicate | Count | Ported | Intentional |
|
|
4722
4758
|
|---|---|---|---|
|
|
@@ -4835,15 +4871,15 @@ Common acceptance criteria:
|
|
|
4835
4871
|
|
|
4836
4872
|
1. **2a: Ratchet and trust gate.** Area note: `tests.md`. Size S.
|
|
4837
4873
|
- Paths:
|
|
4838
|
-
- new `test/daemon-native-branches.test.ts` and `test/daemon-native-branches.json` (rows `{predicate, function, count, reason}`; TypeScript AST over `src/hub/daemon.ts` and `src/hub/claude-launch-proof.ts`, reusing
|
|
4874
|
+
- new `test/daemon-native-branches.test.ts` and `test/daemon-native-branches.json` (rows `{predicate, function, count, reason}`; TypeScript AST over `src/hub/daemon.ts` and `src/hub/claude-launch-proof.ts`, reusing `scripts/lib/ts-ast.ts` from #684 phase 5; never imports daemon code);
|
|
4839
4875
|
- `src/adapters/registry.ts` (`isFixedBuiltinDefinition`; `session.nativeRevert` restricted);
|
|
4840
4876
|
- `test/adapter-capability-scope.test.ts`;
|
|
4841
4877
|
- `src/adapters/conformance/kit.ts` and `enrollment.ts`, with a ports leg.
|
|
4842
4878
|
- ACs:
|
|
4843
|
-
- The table equals
|
|
4879
|
+
- The table equals the current baseline above (61 predicate/function rows, 85 calls at `f11a2a1c`); the older inventory remains historical context.
|
|
4844
4880
|
- An inline fixture with one extra predicate call fails, and so does a row with an empty reason.
|
|
4845
4881
|
- A configured definition that declares `session.nativeRevert` is refused.
|
|
4846
|
-
- Kit leg: a non-fixed-builtin definition whose product has `frontendProxy`, `statusNotice` or `revertTurn` fails the kit. (
|
|
4882
|
+
- Kit leg: a non-fixed-builtin definition whose product has `frontendProxy`, `statusNotice` or `revertTurn` fails the kit. (The proposed `frontendProxy` and `revertTurn` members arrive in 2d; Codex already has `statusNotice`. The foreign-product check is active in 2a and rejects presence even when a value is undefined.)
|
|
4847
4883
|
- The ACP ports leg (`deliveryOnly`, `hubOwned`, `form: "headless"`) is not part of 2a: no ACP product exposes those members until 2b adds them, so 2a cannot assert them. 2a only reserves the leg in `UNSUPPORTED_LEGS` with the reason "ports added in 2b".
|
|
4848
4884
|
- Builtins that do not apply name their reason in `UNSUPPORTED_LEGS`.
|
|
4849
4885
|
2. **2b: Delivery-only, owned stop, form and roster detach.** Area note: `adapters.md` (Spawned processes; Turns). Size M.
|
|
@@ -327,7 +327,9 @@ the runner (refusing while one is alive) and append to the receipt's audit:
|
|
|
327
327
|
live instance through the lifecycle stop, which verifies the instance and
|
|
328
328
|
waits until its manifest and registry claim are gone, and is then inspected
|
|
329
329
|
again. A daemon this operation does not own, or one already released, is left
|
|
330
|
-
running.
|
|
330
|
+
running. Its audit entry keeps the operation's error at that moment as
|
|
331
|
+
`cause` (#776): the error itself is cleared on disposal, and without it an
|
|
332
|
+
abandoned operation could not be diagnosed. Each project's outcome is recorded as it happens; a disposition that
|
|
331
333
|
stops partway keeps the lock, and resume and `--fresh-session` then refuse
|
|
332
334
|
until `--stop-and-archive` is run again and finishes. A stopped project's committed
|
|
333
335
|
snapshot of this operation moves to `restart.abandoned.<hash>.json`, which is
|
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
# 0.17.0 release verification
|
|
2
|
+
|
|
3
|
+
This record covers release preparation and source review. It claims no installation, running-hub restart or live TUI launch.
|
|
4
|
+
|
|
5
|
+
## Candidate
|
|
6
|
+
|
|
7
|
+
Version 0.17.0, built from merged main 57384ce9. The release commit 66db7605 carries the version, CHANGELOG section and bundle (plugin 0.17.0, built with the pinned Bun 1.4.2), and every document stamp names it. Control protocol stays 16. The person directed the release on 2026-10-25, and chose to include #805 after it found real-binary Qwen TUI defects. The minor rises because #778, #790 and #791 are features. It includes:
|
|
8
|
+
|
|
9
|
+
- Qwen Code's own TUI driven by the hub over dual output, `ahub qwen --mode tui`, with its two stated limits: a hub deny ends the turn without an answer, and a stalled turn is never cancelled (#286 Part 3, #791). It is gated by the `qwen-dual` compat entry, verified 0.26.1 only, from the live T0 receipt `2026-10-25-qwen-dual-output-t0.md`.
|
|
10
|
+
- The Codex reviewer approves exactly `hub_review` and `hub_task_show`, per tool (#783, #796), with a model-backed receipt (`2026-10-25-codex-0164-reviewer-tool-call.md`). Before it, Codex 0.164.0 `exec` refused every MCP tool call, so `codex-review` recorded no verdict.
|
|
11
|
+
- The Qwen TUI's real-binary fixes (#286, #805): the owner is bound at the root of Qwen's process tree, a hub deny completes the delivery, and the exec runs in the person's environment without another agent's markers. Receipt: `2026-10-25-qwen-dual-output-hub-e2e.md`, rerun by jcode on the merged head c71f07d6.
|
|
12
|
+
- A reviewer turn that ends without a verdict is logged in one screened line and re-offered, or goes to the person when the person named that reviewer (#453, #804). Receipt: `2026-10-25-reviewer-no-verdict-diagnostics.md` (fake transport).
|
|
13
|
+
- Reviewer admission refusals no longer mark the reviewer unavailable (#781, #789); an unavailable reviewer slot no longer fences agy task reads (#794); the Kimi 2.3.1 fresh reviewer is verified model-free (#797).
|
|
14
|
+
- Stop-and-archive keeps the blocking error as the audit `cause` (#776, #777).
|
|
15
|
+
- Recurring-finding cards count a task only where the finding was recorded open or accepted (#328, #780).
|
|
16
|
+
- Hub briefs and plan guards are served from agent-mem's intent brief (#779).
|
|
17
|
+
- The native-branch ratchet and the fixed-builtin gate for proof-adjacent ports (#625 slice 2a, #790).
|
|
18
|
+
- Shared AST helpers and seed-rot diagnostics; seeded CI passes the base with `--base` (#684 phase 5, #778).
|
|
19
|
+
- The missing generation sync remote is quiet (#401, #748).
|
|
20
|
+
- Documentation: live-leg classes and this repository's reviewer-merge convention (#784 P0, #795); the Kimi AC11 usage receipt (#285, #782).
|
|
21
|
+
|
|
22
|
+
Still open, not in this release:
|
|
23
|
+
- #466 (#793 remote check runner, PR #806): changes requested; on a real Docker daemon the source copy fails and comma-bearing labels can wedge cleanup.
|
|
24
|
+
- #783 AC3: the first real `codex-review` verdict on a hub running this release.
|
|
25
|
+
|
|
26
|
+
## Reviews
|
|
27
|
+
|
|
28
|
+
The planner reviewed every PR it did not author; the person approved the planner's own PRs (#748, #777, #780, #795) after OCR-delegated review. Where a PR changed reviewer isolation, permissions or a native adapter, an independent read-only reviewer also checked it. Every codex-connector thread was fixed or answered and resolved before merge.
|
|
29
|
+
|
|
30
|
+
| PR | Merge | Review | CI |
|
|
31
|
+
| --- | --- | --- | --- |
|
|
32
|
+
| #748 (#401) | 38cde512 | Person, after a rebase | exact head, success |
|
|
33
|
+
| #778 (#684 p5) | f11a2a1c | Two rounds: seed-rot context in a depth-1 CI checkout fixed with full history and `--base` | exact head, success (one unrelated #525 flake on a first run) |
|
|
34
|
+
| #779 (agent-mem#40 AC6) | ebbbcd84 | Two rounds: plan-guard fails kept first under the library cap; the fixture uses the production predicates | exact head, success |
|
|
35
|
+
| #780 (#328) | 0c6fdbc5 | Person, after OCR: occurrence by id, withheld reviews skipped | exact head, success |
|
|
36
|
+
| #782 (#285 AC11) | 383b435f | Receipt arithmetic and scope; credential symlink nit | exact head, success (one unrelated #525 flake) |
|
|
37
|
+
| #777 (#776) | 0af97add | Person, after OCR: audit contract comment | exact head, success |
|
|
38
|
+
| #789 (#781) | d308268d | Admission refusal separated from start failures | exact head, success |
|
|
39
|
+
| #790 (#625 2a) | e8deb15d | Independent review: builtin behaviour unchanged, identity gate, static ratchet | exact head, success |
|
|
40
|
+
| #791 (#286 P3) | f3a6e701 | Spec limits and security boundary by hand; three bot P2s fixed | exact head, success |
|
|
41
|
+
| #794 | 4d5bd7cf | Reviewed in its own task | exact head, success |
|
|
42
|
+
| #795 (#784 P0) | 42929225 | Person, after a by-hand review (OCR selected no files): merge duty kept out of shipped templates | exact head, success |
|
|
43
|
+
| #796 (#783) | 95374582 | Exact two-tool map, model-backed receipt | exact head, success |
|
|
44
|
+
| #797 | 95e032b4 | Reviewed in its own task | exact head, success |
|
|
45
|
+
| #804 (#453) | f0517b5d | Independent review: AC1 and AC2 met, three nits left open; re-filed at an unchanged tree after a procedural changes_requested | exact head, success |
|
|
46
|
+
| #805 (#286) | 57384ce9 | OCR delegated review found another agent's markers reaching Qwen's hub MCP server through the person's env; fixed in the PR with a regression test; jcode reran the real-binary probe on the final head | exact head, success |
|
|
47
|
+
|
|
48
|
+
## Document review
|
|
49
|
+
|
|
50
|
+
Eleven documents were stale against the 0.16.0 stamps (267f39c0). Three read-only reviewers checked each one against `git log 267f39c0..HEAD` over its paths and the PRs above, and corrected what was wrong (commit 0ca857af, rebased from 24b654a9).
|
|
51
|
+
|
|
52
|
+
| Document | Result | Checked claims and correction |
|
|
53
|
+
| --- | --- | --- |
|
|
54
|
+
| `README.md` | corrected | Status 0.17.0, control protocol 16, and the verification link. Feature bullets unaffected. |
|
|
55
|
+
| `docs/quickstart.md` | corrected | Install pin `v0.17.0`. A new "Upgrading to 0.17.0" section: the Qwen TUI opt-in and its limits (end it in its terminal before an upgrade, then rerun `ahub qwen --mode tui`), the Codex reviewer fix, reviewer availability, agy task reads, the upgrade audit cause. |
|
|
56
|
+
| `src/cli/help.ts` | no change | The Qwen row comes from the definition's `cli.usage`, updated by #791; main.ts, launch.ts and the other native entries are unchanged. help.test passes. |
|
|
57
|
+
| `docs/operations.md` | corrected | Qwen TUI start refusals; the Codex per-tool approval and the model-backed receipt rule; reviewer admission; the audit cause; seed-rot `--base`; the quiet missing remote. Known gaps fixed: the memory project key sentence (no claude-mem aliases) and `memory.worker_url` marked ignored. |
|
|
58
|
+
| `docs/security.md` | corrected | The state-dir refusal exempts the hub's own tools; Codex per-tool approval; the agy placeholder slot fences nothing; native proof ports read only from fixed builtin definitions; `memory.worker_url` ignored. |
|
|
59
|
+
| `docs/agent-notes/adapters.md` | corrected | The native-branch ratchet and proof-port rules (#790); Qwen TUI refusals, `holdsTerminal()`, the submit prefix, the unwritten-answer rule, event lines over 1 MiB skipped (#791); the agy reviewer-slot fence (#794). |
|
|
60
|
+
| `docs/agent-notes/daemon.md` | corrected | Audit cause; `quietSyncError`; library-served briefs with host policy (#779). Scope gains `src/memory/brief.ts` and `src/hub/memory-plan-guard.ts`. |
|
|
61
|
+
| `docs/agent-notes/tasks.md` | corrected | The #780 occurrence rule made exact (by id, non-withheld reviews). |
|
|
62
|
+
| `docs/agent-notes/tests.md` | corrected | Seed-rot context and `--base`; `scripts/lib/ts-ast.ts` is lexical and never imported from `src/`; the proof-gate test cuts daemon code by exact declaration text; the Qwen fake. |
|
|
63
|
+
| `docs/agent-notes/budget.md` | corrected | Scope line only: `src/hub/instance-budget.ts` and `src/hub/usage-rollup.ts`. |
|
|
64
|
+
| `docs/agent-notes/bus.md` | no change | bus.ts unchanged; the Qwen adapter addresses replies through `replyParent` and `replyAudience`. |
|
|
65
|
+
|
|
66
|
+
After #804 and #805 merged, a second recheck (commit 1ee886cb) added the no-verdict log line and re-offer rule to `docs/operations.md` and `docs/quickstart.md`, bound the Qwen owner at the root of its process tree in `docs/security.md` and the adapters note, stated the person-env rule for the Qwen exec in `docs/security.md`, and noted in `docs/quickstart.md` that a deny completes the delivery. `docs/agent-notes/README.md` went stale only through the daemon note's scope line; it holds background only and needs no change.
|
|
67
|
+
|
|
68
|
+
The AGENTS.md area lists and the `docs/verified.json` paths gain `src/memory/brief.ts`, `src/hub/memory-plan-guard.ts` (Daemon) and `scripts/lib/ts-ast.ts` (Tests).
|
|
69
|
+
|
|
70
|
+
## Known gaps, not blocking
|
|
71
|
+
|
|
72
|
+
- No live Qwen TUI turn with a model has run; #286 stays open for it.
|
|
73
|
+
- `codex-review` takes effect only after the running hub is upgraded to this release; #783 AC3 is recorded then.
|
|
74
|
+
- `test/claude-implementer-daemon.test.ts` flaked twice on unrelated PRs with "JSON Parse error: Unexpected EOF" (task #454), and `test/memory-code-head-684.test.ts` "#367 cached HEAD proof" fails about one run in three on main (hub fail note d13a75e3).
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
# Codex 0.164.0 reviewer tool-call receipt
|
|
2
|
+
|
|
3
|
+
Result: one real model review recorded one approved verdict through the production stdio MCP and `Tasks` boundary. Date: 2026-10-25, macOS arm64. Hub task: 465; Refs #783. The successful run took approximately 30 seconds, from 04:26:28 UTC to 04:26:58 UTC. No production task, PR, daemon or personal MCP configuration was changed.
|
|
4
|
+
|
|
5
|
+
## Execution and identity
|
|
6
|
+
|
|
7
|
+
The fixture used production `startDaemon`, `ReviewerPrintPeer`, `codexReviewerBackend`, the stdio hub plugin and `Tasks`. A builder proposed and accepted one temporary task, the fixture console assigned its reviewer, and builder completion triggered the actual native model process. The fixture never called `hub_review` itself or substituted a fake tool result. Success required a persisted reviewer verdict containing the task's private synthetic marker; a model chat answer would not satisfy the check.
|
|
8
|
+
|
|
9
|
+
| Evidence | Observed value |
|
|
10
|
+
|---|---|
|
|
11
|
+
| Native version | `0.164.0` |
|
|
12
|
+
| Backend-reported binary identity SHA-256 | `5b9983835177b544bba49d54b6679faf2f833a8b278068a158698f5c4eef1395` |
|
|
13
|
+
| Tested backend-source SHA-256 | `0f76a216b51884909987e64af08ff92d2c90b8d28086ed523fefe211e4feb78e` |
|
|
14
|
+
| Tested stdio plugin SHA-256 | `55b8b41c2e78b038d8422f9680ef7895f711cfaf8c83fbe849f9d280bb6edee9` |
|
|
15
|
+
| Startup admission profile SHA-256 | `cbfe3fc42ebe09064e0c03c64e69913c9c348f07d0617f040144abccd2d50519` |
|
|
16
|
+
| Fixture head | `fe03a0ab4147be4ccfce8226fbc679aa9e3580e2` |
|
|
17
|
+
| Fixture task / reviewer | `1` / `codex-review` |
|
|
18
|
+
| Persisted state / reviewer verdicts | `approved` / `1` |
|
|
19
|
+
| Synthetic marker matched | `true` |
|
|
20
|
+
| Synthetic marker SHA-256 | `293f1e5564e058979d2d4e1586d2bf4f8e9581eceaccd17346627e48a0b9e6b6` |
|
|
21
|
+
| Persisted history SHA-256 | `0301f21d7f02b4c8635abffa0701f965b935793047771c1db1d653798c684ed4` |
|
|
22
|
+
| Delivery states / final reviewer state | `accepted`, `completed` / `idle` |
|
|
23
|
+
|
|
24
|
+
The plugin hash identifies the stdio server artifact used in the native run. The backend ran from the modified TypeScript source with the hash above; the plugin was subsequently rebuilt for packaging. The packaging gate checks that regenerated artifact separately. No model run of the subsequently regenerated artifact is claimed.
|
|
25
|
+
|
|
26
|
+
The readonly database readback checked the persisted actor, approved state, verdict count and marker. The head was bound to the request and enforced by the production running-child/`Tasks.reviewBindingCurrent` authority checks; the fixture did not perform a separate final SQL head/refs readback. The profile hash above is the initial startup admission receipt. `ReviewerPrintPeer` performs production preparation again for the actual review child; that second profile hash was not separately retained.
|
|
27
|
+
|
|
28
|
+
## Isolation and native policy
|
|
29
|
+
|
|
30
|
+
The backend inherits its host environment and does not itself create an isolated home. The fixture supplied fresh HOME and CODEX_HOME directories and only one auth.json symlink to the configured account. It copied no personal configuration or credential value. Eighteen additional absolute private roots covered the original account root and the original HOME's bounded credential locations, because replacing HOME would otherwise hide those locations from the backend's standard credential-root enumeration.
|
|
31
|
+
|
|
32
|
+
The native preparation receipt reported `readOnly: true`, `strictMcp: true`, `ephemeral: true` and all 14 existing evidence markers. Effective Apps, plugins and hooks were disabled; legacy notify and web search were disabled; the project was untrusted; only the two hub review tools were allowed. The existing native public-read, exact private-path, glob, symlink, portable-core, host-credential and write-denial canaries remained required and passed.
|
|
33
|
+
|
|
34
|
+
The source change supplies explicit `approval_mode: "approve"` overrides for the two allowed tools. The [official configuration reference](https://learn.chatgpt.com/docs/config-file/config-reference) documents `mcp_servers.<id>.tools.<tool>.approval_mode` with `auto | prompt | writes | approve` values. That reference establishes the format; the actual 0.164.0 native/model result is the runtime evidence here. Native 0.164.0 `mcp get --json` does not echo those approval fields; the inventory proves the enabled-tool and transport contract, not approval-policy readback. The real persisted verdict establishes that the tested model could invoke the allowed review tool through that configuration. Native MCP call-frame counts and a separate `hub_task_show` call count were not retained.
|
|
35
|
+
|
|
36
|
+
## Admission and cleanup
|
|
37
|
+
|
|
38
|
+
The controller atomically acquired the repository's existing local heavy-job lock before creating auth links or running native preparation. An existing lock would have caused immediate exit 75 without retry or reaping. Remote-job admission was checked separately by the coordinator; this is not global enforcement.
|
|
39
|
+
|
|
40
|
+
The fixture deadline was 110 seconds and the controller deadline 120 seconds. The one actual model review finished within both. Cleanup awaited the production daemon and `OwnedPrintChild` process-group teardown. The driver group was subsequently absent, and an additional scan found zero new processes whose argv named the fixture root. That extra scan is limited to the fixture root and is not universal orphan detection.
|
|
41
|
+
|
|
42
|
+
The source-account auth metadata comparison was unchanged before and after the successful run, without reading credential values. Both metadata hashes were `fa8d925c16ed3503eda3a0cbcb037101fbde4d6bc3b448a9693a39d6ec84fe81`. Temporary auth link, HOME, CODEX_HOME, project and task state were removed, and the controller's own lock was released after cleanup confirmation. The synthetic source file remained unchanged during review.
|
|
43
|
+
|
|
44
|
+
Private proof metadata remained at mode 0600, 2612 bytes, SHA-256 `eff722e3085acf1a0a21cc6569a793f1eb51e152cfd4a3d57e8d168ce94b9d32`. The mode-0600 harness output log was empty. No credential value, control token, private path, marker value or model prose is reproduced here.
|
|
45
|
+
|
|
46
|
+
## Earlier attempts and limits
|
|
47
|
+
|
|
48
|
+
The initial admitted attempt selected PATH's `codex-cli 0.146.0` rather than the separately installed 0.164.0 launcher. Reviewer startup returned successfully, then the fixture's explicit version guard refused before a model-review request. The failure-message hash was `f8b54e55485eb602d97d36e3705a749b21695a3ba3705ce79cb90dec92a9d1c0`, later matched to the harness's own static version-guard error. Positive cleanup was recorded; source-account metadata comparison was not retained on that initial failure path and remains unverified for that attempt.
|
|
49
|
+
|
|
50
|
+
A separately authorized setup-only diagnosis passed daemon startup and console connection in 0.25 seconds, with zero native preparations and zero model requests. It confirmed unchanged account metadata and cleanup. The harness then selected the retained 0.164.0 launcher explicitly, and the coordinator authorized the single actual model run documented above. There was no automatic model retry.
|
|
51
|
+
|
|
52
|
+
This receipt proves the tested source/native/model/tool/host round trip in the temporary fixture. It does not prove production deployment, native Linux behavior, an effective network-isolation probe, or every possible reviewer workflow. The installed 0.16.0 runtime remained unchanged; production use still requires a separate release and runtime upgrade.
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
# Kimi 2.3.1 fresh-reviewer compatibility receipt
|
|
2
|
+
|
|
3
|
+
## Scope and identity
|
|
4
|
+
|
|
5
|
+
This #792 (K1) receipt repeats the model-free 2.3.0 procedure (`2026-10-22-kimi-qwen-compat.md`) on the installed Kimi 2.3.1. No real account, provider inference, personal configuration, native login, credential refresh or personal machine trust was used or changed. Per the person's 2026-10-25 decision the result is a version verification only: no `kimi-review` instance is declared, and Kimi stays a pull-only TUI peer. The reviewer remains off until a project declares one.
|
|
6
|
+
|
|
7
|
+
| Artifact | Binding |
|
|
8
|
+
| --- | --- |
|
|
9
|
+
| Kimi native macOS arm64 executable | version `2.3.1`; SHA-256 `f06ba6a3bb7ed2fc1f82590f7c22570736d9ee7344059fa92404ca75dc2d5b45` |
|
|
10
|
+
| Kimi source | tag `@moonshot-ai/kimi-code@2.3.1`, commit [`684bae9ea6a437f3bae98bba1dcb797a917d500b`](https://github.com/MoonshotAI/kimi-code/tree/684bae9ea6a437f3bae98bba1dcb797a917d500b) |
|
|
11
|
+
|
|
12
|
+
## What changed since 2.3.0
|
|
13
|
+
|
|
14
|
+
Four commits (`b5e28fd6..684bae9e`): workspace trust decoupled from runtime initialization (c569bab5), session-lock holder names and a lock takeover path (0e3980e7), a web dist sync and the release. The two that touch reviewer isolation:
|
|
15
|
+
|
|
16
|
+
- `workspaceMcpConfigService.ts` reloads file MCP servers after a trust change and adds `KIMI_CODE_TRUST_WORKSPACE` (`trustEnv.ts`). Project MCP is still loaded only for a trusted workspace. The reviewer child runs with a replaced environment and a clean temporary cwd, so neither the variable nor a project MCP file reaches it. The probe sets the variable in the host environment as a decoy and asserts it is absent from the launch environment.
|
|
17
|
+
- Session locks gained a takeover path and `KIMI_CODE_SESSION_LOCK=0`. A fresh reviewer never resumes or shares a session, so this does not change its contract.
|
|
18
|
+
|
|
19
|
+
## Results
|
|
20
|
+
|
|
21
|
+
The 2.3.0 producer was copied with only the version, source commit, output paths and the decoy changed. It ran against an isolated HOME and a synthetic native `KIMI_CODE_HOME` (managed `kimi` provider, invented access-only OAuth record, device identity). The native print leg redirected only the managed provider endpoint and its scoped synthetic OAuth slot to a loopback fixed-response server, which performs no inference.
|
|
22
|
+
|
|
23
|
+
| Leg | Fixture trust run | Verified run (no trust) |
|
|
24
|
+
| --- | --- | --- |
|
|
25
|
+
| `kimiReviewerBackend.prepare` (private config, exact two-tool agent, empty skills, replace env, `current()`) | pass | pass (`fixtureTrustOnly: false`) |
|
|
26
|
+
| Task-bound MCP: exactly `hub_review` and `hub_task_show`; other task id, third tool and missing auth refused; revocation refuses reads | pass | pass |
|
|
27
|
+
| Native exact-two-tools: 4 requests, every inventory exactly the two hub names, both calls reached the host, injected `Write` refused, marker `unchanged` | pass | pass |
|
|
28
|
+
| Native positive control: `Write` added to the agent; inventory includes it, marker written | pass | pass |
|
|
29
|
+
| Trust-env decoy absent from the launch environment | pass | pass |
|
|
30
|
+
| Cleanup of owned groups and temporary trees | pass | pass |
|
|
31
|
+
|
|
32
|
+
Before and after the verified run, the SHA-256 of the real `~/.kimi-code` `config.toml`, `mcp.json`, `device_id` and a digest over every file in `credentials/` and `oauth/` were identical.
|
|
33
|
+
|
|
34
|
+
The producer ran with Bun 1.4.3 (the installed one); the 2.3.0 producer used pinned Bun 1.4.2. The hub code it exercises is plain TypeScript with no bundle step.
|
|
35
|
+
|
|
36
|
+
## Not verified
|
|
37
|
+
|
|
38
|
+
A real-account review, attached-TUI coexistence during a review, OAuth refresh under a real token, review quality, token usage and quota behaviour remain unverified, as for 2.3.0. No future version or compatible line is inferred.
|
|
39
|
+
|
|
40
|
+
## Evidence
|
|
41
|
+
|
|
42
|
+
Kept outside the repository in `scratchpad/temp/agent-hub-792`:
|
|
43
|
+
|
|
44
|
+
| Item | SHA-256 |
|
|
45
|
+
| --- | --- |
|
|
46
|
+
| `kimi-probe.ts` | `0a7ed09fa3f27afd7177d262b668cc8a5700f4b4292141210806baba9fd540e1` |
|
|
47
|
+
| `kimi-native-result.json` (fixture trust) | `42368dab8aa210934ed66dae6e93113d078245461f9da10ee97286b582ff063d` |
|
|
48
|
+
| `kimi-verified-result.json` (no trust) | `b962a7ed389cf915051c1e7c73c54bb5dccb0dd96c522806371f6d7eb28e047d` |
|