@staix/agent-hub 0.20.0 → 0.21.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 +59 -3
- package/README.md +2 -2
- package/docs/agent-notes/README.md +11 -0
- package/docs/agent-notes/adapters.md +7 -3
- package/docs/agent-notes/benchmarks.md +27 -0
- package/docs/agent-notes/budget.md +2 -1
- package/docs/agent-notes/bus.md +2 -2
- package/docs/agent-notes/daemon.md +15 -6
- package/docs/agent-notes/local-worker.md +4 -0
- package/docs/agent-notes/models.md +2 -0
- package/docs/agent-notes/tasks.md +17 -9
- package/docs/agent-notes/tests.md +9 -2
- package/docs/bench.md +1 -1
- package/docs/data-policy.md +2 -0
- package/docs/dogfood.md +37 -4
- package/docs/events.md +2 -1
- package/docs/memory.md +9 -3
- package/docs/operations.md +565 -62
- package/docs/profiles.md +13 -1
- package/docs/quickstart.md +14 -9
- package/docs/research.md +7 -4
- package/docs/sdlc.md +54 -10
- package/docs/security.md +396 -34
- package/docs/smoke.md +127 -0
- package/docs/specs/2026-09-19-agent-hub-design.md +1070 -22
- package/docs/verification/2026-10-27-0.19.0.md +5 -5
- package/docs/verification/2026-10-28-claude-2.1.311-2.1.312-bash-permission.json +635 -0
- package/docs/verification/2026-10-28-claude-2.1.311-2.1.312-permission-transport.json +403 -0
- package/docs/verification/2026-10-28-claude-implementer-permission-triage.md +123 -0
- package/docs/verification/2026-10-28-jcode-0.99.1-usage.md +41 -0
- package/docs/verification/2026-10-28-realpath-name-audit.md +222 -0
- package/docs/verification/2026-10-28-routing-f0bb8ad0.md +1000 -0
- package/docs/verification/2026-10-29-0.21.0.md +61 -0
- package/docs/verification/2026-10-29-model-effort-capabilities.md +123 -0
- package/docs/verification/2026-10-29-modelrouting-b152ddb9.md +953 -0
- package/docs/verification/claude-implementer-permission-triage.ts +160 -0
- package/docs/verification/claude-permission-transport-native.py +71 -14
- package/docs/verification/profiles/YYYY-MM-DD-research.md +1 -1
- package/docs/verified.json +15 -15
- package/package.json +2 -2
- package/plugins/agent-hub/.claude-plugin/plugin.json +1 -1
- package/plugins/agent-hub/server.js +1016 -280
- package/src/adapters/acp.ts +79 -3
- package/src/adapters/channel-peer.ts +4 -0
- package/src/adapters/claude-channel.ts +13 -6
- package/src/adapters/claude-implementer.ts +60 -7
- package/src/adapters/codex-appserver.ts +77 -4
- package/src/adapters/conformance/fakes/jcode-agent.ts +6 -2
- package/src/adapters/conformance/kit.ts +3 -9
- package/src/adapters/definitions/builtin-metadata.ts +3 -114
- package/src/adapters/definitions/native-config-defaults.ts +1 -1
- package/src/adapters/definitions/native-config-policy.ts +3 -2
- package/src/adapters/definitions/native-context.ts +12 -0
- package/src/adapters/external-metadata.ts +48 -0
- package/src/adapters/external-modules.ts +63 -3
- package/src/adapters/jcode.ts +6 -2
- package/src/adapters/local-worker.ts +40 -25
- package/src/adapters/native-proof-ports.ts +10 -0
- package/src/adapters/registry-types.ts +4 -0
- package/src/broker/protocol.ts +203 -0
- package/src/broker/server.ts +314 -0
- package/src/broker/store.ts +548 -0
- package/src/cli/bench.ts +3 -2
- package/src/cli/broker.ts +58 -0
- package/src/cli/conductor-recovery.ts +142 -0
- package/src/cli/configured-acp-entry.ts +26 -6
- package/src/cli/console-state.ts +59 -17
- package/src/cli/console.ts +17 -2
- package/src/cli/facts-hook.ts +3 -1
- package/src/cli/help.ts +28 -16
- package/src/cli/identity.ts +23 -10
- package/src/cli/init.ts +34 -7
- package/src/cli/intake-output.ts +33 -0
- package/src/cli/main.ts +314 -71
- package/src/cli/output.ts +41 -1
- package/src/cli/recovery-package.ts +18 -1
- package/src/cli/role-holders.ts +35 -0
- package/src/cli/room.ts +304 -0
- package/src/cli/say-input.ts +16 -0
- package/src/cli/schedule-output.ts +49 -0
- package/src/cli/sdlc-output.ts +44 -6
- package/src/cli/smoke-pty-bridge.ts +33 -0
- package/src/cli/smoke-pty.ts +145 -0
- package/src/cli/smoke-run.ts +139 -0
- package/src/cli/smoke-target.ts +130 -0
- package/src/cli/source-output.ts +29 -0
- package/src/cli/task-output.ts +3 -2
- package/src/cli/upgrade-runtime.ts +98 -16
- package/src/cli/upgrade.ts +66 -7
- package/src/hub/ask.ts +76 -12
- package/src/hub/board.ts +132 -22
- package/src/hub/bus.ts +399 -56
- package/src/hub/child-process.ts +2 -0
- package/src/hub/ci-watch.ts +42 -20
- package/src/hub/claude-failure-stage.ts +85 -0
- package/src/hub/claude-shell-wrapper.ts +11 -5
- package/src/hub/conductor.ts +17 -4
- package/src/hub/config-apply.ts +32 -5
- package/src/hub/config-reload.ts +7 -5
- package/src/hub/config-trust.ts +7 -1
- package/src/hub/context-window.ts +33 -3
- package/src/hub/control-client.ts +14 -2
- package/src/hub/corpus-config.ts +34 -0
- package/src/hub/corpus-evidence.ts +184 -0
- package/src/hub/corpus.ts +373 -0
- package/src/hub/cost-governance.ts +26 -0
- package/src/hub/daemon.ts +1150 -171
- package/src/hub/deliverable-files.ts +8 -2
- package/src/hub/delivery-journal.ts +123 -17
- package/src/hub/dogfood-profile.ts +32 -5
- package/src/hub/dogfood-read.ts +6 -2
- package/src/hub/envelope.ts +11 -0
- package/src/hub/eval-gate.ts +131 -0
- package/src/hub/events.ts +1 -1
- package/src/hub/external-instance.ts +69 -0
- package/src/hub/facts.ts +15 -5
- package/src/hub/folder-trigger.ts +223 -0
- package/src/hub/hub-tools.ts +2 -1
- package/src/hub/inference.ts +20 -5
- package/src/hub/instance-config.ts +69 -3
- package/src/hub/instance-execution.ts +6 -2
- package/src/hub/instances.ts +32 -4
- package/src/hub/intake.ts +125 -0
- package/src/hub/learning.ts +85 -0
- package/src/hub/lifecycle.ts +7 -3
- package/src/hub/limits.ts +12 -5
- package/src/hub/native-permission-request.ts +77 -0
- package/src/hub/operational-authority.ts +63 -5
- package/src/hub/operational-settings.ts +76 -0
- package/src/hub/operational-view.ts +1 -1
- package/src/hub/packs.ts +132 -0
- package/src/hub/path-policy.ts +7 -0
- package/src/hub/peer-doing.ts +130 -0
- package/src/hub/peers.ts +12 -0
- package/src/hub/queue-backpressure.ts +58 -0
- package/src/hub/read-tools.ts +2 -0
- package/src/hub/recovery-authority.ts +151 -0
- package/src/hub/recovery-policy.ts +220 -0
- package/src/hub/research.ts +24 -14
- package/src/hub/restart.ts +7 -0
- package/src/hub/routing.ts +54 -11
- package/src/hub/schedules.ts +266 -0
- package/src/hub/sdlc-merge.ts +38 -14
- package/src/hub/sdlc-metrics.ts +32 -10
- package/src/hub/sdlc-queue.ts +7 -1
- package/src/hub/sdlc-release.ts +1 -1
- package/src/hub/sdlc.ts +37 -7
- package/src/hub/security-baseline.ts +38 -0
- package/src/hub/settings.ts +6 -1
- package/src/hub/shipped-packs.ts +8 -0
- package/src/hub/snapshots.ts +72 -6
- package/src/hub/source-read.ts +160 -0
- package/src/hub/sources.ts +311 -0
- package/src/hub/tasks.ts +910 -110
- package/src/hub/workflow.ts +1 -1
- package/src/local/doc-read.ts +69 -0
- package/src/local/proxy.ts +4 -13
- package/src/local/tools.ts +37 -3
- package/src/local/web-fetch-config.ts +43 -0
- package/src/local/web-fetch.ts +183 -0
- package/src/memory/brief.ts +85 -13
- package/src/memory/capture.ts +1 -1
- package/src/rooms/application.ts +114 -0
- package/src/rooms/boundary.ts +103 -0
- package/src/rooms/commands.ts +294 -0
- package/src/rooms/crypto.ts +371 -0
- package/src/rooms/link.ts +584 -0
- package/src/rooms/policy.ts +82 -0
- package/src/rooms/remote-peer.ts +80 -0
- package/src/rooms/service.ts +426 -0
- package/src/rooms/status.ts +146 -0
- package/src/rooms/store.ts +1210 -0
- package/src/rooms/wire.ts +465 -0
- package/src/ui/index.html +91 -4
- package/templates/packs/example.pack.json +13 -0
package/docs/operations.md
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# Operations guide
|
|
2
2
|
|
|
3
|
-
This guide describes ahub 0.
|
|
3
|
+
This guide describes ahub 0.21.0 and control protocol 16. Live verification
|
|
4
4
|
results and remaining prerequisites are recorded separately in [the smoke ledger](smoke.md).
|
|
5
5
|
|
|
6
6
|
## Agent-operated live verification
|
|
@@ -67,7 +67,7 @@ smoke ledger; the presence of this option proves no outstanding #273 live leg.
|
|
|
67
67
|
`status --all` uses the projects table. `queue show`, `models status` and `budget execution status` show labelled fields; add `--json` for the raw fetched document.
|
|
68
68
|
`report --by task` keeps the attribution and coverage sentences with section headings and aligned counters.
|
|
69
69
|
Use `--json` for scripts; it returns the fetched data without color, and `status --json` keeps its existing document.
|
|
70
|
-
`sdlc status`, `sdlc explain`, `sdlc queue`, `sdlc release`, `sdlc metrics`, `sdlc costs` and `sdlc authorize-release` print readable output by default (#814: aligned columns, wrapped text, readable times, `--color=auto|always|never`). A script that parsed their JSON must pass `--json`, which prints exactly the document they printed before. `sdlc export` is a data format and stays JSON, as do `export`, `research export` and `bench export`.
|
|
70
|
+
`sdlc status`, `sdlc explain`, `sdlc queue`, `sdlc release`, `sdlc metrics`, `sdlc costs`, `sdlc merge` and `sdlc authorize-release` print readable output by default (#814, #899: aligned columns, wrapped text, readable times, `--color=auto|always|never`). A script that parsed their JSON must pass `--json`, which prints exactly the document they printed before. `sdlc export` is a data format and stays JSON, as do `export`, `research export` and `bench export`.
|
|
71
71
|
`restart` and `upgrade` off an interactive terminal (a pipe, `--dry-run`), `recovery status` and the results of `recovery reattach`, `resume`, `abort` and `dispose`, `models setup` and `start`, `queue resolve`, and `budget execution configure` and `disable` print readable text by default (#814); a script that parsed their JSON must pass `--json`, which prints exactly the document they printed before, including `restart --json --yes`. A readable plan lists each project, each peer with how it runs and what happens to it, every blocker, every reconnect-only session and every fresh start, and says nothing was changed; `--json` keeps those as stderr lines. A pipe wraps at `COLUMNS` when it is set, else at 80 columns.
|
|
72
72
|
`task show`, `task cancel`, `backlog groom`, `backlog return|plan|pin|split|undo|pull-ready` and the `release` commands (`list`, `show`, `new`, `plan`, `activate|freeze|ship|drop`) print a readable block by default (#814): the task or release as the hub holds it, a table for lists, every identifier whole, long text wrapped to the terminal. A script that parsed their JSON must pass `--json`, which prints the hub tool's text exactly as before. A private task arrives from the hub as its stub and is shown as that stub. Because these commands also take free text, `--json` and `--color=` count as flags only at the start or the end of the arguments: `--json` in the middle of a reason is part of the reason, and a value flag's value (`task cancel --reason --json`) is never a flag. A reason that ends in a bare `--json` loses it to the flag. `backlog add|pull`, `task escalate|assign|unassign` already answered one sentence and are unchanged.
|
|
73
73
|
In `projects --json` and `status --all --json`, an unavailable hub that explicitly refuses an inspection for SQLite contention also carries `failure: { "code": "database_busy" }`; it is additive metadata alongside the neutral `error` sentence.
|
|
@@ -219,20 +219,21 @@ the stream; only the header is tab-flattened before tokenizing. Body lines retai
|
|
|
219
219
|
the previous rendering and stay plain, as does command output; colour never changes
|
|
220
220
|
what the panel's text says.
|
|
221
221
|
|
|
222
|
-
Peers, Approvals, Tasks, Queue, Settings and
|
|
222
|
+
Peers, Approvals, Tasks, Queue, Settings, Usage and Rooms are tables with a header row. A column is as
|
|
223
223
|
wide as its widest cell, up to a third of the terminal; the last column takes
|
|
224
224
|
the rest and is cut with `...`. In Tasks, TITLE takes the rest and the STAGE
|
|
225
225
|
meter keeps a fixed column at the right edge. In Usage, LAST RESET is the column that gives way on a narrow terminal.
|
|
226
226
|
|
|
227
227
|
| Panel | Columns |
|
|
228
228
|
|---|---|
|
|
229
|
-
| Peers | PEER STATE MODE LINK Q ! WIP REVIEW PAUSE QUOTA MODEL |
|
|
229
|
+
| Peers | PEER STATE MODE LINK Q ! WIP REVIEW PAUSE QUOTA MODEL DOING |
|
|
230
230
|
| Approvals | ID PEER LEFT TITLE |
|
|
231
231
|
| Tasks | ID STATE OWNER REVIEWER CLASS AGE TITLE STAGE |
|
|
232
232
|
| Tasks, backlog view (`b`) | ID PRI RESERVED DEPS CLASS AGE TITLE |
|
|
233
233
|
| Queue | ID PEER STATE REV AGE |
|
|
234
234
|
| Settings | GROUP KEY VALUE SOURCE APPLIES |
|
|
235
235
|
| Usage | PEER TODAY 7D LIFETIME PAUSE QUOTA LAST RESET |
|
|
236
|
+
| Rooms | ROOM LINK MEMBERS REMOTE |
|
|
236
237
|
|
|
237
238
|
In Tasks, `b` toggles open work and backlog. The backlog heading counts its rows;
|
|
238
239
|
rows sort by rank, then id. PRI shows priority/rank, RESERVED the reserved peer,
|
|
@@ -263,6 +264,20 @@ including `0/2`; a peer without an entry reads `-`. At or above the limit, the
|
|
|
263
264
|
cell uses the attention tone. At narrow widths WIP gives way before PEER, STATE
|
|
264
265
|
and QUOTA, whether or not MODE is present.
|
|
265
266
|
|
|
267
|
+
DOING (#878) is what a peer is working on, from signals the hub already receives:
|
|
268
|
+
a Claude session's title (its transcript's latest `ai-title` row) and the
|
|
269
|
+
in-progress item of its `TodoWrite` list or the task `TaskUpdate` put in
|
|
270
|
+
progress; a Codex thread's name and its plan's in-progress step; an ACP agent's
|
|
271
|
+
plan entry in progress. The current item wins over the title, cut to 120
|
|
272
|
+
characters. A peer with no such source, or one that is offline, shows nothing,
|
|
273
|
+
and the column appears only when some peer has a value; it is the first column
|
|
274
|
+
to give way on a narrow terminal. The value is a `[private]` stub while the peer
|
|
275
|
+
owns or reviews a PII task or is in a private turn, when it was observed then,
|
|
276
|
+
or when the public metadata screen refuses it or fails. `ahub status`, the
|
|
277
|
+
dashboard's peer rows and the conductor's `hub_status` show the same value; it
|
|
278
|
+
lives in the daemon's memory only, never in `status.json`, a log, an event or
|
|
279
|
+
memory capture. Board WIP is unchanged.
|
|
280
|
+
|
|
266
281
|
In Peers, select a row and press `w` to edit `tasks.wip_limits.<peer>`, prefilled
|
|
267
282
|
with its current limit. The editor stays on Peers and uses the existing Settings
|
|
268
283
|
value, daemon preview, nonempty reason and `y` confirmation flow; Esc cancels without writing. The
|
|
@@ -343,15 +358,16 @@ The local worker and headless ACP peers, including Jcode, receive tools only ins
|
|
|
343
358
|
Every console resolution needs a reason. Queue `discard` removes queued originals or
|
|
344
359
|
settles a needs_review receipt; it never cancels a task or native turn.
|
|
345
360
|
|
|
346
|
-
`ahub task unassign <id> [--role owner|reviewer] [--revision N]` is
|
|
347
|
-
|
|
361
|
+
`ahub task unassign <id> [--role owner|reviewer] [--revision N]` is available to
|
|
362
|
+
the person or configured conductor. The board records the caller that performed
|
|
363
|
+
the unassign. Owner removal is allowed in `proposed`, `in_progress` and
|
|
348
364
|
`changes_requested`, returns active work to `proposed`, and clears any owner
|
|
349
365
|
reservation. It is refused in `in_review`, `approved` and `backlog`. Reviewer
|
|
350
366
|
removal retains the state, including `in_review`, and refuses to remove the human
|
|
351
367
|
reviewer of PII work. No replacement is routed and no native process or external
|
|
352
|
-
effect is rolled back.
|
|
368
|
+
effect is rolled back. An owner-unassigned task stays out of automatic owner
|
|
353
369
|
offers across dependency release, planner-pull processing, release timers and
|
|
354
|
-
restarts until
|
|
370
|
+
restarts until a later authorized explicit assignment or reservation. The later unassign also
|
|
355
371
|
fences planner undo, so undo and re-pull cannot restore the removed reservation.
|
|
356
372
|
Former queued assignment or review requests are withdrawn without resolving
|
|
357
373
|
uncertain deliveries, and the former agent loses current authority. A withdrawal
|
|
@@ -370,8 +386,8 @@ the time); `dispatching` and `accepted` deliveries stay refused, and `retry` kee
|
|
|
370
386
|
`stale revision`, `unknown delivery`, `already resolved`, `queued delivery is already scheduled`, `recipient queue is full`, `delivery journal unavailable`), never one
|
|
371
387
|
generic sentence (#713).
|
|
372
388
|
|
|
373
|
-
`ahub queue discard --peer <id> --reason <text> [--state queued|needs_review] [--yes] [--json]` clears one peer's backlog and is for the person
|
|
374
|
-
refused). Without `--yes` it only says how many deliveries it would discard; with `--yes` it discards each queued (default) or `needs_review` delivery of that peer through the
|
|
389
|
+
`ahub queue discard --peer <id> --reason <text> [--state queued|needs_review] [--yes] [--json]` clears one peer's backlog and is for the person or the configured conductor (any other agent identity is
|
|
390
|
+
refused, and a remote `peer@label` queue is the person's only). Without `--yes` it only says how many deliveries it would discard; with `--yes` it discards each queued (default) or `needs_review` delivery of that peer through the
|
|
375
391
|
same resolution as `queue resolve --action discard`, each recorded with the reason, and prints `discarded N of M` with the causes of any it could not discard. Only the
|
|
376
392
|
named peer's queue is read, so other peers are untouched; it is refused while recovery or shutdown holds queue mutations. In the console Queue panel, `D` on a row opens
|
|
377
393
|
the same command for that row's peer, asks for a reason, then a `y` that names the count. No task is cancelled and no native turn is stopped.
|
|
@@ -551,16 +567,19 @@ and so is every help entry the gate has no class for yet;
|
|
|
551
567
|
| `ahub release show <name>` | Allowed | public release metadata; private scope and internal history remain withheld |
|
|
552
568
|
| `ahub projects [--json] [--color=...]` | Allowed | lists registered projects; `projects remove` stays Console |
|
|
553
569
|
| `ahub models status [--json] [--color=...]` | Allowed | reads local model state; `setup\|start\|stop` stay Console |
|
|
554
|
-
| `ahub recovery status [<op-id>] [--json]` | Allowed | reads a preflight operation; the bare interactive screen
|
|
555
|
-
| `ahub upgrade ... --dry-run` (no `--yes`) | Allowed | reviews the upgrade plan and applies nothing;
|
|
556
|
-
| `ahub restart --dry-run [--json]` (no `--yes`) | Allowed | reviews the restart plan and applies nothing
|
|
570
|
+
| `ahub recovery status [<op-id>] [--json]` | Allowed | reads a preflight operation; the bare interactive screen and `reattach` stay Console |
|
|
571
|
+
| `ahub upgrade ... --dry-run` (no `--yes`) | Allowed | reviews the upgrade plan and applies nothing; apply is a separate person or conductor operation |
|
|
572
|
+
| `ahub restart --dry-run [--json]` (no `--yes`) | Allowed | reviews the restart plan and applies nothing |
|
|
573
|
+
| `ahub task cancel\|label\|role <id> ...` | Allowed | the daemon decides per task (#933): `label` and `role` only on a task the caller owns, `role` not while it is in review, and `cancel` by the `hub_task_cancel` rule (the planner's own unaccepted backlog or proposed work) |
|
|
574
|
+
| `ahub deliverables [--task <id>] [--approved] [--check] [--json]` | Allowed | only the rows `hub_task_show` would show the caller, through the same public screening (#933) |
|
|
575
|
+
| `ahub room list [--json]`, `ahub room status [<name>] [--json]` | Allowed | room metadata only; every other `room` form, the inbox included, stays Console |
|
|
557
576
|
|
|
558
577
|
`backlog list`, `release list` and `sdlc costs|metrics` retain their existing
|
|
559
578
|
planner/person policy. Their CLI forms stay Console for ordinary agents; a
|
|
560
579
|
configured planner can use the existing planner tools without borrowing console
|
|
561
580
|
authority. All newly admitted reads use the ordinary tools connection and public
|
|
562
581
|
views, never console identity. The person's full readback stays unchanged.
|
|
563
|
-
Conductor operations are listed in [the operational command table](#conductor-operations-812-t2).
|
|
582
|
+
Conductor operations are listed in [the operational command table](#conductor-operations-812-t2-933-900).
|
|
564
583
|
|
|
565
584
|
## Conducting a team from Claude Code or Codex
|
|
566
585
|
|
|
@@ -596,8 +615,11 @@ cause an otherwise unassigned owner to claim duplicate work.
|
|
|
596
615
|
The person answers approvals in the console, resolves `needs_review` deliveries
|
|
597
616
|
with `ahub queue resolve`, stops a headless peer with `ahub stop`, and handles
|
|
598
617
|
budget overrides and hub lifecycle.
|
|
599
|
-
|
|
600
|
-
|
|
618
|
+
The configured conductor may itself run `queue resolve`, `stop <peer>` and the
|
|
619
|
+
other forms in [the operational command table](#conductor-operations-812-t2-933-900);
|
|
620
|
+
approvals and budget overrides stay the person's. From any other agent shell these
|
|
621
|
+
commands are refused; the CLI retains the agent's identity even when invoked
|
|
622
|
+
through a shell tool.
|
|
601
623
|
|
|
602
624
|
`conductor.feed` is `own` by default, `all` for all tasks, or `off`. Ordinary
|
|
603
625
|
milestones share the existing digest window and collapse repeated queued
|
|
@@ -908,7 +930,7 @@ directly under a `.git`, `commondir` below it, and `.agenthub` anywhere in the
|
|
|
908
930
|
project, plus `config.worktree` beside each such `config`. A submodule's git
|
|
909
931
|
directory gets the same `config` and `hooks` refusal under every `modules/`
|
|
910
932
|
tree the project can reach: the common dir's, the worktree's own and every
|
|
911
|
-
linked worktree's (#281,
|
|
933
|
+
linked worktree's (#281, task 518). Nothing else may be written under such a tree
|
|
912
934
|
outside a discovered submodule git dir (#865). A command can therefore not
|
|
913
935
|
create, rename or remove a `.git`: `git init`, `git clone` and
|
|
914
936
|
`git worktree add` do not run inside the sandbox, and neither do
|
|
@@ -1128,7 +1150,7 @@ listed and the rest are counted. The block is advice: deleted definitions are no
|
|
|
1128
1150
|
|
|
1129
1151
|
The dashboard's task sheet has the same block as a "Code" group (#684 phase 2c): the changed symbols as buttons marked `matching` or `unplanned` against the plan, the plan names nothing changed under as `untouched`, and the same advisory line (deleted symbols are not reported, an incomplete observation may hide a changed one). The hub classifies them (`codeGroup` on each task of the UI snapshot, from the same public observation the console reads), so the page never matches names itself. A symbol opens the Memory tab with its canonical node (`symbol:<name>@<path>`) as the graph start; the list is capped at 40 with the count of what is left out, and a summary line counts matching, unplanned and untouched before the cap. A plan name the hub withholds (screened out or malformed) is counted, and the line then says a symbol marked unplanned may be in the plan. Without code facts the group reads `unknown (no code facts)`, and a private task has no group at all, so the sheet shows nothing for it.
|
|
1130
1152
|
|
|
1131
|
-
Task detail also shows an `overlaps` block (
|
|
1153
|
+
Task detail also shows an `overlaps` block (task 379), using the hub's read-only
|
|
1132
1154
|
`symbolOverlaps` projection of its existing open-task/other-owner overlap selection.
|
|
1133
1155
|
Each row names the other task ID and screened shared paths. A row says "same file,
|
|
1134
1156
|
different symbols" only when the same current screened predicate used by completion
|
|
@@ -1153,10 +1175,10 @@ a reviewable path unaccounted is refused, and advise notes the gap in the verdic
|
|
|
1153
1175
|
(#520). The hub
|
|
1154
1176
|
records how each review turned out, per implementer, reviewer and class:
|
|
1155
1177
|
approved; caught (changes were requested and the owner's redo was approved);
|
|
1156
|
-
contradicted (within
|
|
1178
|
+
contradicted (within `tasks.contradiction_window_days`, default 7, work on the same file or symbol failed its check
|
|
1157
1179
|
or review); escalated (after the reviewer asked for changes). A record counts
|
|
1158
1180
|
tasks, not verdicts. `ahub task show <id>` lists a task's outcomes and `ahub
|
|
1159
|
-
route explain` shows each reviewer's record with the implementer. `ahub route explain <id>` also prints the planner walk (`planner candidate X: ...` lines, then `planner: X (rotation | named | kept)`): the
|
|
1181
|
+
route explain` shows each reviewer's record with the implementer. `ahub route explain <id>` also prints the planner walk (`planner candidate X: ...` lines, then `planner: X (rotation | named | kept)`): the same planner walk used when a new or pulled task records its planner (#525-2, #525-3). The task detail, dashboard and console show the recorded planner and selection reason; only that effective planner or the person may change its planning. A peer that accepted or finished a task never reviews it; when every eligible reviewer did, the person reviews and the trace says so. With
|
|
1160
1182
|
`"review": { "adaptive": true }` in `.agenthub/config.json`, reviewers with at
|
|
1161
1183
|
least `min_reviews` (5) reviews of that implementer in the class are ordered by
|
|
1162
1184
|
how their reviews held up, ahead of quota but after idle before busy; off by
|
|
@@ -1244,6 +1266,16 @@ ahub say @claude "[STATUS] the test run is complete"
|
|
|
1244
1266
|
ahub say @kimi "[FYI] the result is recorded"
|
|
1245
1267
|
```
|
|
1246
1268
|
|
|
1269
|
+
The `hub_send` tool declares `to` as an array of peer ids (#963). The control `send`
|
|
1270
|
+
boundary also accepts one nonempty string as a single recipient; it never
|
|
1271
|
+
turns that string into a broadcast. Without `reply_to`, omitting `to` or sending
|
|
1272
|
+
an empty array keeps the broadcast meaning. A reply without recipients inherits
|
|
1273
|
+
the sender it answers. Other recipient shapes, including `null`, empty strings
|
|
1274
|
+
and arrays containing a non-string or empty item, are refused before publication.
|
|
1275
|
+
The tool reports `not sent: to must be an array of peer ids` (the control reply's
|
|
1276
|
+
error is `to must be an array of peer ids`). Valid arrays still deduplicate
|
|
1277
|
+
recipients and pass through the existing unknown-peer, roster and privacy gates.
|
|
1278
|
+
|
|
1247
1279
|
What agents send is limited per sender (`limits` in `.agenthub/config.json`; a
|
|
1248
1280
|
project config gets the values below unless it sets others, `0` turns one off):
|
|
1249
1281
|
|
|
@@ -1322,7 +1354,7 @@ rescans the whole file on each call.
|
|
|
1322
1354
|
for the history it already spent. A task with no link, and a record with no task, are `unattributed`.
|
|
1323
1355
|
|
|
1324
1356
|
Every number names its source: `native` (the provider's own counter), `hub count` (the hub's own `tokens` increments, which cover peers that
|
|
1325
|
-
report no usage record
|
|
1357
|
+
report no usage record), or `unknown`. Jcode's rows are `native` from 0.99.1 on: the hub records the per-turn buckets of its ACP prompt result as `jcode_acp` usage records (#801). Kimi's rows are `native`: the hub reads Kimi's own per-request counters from its session wire log and records them as `kimi_wire` usage records (#285 phase 2). Unknown is never shown as 0. `TOTAL` includes cache reads and `WITHOUT CACHE`
|
|
1326
1358
|
removes them; Claude's total is the sum of its four reported counters because its transcript carries none (`ahub report` still shows it as unknown), and a source whose input already
|
|
1327
1359
|
contains the cached part (Codex, the gateway) gets no derived total. A lifetime total that mixes a native and a hub-counted peer is labelled
|
|
1328
1360
|
`hub count`. When a peer is unknown the overall figure is `partial, at least`: a lower bound that leaves the named peers out, never a sum that counts them
|
|
@@ -1367,7 +1399,7 @@ ahub memory forget 1a2b3c4d # a person's; also promote, demote, sup
|
|
|
1367
1399
|
|
|
1368
1400
|
`"memory": { "native": false }` in `.agenthub/config.json` turns it off for a project.
|
|
1369
1401
|
|
|
1370
|
-
The exact-pinned `@staix/agent-mem` 0.
|
|
1402
|
+
The exact-pinned `@staix/agent-mem` 0.13.0 resolves `AMEM_HOME`, then `$AGENTHUB_HOME/memory`, then `~/.agenthub/memory`. An absent `M/profile.json` selects shared v1; an explicit generation selects `M/generations/G` with the package's readonly legacy bridge. All daemon memory consumers and the hub memory/goal CLI use that selected profile. Invalid metadata or a missing selected generation makes native memory unavailable while the bus and board continue; there is no fallback to legacy writes. Inspect the daemon's memory binding/availability before interpreting missing memory as an empty store.
|
|
1371
1403
|
|
|
1372
1404
|
The daemon checks its selector at hub operation entry and before candidate/counter writes. It does not hot-switch handles or provide an atomic fence against selection changes during an in-flight core call. Quiesce consumers and close their handles before migration or selection changes; reopen through the supported session-preserving lifecycle afterward. Candidate/counter storage remains `M/candidates.db`, independent of `G`; since agent-mem 0.7.0 `amem review` also lists and accepts candidates on a selected generation. Every running hub using this home needs an authenticated receipt: a matching ready binding, or a generation-capable unbound receipt (`disabled` when native memory is off, `unavailable` when opening failed) proving it holds no memory handle. An unbound daemon and zero running hubs are quiesced for memory; a stale bound handle, unreadable registry or unknown runtime is not ready. Existing JSON memory/goal results remain unchanged; the additive binding receipt belongs to daemon status, profile-check and the decay object. This source integration does not authorize installing a new runtime, migrating real memory, restarting sessions or claiming real-machine sync/capture receipts. Migration/deactivation and their limits belong to [agent-mem](https://github.com/STAIxBWLB/agent-mem); see [the hub profile contract](memory.md#selected-memory-profile).
|
|
1373
1405
|
|
|
@@ -1392,7 +1424,7 @@ ahub goal # open ones
|
|
|
1392
1424
|
ahub goal take 1a2b3c4d --force # take one over from another machine
|
|
1393
1425
|
```
|
|
1394
1426
|
|
|
1395
|
-
Without a remote the periodic sync stays quiet; any other sync failure writes `hub memory sync failed: <reason>` to `hub.log` (
|
|
1427
|
+
Without a remote the periodic sync stays quiet; any other sync failure writes `hub memory sync failed: <reason>` to `hub.log` (task 401).
|
|
1396
1428
|
|
|
1397
1429
|
## Benchmarks
|
|
1398
1430
|
|
|
@@ -1403,7 +1435,7 @@ task's commit, so never enable it in a project you work in. The project must be
|
|
|
1403
1435
|
nothing tracked under `.agenthub/`, and the suite file lives outside its tree ([suite format and store](bench.md)).
|
|
1404
1436
|
|
|
1405
1437
|
```bash
|
|
1406
|
-
ahub bench run ../suites/suite.json --arm baseline --repeat 5 #
|
|
1438
|
+
ahub bench run ../suites/suite.json --arm baseline --repeat 5 # the person's or the conductor's: resets the tree, proposes the tasks
|
|
1407
1439
|
ahub bench status # the run in progress
|
|
1408
1440
|
ahub bench compare baseline ripwire-on # arms side by side, inconclusive below 5 attempts each
|
|
1409
1441
|
ahub bench export --format csv
|
|
@@ -1444,6 +1476,13 @@ ahub undo <turn> --yes --context # Codex's latest turn: also drop it from Codex
|
|
|
1444
1476
|
Nothing is restored then. It plans again right before restoring and stops if
|
|
1445
1477
|
anything moved meanwhile. A turn that is already undone says so.
|
|
1446
1478
|
- A file the turn created is deleted; a file it deleted comes back.
|
|
1479
|
+
- The hub does not observe inside a git submodule (#798). A submodule the turn
|
|
1480
|
+
left dirty is listed as `<path> (changed, contents unobserved)` in `ahub turns`
|
|
1481
|
+
(`unobserved` in `--json`), and `ahub undo` says its contents cannot be restored:
|
|
1482
|
+
undo them in the submodule itself. A task that declares a path inside a submodule
|
|
1483
|
+
gets a notice at propose and accept, never a refusal. Without `.gitmodules` no git
|
|
1484
|
+
call is added; with one, each turn end adds one bounded `git status` probe, and a
|
|
1485
|
+
probe that cannot answer records unknown, never none.
|
|
1447
1486
|
- When HEAD moves during a turn, the hub reads the appended HEAD reflog (bounded to 64 moves and 128 KiB). The turn's own commits, amends, cherry-picks and rebase picks still count as its changes. Files brought in by a pull, merge, rebase start, branch checkout or fast-forward reset are left out, unless they are still dirty at the turn's end. An unreadable or ambiguous reflog, a rewinding reset, or a file touched by both kinds of move makes the turn's files unknown (#576).
|
|
1448
1487
|
- `--context` asks Codex (`thread/revert`) to drop the turn, and every later one,
|
|
1449
1488
|
from its thread's saved history before the files are restored; Codex receives
|
|
@@ -1668,8 +1707,8 @@ stays the default until an evaluation says otherwise (`docs/cooperbench.md`).
|
|
|
1668
1707
|
other owner, while the overlapping task is not started; the routing record,
|
|
1669
1708
|
and a cohort record formed as a task is assigned, are taken before the task is
|
|
1670
1709
|
sent, so a busy routed peer is not available then) with no other open work (an overlapping task its owner
|
|
1671
|
-
has started counts), and each has five measured tasks with no more
|
|
1672
|
-
failures and comparable work times. The work stage of a task ends at its first
|
|
1710
|
+
has started counts), and each has `tasks.split_min_observations` (default five) measured tasks with no more
|
|
1711
|
+
than `tasks.split_max_failure_share` (default 30%) failures and comparable work times. The work stage of a task ends at its first
|
|
1673
1712
|
`hub_task_done`, and the task itself is never one of its own observations.
|
|
1674
1713
|
- `"experiments": {"stale_notices": "deliver"}` in `.agenthub/config.json`
|
|
1675
1714
|
turns the stale-notice drop off, for a controlled comparison only (the #106
|
|
@@ -1814,7 +1853,8 @@ ahub settings set roster.kimi false --preview
|
|
|
1814
1853
|
ahub settings set roster.kimi false --yes
|
|
1815
1854
|
```
|
|
1816
1855
|
|
|
1817
|
-
These are person's commands
|
|
1856
|
+
These are the person's commands (`roster add|remove` also the configured conductor's,
|
|
1857
|
+
see [the operational command table](#conductor-operations-812-t2-933-900)) and use the same settings preview, write and undo as
|
|
1818
1858
|
the dashboard's Roster settings. An ordinary dashboard session may remove a peer;
|
|
1819
1859
|
adding requires `ahub ui --settings` or a person's terminal. Removal requires the
|
|
1820
1860
|
peer id as confirmation. Without `--now`, a busy peer is refused. A conductor
|
|
@@ -1914,9 +1954,73 @@ A guarded file edit that the daemon keeps at its accepted value remains visible
|
|
|
1914
1954
|
| Switches | `task_sweep.enabled`, `.auto_reassign`, `.interval_s`, `.unaccepted_min`, `.idle_min`, `.review_min`, `.ready_min`, `.ladder_min`, `.no_writes_turns` | `config.local.json` | live; `.no_writes_turns` sets the turn threshold independently; other keys reschedule the sweep timer |
|
|
1915
1955
|
| Routing | `tasks.wip_limits.<peer>` | `config.local.json` | live |
|
|
1916
1956
|
| Routing | `tasks.escalate_after` (1-10, default 3: consecutive `changes_requested` before a task moves to the next `escalate_to` peer) | `config.local.json` | live; the next verdict uses it |
|
|
1957
|
+
| Routing | `tasks.groom_fyi_window_min` (1-1440 minutes, default 10) | `config.local.json` | live; the next planner-notice flush uses it; planning-change task notices are unchanged |
|
|
1958
|
+
| Routing | `tasks.demote_at` (0.5-20 decayed recent failures, default 1.5) | `config.local.json` | live; the next demotion read (routing, `ahub route explain`) uses it |
|
|
1959
|
+
| Routing | `tasks.demotion_half_life_h` (1-168 hours, default 24) | `config.local.json` | live; the next demotion read uses it |
|
|
1960
|
+
| Routing | `tasks.contradiction_window_days` (1-90 days, default 7) | `config.local.json` | live; the next changes_requested verdict uses it |
|
|
1961
|
+
| Routing | `tasks.rescreen_max` (1-100 attempts, default 10) | `config.local.json` | live; the next release-timer re-screen uses it |
|
|
1962
|
+
| Routing | `tasks.split_min_observations` (3-100 measured tasks, default 5) | `config.local.json` | live; the next shadow split prediction uses it |
|
|
1963
|
+
| Routing | `tasks.split_max_failure_share` (0-1, default 0.3) | `config.local.json` | live; the next shadow split prediction uses it |
|
|
1917
1964
|
| Switches | `tasks.auto_pull` (default off: pull the top explicitly reserved ready card when a planner slot frees) | `config.local.json` | live; the next freed planner slot uses it |
|
|
1965
|
+
| Routing | `tasks.queue_backpressure.max_queued` (0-10000), `.max_oldest_s` (0-604800); 0, the default, disables each ([backpressure](#queue-backpressure-on-automatic-task-offers-800)) | `config.local.json` | live |
|
|
1918
1966
|
| Roles | `planner` (one or up to eight planner holders) | `config.local.json` | at once |
|
|
1919
1967
|
|
|
1968
|
+
### Operational numeric settings (#489 H1)
|
|
1969
|
+
|
|
1970
|
+
Numeric operational conversions use the existing settings writer, loader and
|
|
1971
|
+
reload path. A fixed row in `src/hub/operational-settings.ts` supplies the key,
|
|
1972
|
+
unit, default, bounds, apply class and console/dashboard metadata. The closed
|
|
1973
|
+
`config-apply.ts` schema still names each leaf explicitly; there is no dynamic
|
|
1974
|
+
registration, extra configuration file, environment knob or per-call override.
|
|
1975
|
+
`configApply` and the UI row consume the same metadata. The default enters the
|
|
1976
|
+
existing `DEFAULT_CONFIG`; unset therefore keeps the previous value.
|
|
1977
|
+
|
|
1978
|
+
The worked H0 conversion is `tasks.groom_fyi_window_min`, the spacing between
|
|
1979
|
+
state-only planner board-update FYIs. Its range is 1-1440 minutes and its
|
|
1980
|
+
default is the existing 10 minutes. A settings-session or terminal change takes
|
|
1981
|
+
effect at the next notice flush, without restarting a peer or altering task
|
|
1982
|
+
ownership. It does not delay new planning/dependency task notices. Removing a
|
|
1983
|
+
local value inherits the shared value or default through the ordinary settings
|
|
1984
|
+
undo/write path. This new key grants no conductor authority.
|
|
1985
|
+
|
|
1986
|
+
For a later operational group, add its metadata row, exact schema leaf,
|
|
1987
|
+
default and runtime config read, then its settings-table entry and independent
|
|
1988
|
+
pin against the original constant. Test schema/UI bounds and apply-class parity,
|
|
1989
|
+
inherit/undo and the real runtime boundary. H0 safety floors and contract
|
|
1990
|
+
constants stay outside this table. Exposing a floor requires schema bounds that
|
|
1991
|
+
permit tightening only, tested for that floor; UI `floor` metadata alone does
|
|
1992
|
+
not enforce a safety ceiling. This H1 example changes no safety floor, protocol,
|
|
1993
|
+
wire format or storage contract. The remaining H0 conversions belong to later
|
|
1994
|
+
area PRs.
|
|
1995
|
+
|
|
1996
|
+
#### Tasks and routing conversions (#489 H2)
|
|
1997
|
+
|
|
1998
|
+
Six more operational numbers use the same fixed rows, all `live`, `risk: raises`, config store, with the
|
|
1999
|
+
default equal to the constant it replaces (each pinned by a literal in `test/operational-settings.test.ts`):
|
|
2000
|
+
`tasks.demote_at` (DEMOTE_AT), `tasks.demotion_half_life_h` (DEMOTION_HALF_LIFE_MS),
|
|
2001
|
+
`tasks.contradiction_window_days` (CONTRADICTION_WINDOW_MS), `tasks.rescreen_max` (RESCREEN_MAX),
|
|
2002
|
+
`tasks.split_min_observations` (SPLIT_MIN) and `tasks.split_max_failure_share` (SPLIT_FAILURE_SHARE). Each is
|
|
2003
|
+
read where its constant was used, so a change applies at the next demotion read, verdict, re-screen or
|
|
2004
|
+
prediction without a restart. The split thresholds only feed the shadow prediction, which never changes
|
|
2005
|
+
assignment. None grants conductor authority.
|
|
2006
|
+
|
|
2007
|
+
The H0 operational items of `tasks.ts`, `routing.ts` and `task-sweep.ts` that stay as they are (42 listed, 6 converted here, 1 in H1):
|
|
2008
|
+
|
|
2009
|
+
- Already settings, so nothing to add: `task_sweep.interval_s`, `unaccepted_min`, `idle_min`, `review_min`, `ready_min`
|
|
2010
|
+
(both uses), `ladder_min` and `no_writes_turns` (the H0 rows are the defaults of the existing keys), `tasks.escalate_after`
|
|
2011
|
+
(ESCALATE_AFTER), `tasks.wip_limits` (the default of 1) and `tasks.groom_fyi_window_min` (H1).
|
|
2012
|
+
- Already `routing.toml` keys, not config-store: `pi.dgx_max_context_tokens`, `pi.mlx_max_context_tokens`, the
|
|
2013
|
+
`pi.efficient_wait_ms` bound and `long_context_tokens`. H1 reaches the config store only, and
|
|
2014
|
+
`routing.local.toml` may not hold them (`checkOverlay`), so converting them would need a different mechanism.
|
|
2015
|
+
- Resource bounds, not tunables: the 256-entry plan caches (`observedPlans`, `planReads`), the 1024-entry conditional
|
|
2016
|
+
map, the 500-task and 256-item grooming limits (`GROOMING_TASK_LIMIT`, `GROOMING_ITEM_LIMIT`, the digest's 500
|
|
2017
|
+
slices), and the 8, 16 and 32 read counts and 16-id lists of the digest, cost report and release cut. Raising any of
|
|
2018
|
+
them raises memory or work per call, so each would need a tighten-only schema bound and a test per floor
|
|
2019
|
+
(acceptance criterion 3), which this group does not add.
|
|
2020
|
+
- Host deadlines for the `gh` reads (1_000, 4_000 and 20_000 ms in five places) and the 5 000 ms release-window
|
|
2021
|
+
freshness check. They bound a whole call, a refusal of a stale host window protects the release, and none is
|
|
2022
|
+
independently tunable without changing what a partial result means.
|
|
2023
|
+
|
|
1920
2024
|
- Writes go only to `.agenthub/config.local.json` and `.agenthub/routing.local.toml`,
|
|
1921
2025
|
the machine-local files (`ahub init` ignores both in git). The shared
|
|
1922
2026
|
`config.json` and the hand-written `routing.toml` are never rewritten, and a
|
|
@@ -1967,7 +2071,8 @@ Pi relay model aliases (`pi.dgx_coding`, `pi.dgx_fast`) and `routing.pi` retain
|
|
|
1967
2071
|
limit to 0, turns `task_sweep.enabled` or `.auto_reassign` off and changes
|
|
1968
2072
|
the other Switches. Everything
|
|
1969
2073
|
else needs `ahub ui --settings`, whose session may raise for 15 minutes, or
|
|
1970
|
-
`ahub settings` in a terminal
|
|
2074
|
+
`ahub settings` in a terminal; the configured conductor may set only the
|
|
2075
|
+
thirteen keys in [the operational command table](#conductor-operations-812-t2-933-900). `never-ask` needs the peer id typed in the
|
|
1971
2076
|
dashboard, or `--yes` in the terminal, and a stored never-ask default still
|
|
1972
2077
|
waits for `y` in `ahub console` at each hub start. Tool approvals stay
|
|
1973
2078
|
deny-only for Pi and local in every session.
|
|
@@ -1991,7 +2096,7 @@ Instances, the review and approval blocks and the lifecycle can be edited withou
|
|
|
1991
2096
|
- `mode` and `required_checks` in `.agenthub/sdlc.toml`. Only those two lines change: comments and every other table stay as written, and a layout the editor cannot change faithfully (a key repeated, or one inside a multi-line string) is refused with its own reason.
|
|
1992
2097
|
|
|
1993
2098
|
`roster.*`, `tasks.wip_limits.*`, `permission_modes`, `peers` and the rest of the registry keep their own `ahub settings set` path.
|
|
1994
|
-
3. **Executable and secret-locating keys** are console only: the rest of `NEVER_EDITABLE` (`kimi_cmd`, `jcode_cmd`, `codex_bin`, `pi.cmd`, `checks`, `mlx`, `omniroute`, `terminal`, `adapters`, `adapter_modules` and the like) and the Jcode instance fields `jcode_home`, `provider`, `model` and `provider_profile`. The console shows "this runs code with the daemon's rights, or chooses where secrets are read from" and writes only when the key's exact name is typed, one key at a time; `--yes` never confirms one. The dashboard lists each of them with the console command that changes it and nothing else: no value, no path, no input. A forged dashboard request for one is refused by the hub.
|
|
2099
|
+
3. **Executable and secret-locating keys** are console only: the rest of `NEVER_EDITABLE` (`kimi_cmd`, `jcode_cmd`, `codex_bin`, `pi.cmd`, `checks`, `mlx`, `omniroute`, `terminal`, `schedules`, `folder_triggers`, `corpus`, `rooms`, `local.doc_read`, `local.web_fetch`, `adapters`, `adapter_modules` and the like) and the Jcode instance fields `jcode_home`, `provider`, `model` and `provider_profile`. The console shows "this runs code with the daemon's rights, or chooses where secrets are read from" and writes only when the key's exact name is typed, one key at a time; `--yes` never confirms one. The dashboard lists each of them with the console command that changes it and nothing else: no value, no path, no input. A forged dashboard request for one is refused by the hub.
|
|
1995
2100
|
|
|
1996
2101
|
In the console (`ahub console`, Settings tab `6`): `c` asks what to edit (`1` an instance, `2` the lifecycle, `3` another key), asks each field, shows the hub's diff or its loader reason, asks for a reason, then `y`. Esc cancels at any step, and pasted text never confirms. `R` appears while a restart is pending and prefills `restart`; the command still asks its own `y`. In the dashboard, open `ahub ui --settings`: the Structure group has the same forms; outside a settings session it is read only.
|
|
1997
2102
|
|
|
@@ -2009,7 +2114,7 @@ Rules:
|
|
|
2009
2114
|
- A whole instance that holds a native home, account or model (`jcode_home`, `provider`, `model`, `provider_profile`) is tier 3 to remove or replace, so a dashboard can neither see nor delete those values; an existing instance changes leaf by leaf (`instances.<id>.<field>`), and `clear` removes one field. A preview or write shows a tier 3 value only as `(set)`, and a write changes exactly one tier 3 key, counting the keys the file adds (removing two Jcode instances is two writes, each confirmed by its own typed name). The audit record gives a tier 3 key no value hash at all, because a model or provider name is easy to recover from an unsalted hash; the file revisions bind the change instead. An instance already in `config.local.json` is changed leaf by leaf even before a restart has started it. If `.agenthub` or `config.local.json` is a symlink, the Settings view reports its revision as `unavailable` and no edit is planned. An edit of `sdlc.toml` keeps the file's own line endings.
|
|
2010
2115
|
- Every write records a `config_edit` event: the file, each key path with a short hash of its value before and after (never a value), the file revisions before and after, the tier, the source (`dashboard` or `terminal`) and `by: person`. The notice and `hub.log` name key paths only.
|
|
2011
2116
|
- A changed instance declaration is restart-bound: it is listed as `pending restart` (`instances`) and takes effect at the next hub start. A changed `sdlc.toml` is read once at start and is listed as `sdlc.mode` or `sdlc.required_checks` until the running hub holds the same value. `review`, `snapshots`, `approvals` and `context.gate` publish through the one reload path, one key at a time.
|
|
2012
|
-
- Person only: an agent shell or a hub tool is refused (`ahub settings` is a console command in the identity gate, and the three control requests answer only the console role). Recovery or shutdown holds every edit.
|
|
2117
|
+
- Person only: an agent shell or a hub tool is refused (`ahub settings edit` is a console command in the identity gate, and the three control requests answer only the console role). Recovery or shutdown holds every edit.
|
|
2013
2118
|
- The hub must run this release: an older hub answers `unknown message` and the command says to upgrade and restart it.
|
|
2014
2119
|
|
|
2015
2120
|
## Start modes: TUI by default, headless as opt-in
|
|
@@ -2216,10 +2321,10 @@ Quota also shapes routing and handoffs:
|
|
|
2216
2321
|
while it waits (a week window crosses the gate), its work is handed over after
|
|
2217
2322
|
all. `ahub budget` shows each decision and why in the pause reason.
|
|
2218
2323
|
- A peer whose recent failures in a class (failed checks, changes requested,
|
|
2219
|
-
escalations by hand) reach 1.5 after decay, and outweigh its recent
|
|
2324
|
+
escalations by hand) reach `tasks.demote_at` (default 1.5) after decay, and outweigh its recent
|
|
2220
2325
|
approvals there (a task without a reviewer counts when done), goes behind the
|
|
2221
2326
|
other candidates in the same state for that class, `local` and `pi` included.
|
|
2222
|
-
A failure counts half after
|
|
2327
|
+
A failure counts half after `tasks.demotion_half_life_h` (default 24 hours). `ahub route explain` names demoted peers.
|
|
2223
2328
|
|
|
2224
2329
|
## Durable delivery and queue resolution
|
|
2225
2330
|
|
|
@@ -2333,6 +2438,8 @@ ahub tail
|
|
|
2333
2438
|
ahub kill
|
|
2334
2439
|
```
|
|
2335
2440
|
|
|
2441
|
+
`ahub down` is the same verified stop as `ahub kill`, named as the counterpart of
|
|
2442
|
+
`ahub up` (#957); both are the person's, refused from an agent shell.
|
|
2336
2443
|
The daemon waits for shutdown work that must survive termination. Afterward,
|
|
2337
2444
|
run `ahub status` or `ahub up` and read the project state before restarting
|
|
2338
2445
|
peers. A stopped daemon does not delete task records or the durable journal.
|
|
@@ -2417,10 +2524,12 @@ the top card explicitly reserved for the peer whose slot just freed.
|
|
|
2417
2524
|
|
|
2418
2525
|
When `hub_task_done` leaves a review whose exact-head CI is not yet green (the
|
|
2419
2526
|
lifecycle must be on, the task linked to a PR and not private), the hub starts one watcher
|
|
2420
|
-
for that head (#785): one `gh pr view` a minute, ended by
|
|
2527
|
+
for that head (#785): one `gh pr view` a minute, ended by a passed CI result, by a
|
|
2421
2528
|
45 minute deadline, by a newer head, a verdict or a cancellation, or by hub
|
|
2422
2529
|
shutdown; a restart does not restore it, and the next `hub_task_done` starts a new
|
|
2423
|
-
one.
|
|
2530
|
+
one. A failed or incomplete result is told once and the watcher keeps reading for a
|
|
2531
|
+
rerun of the same head: the first pending read after a newly told result renews the
|
|
2532
|
+
deadline once, and a deadline after a told result ends silently (#540). On the result the owner and the reviewer each get one
|
|
2424
2533
|
`ci_completed: #<id> head <sha12> passed|failed|incomplete` task notice (ids only, the
|
|
2425
2534
|
head cut to 12 characters; a person reviewer gets a console line instead), and
|
|
2426
2535
|
unless CI failed, a review the hub held as premature for that head is offered
|
|
@@ -2570,6 +2679,7 @@ Agent CLIs update themselves often, and a hub release cannot follow every one. O
|
|
|
2570
2679
|
- What trust cannot prove is printed when you trust.
|
|
2571
2680
|
- `ahub doctor` shows one line for each installed CLI that is not on a verified version.
|
|
2572
2681
|
- A daily workflow (`.github/workflows/agent-compat.yml`) runs `scripts/agent-compat-watch.ts`, which reads the latest published version of every gated CLI that has a public source: the npm packages `@anthropic-ai/claude-code`, `@openai/codex`, `@moonshot-ai/kimi-code`, `@qwen-code/qwen-code` (for both `qwen` and `qwen-dual`) and `@earendil-works/pi-coding-agent` (for `pi`, since #847 gave it a COMPAT entry), and the GitHub releases of Jcode. agy has no public source, so its row reads "no public source; local drift only" and is never a failure. Every COMPAT entry names the receipt to repeat (`receipt`), and a test requires that file to exist and the entry to have a watcher source. A version that cannot be read reads as unknown, not as a crash. The workflow keeps one `agent-compat` issue current while a source is ahead and closes it when none is.
|
|
2682
|
+
- **Tracking issue identity (#964):** only the exact title `agent CLI versions ahead of the verified set` identifies the issue; the `agent-compat` label is for people. The workflow reads open issues without filtering by label, then `scripts/agent-compat-issue.ts` selects one valid issue number or confirmed absence. Multiple exact-title matches fail with their issue numbers before any label or issue write. A failed read, malformed inventory or full 1000-issue result also stops without mutation. With no match, an ahead report creates the tracker and an all-verified report does nothing; unrelated labelled issues and their bodies are never selected.
|
|
2573
2683
|
- **Maintenance loop (#843):** the daily upstream watch is the trigger; `ahub doctor` is the local drift notice for versions installed on a machine, and the hub itself tells the planner (below). The planner files one receipt task per (id, version): the implementer repeats the procedure in the entry's last `docs/verification/` receipt, writes a new one, and adds the version to `verified` in that same PR; the next release ships the bump. A person's `ahub agents trust` stays the stopgap that keeps a machine running meanwhile, and the person may revoke it once the bump lands.
|
|
2574
2684
|
- **Local drift notice (#843 AC3):** at hub start and once a day the daemon reads each gated CLI's installed version with the probe `ahub agents` uses (no network, no install). For every installed CLI on a version that is not `verified` (in-line, trusted or refused) the effective planner gets one notice per (id, version): the id, the version, the state and the last receipt path, nothing the CLI printed. A CLI that is not installed, or whose version cannot be read, is not drift. The marker is kept per (id, version, planner) in `hub.db` (`agent_drift_notices`), so a restart repeats nothing, a planner whose notice could not be queued (or who joins later) is retried alone without telling the others again, and a new version is a new notice; with no rostered planner nothing is recorded. `ahub status` shows one `agent CLI drift` attention line per drifted id until the installed version is verified.
|
|
2575
2685
|
|
|
@@ -2592,7 +2702,7 @@ section is what they run.
|
|
|
2592
2702
|
|
|
2593
2703
|
Upgrade running projects with the target release's own coordinator. It accepts
|
|
2594
2704
|
a running source on control protocol 9 (0.6.x), 10 (0.7.0 through 0.12.0),
|
|
2595
|
-
11 (0.12.1 and 0.12.2), 12 (0.12.3), 13 (0.12.4 through 0.12.15), 14 (0.12.16), 15 (0.12.17 through 0.12.19) or 16 (0.12.20 through 0.
|
|
2705
|
+
11 (0.12.1 and 0.12.2), 12 (0.12.3), 13 (0.12.4 through 0.12.15), 14 (0.12.16), 15 (0.12.17 through 0.12.19) or 16 (0.12.20 through 0.21.0), and only
|
|
2596
2706
|
a target on its own protocol, so the target's coordinator fits every supported
|
|
2597
2707
|
source and carries every recovery fix released up to it. Protocol 8 and older
|
|
2598
2708
|
(0.5.x and earlier) are refused as `manual-bootstrap-required`. Run from the
|
|
@@ -2606,7 +2716,7 @@ bunx --package @staix/agent-hub@0.12.43 ahub upgrade --to 0.12.43 --yes
|
|
|
2606
2716
|
| Running now | Coordinator to use |
|
|
2607
2717
|
| --- | --- |
|
|
2608
2718
|
| 0.6.x (protocol 9) | the target's, through `bunx` as above |
|
|
2609
|
-
| 0.7.0 through 0.12.0 (protocol 10), 0.12.1 and 0.12.2 (protocol 11), 0.12.3 (protocol 12), 0.12.4 through 0.12.15 (protocol 13), 0.12.16 (protocol 14), 0.12.17 through 0.12.19 (protocol 15), 0.12.20 through 0.
|
|
2719
|
+
| 0.7.0 through 0.12.0 (protocol 10), 0.12.1 and 0.12.2 (protocol 11), 0.12.3 (protocol 12), 0.12.4 through 0.12.15 (protocol 13), 0.12.16 (protocol 14), 0.12.17 through 0.12.19 (protocol 15), 0.12.20 through 0.21.0 (protocol 16) | the target's, through `bunx` as above |
|
|
2610
2720
|
| any supported source, with the installed CLI already at the target | `ahub upgrade` below, which is the same coordinator |
|
|
2611
2721
|
| 0.5.x or earlier (protocol 8 and older) | not supported: bootstrap by hand with the matching CLI |
|
|
2612
2722
|
|
|
@@ -3511,7 +3621,15 @@ output through unchanged and appends one byte to a progress file for each of tho
|
|
|
3511
3621
|
the watchdog polls that file's size. On a stop it prints where the run is and the process
|
|
3512
3622
|
table below it first. `AHUB_CHECK_HANG_S` (960 by default) is only the backstop for a run
|
|
3513
3623
|
that keeps printing results while hung; hosted CI sets 1500 on Linux inside a 35-minute
|
|
3514
|
-
job limit
|
|
3624
|
+
job limit (#378, #593, #752, task 516). The macOS leg runs as four shards (#548): `AHUB_CHECK_SHARD=i/n`
|
|
3625
|
+
makes `scripts/check.sh` run slice `i` of the full suite through `bun test --shard=i/n --timings=scripts/test-timings.json`, which puts
|
|
3626
|
+
every test file in exactly one shard and balances them by the recorded per-file times; shard 1 also runs the docs check, typecheck, role check, bundle freshness and
|
|
3627
|
+
package contents, so each of those runs once per gate, and the others install and test. Each shard runs under the same 1500 s backstop as Linux inside a 25-minute job
|
|
3628
|
+
limit, and its last line is `check: OK (shard i/n)`. The required check `check (macos-latest)` is an aggregator job that passes only when every shard passed (or an
|
|
3629
|
+
identical-tree full proof was reused), so branch protection and the reuse proof read the same names as before. `AHUB_CHECK_SHARD` is the full suite only: it cannot be
|
|
3630
|
+
combined with `--changed`. A shard execs the system git once before its tests (`AHUB_CHECK_WARM_GIT`, default `/usr/bin/git`; a failure of it is ignored), because the first execs of `/usr/bin/git` on a fresh hosted macOS runner outlast the 1000 ms some tests give it. `scripts/test-timings.json` is advice for balance, never a list of tests: a file missing from it still runs (Bun gives it the median time).
|
|
3631
|
+
Regenerate it from a green macOS leg when the shards drift apart: `gh api --allow-escape-sequences repos/<owner>/<repo>/actions/jobs/<macOS shard job id>/logs`
|
|
3632
|
+
for each shard, or from one unsharded leg's log, then `bun scripts/make-test-timings.ts <log>`.
|
|
3515
3633
|
The `closing-refs` workflow fails a PR whose title, body or commit messages would make
|
|
3516
3634
|
GitHub close an issue the text only mentions (`Refs #N`, a negated or qualified keyword);
|
|
3517
3635
|
an issue linked only in the sidebar passes (#760).
|
|
@@ -3559,6 +3677,7 @@ own 5 h and weekly ratios stay unknown. Not yet verified live against Kimi's `/u
|
|
|
3559
3677
|
update whenever it names the current session, because 2.1.1's source text
|
|
3560
3678
|
emits it after the turn settles (read from the source, not yet observed live
|
|
3561
3679
|
through the hub; docs/smoke.md), so the peer may already be idle.
|
|
3680
|
+
Jcode's context occupancy comes from its ACP `usage_update` (`{used, size}`, source `jcode_usage_update`, bound to the adapter's own session id) on the verified 0.99.1 shape; its consumption is one `usage` event per prompt (source `jcode_acp`: the prompt result's `inputTokens`, `outputTokens`, `cachedReadTokens` and `cachedWriteTokens` as the hub's buckets, never Jcode's `totalTokens`, which counts the cached read twice for OpenAI-style providers) and never a `tokens` event or a window. Jcode has no native quota reader: `jcode usage --json` carries a free-text limit name and no window length, so `ahub budget` keeps `no reading` for it and `ahub budget set` stays the way in (receipt: `docs/verification/2026-10-28-jcode-0.99.1-usage.md`).
|
|
3562
3681
|
Pi, Qwen (until a native source is verified, #625) and other unsupported surfaces show unknown.
|
|
3563
3682
|
A stale or disconnected reading is unknown, not 0%. Codex's accumulated session
|
|
3564
3683
|
usage is never used as context occupancy. In the tail (`ahub tail` and the
|
|
@@ -3596,6 +3715,32 @@ nor hands work over. Continue normally or deliberately restart into a fresh
|
|
|
3596
3715
|
session with your chosen checkpoint as preface. No automatic restart or native
|
|
3597
3716
|
compaction override is performed.
|
|
3598
3717
|
|
|
3718
|
+
## Context headroom admission for new work (#802)
|
|
3719
|
+
|
|
3720
|
+
`context.max_assignment_used` is a separate, default-off admission threshold.
|
|
3721
|
+
In an untracked `.agenthub/config.local.json`, set it to a fraction in `(0, 1]`,
|
|
3722
|
+
for example `{"context":{"max_assignment_used":0.85}}`. Zero disables it.
|
|
3723
|
+
Tracked configuration cannot enable it on another machine. Restart deliberately,
|
|
3724
|
+
or preview and confirm `ahub settings reload` as the person; it applies to the
|
|
3725
|
+
next routing decision. `context.stale_min` is the freshness limit.
|
|
3726
|
+
|
|
3727
|
+
A fresh native occupancy at or above the threshold skips that peer for a new
|
|
3728
|
+
automatic owner offer. `ahub route explain` uses the same pure decision and
|
|
3729
|
+
prints the measured fraction and limit. Missing, malformed, future, stale,
|
|
3730
|
+
detached or replaced-session evidence is explicitly unknown: it neither blocks
|
|
3731
|
+
nor establishes spare capacity, and the existing routing rules still decide.
|
|
3732
|
+
Only adapters with declared native context readers supply evidence, including
|
|
3733
|
+
Jcode's session-bound reader; unsupported surfaces stay unknown.
|
|
3734
|
+
|
|
3735
|
+
A name or reservation alone does not bypass admission. Explicit assignments by
|
|
3736
|
+
the person or current configured conductor, and a peer's own self-claim, may
|
|
3737
|
+
bypass this context limit; roster, privacy, quota, capability and other gates
|
|
3738
|
+
still apply. Reviewer selection is unchanged, including review-class owners.
|
|
3739
|
+
The rule never pauses, compacts, replaces or moves an active session or task.
|
|
3740
|
+
It leaves `context.gate` checkpoint events and requests unchanged, and does not
|
|
3741
|
+
add a telemetry-driven reassignment or retry timer. Re-evaluate or explicitly
|
|
3742
|
+
assign an unassigned task when its owner's context has changed.
|
|
3743
|
+
|
|
3599
3744
|
## Pi tool refusals, exit causes and idle automatic restart
|
|
3600
3745
|
|
|
3601
3746
|
Pi hub-task/conductor refusals proven before any task or board effect return
|
|
@@ -3689,8 +3834,9 @@ request and does not apply to model-origin bash calls.
|
|
|
3689
3834
|
|
|
3690
3835
|
`ahub stop kimi`, `ahub stop pi` (headless mode), or `ahub stop local` stops
|
|
3691
3836
|
that hub-owned runtime through its adapter and prints `<peer>: offline`.
|
|
3692
|
-
The hub and its other peers keep running. The command is
|
|
3693
|
-
|
|
3837
|
+
The hub and its other peers keep running. The command is the person's or the
|
|
3838
|
+
configured conductor's (an audited operational admission); other agent shells and
|
|
3839
|
+
tools-role connections are refused at the `peer_stop` request.
|
|
3694
3840
|
An older hub that does not know the request needs an upgrade before this
|
|
3695
3841
|
command is available.
|
|
3696
3842
|
|
|
@@ -3756,7 +3902,7 @@ that the file is untracked; a dropped value is reported by the settings result.
|
|
|
3756
3902
|
|
|
3757
3903
|
## SDLC artifact receipts and gates
|
|
3758
3904
|
|
|
3759
|
-
The playbook lifecycle (#257, #481) is off without `.agenthub/sdlc.toml`. When enabled its stages are intent, spec, plan, build, test, review, merge, release and maintain, grouped as Plan, Design, Build, Test, Deploy and Maintain. Advise shows blockers; enforce checks implemented head-bound review and approval gates. Stored implement/verify receipts read as build/test; new enabled receipts use canonical ids. See [the SDLC contract](sdlc.md) for gates, required check names, local receipts, console-only readbacks and the S0-S8 delivery table. A person accepts a spec issue with `ahub sdlc accept <task> --issue <n>` (S1). The planner records plan reviews with review and CI scope (`hub_plan_review`, `ahub sdlc plan-review`) and grooms with `ahub backlog groom`; `ahub backlog return <task> <revision> <reason>` returns accepted, untouched work (S9). `ahub sdlc costs [owner/repo]` reports review rounds and CI runs of merged PRs without writing the board (S10). `ahub sdlc metrics [--since <YYYY-MM-DD|ISO time>]` (the planner tool `hub_sdlc_metrics`, planner or person, read-only) reports lead times, first-pass approval and check rates, rework rounds, stale-head re-reviews and open stalls from board history, with `unknown` counted and never 0; optional `[bands.<metric>]` in `sdlc.toml` add control bands that the daemon checks every five minutes in advise or enforce, writing a hub notice (`log`), a read-only `triage` backlog task (`diagnose`) or a `plan` backlog card (`propose`), one per breach episode and never a GitHub issue (S7, see [metrics](sdlc.md#sdlc-metrics-and-control-bands-481-s7)). With the lifecycle on, a task's issue identity is linked when it is proposed or accepted, from the one issue it names (an imported GitHub issue, or a `#N`, `owner/repo#N` or issue URL in its title); a title that names a sibling repository (`agent-mem#13`) links only when `refs.repo` names it, and without `refs.repo` the propose result says to set it. `ahub backlog groom` also backfills up to 32 links per call, within 1 s (#723). S11 release trains ship in 0.12.37 (see [release scheduling](#planner-release-scheduling)). S3 structured review findings (an open Important finding blocks approval in enforce; `max_nits` in `sdlc.toml` caps nits) and S2 literal-path plan conformance with the person-only `ahub sdlc accept-departure <task> <head> <reason...>` are in source; see [findings](sdlc.md#structured-review-findings-481-s3) and [plan conformance](sdlc.md#plan-conformance-481-s2). S4 [test integrity](sdlc.md#test-integrity-481-s4) and S6 [release authorization](#release-approval-and-optional-tag-guard) are in source too. S5
|
|
3905
|
+
The playbook lifecycle (#257, #481) is off without `.agenthub/sdlc.toml`. When enabled its stages are intent, spec, plan, build, test, review, merge, release and maintain, grouped as Plan, Design, Build, Test, Deploy and Maintain. Advise shows blockers; enforce checks implemented head-bound review and approval gates. Stored implement/verify receipts read as build/test; new enabled receipts use canonical ids. See [the SDLC contract](sdlc.md) for gates, required check names, local receipts, console-only readbacks and the S0-S8 delivery table. A person accepts a spec issue with `ahub sdlc accept <task> --issue <n>` (S1). The planner records plan reviews with review and CI scope (`hub_plan_review`, `ahub sdlc plan-review`) and grooms with `ahub backlog groom`; `ahub backlog return <task> <revision> <reason>` returns accepted, untouched work (S9). `ahub sdlc costs [owner/repo]` reports review rounds and CI runs of merged PRs without writing the board (S10). `ahub sdlc metrics [--since <YYYY-MM-DD|ISO time>]` (the planner tool `hub_sdlc_metrics`, planner or person, read-only) reports lead times, first-pass approval and check rates, rework rounds, stale-head re-reviews and open stalls from board history, with `unknown` counted and never 0; optional `[bands.<metric>]` in `sdlc.toml` add control bands that the daemon checks every five minutes in advise or enforce, writing a hub notice (`log`), a read-only `triage` backlog task (`diagnose`) or a `plan` backlog card (`propose`), one per breach episode and never a GitHub issue (S7, see [metrics](sdlc.md#sdlc-metrics-and-control-bands-481-s7)). With the lifecycle on, a task's issue identity is linked when it is proposed or accepted, from the one issue it names (an imported GitHub issue, or a `#N`, `owner/repo#N` or issue URL in its title); a title that names a sibling repository (`agent-mem#13`) links only when `refs.repo` names it, and without `refs.repo` the propose result says to set it. `ahub backlog groom` also backfills up to 32 links per call, within 1 s (#723). S11 release trains ship in 0.12.37 (see [release scheduling](#planner-release-scheduling)). S3 structured review findings (an open Important finding blocks approval in enforce; `max_nits` in `sdlc.toml` caps nits) and S2 literal-path plan conformance with the person-only `ahub sdlc accept-departure <task> <head> <reason...>` are in source; see [findings](sdlc.md#structured-review-findings-481-s3) and [plan conformance](sdlc.md#plan-conformance-481-s2). S4 [test integrity](sdlc.md#test-integrity-481-s4) and S6 [release authorization](#release-approval-and-optional-tag-guard) are in source too. S5 is in source as an [eval gate mechanism](sdlc.md#eval-gate-for-agent-configuration-481-s5) with no suite chosen, and S7 adds merge-time readback, [release metrics](sdlc.md#release-metrics-481-ac12) and an opt-in security baseline; a full live advise/release cycle and the per-release gates-run record remain pending. Owners/reviewers record bounded identities through `hub_sdlc_record`; these grant no additional authority. Off output, receipt and schema shapes stay unchanged.
|
|
3760
3906
|
## Optional Qwen Code peer
|
|
3761
3907
|
|
|
3762
3908
|
Select Qwen per project with `ahub roster add qwen`; it defaults off and is absent
|
|
@@ -3771,7 +3917,7 @@ selected roster and existing capabilities; the TUI is always a person's start.
|
|
|
3771
3917
|
`ahub qwen --mode tui` runs `qwen --input-file <in> --json-file <out> --mcp-config <hub server> --approval-mode default` in the terminal you ran it from. The daemon prepares the two files (mode 0600, under `<state dir>/qwen/qwen-dual/`) and returns the launch; the hub then appends one `submit` line per delivery and tails the event file from a saved offset. It needs a Qwen the dual-output compat entry verifies (`ahub agents` shows `qwen-dual`, verified 0.26.1 only; the headless `qwen` entry is separate and unchanged) and refuses another version before it creates a file. The TUI is explicit only: `ahub qwen` without `--mode` is still headless, Qwen is not a start-mode peer, and the hub never opens this terminal by itself. The TUI start is refused for an extra Qwen instance, off roster, outside a terminal, and while a headless Qwen runs (`ahub stop qwen` first); a headless start is refused while the TUI holds its terminal.
|
|
3772
3918
|
|
|
3773
3919
|
- What the hub does: a delivery becomes a `submit`; the last assistant message of the turn (an `assistant` event whose `stop_reason` is not `tool_use`, then `message_stop`) is shared as the answer; busy and idle follow the events. A `can_use_tool` request goes to the console approval path (`ahub permit`, the dashboard), and the hub's own tools are approved only by their exact `mcp__agent-hub__<tool>` name. If you answer a prompt in the Qwen terminal first, the hub's pending approval is withdrawn.
|
|
3774
|
-
- **Limit 1, a deny ends the turn without an answer.** Qwen emits nothing when the hub denies: it cancels the whole turn. The hub ends the turn itself, shares no answer, settles the delivery as `completed` (a deny is a known outcome, and a `needs_review` would hold every later message to Qwen until a person resolved it) and says so in a notice. A turn whose outcome is not known (Qwen exited, an Esc followed by a new prompt, an event file that became unreadable) is the one that settles `needs_review`. Qwen's own notice that a background shell finished (a `user` event that is wholly one or more `<task-notification>` envelopes) is not a new prompt: it ends no hub turn and leaves an idle peer idle (
|
|
3920
|
+
- **Limit 1, a deny ends the turn without an answer.** Qwen emits nothing when the hub denies: it cancels the whole turn. The hub ends the turn itself, shares no answer, settles the delivery as `completed` (a deny is a known outcome, and a `needs_review` would hold every later message to Qwen until a person resolved it) and says so in a notice. A turn whose outcome is not known (Qwen exited, an Esc followed by a new prompt, an event file that became unreadable) is the one that settles `needs_review`. Qwen's own notice that a background shell finished (a `user` event that is wholly one or more `<task-notification>` envelopes) is not a new prompt: it ends no hub turn and leaves an idle peer idle (task 517).
|
|
3775
3921
|
- **Limit 2, the hub cannot stop a Qwen turn.** The input protocol has only `submit` and `confirmation_response`, and a signal does not stop a running turn. The inactivity watchdog therefore never cancels: after `watchdog_ms` of silence it marks the turn stalled (`ahub status`, the console and the dashboard say "press Esc in the Qwen terminal") and keeps the peer busy. The hub never types into your terminal and never signals Qwen to stop a turn.
|
|
3776
3922
|
- A Qwen write or shell command that names the hub state directory is refused (see [security](security.md): the input file is a prompt channel).
|
|
3777
3923
|
- If the hub cannot read the event file the peer is `detached`, shown as offline with a notice, never idle: Qwen itself keeps working, and the hub reattaches when the file is readable again.
|
|
@@ -3866,7 +4012,7 @@ Configured kinds are cloud peers and never receive PII work. Quota and context a
|
|
|
3866
4012
|
|
|
3867
4013
|
## External adapter modules (#451 A4)
|
|
3868
4014
|
|
|
3869
|
-
A bundled adapter module is authorized by the person with `adapter_modules` (at most 16 rows of `path` and `sha256`) in an untracked `.agenthub/config.local.json`; in `config.json` or a tracked file it is ignored with a notice, and settings never edit it. Only daemon startup imports a module: settings, reload, previews and CLI reads never do, so a change needs a restart. The module must be a single self-contained ESM file inside the project (no symlinked, world-writable or oversized file) whose bytes match the pin, and it may import only Node/Bun builtins and `@staix/agent-hub/adapter`; the scanner also refuses any `require`, `eval` or `Function` word token. Each import must settle within 2 s. A module that fails any check is refused on its own, with a bounded reason in the status reply, while startup and the other adapters continue; its roster row stays inert. A loaded kind is headless, cloud (never PII), off roster until `ahub roster add <kind>`, and has no controlled recovery: stop it and start it again with its recorded session id. While modules are declared, the upgrade or restart source and target must advertise `external_adapter_modules`. Bundling, the accepted definition subset and the conformance kit (`@staix/agent-hub/conformance`, for module CI only) are in [Adding an AI tool](adding-an-ai-tool.md#5-external-adapter-modules-451-a4). A pin authorizes code that runs with the daemon's rights; it is not a sandbox ([security](security.md)).
|
|
4015
|
+
A bundled adapter module is authorized by the person with `adapter_modules` (at most 16 rows of `path` and `sha256`) in an untracked `.agenthub/config.local.json`; in `config.json` or a tracked file it is ignored with a notice, and settings never edit it. Only daemon startup imports a module: settings, reload, previews and CLI reads never do, so a change needs a restart. The module must be a single self-contained ESM file inside the project (no symlinked, world-writable or oversized file) whose bytes match the pin, and it may import only Node/Bun builtins and `@staix/agent-hub/adapter`; the scanner also refuses any `require`, `eval` or `Function` word token. Each import must settle within 2 s. A module that fails any check is refused on its own, with a bounded reason in the status reply, while startup and the other adapters continue; its roster row stays inert. A loaded kind is headless, cloud (never PII), off roster until `ahub roster add <kind>`, and has no controlled recovery: stop it and start it again with its recorded session id. While modules are declared, the upgrade or restart source and target must advertise `external_adapter_modules`. Extra `instances` of a module kind (#754) exist only when the module opts in with `headlessInstances` (headless; implementer, verifier or continuing reviewer) and the person declares them in `config.local.json`; each needs its own worktree, is bound to what startup verified, and needs `external_adapter_instances` on both sides of a restart or upgrade ([security](security.md)). Bundling, the accepted definition subset and the conformance kit (`@staix/agent-hub/conformance`, for module CI only) are in [Adding an AI tool](adding-an-ai-tool.md#5-external-adapter-modules-451-a4). A pin authorizes code that runs with the daemon's rights; it is not a sandbox ([security](security.md)).
|
|
3870
4016
|
|
|
3871
4017
|
# Headless ACP watch (#286)
|
|
3872
4018
|
|
|
@@ -3879,7 +4025,7 @@ A bundled adapter module is authorized by the person with `adapter_modules` (at
|
|
|
3879
4025
|
# Acceptance-criteria checklist (#481)
|
|
3880
4026
|
|
|
3881
4027
|
- The task reviewer/person records a head-bound `criteria` receipt with criterion ids matching the issue's AC ids, required/result fields and hashed evidence refs. This checklist belongs to the Compliance review pass. Use `ahub sdlc record <id> '<receipt JSON>'`; [schema and example](sdlc.md#acceptance-criteria-checklist).
|
|
3882
|
-
- Deliverables (#509a, docs/sdlc.md, Deliverable records): `hub_task_done` and `hub_sdlc_record` take `deliverables: [{path, format, version?}]`; the hub hashes the files itself (a claimed hash is refused), in a plain folder with no git or PR, under the owner's execution root (its own worktree for a declared worktree instance; an unproven root refuses and reads `unknown`), and a reviewer's approval is refused with `deliverable changed since it was delivered` when a file changed since delivery (the owner calls `hub_task_done` again, or the person approves the current bytes). `ahub task show <id>` shows each file's state (`unchanged`, `changed`, `missing`, `unknown`) and `changedSinceReview`; a PII task or `pii.paths` file shows id, hash and size only. The person lists every task's delivered and approved files with `ahub deliverables [--task <id>] [--approved] [--check] [--json]`: without `--check` it reads the board's records and opens no file; `--check` measures them now and marks a row `changed since review`, `a file is missing` or `could not be measured`. The dashboard card shows a `Deliverables n` chip and the task sheet a Deliverables group from the same records; it shows a change only after a live check. An agent's shell
|
|
4028
|
+
- Deliverables (#509a, docs/sdlc.md, Deliverable records): `hub_task_done` and `hub_sdlc_record` take `deliverables: [{path, format, version?}]`; the hub hashes the files itself (a claimed hash is refused), in a plain folder with no git or PR, under the owner's execution root (its own worktree for a declared worktree instance; an unproven root refuses and reads `unknown`), and a reviewer's approval is refused with `deliverable changed since it was delivered` when a file changed since delivery (the owner calls `hub_task_done` again, or the person approves the current bytes). `ahub task show <id>` shows each file's state (`unchanged`, `changed`, `missing`, `unknown`) and `changedSinceReview`; a PII task or `pii.paths` file shows id, hash and size only. The person lists every task's delivered and approved files with `ahub deliverables [--task <id>] [--approved] [--check] [--json]`: without `--check` it reads the board's records and opens no file; `--check` measures them now and marks a row `changed since review`, `a file is missing` or `could not be measured`. The dashboard card shows a `Deliverables n` chip and the task sheet a Deliverables group from the same records; it shows a change only after a live check. An agent's shell gets only the tasks `hub_task_show` would show it (#933).
|
|
3883
4029
|
- The review request lists the recorded criteria with their ids and results, and a `hub_review` unmet item that starts with a recorded id (`AC2: ...`) is recorded on the verdict as `unmetCriteria` (#507b); `ahub task show` lists those ids in the history. A task with no checklist, and a PII task, are unchanged.
|
|
3884
4030
|
- Advise shows incomplete/stale checklist blockers; enforce checks them at approval. The `criteria` gate does not need a workflow selector. Dependencies stay with `ahub task propose --after`; checklist task import is removed.
|
|
3885
4031
|
- `status <id>`, `explain <id>` and `export <id>` use the same current board/config projection. Export keeps sanitized state and source hashes, with no external text, extra authority or verification claim.
|
|
@@ -3893,7 +4039,7 @@ A bundled adapter module is authorized by the person with `adapter_modules` (at
|
|
|
3893
4039
|
|
|
3894
4040
|
# Project profiles (#259)
|
|
3895
4041
|
|
|
3896
|
-
- Initialize new projects with `ahub init --profile research|writing|general`; default dev retains its existing templates and contracts. Profile aliases map to the existing base flow and are displayed beside it.
|
|
4042
|
+
- Initialize new projects with `ahub init --profile research|writing|general`; default dev retains its existing templates and contracts. Profile aliases map to the existing base flow and are displayed beside it. `--profile <pack>` applies a domain pack (`.agenthub/roles/<name>.pack.json` or a shipped one) as review-checklist and example-check overrides; `--dry-run` previews ids only, and a pack is data, never a command ([domain packs](profiles.md#domain-packs-513a), #513a).
|
|
3897
4043
|
- Non-dev plans declare paths, section anchors, source ids and claims. Configure completion checks explicitly in machine-local config; selecting a profile never starts a check or study. Schema, examples and pending converter/policy slices: [profiles](profiles.md).
|
|
3898
4044
|
|
|
3899
4045
|
# Private and read-only data paths (#259)
|
|
@@ -3913,7 +4059,7 @@ A bundled adapter module is authorized by the person with `adapter_modules` (at
|
|
|
3913
4059
|
# Person catalog-role assignment (#295)
|
|
3914
4060
|
|
|
3915
4061
|
- Use `ahub role assign <peer> <id>` or `ahub role clear <peer> [<id>]`, or the dashboard Roles settings group. The selection is bounded to two compatible catalog ids, writes only machine-local settings and applies at the next peer start. Inherit and explicit no roles are distinct.
|
|
3916
|
-
- Change an open task's guidance with `ahub task role <id> <role|none>`. Agent shells and peer/tools control clients cannot use
|
|
4062
|
+
- Change an open task's guidance with `ahub task role <id> <role|none>`. Agent shells and peer/tools control clients cannot use `role assign|clear`; an agent may run `task role` on a task it owns while it is not in review, and a child, read-only or private-context connection is refused (#933). Details and unconfirmed native consumption: [roles](roles.md).
|
|
3917
4063
|
|
|
3918
4064
|
# Document projection review and undo
|
|
3919
4065
|
|
|
@@ -4102,7 +4248,7 @@ The manager allows split screening, the bounded database wait and forwarding mar
|
|
|
4102
4248
|
## One-way GitHub intake (#263)
|
|
4103
4249
|
|
|
4104
4250
|
`ahub board import github --label <label> [--milestone <milestone>]
|
|
4105
|
-
[--repo <owner/repo>]` is person
|
|
4251
|
+
[--repo <owner/repo>]` is the person's or the configured conductor's (#933; the conductor is recorded as the actor). Omitted repo is resolved by gh in the
|
|
4106
4252
|
project directory. The command reads open issues with fixed bounded argv and
|
|
4107
4253
|
stages up to 32 into unoffered Backlog, retaining canonical repo/issue identity
|
|
4108
4254
|
across restarts. Repeating intake does not refresh or overwrite existing cards.
|
|
@@ -4370,7 +4516,7 @@ work. `ahub status` and the dashboard show the provider, model and profile as
|
|
|
4370
4516
|
declared, and `ahub doctor` lists the MCP servers each rostered instance's home would
|
|
4371
4517
|
load.
|
|
4372
4518
|
|
|
4373
|
-
Upgrade note (
|
|
4519
|
+
Upgrade note (task 368): the project's `.jcode/mcp.json` entry for Jcode no longer names
|
|
4374
4520
|
`AGENTHUB_PEER_ID`, because the file is shared by every Jcode in the project and Jcode
|
|
4375
4521
|
gives an entry's own env precedence over the process env. Each hub-started Jcode now
|
|
4376
4522
|
supplies its own peer id and a launch id, and the hub admits a hub-tools connection
|
|
@@ -4387,7 +4533,7 @@ An extra Claude instance is one you start yourself: declare it with `roles` (for
|
|
|
4387
4533
|
terminal. It keeps its own `claude-*.claude-review.json` state files. A planner or reviewer instance reads the
|
|
4388
4534
|
project checkout and refuses `worktree`. From #525-1 an `implementer` or `verifier` instance is accepted and
|
|
4389
4535
|
requires its own `worktree` (for example `"claude-impl": { "kind": "claude", "roles": ["implementer"],
|
|
4390
|
-
"worktree": ".worktrees/claude-impl" }`). `ahub claude --as claude-impl` validates that registered branch worktree and starts Claude there while keeping its canonical hub connection. Its private launch record stores the execution root. The channel reports its own canonical cwd (verified model-free with Claude 2.1.309); the daemon grants owner isolation only when that root, the current launch id, the recorded root and its own worktree validation all agree. Missing, legacy or mismatched evidence still permits a channel attachment, but write-owner routing is refused with `owner admission: declared write owner requires a verified isolated execution root`. Status
|
|
4536
|
+
"worktree": ".worktrees/claude-impl" }`). `ahub claude --as claude-impl` validates that registered branch worktree and starts Claude there while keeping its canonical hub connection. Its private launch record stores the execution root. The channel reports its own canonical cwd (verified model-free with Claude 2.1.309); the daemon grants owner isolation only when that root, the current launch id, the recorded root and its own worktree validation all agree. Missing, legacy or mismatched evidence still permits a channel attachment, but write-owner routing is refused with `owner admission: declared write owner requires a verified isolated execution root`. Status renders the last verified binding from memory, without workspace-Git revalidation; this display is not permission or owner-admission authority. Detach or replacement claims invalidate the binding. A launch-record or filesystem/branch change is checked at the next routing admission, facts permission reply or recovery owner proof, which revokes a mismatch before effects. Facts and permission grants use the verified execution root; an unverified implementer gets no automatic grant. Recovery records retain that execution root, checked against the current declaration, separately from the Orca grouping root. This is same-user launch evidence, not kernel filesystem isolation. A Claude instance holding only planner or reviewer roles still uses the canonical checkout. A headless Claude reviewer retains its reviewer-only declaration. A headless implementer is available with the separate declaration below (#525-4).
|
|
4391
4537
|
|
|
4392
4538
|
A headless Claude implementer (#525-4) is declared separately:
|
|
4393
4539
|
|
|
@@ -4413,7 +4559,7 @@ Project Git retains the core policy that clears credential.helper and core.askpa
|
|
|
4413
4559
|
|
|
4414
4560
|
The [native permission receipt](verification/2026-10-23-headless-claude-permissions.md) covers Claude Code 2.1.309, and the [2.1.310 receipt](verification/2026-10-26-claude-implementer-2.1.310.md) repeats it on 2.1.310 (with 2.1.309 as a control), each with a deterministic loopback fixture and no real account/model call. It is not sandbox proof or a runtime receipt gate. The real account/model/person interaction remains a separate live leg.
|
|
4415
4561
|
|
|
4416
|
-
A headless Claude reviewer is run by the hub instead (
|
|
4562
|
+
A headless Claude reviewer is run by the hub instead (task 223): declare
|
|
4417
4563
|
`"claude-review": { "kind": "claude", "roles": ["reviewer"], "start_mode": "headless" }`
|
|
4418
4564
|
and start it with `ahub claude --as claude-review`, which asks the running hub to start it (nothing runs in that terminal); `ahub stop claude-review` stops it. It holds no process while idle; each
|
|
4419
4565
|
review request runs one new `claude -p` session (never resumed) that may only read
|
|
@@ -4447,7 +4593,7 @@ From 0.12.37 reviews can rotate across fresh headless Claude and Codex reviewers
|
|
|
4447
4593
|
|
|
4448
4594
|
A reviewer has a review scope (#852): `full` (reads the code, runs the affected tests, reads CI) or `code` (reads the code only). The fresh Codex and Kimi reviewers run in a sandbox that cannot run `bun test` or read CI, so they default to `code`; every other reviewer is `full`. A `code` reviewer's review request replaces the check and exact-head CI lines with one: its sandbox cannot run tests or read CI, review the code against the plan, and the merger gates on exact-head CI. Findings are only defects in the reviewed code, docs or tests; a missing check result, CI result, test run or PR/branch identity belongs in `unmet`, never in `findings`. A `code` reviewer's approval and verdicts record `reviewScope: "code"` in the task history. Its `changes_requested` is always recorded and never refused: with at least one structured finding whose disposition is `open` it counts as a rejection as usual; without one it is recorded as evidence-only, which does not count toward `rejections` or escalation, still returns the task to its owner, and sends the conductor one task notice with ids only. The review request therefore asks a `code` reviewer to report every code defect as a structured finding with an id. A person's verdict is never evidence-only. The scope is a fixed property of the definition. To change it for one instance, set `"review_scope": "full"` or `"code"` on that fresh-reviewer instance in the untracked `config.local.json` (a tracked declaration is ignored with a reason; the key is tier 3, so the dashboard and settings sessions cannot read or change it, and a hub daemon restart applies it). `ahub status` shows `review scope` on each reviewer row. A task approved by a `code` reviewer is merged only after exact-head CI is green and no review thread is unresolved, and the merge note names the CI run.
|
|
4449
4595
|
|
|
4450
|
-
Start them with `ahub claude --as claude-review`, `ahub codex --as codex-review` and `ahub kimi --as kimi-review`; stop with `ahub stop <id>`. For the Claude and Codex reviewers the command asks the running hub to start the reviewer and returns: nothing runs in that terminal, it takes no launch arguments, and `--print-command` or `--dry-run` prints a hub-start note instead of a launch (#720). Fresh Claude and Codex variants share the project checkout; the Kimi variant runs in a clean temporary working directory and reviews only the screened brief and host facts. All fresh variants refuse a custom worktree or permission mode. Codex and Kimi require `review_mode: "fresh"`; without it, an existing headless instance with the reviewer role keeps its native app-server/ACP behavior, worktree and permission options and does not join this pool. Claude retains its
|
|
4596
|
+
Start them with `ahub claude --as claude-review`, `ahub codex --as codex-review` and `ahub kimi --as kimi-review`; stop with `ahub stop <id>`. For the Claude and Codex reviewers the command asks the running hub to start the reviewer and returns: nothing runs in that terminal, it takes no launch arguments, and `--print-command` or `--dry-run` prints a hub-start note instead of a launch (#720). Fresh Claude and Codex variants share the project checkout; the Kimi variant runs in a clean temporary working directory and reviews only the screened brief and host facts. All fresh variants refuse a custom worktree or permission mode. Codex and Kimi require `review_mode: "fresh"`; without it, an existing headless instance with the reviewer role keeps its native app-server/ACP behavior, worktree and permission options and does not join this pool. Claude retains its task 223 automatic headless-reviewer declaration. The agy reviewer slot remains automatically unavailable (`agy is an implementer peer (#485)`). A failed or unsupported fresh start never downgrades to a native continuing session. Ordinary implementer instances retain their own adapters. The default logical pool order is Claude, Codex, Kimi. Only configured kinds with at least one declared reviewer count as slots, including declared ones that are offline, paused, quota-limited or failing, so availability never renumbers them; configured kinds with no declaration are listed after the walk as `no declared reviewer`. `review.rerotate` defaults to false. Pool selection starts at PR number modulo the number of declared kind slots, or task id when there is no PR, then skips the implementer's family and unavailable candidates. Delta review prefers the preceding eligible independent family in a fresh child. A preceding same-family fallback does not outrank a newly available independent family. `ahub route explain` shows the same pure walk.
|
|
4451
4597
|
|
|
4452
4598
|
Codex requires a verified `codex-cli` (0.146.0, 0.163.0 or 0.164.0) or one the person trusts on this machine, and a passing bounded runtime receipt before its model child starts. 0.146.0 and 0.163.0 passed the [2026-10-22 model-free production admission](verification/2026-10-22-codex-reviewer-isolation.md) and 0.164.0 the [2026-10-24 one](verification/2026-10-24-codex-0164-reviewer-isolation.md). A refusal names the installed and verified versions. Either set `codex_bin` to a verified executable, or run `ahub agents trust codex-review <version>`: trust lifts only the version pin, and every proof below still runs before each child (#691). Each launch approves exactly `hub_review` and `hub_task_show` per tool (`approval_mode = "approve"`), never through a server-wide default: without it Codex 0.164.0 listed the tools but refused the call, so reviews ended with no verdict (#783). A model-free receipt proves the inventory, not that a verdict reaches the hub; a [model-backed 0.164.0 run](verification/2026-10-25-codex-0164-reviewer-tool-call.md) recorded one verdict through that path. The producer checks native binary/config stability, configured MCP inventory with only the two hub tools, effective Apps/plugins/hooks disablement, an explicit empty legacy `notify` command and a named filesystem profile. Apps needs separate feature readback because configured inventory omits runtime connectors. Web search is requested disabled; its effective turn mode is not read back and managed requirements can select Cached (see the verification ceiling). Each launch proves public-read, exact/glob/symlink private-denial, standard host credential denial and write-denial in temporary fixtures. Standard credential and configured tool-home/XDG subtrees are denied independently of project `local.deny`. Its sandboxed shell is filesystem read-only with private paths denied and network configured disabled; it is not Claude's Read/Grep/Glob-only tool whitelist. No credential home is copied.
|
|
4453
4599
|
|
|
@@ -4457,9 +4603,9 @@ A dangling or unreadable standard/configured credential root makes the Codex rev
|
|
|
4457
4603
|
|
|
4458
4604
|
agy is not a reviewer (person decision, 2026-10-19): it is an optional implementer peer (see [Optional Antigravity peer](#optional-antigravity-agy-peer)), and its retained reviewer slot is outside the default pool and always unavailable with `agy is an implementer peer (#485)`. A manual pool accepts only registered reviewer kinds (Claude, Codex, Kimi, agy), so local/Pi are refused. An explicit pool without declared candidates blocks for person action; an omitted pool retains legacy routing without declarations and falls back to an eligible planner when fresh candidates are exhausted. The exhausted pool walk remains recorded; without an eligible planner, the person reviews. A manual pool may name agy, or a Kimi reviewer whose preparation fails; the reason appears as an unavailable skip, and they never run or count as approval. Do not substitute plan mode for isolation.
|
|
4459
4605
|
|
|
4460
|
-
A failed start or any child ending without a saved verdict advances to the remaining eligible candidates; no quota meaning is guessed from text. A request that went stale before its child started (a new head, review revision or reviewer) is skipped with one `hub.log` line; a still-current request whose review policy changed moves on as `no_verdict`. Neither marks the reviewer unavailable (#781). A child that ends without a verdict leaves one bounded `hub.log` line: its exit code and the hub tools it called, screened like any refusal, never tool arguments or model text. Its delivery completes only once the re-offer is confirmed; an unconfirmed re-offer stays `needs_review`. A manually named reviewer's no-verdict, and one with no declared pool, goes to the person, never to an agent the person did not choose (
|
|
4606
|
+
A failed start or any child ending without a saved verdict advances to the remaining eligible candidates; no quota meaning is guessed from text. A request that went stale before its child started (a new head, review revision or reviewer) is skipped with one `hub.log` line; a still-current request whose review policy changed moves on as `no_verdict`. Neither marks the reviewer unavailable (#781). A child that ends without a verdict leaves one bounded `hub.log` line: its exit code and the hub tools it called, screened like any refusal, never tool arguments or model text. Its delivery completes only once the re-offer is confirmed; an unconfirmed re-offer stays `needs_review`. A manually named reviewer's no-verdict, and one with no declared pool, goes to the person, never to an agent the person did not choose (task 453). A same-family fallback is a distinct fresh reviewer session. Exhaustion records `blocked: no reviewer can run`; with no explicit pool the eligible planner fallback is retained, while an explicit pool or absent eligible planner leaves person action. It never auto-approves. A per-review isolation preparation failure excludes that peer from later tasks until a verified restart. Its reason names one cause from a closed list (`version`, `binary`, `mcp_inventory`, `plugins_hooks_apps`, `sandbox_proof`, `credential_root`, `timeout`, `unknown`) with a fixed hint, on the peer's `reviewer` line in `ahub status` and in a `hub.log` line, and never a path, native CLI text or token (#722). Only the Codex preparation classifies its failures; other reviewers report `unknown`. Recovery targets must advertise `reviewer_pool` when a pool is explicitly configured or fresh reviewer instances are declared. Verdict history retains `reviewContext` and adds `reviewerFamily` and `reviewerReason` from the host.
|
|
4461
4607
|
|
|
4462
|
-
Fake-CLI tests and CI cover these host contracts. [Offline isolation evidence](verification/2026-10-19-reviewer-isolation.md) and fake-CLI tests do not prove an account/model review or observed network denial. Native acceptance remains a separate [manual smoke leg](smoke.md#reviewer-pool-native-legs-519-2026-10-19); existing
|
|
4608
|
+
Fake-CLI tests and CI cover these host contracts. [Offline isolation evidence](verification/2026-10-19-reviewer-isolation.md) and fake-CLI tests do not prove an account/model review or observed network denial. Native acceptance remains a separate [manual smoke leg](smoke.md#reviewer-pool-native-legs-519-2026-10-19); existing task 223 Claude behavior has no new live proof here. Kimi's authenticated fresh-verdict receipts, and its still-open interactive-TUI coexistence and usage checks, are tracked in [Kimi smoke legs](smoke.md#kimi-reviewer-and-task-bound-mcp-533).
|
|
4463
4609
|
|
|
4464
4610
|
An additional implementer must have an existing registered Git worktree and
|
|
4465
4611
|
attached branch at `.worktrees/<instance-id>`, named explicitly by `worktree` in
|
|
@@ -4498,13 +4644,13 @@ Run `ahub sdlc authorize-release <version>` from the running daemon's release-ca
|
|
|
4498
4644
|
|
|
4499
4645
|
`ahub init --release-guard` installs a managed Git pre-push guard and release instructions (`--dry-run --json` previews them). It preserves an existing default hook as `pre-push.agent-hub-original` and passes original argv/stdin to it only after approval matches. Unknown or mismatched `v*` tag pushes fail closed, including when the daemon is down. Non-version branch pushes remain independent of the daemon. Custom `core.hooksPath` and linked/hardlinked hook files require manual integration. Re-run init after changing the Bun/package location. The person's `git push --no-verify` remains an explicit bypass; agents are instructed never to use it.
|
|
4500
4646
|
|
|
4501
|
-
For `gh release create` and `npm publish`, use the exact literal command after `ahub sdlc release-guard --`. This preflight reads evidence and never executes the command. New GitHub tags require an explicit full commit target; alternate-repository, workspace/archive and bulk target forms refuse. GitHub/npm adapter enforcement is deferred to
|
|
4647
|
+
For `gh release create` and `npm publish`, use the exact literal command after `ahub sdlc release-guard --`. This preflight reads evidence and never executes the command. New GitHub tags require an explicit full commit target; alternate-repository, workspace/archive and bulk target forms refuse. GitHub/npm adapter enforcement is deferred to tasks 312/313. This consent record does not certify a tarball, native acceptance or live publication. Detailed limits: [S6 authorization](sdlc.md#person-release-authorization-481-s6).
|
|
4502
4648
|
|
|
4503
4649
|
## Planner release scheduling
|
|
4504
4650
|
|
|
4505
4651
|
Use `ahub sdlc train report <release-id>` for scope readiness and recorded release metrics. The planner creates future release rows, activates one train, and assigns tasks through the release-only hub tools. A due date requests a cut review; it never clears unfinished work. Move missed tasks to the next row with a reason, then use `ahub sdlc train cut <release-id> <revision>` from the person's terminal or `hub_release_cut` as the planner. The cut retains a short host admission lease through freeze and defers on held gate slots or unknown/quota-limited peer windows with a real quota source. Accounts without a quota reader (Kimi included when `kimi_tokens_5h` is zero) report `no quota source`; manual and budget-record pauses still defer. It does not run release preparation or publish anything. Schedule bundle/check/docs work under separate heavy admission and retain the person's native verification, authorization and upgrade window. A hotfix records its enumerated cause and keeps every gate.
|
|
4506
4652
|
|
|
4507
|
-
The per-release "gates run" record is not built:
|
|
4653
|
+
The per-release "gates run" record is not built: it needs gate-execution evidence the S6 authorization path would supply, and a cut record is not that evidence. Release interval, approval-to-release lead time and hotfix rate are shown by the release report (#442). Person ruling 2026-10-19 supersedes the earlier S11 upgrade-admission ruling: upgrades use pre-S11 recovery readiness (no busy peers, pending approvals resolved, completion checks settled) and retain the existing operation checks. S11 adds no host-admission, quota or all-idle requirement to upgrade. An active/frozen configured train still requires recovery-target `release_trains` compatibility. Release-cut admission remains unchanged: follow its refusal's applicable exit by stopping or resuming the paused peer, adjusting `ahub budget set`, or using `ahub release drop`. Missing/shadowed `dot` requires the resource controller or a PATH correction for the cut; nested admission requires retrying the cut outside the outer lease.
|
|
4508
4654
|
|
|
4509
4655
|
|
|
4510
4656
|
## Codex cancellation identity notice
|
|
@@ -4522,7 +4668,24 @@ The design for planner, implementer and reviewer instances per stage is in the s
|
|
|
4522
4668
|
- 525-4: a headless Claude implementer runs one owned child per turn in its declared worktree, with a separate persisted session per task (`start_mode: "headless"`, permission `ask` or `ask-when-needed`; `never-ask` refused).
|
|
4523
4669
|
- 525-5: terminal Claude implementer instances own write-class tasks once the daemon has verified their execution root.
|
|
4524
4670
|
|
|
4525
|
-
|
|
4671
|
+
For a Codex planner, put a declaration such as the following in the untracked `.agenthub/config.local.json`, then restart the daemon. A planner instance holds `planner` and neither `implementer` nor `verifier`; adding `reviewer` does not grant code-write ownership. It uses the project checkout, so `worktree` is refused.
|
|
4672
|
+
|
|
4673
|
+
```json
|
|
4674
|
+
{
|
|
4675
|
+
"instances": {
|
|
4676
|
+
"codex-plan": { "kind": "codex", "roles": ["planner"], "start_mode": "headless" }
|
|
4677
|
+
},
|
|
4678
|
+
"planner": ["claude", "codex-plan"]
|
|
4679
|
+
}
|
|
4680
|
+
```
|
|
4681
|
+
|
|
4682
|
+
Run `ahub codex --as codex-plan` from the person's terminal. The hub opens the app-server proxy without creating a synthetic host thread. Each native `thread/start` or `thread/resume` is requested read-only. The host rechecks the current settled roles when each thread is admitted and before hub injection, so changing a running instance from a continuing reviewer to a planner cannot reuse an unconfirmed writable thread. A live planner transition with a declared writing worktree is refused until the declaration is corrected and the daemon restarted. A reply that does not positively confirm read-only refuses that RPC and closes the unconfirmed link before the hub adopts the thread or delivers work; missing or malformed evidence is not read-only. The attached TUI belongs to the person and can change its native sandbox later: this is an initial thread ceiling, not a claim that the hub confines the person's TUI. A planner can own `plan` work but cannot become the code owner without the implementer role.
|
|
4683
|
+
|
|
4684
|
+
A planner-only Claude instance stays a channel instance on the project checkout, with no `worktree` or headless form. Its facts hook gives no automatic grant for mutating file tools or shell, whatever the hub permission mode. The daemon also returns `ask` for those calls in the existing grant reply field, protecting older hooks that do not recognize the added ceiling flag; the person's runtime mode itself is unchanged. A native choice in that person's terminal remains the person's; the builtin `claude` keeps its existing behavior.
|
|
4685
|
+
|
|
4686
|
+
Inspect the running holders with `ahub status` and the dashboard's Agents view. Each peer's `roleInfo` includes its running `form`, `capableRoles` and effective `flowRoles`; a planner holder also names its planner source. `form` distinguishes a channel, headless variant, terminal implementer or builtin form. Capability describes the allowed ceiling, not an extra assigned role. Each declared extra shows owner isolation as `isolated` or `not isolated: <reason>`; a planner using the canonical checkout is not an isolated write owner by design. Missing proof never counts as isolation. The isolation fields and status stall projection are cached display evidence; they may still show the last verified root before an effect point checks a filesystem change. Actual assignment, facts grants and recovery never authorize from this display cache.
|
|
4687
|
+
|
|
4688
|
+
Use `ahub task show <id>`, the console Tasks panel and the dashboard task detail to inspect planner, owner and reviewer with their recorded reasons. `ahub task show <id> --json` and `ui_snapshot` carry `planner` and `plannerReason` as additive fields; a legacy null planner stays unbound. Compare the holder walk with `ahub route explain <id>`. Use `ahub task assign <id> <peer> --role planner` from the person's terminal to name another eligible holder without moving owner or reviewer. Planner settings, bindings, independent plan review and the recovery floor are described in "The planner" above; implementer/reviewer setup is in "Additional headless peer instances". The [six-instance person smoke leg](smoke.md#six-role-instances-525-6) remains unverified until it is actually run.
|
|
4526
4689
|
|
|
4527
4690
|
## Daemon responsiveness and launch connects (#719)
|
|
4528
4691
|
|
|
@@ -4532,7 +4695,7 @@ Native launcher connections retry transport timeouts/unclean closes with bounded
|
|
|
4532
4695
|
|
|
4533
4696
|
## amem runtime (agent-mem#13 P4)
|
|
4534
4697
|
|
|
4535
|
-
The hub exact-pins @staix/agent-mem 0.
|
|
4698
|
+
The hub exact-pins @staix/agent-mem 0.13.0 for recall, curated evidence and local deterministic capture. No runtime call reaches a claude-mem worker; `memory.worker_url` is ignored. The person's `ahub memory import claude-mem` remains explicit and off by default. Existing profile, alias, PII and private-turn fences still apply. `ask --remember` and context checkpoints propose machine-local candidates, never automatic curated evidence. See [memory integration](memory.md#in-process-recall-and-capture-agent-mem13-p4) for changed behavior and bounded-view ceilings.
|
|
4536
4699
|
|
|
4537
4700
|
## Backlog issue identity and notes
|
|
4538
4701
|
|
|
@@ -4564,7 +4727,7 @@ Optional code work runs only while Memory is open and visible. A cold console co
|
|
|
4564
4727
|
|
|
4565
4728
|
## Authority by command
|
|
4566
4729
|
|
|
4567
|
-
### Conductor operations (#812 T2)
|
|
4730
|
+
### Conductor operations (#812 T2, #933, #900)
|
|
4568
4731
|
|
|
4569
4732
|
The selected project's configured conductor may run the commands below through its ordinary peer identity. Other peers retain the guidance "the person runs it in ahub console or a terminal". A missing or dead descriptor refuses promptly. For a live descriptor the connect and local authorization handshakes each keep a 3-second bound; connection unavailability is not presented as proof that the peer lacks the conductor role. Actual authority refusals are audited as refused, while ordinary operation failures retain their original cause. The daemon checks the authenticated connection, current role and project membership before effects and after asynchronous boundaries. Roster handoffs carry that same authority check through plan admission, owner delivery and each later task; a saved assignment is never rolled back when delivery is interrupted. Effectful operations and operational history reads retain an `agent_cli` admission with the initiating peer; audit failure refuses those operations. Authorization negotiation, `settings_preview` and `queue show` still check current authority but emit no extra admission record or conductor notice. Admission records are not a unique invocation counter.
|
|
4570
4733
|
|
|
@@ -4573,15 +4736,355 @@ The selected project's configured conductor may run the commands below through i
|
|
|
4573
4736
|
| roster add/remove | Existing roster lifecycle and confirmations; no role or permission-mode grant |
|
|
4574
4737
|
| stop peer | Existing hub-owned peer stop, ownership and recovery guards |
|
|
4575
4738
|
| queue show/resolve/discard | Public queue projection and existing revision/confirmation guards |
|
|
4576
|
-
|
|
|
4577
|
-
|
|
|
4739
|
+
| board import / intake approve | Existing import and intake admission; the actual conductor remains the actor |
|
|
4740
|
+
| schedule run/pause/resume | Existing machine-local schedule controls; no role or policy changes |
|
|
4741
|
+
| research backfill / bench run / source add / goal link | Existing bounded CLI or daemon paths, with fixed audit labels |
|
|
4742
|
+
| start Jcode | Launcher-only peer the conductor is configured to start |
|
|
4743
|
+
| task unassign <id> [--role owner\|reviewer] [--revision N] | The actual conductor is recorded; no replacement is routed and no native work is stopped |
|
|
4744
|
+
| restart --yes | Only with a source-issued continuation proof for this project's own operation |
|
|
4745
|
+
| upgrade --yes --to <exact-version> | Only with proof for this project's supported exact-version upgrade; newer coordinator handoff is person-only |
|
|
4746
|
+
| recovery resume\|abort [<op-id>] | Only the initiating conductor's own restart/authorized-upgrade operation; the person keeps the normal recovery path |
|
|
4747
|
+
| doctor --orphans --kill | Selected project's positively identified hub-owned process only; no cross-project registrations or unsigned process files. Prints the stopped pids for a person; `--json` prints the hub's reply |
|
|
4578
4748
|
| logs / export | Bounded daemon projections, with unsupported/private records withheld; logs -f ends when authority ends |
|
|
4579
|
-
| settings set | Only the
|
|
4749
|
+
| settings set | Only the thirteen existing live registry keys listed below |
|
|
4580
4750
|
|
|
4581
|
-
The exact settings keys are `task_sweep.enabled`, `task_sweep.auto_reassign`, `task_sweep.no_writes_turns`, `task_sweep.interval_s`, `task_sweep.unaccepted_min`, `task_sweep.idle_min`, `task_sweep.review_min`, `task_sweep.ready_min`, `task_sweep.ladder_min`, `snapshots.enabled`, `research.enabled` and `
|
|
4751
|
+
The exact settings keys are `task_sweep.enabled`, `task_sweep.auto_reassign`, `task_sweep.no_writes_turns`, `task_sweep.interval_s`, `task_sweep.unaccepted_min`, `task_sweep.idle_min`, `task_sweep.review_min`, `task_sweep.ready_min`, `task_sweep.ladder_min`, `snapshots.enabled`, `research.enabled`, `approvals.notify` and `tasks.escalate_after`. New keys receive no authority by prefix. Generic roster setting writes still use the dedicated roster lifecycle. Conductor setting records name the initiating peer, and conductor writes preserve the person's pending undo record. An intervening write can still invalidate that undo through the existing file-digest guard; preserving the record does not bypass that guard. Permission modes, roles, `pet.window`, `models start|stop`, arbitrary configuration reload and undo remain person commands. `task cancel` remains a separate own-task `hub_task_cancel` permission, not a conductor operation.
|
|
4582
4752
|
|
|
4583
4753
|
Log history is limited to the last 100 retained lines and export to the last 1000 retained events, each within a 128 KiB read. Truncation is explicit. An incomplete public projection reports one bounded reason on stderr, preserving export JSON on stdout. The complete privacy inventory has no 500-task cutoff and is cached until board or privacy/config revisions change, so log-follow does not rebuild it for every line. Export accepts closed metadata shapes; unknown fields and nested records are withheld rather than copied. Task titles, paths and private delivery bodies are not part of this projection. It does not change the person's ordinary logs/export output.
|
|
4584
4754
|
|
|
4585
4755
|
Orphan cleanup requires a missing selected root, signed process identity and the host-known hub executable/entrypoint and argv. An inaccessible root or unavailable proof remains unknown; there is no offline config-based role grant. The daemon rechecks authority and process ownership before signals. A confirmed self candidate uses the existing lifecycle stop after its reply. A roster write already committed before authority ends is acknowledged as saved, without authorizing further lifecycle work or commands.
|
|
4586
4756
|
|
|
4587
|
-
|
|
4757
|
+
### Conductor-bound restart and recovery continuation (task 535, #900)
|
|
4758
|
+
|
|
4759
|
+
The configured conductor may start a restart with `ahub restart --yes` or an
|
|
4760
|
+
upgrade with `ahub upgrade --yes --to <exact-version>` only after the source hub
|
|
4761
|
+
issues a bounded, fixed-shape private continuation record for that exact operation. The record is
|
|
4762
|
+
separate from public recovery JSON and its `delegatedAudits` history. A
|
|
4763
|
+
source-issued HMAC binds the operation id, initiating peer, project id and
|
|
4764
|
+
canonical root, source daemon instance, target root and package bytes, plan
|
|
4765
|
+
fingerprint, and a version-neutral digest of bounded source inputs and fresh
|
|
4766
|
+
actor/privacy evidence. A public operation entry or audit label alone grants
|
|
4767
|
+
nothing. The digest covers raw config-layer and routing-file texts, plus the
|
|
4768
|
+
source-resolved public routing template bytes when no project routing file
|
|
4769
|
+
exists; it does not hash expanded defaults, default roles or parsed routing.
|
|
4770
|
+
The public `conductorRoutingDefaults` map is advisory; the target inherits the
|
|
4771
|
+
sealed source fallback. The private record stores digests, not raw config,
|
|
4772
|
+
routing contents or the HMAC key.
|
|
4773
|
+
|
|
4774
|
+
Every planned project is preflighted for source recovery support and current
|
|
4775
|
+
authority before staging begins. An upgrade then stages and verifies its exact
|
|
4776
|
+
target; restart has no package stage. The source issues the private HMAC record
|
|
4777
|
+
after the target bytes and feature floor are verified, before publishing the
|
|
4778
|
+
public operation receipt, taking the lock or applying runtime holds. A source
|
|
4779
|
+
preflight refusal prevents staging; an unsupported staged target or failed
|
|
4780
|
+
proof prevents the public operation and its holds. A target requiring handoff
|
|
4781
|
+
to a newer coordinator remains person-only. At every effect boundary, the host
|
|
4782
|
+
rechecks the same peer's live conductor role and roster membership, project
|
|
4783
|
+
authority, read-only status and current non-PII/privacy policy. Offline
|
|
4784
|
+
continuation uses only bounded current actor/privacy checks; unrelated public
|
|
4785
|
+
board writes do not invalidate it. Changed or unreadable authority evidence
|
|
4786
|
+
stops the next effect and leaves the operation for the person.
|
|
4787
|
+
|
|
4788
|
+
An upgrade digest includes the exact staged `@staix/agent-mem` package from its
|
|
4789
|
+
hoisted install; a restart digest includes its verified source dependency.
|
|
4790
|
+
Protected or restart-bound source config/routing edits that have not been
|
|
4791
|
+
accepted remain person-only. The replacement validates the captured inputs
|
|
4792
|
+
before adapter imports and uses the sealed source fallback for startup routing.
|
|
4793
|
+
|
|
4794
|
+
Captured TUI terminal restoration remains person-only: a delayed native
|
|
4795
|
+
launcher cannot re-prove the conductor at its eventual spawn. Headless
|
|
4796
|
+
restoration uses the target-owned snapshot through a tools connection.
|
|
4797
|
+
|
|
4798
|
+
An unmanaged reconnect-only Claude channel may reattach itself without a native launch. The source keeps a SHA-256 fingerprint of its observed session identity separate from restoration metadata (the raw unmanaged session id is omitted from the plan) and verifies the exact peer, kind and attachment against the live channel. This exception requires no terminal or restoration arguments, no managed launch proof, and an absent or confirmed-ended launcher; unknown launcher evidence refuses. Other peer identities still compare exactly.
|
|
4799
|
+
|
|
4800
|
+
|
|
4801
|
+
Only the conductor that created the restart or authorized upgrade may run
|
|
4802
|
+
`recovery resume|abort` for that operation. A different conductor cannot take
|
|
4803
|
+
over; the person can use the ordinary recovery commands. The detached
|
|
4804
|
+
`recovery-run` and target launcher are internal continuations bound to the
|
|
4805
|
+
recorded operation and captured target arguments. They do not grant arbitrary
|
|
4806
|
+
console CLI commands. The existing `--live-test` path remains separate and does
|
|
4807
|
+
not create a conductor continuation.
|
|
4808
|
+
|
|
4809
|
+
`task unassign` runs as the conductor's own actor and does not route a
|
|
4810
|
+
replacement or stop native work. An owner unassign suppresses automatic owner
|
|
4811
|
+
offers until an authorized explicit assignment or reservation clears that
|
|
4812
|
+
suppression. This behavior has fixture coverage only; it does not establish a
|
|
4813
|
+
current installed-release or live-account receipt.
|
|
4814
|
+
|
|
4815
|
+
## Standalone ciphertext broker (#446 X2)
|
|
4816
|
+
|
|
4817
|
+
From the operator's terminal, run `ahub broker serve --port 8788 --state-dir <private-directory>`.
|
|
4818
|
+
Default bind is 127.0.0.1; default storage is `broker/` under the selected hub state
|
|
4819
|
+
directory. Ctrl+C or SIGTERM closes the listener and SQLite handle together.
|
|
4820
|
+
Accepted ciphertext remains across restart. `GET http://127.0.0.1:8788/health`
|
|
4821
|
+
returns fixed version/status data.
|
|
4822
|
+
|
|
4823
|
+
For another machine, configure TLS termination and use
|
|
4824
|
+
`--public-url wss://relay.example/ --owner <owner-signing-fingerprint>`.
|
|
4825
|
+
Repeat `--owner` for each approved creator. Public WSS audiences, even on a loopback
|
|
4826
|
+
listener behind a proxy, require this allowlist. It controls new rooms only;
|
|
4827
|
+
existing member revocation still uses the owner's signed roster. Omitting the
|
|
4828
|
+
allowlist is allowed only in the loopback/no-public-audience development mode;
|
|
4829
|
+
do not expose that open mode through a proxy.
|
|
4830
|
+
The audience must be a canonical root URL without credentials, query or fragment.
|
|
4831
|
+
Proxy `/v1/rooms/<room>` as binary WebSocket, preserve Origin and expose WSS only.
|
|
4832
|
+
A non-loopback bind also needs `--hostname <address>` and an explicit WSS URL.
|
|
4833
|
+
The command configures no proxy, certificate, firewall or hosting account; live
|
|
4834
|
+
TLS and two-machine operation need their own operator receipts.
|
|
4835
|
+
|
|
4836
|
+
Keep `broker.db` and its directory outside version control. There is one process
|
|
4837
|
+
per database, enforced by SQLite's kernel lock; a second instance reports busy.
|
|
4838
|
+
A crash releases that lock. Do not delete the database as lock recovery: owner/
|
|
4839
|
+
epoch pins protect membership. Empty/corrupt/unsupported state refuses rather than
|
|
4840
|
+
forgetting pins. Restore a known valid backup or arrange a fresh room lifecycle
|
|
4841
|
+
explicitly; X3 clients must retain their own owner/epoch/replay state.
|
|
4842
|
+
|
|
4843
|
+
The first room connection proves its signed roster's owner identity; later ones
|
|
4844
|
+
prove current membership. Room create/invite/join/expose and the hub link are X3;
|
|
4845
|
+
ordinary chat forwarding and local policy are X4. A stored or queue_ack packet is
|
|
4846
|
+
transport/storage only; application delivery needs the recipient's separate signed
|
|
4847
|
+
receipt. Queue-full/backpressure refuses new work without evicting accepted data.
|
|
4848
|
+
See the [protocol and caps](specs/2026-09-19-agent-hub-design.md#encrypted-rooms-x2-blind-broker-446).
|
|
4849
|
+
|
|
4850
|
+
|
|
4851
|
+
### Reclaim one broker room
|
|
4852
|
+
|
|
4853
|
+
Stop the broker first, then run
|
|
4854
|
+
`ahub broker room drop <room> --yes --state-dir <private-directory>` from the
|
|
4855
|
+
operator's terminal. Online attempts report busy; agent markers and `--live-test`
|
|
4856
|
+
delegation refuse. The directory/database must already exist. This forgets only
|
|
4857
|
+
that room's pins, sender floors and pending ciphertext, freeing its slot; it keeps
|
|
4858
|
+
other rooms and their pins. It is a destructive room reset, not membership
|
|
4859
|
+
revocation, secure erasure or confirmation that pending messages were delivered.
|
|
4860
|
+
Clients must retain their own owner/epoch/replay state. Remove an unwanted owner
|
|
4861
|
+
from the startup allowlist before restarting so it cannot recreate the dropped id.
|
|
4862
|
+
Never delete the whole database to free one room.
|
|
4863
|
+
|
|
4864
|
+
Existing populated state directories must contain only recognized broker files
|
|
4865
|
+
and already be 0700. Other populated directories, including a checkout, refuse
|
|
4866
|
+
without chmod. Empty directories may be tightened. Schema v1 migrates to v2 while
|
|
4867
|
+
preserving signed pins, floors and queued ciphertext. The 4 MiB per-room admission
|
|
4868
|
+
share prevents one room taking the whole aggregate byte budget; valid older larger
|
|
4869
|
+
queues stay available but block new admission until drained or expired.
|
|
4870
|
+
|
|
4871
|
+
|
|
4872
|
+
## Outbound room membership (#446 X3)
|
|
4873
|
+
|
|
4874
|
+
Run room mutations from the person's terminal, never an agent shell or `--live-test`.
|
|
4875
|
+
Start a self-hosted broker as described above, then create a room with
|
|
4876
|
+
`ahub room create team --broker wss://broker.example/`. The owner fingerprint must
|
|
4877
|
+
be on a public broker's `--owner` allowlist. `room list` prints that fingerprint.
|
|
4878
|
+
No room joins or exposure are loaded from committed configuration.
|
|
4879
|
+
|
|
4880
|
+
Exchange these artifacts privately out of band:
|
|
4881
|
+
|
|
4882
|
+
1. Owner: `ahub room invite team`, send the invitation token to the joining person.
|
|
4883
|
+
2. Joiner: `ahub room join <token> --name team`, send the printed request back.
|
|
4884
|
+
3. Owner: `ahub room invite team --accept <request>`, send the printed signed roster.
|
|
4885
|
+
4. Joiner: `ahub room join team --roster <signed-roster>`.
|
|
4886
|
+
|
|
4887
|
+
Both consoles print owner and member fingerprints before installing/consuming.
|
|
4888
|
+
Compare them out of band, then answer the TTY's y/N prompt (default No). A non-TTY
|
|
4889
|
+
must pass `--confirm <member-fingerprint-prefix>` with 16..64 matching lowercase
|
|
4890
|
+
hex characters; it does not execute on a missing or mismatched confirmation.
|
|
4891
|
+
This flag does not delegate to an agent or bypass the `--live-test` refusal. Only the invitation token contains its one-time secret.
|
|
4892
|
+
The join request/roster contain no private key or invitation secret. Keep tokens
|
|
4893
|
+
out of shell transcripts, shared logs and source control. A saved pending join can
|
|
4894
|
+
be retried with the same token/name/label; `room leave team` cancels it if needed.
|
|
4895
|
+
|
|
4896
|
+
`room expose team codex` sets the untracked local exposure policy. On a non-owner
|
|
4897
|
+
it prints a signed exposure request: send it through the same owner `invite --accept`
|
|
4898
|
+
and joiner `join --roster` round trip. Empty exposure is the default. `room status`
|
|
4899
|
+
shows ids and closed states. The X4 chat workflow is below.
|
|
4900
|
+
|
|
4901
|
+
`room revoke team <fingerprint>` rotates the owner roster, giving no new key to the
|
|
4902
|
+
removed member. `room leave team` disconnects locally and retains replay/pin state;
|
|
4903
|
+
ask the owner to revoke membership as well. Hub shutdown closes links but keeps
|
|
4904
|
+
membership. Never delete rooms.db to repair a pin or sequence failure. Unknown or
|
|
4905
|
+
corrupt state disables rooms, while local hub work remains available. Real TLS,
|
|
4906
|
+
proxy forwarding and two-machine smoke remain unverified until the person runs X7.
|
|
4907
|
+
|
|
4908
|
+
## Queue backpressure on automatic task offers (#800)
|
|
4909
|
+
|
|
4910
|
+
WIP and a peer's waiting messages measure different constraints. Backpressure is
|
|
4911
|
+
off by default. A person may opt in through untracked local settings:
|
|
4912
|
+
|
|
4913
|
+
```sh
|
|
4914
|
+
ahub settings set tasks.queue_backpressure.max_queued 20
|
|
4915
|
+
ahub settings set tasks.queue_backpressure.max_oldest_s 600
|
|
4916
|
+
```
|
|
4917
|
+
|
|
4918
|
+
The bounds are 10,000 messages and seven days; zero disables that threshold.
|
|
4919
|
+
Values strictly above a threshold hold new automatic owner/reviewer offers.
|
|
4920
|
+
`ahub route explain <task>` uses the actual assignment rule. `ahub status` shows
|
|
4921
|
+
queued count, oldest age and a fixed hold reason separately from active,
|
|
4922
|
+
review-wait and reserved WIP. Unknown age is shown as unknown and holds an offer
|
|
4923
|
+
when the age threshold is on. Existing queued work still drains normally.
|
|
4924
|
+
|
|
4925
|
+
After enabling age policy, old or recovered entries can lack host arrival proof;
|
|
4926
|
+
resaving/restarting does not make them fresh. Drain them or explicitly assign the
|
|
4927
|
+
particular task as the person/conductor. That override applies only to the named
|
|
4928
|
+
role, not other automatic choices, and does not waive privacy/independence/runtime
|
|
4929
|
+
admission. Automatic reserved planner pulls are still guarded.
|
|
4930
|
+
|
|
4931
|
+
Queue-origin holds are retried after committed queue/state/policy changes, without
|
|
4932
|
+
routing work a person explicitly unassigned. Automatic decline/budget reroutes
|
|
4933
|
+
retain their retry intent across planning updates; a named proposal keeps its
|
|
4934
|
+
target. A person reservation waits under pressure rather than moving to another
|
|
4935
|
+
peer. Self-claims enqueue no owner request and skip only that queue guard.
|
|
4936
|
+
Continuing reviewers remain bound unless their own queue refuses the new offer.
|
|
4937
|
+
Setting both values to zero returns
|
|
4938
|
+
to ordinary automatic assignment. This PR changes no live repository setting or
|
|
4939
|
+
routing template; real-use adoption metrics remain separate from fixture checks.
|
|
4940
|
+
|
|
4941
|
+
|
|
4942
|
+
## Screened room chat (#446 X4)
|
|
4943
|
+
|
|
4944
|
+
After joining and confirming fingerprints, expose each receiving native peer with
|
|
4945
|
+
`ahub room expose team codex`. A non-owner returns the signed exposure request to
|
|
4946
|
+
the owner and installs the resulting roster as described above. Exposure is empty
|
|
4947
|
+
by default; this repository enables no room or exposure automatically.
|
|
4948
|
+
|
|
4949
|
+
`ahub room status team` lists remote addresses such as `codex@hub-guest`. Send to
|
|
4950
|
+
one explicitly:
|
|
4951
|
+
|
|
4952
|
+
```sh
|
|
4953
|
+
ahub say @codex@hub-guest "Please inspect the public example"
|
|
4954
|
+
```
|
|
4955
|
+
|
|
4956
|
+
A local exposed agent can use `hub_send` with that exact address. Native peers
|
|
4957
|
+
can exchange round-trips. X5b below adds the person's durable inbox for replies
|
|
4958
|
+
to `user@label`. An older X4-only receiver returns signed `failed_safe` with
|
|
4959
|
+
"person inbox not supported yet (X5)", rather than claiming admission. One remote plus
|
|
4960
|
+
local recipients is supported. X4 refuses multiple remote recipients before any
|
|
4961
|
+
delivery: send separately. Ordinary broadcasts stay local. Unknown or ambiguous
|
|
4962
|
+
addresses refuse. Private turns, PII task references and private envelopes do not
|
|
4963
|
+
cross; secret/path/pattern/model-screen failures produce an ids-only stub.
|
|
4964
|
+
|
|
4965
|
+
Room status and `ahub queue show <delivery-id>` distinguish accepted ciphertext
|
|
4966
|
+
from **completed (admitted)**, meaning the remote hub saved the chat to its local
|
|
4967
|
+
peer queue. Native/model execution is independent. Incoming messages remain
|
|
4968
|
+
untrusted text and grant no local task, role, approval, tool or control authority.
|
|
4969
|
+
|
|
4970
|
+
Offline queues are capped at 200 per room. In untracked config.local.json, optional
|
|
4971
|
+
`rooms.ack_timeout_s` defaults to 600 and `rooms.queue_ttl_h` to 24. A restart,
|
|
4972
|
+
expired deadline or uncertain publication leaves `needs_review`. It is not
|
|
4973
|
+
replayed automatically. A late signed receipt is visible as evidence in queue
|
|
4974
|
+
inspection; the person's disposition is still required. Old text cannot be
|
|
4975
|
+
retried to a replacement fingerprint reusing a label. An unproved terminal retry
|
|
4976
|
+
refuses and requires a deliberately authored new message.
|
|
4977
|
+
An expired or missing original enqueue deadline likewise refuses retry without
|
|
4978
|
+
recording a successful disposition. Send a new message deliberately instead.
|
|
4979
|
+
|
|
4980
|
+
The ACK timeout starts when the application is handed to the room link, even if
|
|
4981
|
+
the broker never confirms storage. A reconnect or restart requests fresh signed
|
|
4982
|
+
presence from the other hub without waiting for its peer state to change.
|
|
4983
|
+
Capacity refusal of one incoming chat retains that pending chat while allowing
|
|
4984
|
+
later packets and receipts through; a later online or person-disposition event
|
|
4985
|
+
rechecks admission. Merely reading room status applies no disposition.
|
|
4986
|
+
Up to eight matching receipt payloads per room have a separate 1,024-byte-per-row
|
|
4987
|
+
durable reserve so a full chat inbox can still settle outstanding deliveries.
|
|
4988
|
+
The normal chat cap is unchanged; unrelated receipts cannot use that reserve.
|
|
4989
|
+
Presence retired at the ordinary cap is re-fetched once after capacity frees,
|
|
4990
|
+
so that a dropped state update does not leave the remote view stale indefinitely.
|
|
4991
|
+
Fresh authenticated chat that reaches the full ordinary inbox is refused and
|
|
4992
|
+
transport-ACKed without admission or a signed receipt. The sender's application
|
|
4993
|
+
expires to `needs_review`; receipt traffic behind the refused chat continues.
|
|
4994
|
+
|
|
4995
|
+
For an incoming uncertain admission, copy its frame id from room status:
|
|
4996
|
+
|
|
4997
|
+
```sh
|
|
4998
|
+
ahub room resolve team --frame FRAME_DIGEST --action accept
|
|
4999
|
+
ahub room resolve team --frame FRAME_DIGEST --action discard
|
|
5000
|
+
```
|
|
5001
|
+
|
|
5002
|
+
Both forms show the owner/member fingerprints before asking y/N. Non-TTY use
|
|
5003
|
+
requires `--confirm` with a matching member fingerprint prefix. Accept records
|
|
5004
|
+
that the person vouches the local peer got it; discard records dropping it.
|
|
5005
|
+
Neither sends, re-admits, or produces a signed completion. The displayed state is
|
|
5006
|
+
**disposed (accept|discard, by person)**. These mutations refuse agent shells and
|
|
5007
|
+
`--live-test`, including after leaving a room.
|
|
5008
|
+
|
|
5009
|
+
Application-id and trace metadata retain 30 days/10,000 per room, evicting settled
|
|
5010
|
+
rows first. Protected uncertainty remains until person disposition. Outside that
|
|
5011
|
+
retention no historical app-id or trace claim is made; permanent seq floors and
|
|
5012
|
+
MAX_HOP 3 still apply. Status prioritizes uncertainty and bounds each direction's
|
|
5013
|
+
rows to 200; queue inspection remains the delivery-history entry point. Broker,
|
|
5014
|
+
TLS/proxy and real two-machine/account receipts remain separate live proof.
|
|
5015
|
+
|
|
5016
|
+
## Room metadata views (#446 X5)
|
|
5017
|
+
|
|
5018
|
+
Press `8` in the panel console for Rooms, or open Agents > Rooms in the dashboard.
|
|
5019
|
+
The views show local room membership, owner/self fingerprints, member count,
|
|
5020
|
+
link state and exposed remote peer ids/states. They are hidden when an older hub
|
|
5021
|
+
supplies no room field or no rooms are configured. An unavailable read clears
|
|
5022
|
+
old metadata and shows an unavailable state. Reading status creates no room
|
|
5023
|
+
storage or keys.
|
|
5024
|
+
|
|
5025
|
+
Relay figures are **since daemon startup**, not lifetime totals: outbound is
|
|
5026
|
+
successful handoffs, inbound is confirmed local admissions, and withheld is a
|
|
5027
|
+
subset of outbound handoffs. Unknown values display as unknown, not zero. A
|
|
5028
|
+
daemon restart resets the counters; neither a handoff nor these totals means a
|
|
5029
|
+
native agent completed its work.
|
|
5030
|
+
|
|
5031
|
+
These views contain no message body/title, reason, broker URL, key material or delivery
|
|
5032
|
+
history. Use the existing queue inspection workflow for delivery uncertainty.
|
|
5033
|
+
The metadata-only views never show inbox bodies; use the separate person-only
|
|
5034
|
+
CLI below.
|
|
5035
|
+
|
|
5036
|
+
## Person-only room inbox (#446 X5b)
|
|
5037
|
+
|
|
5038
|
+
List metadata for all rooms or one named room:
|
|
5039
|
+
|
|
5040
|
+
```sh
|
|
5041
|
+
ahub room inbox [<name>] [--json]
|
|
5042
|
+
```
|
|
5043
|
+
|
|
5044
|
+
The list contains local inbox ids, room/sender metadata, times, byte counts and
|
|
5045
|
+
states, never body previews. One operand is always a room name, even `show`,
|
|
5046
|
+
`done` or `discard`. Only an action plus an exact local UUID acts on a message:
|
|
5047
|
+
|
|
5048
|
+
```sh
|
|
5049
|
+
ahub room inbox show <id>
|
|
5050
|
+
ahub room inbox done <id>
|
|
5051
|
+
ahub room inbox discard <id>
|
|
5052
|
+
```
|
|
5053
|
+
|
|
5054
|
+
Only list accepts `--json`; none of these commands accepts `--confirm` or asks
|
|
5055
|
+
for a fingerprint. All inbox forms refuse agent shells, conductors and
|
|
5056
|
+
`--live-test`. They also refuse during recovery or a Bus hold. Show prints one
|
|
5057
|
+
sanitized, terminal-safe untrusted body and does not mark it done. Done/discard
|
|
5058
|
+
immediately purge the body and keep a metadata stub. Their acknowledgement is
|
|
5059
|
+
sent before any background room retry. Expiry does the same;
|
|
5060
|
+
there is no archive or read-after-done command. Capacity released by done/discard
|
|
5061
|
+
or a real TTL purge wakes already-deferred X4 traffic; no new receipt is sent for
|
|
5062
|
+
the purged entry, and idle ticks never poll queued plaintext. Dashboard, status, logs, memory,
|
|
5063
|
+
models and observers never receive the body.
|
|
5064
|
+
|
|
5065
|
+
The sender's **completed (inbox)** receipt means durable admission, not that the
|
|
5066
|
+
person read or completed the request. Reading, disposing or expiring the local
|
|
5067
|
+
copy never changes that receipt. Inbound session counts increment once at
|
|
5068
|
+
admission. Older X4 senders cannot parse the new inbox receipt reasons and stay
|
|
5069
|
+
uncertain; do not treat mixed-version traffic as verified success.
|
|
5070
|
+
|
|
5071
|
+
Limits are 100 unread messages per room, 64 KiB decoded body per message and
|
|
5072
|
+
4 MiB canonical payload bytes retained globally, including JSON overhead and
|
|
5073
|
+
escaping (the displayed size remains decoded UTF-8 body bytes). The unchanged 64 KiB entire-wire cap
|
|
5074
|
+
leaves less than 64 KiB for a sendable body. Schema 4 starts the 30-day TTL at the
|
|
5075
|
+
first durable local crypto arrival, not remote time or restart. Legacy pending
|
|
5076
|
+
person traffic without that age gets fixed `failed_safe/inbox_age_unavailable`;
|
|
5077
|
+
native peer traffic is unchanged. Full, oversized or already-expired person
|
|
5078
|
+
admissions return fixed `inbox_full`, `inbox_oversize` or `inbox_expired` refusals.
|
|
5079
|
+
|
|
5080
|
+
Up to 1,000 settled stubs per room remain for 30 days after settlement, oldest
|
|
5081
|
+
first. Unread messages and their admission identities are protected, along with
|
|
5082
|
+
unsettled identities at the shared 10,000-per-room application cap. One loaded
|
|
5083
|
+
daemon expiry hook runs every 30 seconds, including for left rooms; it creates
|
|
5084
|
+
no empty room database or keys. Bodies remain plaintext in the protected local
|
|
5085
|
+
store, with no new encryption-at-rest or secure-erasure guarantee.
|
|
5086
|
+
|
|
5087
|
+
Lists are complete up to 1,100 rows. If an all-room list exceeds that limit, the
|
|
5088
|
+
CLI explicitly asks you to choose a room name; it never silently truncates.
|
|
5089
|
+
ponytail: pagination is later work; a filtered room's 100 unread plus 1,000
|
|
5090
|
+
settled stubs fit the current response ceiling.
|