@staix/agent-hub 0.24.0 → 0.26.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.
Files changed (114) hide show
  1. package/CHANGELOG.md +23 -0
  2. package/README.md +35 -8
  3. package/docs/agent-notes/README.md +12 -0
  4. package/docs/agent-notes/adapters.md +2 -1
  5. package/docs/agent-notes/bus.md +1 -0
  6. package/docs/agent-notes/daemon.md +18 -3
  7. package/docs/agent-notes/tasks.md +12 -4
  8. package/docs/agent-notes/tests.md +3 -1
  9. package/docs/dogfood.md +16 -2
  10. package/docs/memory.md +1 -1
  11. package/docs/operations.md +264 -14
  12. package/docs/profiles.md +2 -0
  13. package/docs/quickstart.md +23 -6
  14. package/docs/security.md +198 -3
  15. package/docs/specs/2026-09-19-agent-hub-design.md +25 -1
  16. package/docs/specs/2026-09-19-multi-project-design.md +12 -1
  17. package/docs/specs/2026-11-01-research-campaigns-design.md +354 -0
  18. package/docs/verification/2026-11-01-0.25.0.md +91 -0
  19. package/docs/verification/2026-11-01-0.26.0.md +54 -0
  20. package/docs/verified.json +45 -17
  21. package/package.json +4 -3
  22. package/plugins/agent-hub/.claude-plugin/plugin.json +1 -1
  23. package/plugins/agent-hub/server.js +5294 -4797
  24. package/src/adapters/channel-peer.ts +4 -2
  25. package/src/adapters/claude-channel.ts +29 -6
  26. package/src/adapters/definitions/builtin-metadata.ts +6 -6
  27. package/src/adapters/definitions/native-cli-doctor.ts +30 -0
  28. package/src/adapters/definitions/qwen.ts +2 -0
  29. package/src/adapters/jcode.ts +3 -0
  30. package/src/adapters/registry-types.ts +2 -0
  31. package/src/adapters/registry.ts +2 -1
  32. package/src/cli/acon-register.ts +29 -0
  33. package/src/cli/conductor-recovery.ts +3 -2
  34. package/src/cli/handoff-args.ts +27 -0
  35. package/src/cli/handoff-output.ts +25 -0
  36. package/src/cli/help.ts +11 -8
  37. package/src/cli/identity.ts +7 -1
  38. package/src/cli/main.ts +138 -30
  39. package/src/cli/output.ts +8 -5
  40. package/src/cli/recovery-package.ts +94 -4
  41. package/src/cli/reset.ts +14 -4
  42. package/src/cli/runtime-version-warning.ts +28 -0
  43. package/src/cli/schedule-output.ts +8 -4
  44. package/src/cli/start-agents-run.ts +125 -0
  45. package/src/cli/study-output.ts +71 -0
  46. package/src/cli/study.ts +58 -0
  47. package/src/cli/task-output.ts +6 -0
  48. package/src/cli/upgrade-interactive.ts +4 -4
  49. package/src/cli/upgrade-runtime.ts +105 -57
  50. package/src/cli/upgrade.ts +90 -28
  51. package/src/hub/autonomy-defaults.ts +32 -7
  52. package/src/hub/board.ts +993 -8
  53. package/src/hub/budget.ts +3 -4
  54. package/src/hub/channel-project.ts +61 -0
  55. package/src/hub/child-process.ts +4 -3
  56. package/src/hub/conductor.ts +3 -4
  57. package/src/hub/config-apply.ts +13 -2
  58. package/src/hub/config-edit-host.ts +1 -0
  59. package/src/hub/config-tier.ts +3 -0
  60. package/src/hub/config-trust.ts +10 -2
  61. package/src/hub/control-client.ts +39 -10
  62. package/src/hub/daemon.ts +326 -25
  63. package/src/hub/db-schema.ts +143 -0
  64. package/src/hub/delivery-journal.ts +3 -4
  65. package/src/hub/envelope.ts +2 -0
  66. package/src/hub/events.ts +4 -1
  67. package/src/hub/execution-budget.ts +15 -4
  68. package/src/hub/folder-trigger.ts +3 -4
  69. package/src/hub/handoff.ts +126 -0
  70. package/src/hub/hub-tools.ts +12 -2
  71. package/src/hub/invocations.ts +3 -4
  72. package/src/hub/lifecycle.ts +87 -29
  73. package/src/hub/manager.ts +110 -87
  74. package/src/hub/no-writes.ts +1 -1
  75. package/src/hub/orca-env.ts +19 -0
  76. package/src/hub/path-policy.ts +12 -8
  77. package/src/hub/project-runtime-state.ts +139 -0
  78. package/src/hub/project-runtime.ts +62 -0
  79. package/src/hub/project.ts +14 -5
  80. package/src/hub/protocol-window.ts +19 -0
  81. package/src/hub/provenance.ts +30 -0
  82. package/src/hub/recovery-store.ts +102 -12
  83. package/src/hub/registry.ts +5 -0
  84. package/src/hub/research-campaign.ts +265 -0
  85. package/src/hub/research-experiments.ts +474 -0
  86. package/src/hub/research-runner.ts +172 -0
  87. package/src/hub/restart.ts +35 -0
  88. package/src/hub/runtime-capture-prune.ts +415 -0
  89. package/src/hub/runtime-prune-health.ts +112 -0
  90. package/src/hub/schedules.ts +213 -13
  91. package/src/hub/sdlc-merge.ts +7 -0
  92. package/src/hub/sdlc-queue.ts +13 -1
  93. package/src/hub/session-identity.ts +49 -0
  94. package/src/hub/settings.ts +4 -0
  95. package/src/hub/snapshots.ts +3 -4
  96. package/src/hub/start-agents.ts +44 -0
  97. package/src/hub/task-approval.ts +23 -0
  98. package/src/hub/tasks.ts +1005 -34
  99. package/src/hub/ui.ts +15 -6
  100. package/src/hub/usage-rollup.ts +3 -4
  101. package/src/memory/capture.ts +2 -2
  102. package/src/pi/tool-receipts.ts +3 -4
  103. package/src/ui/index.html +5 -3
  104. package/src/ui/office-space.ts +118 -0
  105. package/src/ui/plugin.ts +293 -0
  106. package/templates/AGENTS.block.md +1 -0
  107. package/templates/profiles/general.json +1 -1
  108. package/templates/profiles/research.json +4 -4
  109. package/templates/profiles/writing.json +1 -1
  110. package/templates/roles/research/analyst.md +1 -0
  111. package/templates/roles/research/fact-checker.md +1 -0
  112. package/templates/roles/research/research-lead.md +1 -0
  113. package/templates/roles/research/researcher.md +1 -0
  114. package/templates/roles/research/statistician.md +1 -0
package/docs/dogfood.md CHANGED
@@ -10,6 +10,11 @@ agent-hub is developed with agent-hub. This page lists every feature, whether it
10
10
 
11
11
  | Feature | State here | Measured by |
12
12
  | --- | --- | --- |
13
+ | Distinguishable sessions and sender provenance (#1036, `session_identity`) | on after release and upgrade: a connection that names a peer id but is not that peer's current session is a distinct `<peer>~<origin>` session that can send and read, holds none of the peer's roles and replaces nobody; the channel plugin binds to its own project; every envelope and attach event records the connection. Nothing is refused to a connection that worked before. The recovery label `session_identity` applies once an unsettled delivery holds a session's message | `grep '"session":true' .agenthub/state/events.jsonl` lists every message a foreign session sent, with its minted id and no path or text; a recipient's header shows `from "claude~<id>"` for it; test/identity-provenance.test.ts (AC1, AC3, AC4, recovery label) and test/identity-channel-project.test.ts (AC2, real plugin against a new and an old hub) hold it. Not authentication: a same-user process can always act as the user |
14
+ | Cross-project task handoff with state follow-back (#604, `task_handoff`) | on after release and upgrade: `ahub task handoff` and `hub_task_handoff` register work for another registered project that runs agent-hub on this machine as an ownerless proposal on its board; the requesting peer is told each state change as a `kind: task` message. Nothing is queued, and the origin has no authority there | `ahub board --handoffs` lists what this project handed over with the last state each target reported; `ahub board` and `ahub task show` in the target name the origin project and peer; test/handoff-daemon.test.ts runs two real daemons (AC1 to AC5 and the recovery label), test/handoff-core.test.ts and test/handoff-board.test.ts hold the pure core and the tables |
15
+ | Staged project upgrades (#1039) | off: available for the person to upgrade projects independently; this development hub is not upgraded by a fixture | test/published-protocol-compat.test.ts uses the published 0.25.0 client/plugin and reverse daemon; test/staged-upgrade.test.ts checks independent holds and scoped effects; test/staged-runtime-daemon.test.ts checks a real release-root daemon after mutable install removal; test/board-schema-guard.test.ts checks named pre-effect refusal and WAL metadata; `test/project-runtime.test.ts` and `test/project-runtime-lifecycle.test.ts` cover updated resolver retention/refusal and real lifecycle/dashboard routing with synthetic captured daemons; `test/runtime-capture-prune.test.ts` covers references, bounded retention and publication races; `test/upgrade-stopped-project.test.ts` verifies no preservation for stopped projects. These fixtures do not prove published pre-0.26 CLI behavior |
16
+ | Approval head binding and re-review (#1047) | off until release and project upgrade; fixture evidence only, this PR does not change the running hub | `test/task-review-refile.test.ts` covers owner re-submit, reviewer withdrawal, legacy verdict heads, merged/cancelled refusals and held-screen races; `test/sdlc-queue.test.ts` and `test/sdlc-merge.test.ts` reject stale approval at queue and dispatch. `ahub task show <id> --json` exposes `approval.head`, `latestHead`, `mismatch` and `current`; verdict history retains `reviewedHead` |
17
+ | Default agent set started by `ahub up` (#608, `start.agents`) | off here: this repository's agents are started by their own terminals; it is a machine-local setting and a committed config cannot turn it on. On for a person who runs `ahub setup` and `ahub up` on a project that lists agents. | each `ahub up` prints one line per agent (started, already attached, or the refusal with the manual command); the `peer_start` replies in `hub.log` and the terminal provider's recorded starts show whether a listed agent attached, and `ahub status` shows its state after |
13
18
  | `ahub down` (counterpart of `ahub up`) | on: an alias of `ahub kill` for stopping this project's hub | the person stops the hub with `ahub down` and `ahub status` then reports it stopped; test/identity.test.ts keeps it person-only (`console`) and labelled `down`, and test/help.test.ts holds its help entry |
14
19
  | Encrypted rooms (#446 X1) | off; source-only crypto/wire, with no broker, room link, invitation CLI or delivery enabled | real WebCrypto vectors and negative wire fixtures; atomic memory fake ports establish ordering only; durable X3 state and the X7 two-machine person receipt remain unverified |
15
20
  | Blind room broker (#446 X2) | off; standalone source/fixture work, no public TLS service or hub room link configured here | fake-client auth/fan-out/revocation/restart, ciphertext-only child-process canary, owner admission/offline room reset, queue caps/TTL/FIFO/migration, safe directory modes, and person-only CLI tests; live proxy/account/X7 receipts remain unverified |
@@ -24,7 +29,7 @@ agent-hub is developed with agent-hub. This page lists every feature, whether it
24
29
  | Headless Claude failure stages (task 551, #845) | source and fake-native fixtures; installed/native/account proof unverified until release and restart | fixed stage and host metadata in failed outcomes/logs; private stub, raw-canary absence, stale/foreign launch and bounded-probe cases in `test/claude-failure-stage-daemon.test.ts` |
25
30
  | Optional model for a headless Claude implementer (#844) | on after the release and once the person sets `model` on `instances.claude-impl` in `config.local.json` (a hub daemon restart applies it); unset it changes nothing | `ahub status` shows `model source` on the instance row; the native argv carries `--model <value>` on every turn and none when unset (test/claude-implementer.test.ts, test/claude-implementer-daemon.test.ts); a tracked-file or non-implementer declaration is refused or ignored with a reason (test/instances.test.ts, test/jcode-instances.test.ts) |
26
31
  | Remote isolated check transport (#793) | off here; fake checks, a bounded local Docker transport receipt and a local run of the lockfile-keyed dependency volume; person opt-in, SSH and the live receipt follow separately | remote tests capability unavailable on failed SSH/Docker/image/transfer probe; one networked install per new lockfile key, none on later runs, the check mounting the volume read-only with no network; exact Git receipt, non-root hardened argv, zero Mac dependency copies and confirmed Docker cleanup before lease release; Darwin tar ignores PATH shims, probe warnings do not pollute JSON, bounded probes do not become unknown task-run orphans |
27
- | Task board, routing, roles | on (claude planner, reviewer and implementer; codex, jcode, agy implementers) | board history: lead time from propose to approve, review rounds, escalations |
32
+ | Task board, routing, roles | on (claude planner, reviewer and implementer; codex, jcode, agy implementers) | board history: lead time from propose to approve, review rounds, escalations; owner new-head re-files and stale-verdict refusals (#1000 fixtures; active after upgrade) |
28
33
  | Readable `sdlc` output by default (#814 T1, #899) | on after the release: `ahub sdlc status\|explain\|queue\|release\|metrics\|costs\|authorize-release\|merge` print tables and labelled fields here; since #899 `ahub sdlc merge` (guarded merge, dry-run included) prints its report for a person by default and the JSON document only with `--json`, and `ahub doctor --orphans --kill` likewise prints the stopped pids by default and the hub's reply only with `--json`; scripts pass `--json` | the person reading `ahub sdlc status`, `sdlc metrics` and a `sdlc merge --dry-run` report without `--json`; `test/sdlc-output.test.ts` holds every `sdlc merge` outcome's readable default with `--json` byte for byte the pre-#899 output and the exit code unchanged; `test/conductor-cli.test.ts` holds the same for `doctor --orphans --kill`; `test/cli-json-guard.test.ts` lists the CLI paths that still print JSON by default (since #899 only the named data formats: its not-yet-converted list is empty) |
29
34
  | Readable restart, upgrade, recovery and model output by default (#814 T3) | on after the release: `ahub restart --dry-run`, `upgrade --dry-run`, `recovery status`, `models start`, `queue resolve` and `budget execution configure\|disable` print text here; scripts pass `--json` | the person reading a dry-run plan and a recovery status without `--json`; `test/cli-json-guard.test.ts` lists the CLI paths that still print JSON by default (target: only named data formats) |
30
35
  | Conductor-bound recovery continuation and task unassign (task 535, #900) | source/fixture coverage only; whether this repository's running installed daemon advertises the matching `conductor_recovery` feature is unverified | fake source/target authorization, same-actor and policy-mismatch checks, task-unassign actor/suppression fixtures; no native or real-account receipt; same-user key and record replacement remains outside the host boundary |
@@ -70,7 +75,7 @@ agent-hub is developed with agent-hub. This page lists every feature, whether it
70
75
  | Planner plan review (#481 S9) | advise | plans reviewed before build |
71
76
  | Planner board-changed notice (#758) | on here, with the lifecycle in `advise`: `claude` is the planner, so a card another peer proposes or re-plans reaches it once at task weight, and its own writes, state moves, reviews and assignments cost it no turn | per planner and week: `task`-kind hub envelopes whose body starts `The board changed` (target: one per planning change, none after the planner's own write), the same envelopes at `fyi` priority (recorded, no turn: at most one per 10 minutes), and the count still queued at once (target: at most one). Read from `ahub log` and the event log; the 12 copies in 44 minutes seen on 2026-10-24 are the baseline |
72
77
  | Planner capacity and dependency notices (#785 P1a) | source/fixture coverage; active after release and hub upgrade | write-triggered `capacity_released` and `backlog_ready` lines in `Planner work ready:` task envelopes; at most one queued envelope per effective planner, peer/WIP/card ids only; no automatic pull |
73
- | Release preparation and guarded tag (#787 P3b) | scratch-repo fixtures with a local bare remote; the real history was dry-run in a disposable clone; first real use is the next release | `scripts/release-prepare.ts` run on a `release/<version>` branch and a release PR that carries the version, CHANGELOG, report and stamps; `scripts/release-tag.ts` refusing without the person's consent and pushing one tag after it |
78
+ | Release preparation and guarded tag (#787 P3b) | scratch-repo fixtures with a local bare remote; the real history was dry-run in a disposable clone; first real use is the next release | `scripts/release-prepare.ts` run on a `release/<version>` branch and a release PR that carries the version, CHANGELOG, report and stamps; `scripts/release-tag.ts` refusing without the person's consent and pushing one tag after it; native version-named receipts retained while dated release reports are excluded (#1004 fixture) |
74
79
  | Opt-in auto pull (#785 P1c) | source/fixture coverage and a real daemon test; off by default, active only where a person sets `tasks.auto_pull` in `config.local.json` | `backlog pulled` history entries with `by: hub` and an `auto_pull:` note; at most one per capacity release; no pull with the setting off, for unreserved, PII, pinned, frozen or dependency-blocked cards |
75
80
  | Opt-in assignment queue backpressure (#800) | off by default; this change enables no live repository policy | `ahub route explain` and status queue-count/age/unknown reasons beside WIP; bounded summary reads, held-offer release, post-await offline/not-attached origin retention with plan-policy handoff, and role-scoped override regressions; no delivery disposition or routing-template change |
76
81
  | Per-head CI watcher and `ci_completed` (#785 P1b) | source/fixture coverage with a fake gh; active after release and hub upgrade | one `ci_completed:` task notice per head to owner and reviewer, a premature review re-offered once green; watchers started per `hub_task_done` head and gone at the result, deadline, newer head, verdict or shutdown; no gh read outside a watcher |
@@ -95,6 +100,7 @@ agent-hub is developed with agent-hub. This page lists every feature, whether it
95
100
  | Source registry, `ahub source add\|list` (#508 AC1, AC2, AC5 CLI) | on after release; local (no daemon needed for the person); `add` is the person's or the configured conductor's (#933, admitted and audited by the hub); `hub_source_read` adds bounded, screened snapshots and private stubs; plan validation and the dashboard list are later slices | `ahub source list` rows (id, size, private, origins); `sources/` under the hub state dir; a refusal in the CLI audit for an agent shell |
96
101
  | Intake drafts, `ahub intake` (#511 AC1 CLI, AC2 to AC4) | on after release; person-only except `approve`, which the configured conductor may run too (#933); a draft is held until `ahub intake approve`, so nothing changes for work that does not use it; the dashboard text box is a later slice | drafts opened, approved and discarded in `ahub backlog list` and the board history (`draft approved`, `intake unsplit` with its reason); the share of drafts the planner or hub model split without a reason on file |
97
102
  | Machine-local schedules (#512 AC1, AC3) | on after release for a person who adds `schedules` to `config.local.json` (`every` `<n>m\|h\|d`, 15 minute minimum); nothing runs without it, and a committed config cannot set it | `ahub status` shows one `schedule <id>` line (next run, last run, task, skipped runs); the backlog cards the hub proposed; no second card while the first is open |
103
+ | Calendar-anchored schedules, `cron` and `tz` (#1017) | on after release for a person who writes `cron` (five fields) with an optional `tz` in `schedules`; `every` is unchanged and a project without `cron` sees no change | `ahub schedule list` and `ahub status` show the expression and the next run in the schedule's zone; one task per fire, not one per missed fire after downtime; a skipped or repeated local time on a change day runs once; a too-frequent expression refused at load with the entry named |
98
104
  | Schedule commands and dashboard next run, `ahub schedule list\|run\|pause\|resume` (#512 AC4, AC3 UI) | on after release for a person who configured `schedules`; `run`, `pause` and `resume` are the person's or the configured conductor's (#933), nothing changes for a project with none; the dashboard table is read-only | `ahub schedule list` rows and the dashboard Schedules table (state, next run, last run, task); `paused` and manual runs in the board history of the task a run makes; a pause that is never replayed as a run on resume |
99
105
  | Folder triggers, `folder_triggers` (#512 AC2) | on after release for a person who adds `folder_triggers` to `config.local.json`; the first poll records only, nothing runs without it, and a committed config cannot set it | tasks whose first detail line is `Folder trigger: <id>`; `folder_seen` rows and `last_error` in hub.db; sources registered by a trigger in `ahub source list`; the share of new files that produced exactly one task |
100
106
  | Jcode peers told their hub tools are deferred MCP tools (#912) | on after release for every Jcode peer; nothing to configure | the first prompt of a Jcode session starts with the guidance; a fresh Jcode v0.99.x peer asked to reply with `hub_send` finds and calls it in its first turn (AC1, a live leg the conductor runs with the person after merge) |
@@ -139,6 +145,12 @@ agent-hub is developed with agent-hub. This page lists every feature, whether it
139
145
  | Cross-hub read invocations (#446 X6) | off until both persons expose peers and independently pin the exact capability/digest/read class; discovery is manual; two-daemon broker/crypto fixtures are the verification target, live X7 receipts remain unverified | source/target invocation ids, request admission versus capability result and result transport outcomes; needs_review counts and no duplicate native dispatch |
140
146
  | Dogfood scorecard (#687) | on after release; read-only weekly receipt | `ahub report --dogfood --since 7d --json`; bounded evidence and explicit unknowns |
141
147
  | Claim verdicts and fact_check provenance (#901) | source/fixture coverage; the live research pilot (a person entering verdicts with `ahub task claim`) remains unverified | `ahub report --dogfood` research rows: `claims.reachedPersonVerdict`, per-slot `personVerdict`/`modelLabel`/`agreement`, `factCheck.selection`/`passed`/`failed`/`missingKeysPerRun`; null until a verdict or a keyed check is recorded |
148
+ | Research campaigns (#930 T1) | source and CPU-only fixtures; no real campaign here; the public study CLI and tools are the T3 row below | draft versus child counts, stable unit/task ids across approved revisions and restart, intake reuse, stale role/source/revision refusals, private views, rollback and research_records recovery floors; no live/model receipt claimed |
149
+ | Study CLI, tools and readback (#930 T3, #601) | Fake peers and a real daemon over its control socket, plus the real `ahub` process against that daemon; no native peer or live model has run a campaign here, so fake versus native evidence stays separate (the `research-live-example` claim in docs/profiles.md is still open) | `test/study-daemon.test.ts` (person's draft, approve and close; no agent reaches an approve or close op; planner drafts and cannot approve; read-only show; a private source shows no source id, question, scope or path; handoff only from the bound owner with every missing field named; the three-source, two-peer example moving from none to incomplete to complete), `test/output-cli.test.ts` (parse before connecting, `--json` byte for byte), `test/study-guidance.test.ts` (guidance names only published tools) |
150
+ | Research dispatch and coverage (#930 T2) | Source/isolated fixtures only; no live research campaign | `test/research-dispatch.test.ts` and projection/recovery fixtures: no expired/revised dispatch, named-budget coverage, handoffs and shared ledger |
151
+ | Experiment records (#932 T1) | Source and isolated fixtures only; no live research attempt | Schema, storage, Tasks and recovery fixtures: declared/host provenance stays distinct, all attempts counted, replay and privacy fences |
152
+ | Experiment comparisons and protocol approval (#932 T2) | Fixture only; no scientific reproduction claimed on this repository | `test/research-comparison.test.ts`, `test/research-comparison-tasks.test.ts`, `test/research-protocol-cli.test.ts` and recovery tests verify incompatibility, fresh exact-hash approval, causal post-hoc context and restart identity; failed/cancelled attempts stay counted. |
153
+ | Registered runner receipts (#1018) | source and test-key fixtures only; no external runner or live scientific attempt has been admitted here | `test/research-runner-tasks.test.ts` covers signed binding, immutable replay, revocation/readback, privacy and schema-2 rollback; parser/projection fixtures cover missing fields and sealing negatives; config/recovery fixtures cover person-only trust roots, raw-key floors and integrity. `evidence.runnerAttested` stays separate from declared/host-measured values and reports current key status |
142
154
  | Dirty-submodule notices (#798) | source/fixture coverage; the live writing/general pilot (a workspace with 26 submodules) remains unverified | `ahub turns` shows `<path> (changed, contents unobserved)` for a turn that only edited a submodule; the D1 notice at `hub_task_propose` and `hub_task_accept`; `ahub undo` names what it cannot restore; a project without `.gitmodules` makes the same git calls per turn (counting-shim test) |
143
155
  | Profile scorecard (#799) | source/fixture coverage; live writing/general/research pilot remains unverified | ahub report --dogfood profile rows; pure and reader dev byte goldens, private-text exclusion, research declarations/unknown verdicts and per-source unknown tests; anonymous weekly receipt templates |
144
156
  | Domain packs (#318/#322, #513) | off here: this repository has no pack in `.agenthub/roles`, so `ahub init --profile <pack>` is not used on it; live adoption in another project is unverified | the `ahub init --profile <pack>` preview and apply, the pack validator and the shipped-pack catalogue, covered by test/packs.test.ts (40 mutants killed); adoption would show as a `.pack.json` under `.agenthub/roles` and a `profile` in `.agenthub/config.json` of the adopting project, read from `ahub init --profile` runs. The writing-pack, analysis and ops packs load with shipped roles; test/domain-packs.test.ts executes all three standalone checks on synthetic passing/refusing fixtures. Their notes remain advisory and no check is auto-selected; live domain-workflow use remains unverified |
@@ -152,10 +164,12 @@ agent-hub is developed with agent-hub. This page lists every feature, whether it
152
164
  | Console and dashboard | on; #719 pending-history reads and bounded native launch retries take effect after release | control-request latency, connection timeouts and launch retry notices; daemon CPU/RSS samples, measured separately from lifetime CPU |
153
165
  | Usage view (#683) | CLI (`ahub usage`, U2), console Usage tab `7` (U3) and dashboard Usage view (U4) shipped | its own panels |
154
166
  | Kimi usage from its wire log (#285 phase 2) | on after the release when `budget.kimi_tokens_5h` is set; this repository sets it | `tokens` and `usage` (`kimi_wire`) events per Kimi turn, the `tokens` window in `ahub budget`, and the count of `kimi usage unknown` notices; the live check against Kimi's `/usage` (AC11) stays opens |
167
+ | Doctor memory-capture rows from agent-mem setup state (#1008) | on after release; `ahub doctor` runs here and reads `amem setup <tool> --json` (a preview that writes nothing) for `codex` and `kimi`; no dotfiles text and no `dot ai memory status` call | `memory capture: codex` and `memory capture: kimi` rows read ok (set up), warn with `amem setup <tool> --apply` (pending), warn with `client missing` (the native CLI is not installed) or unknown (no `amem`, a refusal or unparseable output); the Codex row notes the native `/hooks` review; a fixture run proves `dot` is never called |
155
168
  | Budget window kinds and notes (#285 phase 4) | on after the release; `ahub budget`, the console peer detail, the dashboard Usage budgets row and `ahub doctor` (`kimi usage source`) run here | each window shows its kind and source, a peer without a number says why (Pi, local and Kimi included); a manual `ahub budget set` value outranks the Kimi count at the gate; the doctor row reads ok while the wire log counts |
156
169
  | Settings reload | on | reloads applied, pending restarts |
157
170
  | Config editing from the console and dashboard (#689) | on after release; `ahub settings edit` and console key `c` run here, the dashboard Structure group needs `ahub ui --settings`; executable keys stay console only | `config_edit` events (edits applied by tier and source), refusals shown with the loader reason, restarts left pending |
158
171
  | Upgrade and recovery | on, per release | upgrade receipts in `docs/verification/` |
172
+ | acon console plugin (#935) | off here until a person runs `ahub setup` after the release and `acon ui` on this machine; the plugin registers per agenthub home, not per repository | `acon plugins` shows `ahub verified`; the console's agent-hub panel shows this repository's live board, and an approval answered from the console inbox appears in hub.log as `answered option allow_once by dashboard`. Space (#1012): adopted with the plugin, no separate switch; acon's Space view (Flat, Room, 3D) shows this repository's peers as actors (state and marks matching the dashboard's office), the board columns as queue/work/review/done zones with the same counts, and overlap links plus message arcs (broadcasts to every other desk) as links; clicking an actor or card opens this project's agents or board panel |
159
173
  | Agent CLI version policy and trust (#691) | on after the release: Codex 0.163.0 verified by the [2026-10-22 receipt](verification/2026-10-22-codex-reviewer-isolation.md) and 0.164.0 by the [2026-10-24 receipt](verification/2026-10-24-codex-0164-reviewer-isolation.md); Kimi 2.3.0 and Qwen 0.26.0 verified by the [model-free receipt](verification/2026-10-22-kimi-qwen-compat.md), Kimi 2.3.1 by the [2026-10-25 model-free receipt](verification/2026-10-25-kimi-231-reviewer-compat.md) and 2.3.2 by the [2026-10-26 one](verification/2026-10-26-kimi-232-reviewer-compat.md), with account/provider live legs unverified; agy 1.4.4 in-line | model-free isolation receipts, `ahub agents` verdicts, versions trusted per machine, lead time from an `agent-compat` issue to a receipt |
160
174
  | Qwen's own TUI over dual output (#286 Part 3) | off here: this repository's hub runs no Qwen peer (roster.qwen is off), and the limits (a hub deny ends the turn without an answer; a stalled turn is stopped only with Esc in the Qwen terminal) are for the person at that terminal to accept. Verified on Qwen 0.26.1 under an isolated HOME by the [2026-10-25 receipt](verification/2026-10-25-qwen-dual-output-t0.md); the live account leg is the person's | `test/qwen-dual.test.ts` against a fake dual-output Qwen; in real use `ahub status` stalled/detached notices per peer and the `needs_review` count of Qwen deliveries (an Esc or an exited terminal ends a turn that way; a deny completes) |
161
175
  | Benchmarks (#251, #637) | off: `bench.enabled = false`. The task-routing gate (#637 part 1) is on in CI for every PR: `routing-receipt` fails a change to `templates/routing.toml` without a matching receipt; the model-routing gate (#637 part 2) is the second step of the same workflow: `scripts/check-model-routing-receipt.ts` fails a change to the model-routing keys without a matching `-modelrouting-` receipt, and wants a by-hand `--live` half when an alias or judge moves | bench receipts; `docs/verification/*-routing-*.md` and `*-modelrouting-*.md` receipts; a `routing-eval` check run on every PR with `eval_pass_rate=` and `eval_suite=` in its title (name it as `eval_check` to make it the eval gate); `bun scripts/benchmarks/routing-gate.ts` and `model-routing-gate.ts` print the default's numbers (100% on both splits); the live half (served model ids) has not been run against a gateway yet |
package/docs/memory.md CHANGED
@@ -1,6 +1,6 @@
1
1
  # Hub memory integration
2
2
 
3
- Agent-hub consumes the exact-pinned first-party [`@staix/agent-mem`](https://github.com/STAIxBWLB/agent-mem) library (current pin `0.15.0`, initial pin `0.1.1`). The package owns portable storage, tiers, graph, goals, sync, import and raw commands. Agent-hub owns caller authority, current project privacy policy, task lifecycle integration and presentation. Claude-mem is an optional, explicit import source only; runtime recall and capture make no worker HTTP requests.
3
+ Agent-hub consumes the exact-pinned first-party [`@staix/agent-mem`](https://github.com/STAIxBWLB/agent-mem) library (current pin `0.18.0`, initial pin `0.1.1`). The package owns portable storage, tiers, graph, goals, sync, import and raw commands. Agent-hub owns caller authority, current project privacy policy, task lifecycle integration and presentation. Claude-mem is an optional, explicit import source only; runtime recall and capture make no worker HTTP requests.
4
4
 
5
5
  ## Portable core and data directory
6
6
 
@@ -1,6 +1,6 @@
1
1
  # Operations guide
2
2
 
3
- This guide describes ahub 0.21.0 and control protocol 16. Live verification
3
+ This guide describes ahub 0.26.0 and control protocols 16 through 17. 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
@@ -557,7 +557,7 @@ and so is every help entry the gate has no class for yet;
557
557
 
558
558
  | Command | Class | Why |
559
559
  | --- | --- | --- |
560
- | `ahub doctor [--json] [--full] [--color=...]` | Allowed | a read-only check of tools, daemon, plugin, gateway, models and memory |
560
+ | `ahub doctor [--json] [--full] [--color=...]` | Allowed | a read-only check of tools, daemon, plugin, gateway, models and memory; the `memory capture: codex` and `memory capture: kimi` rows come from agent-mem's setup state (`amem setup <tool> --json` on PATH, a preview that writes nothing), as set up, `amem setup <tool> --apply` needed, client missing, or unknown when `amem` is missing, refuses or prints no setup plan (#1008) |
561
561
  | `ahub doctor --stale-anchors [--json]` | Allowed | a bounded read-only report of memory symbol anchors at current HEAD |
562
562
  | `ahub queue list [--peer <id>] [--json] [--full] [--color=...]` | Allowed | public queue metadata only; held bodies and resolution remain conductor operations |
563
563
  | `ahub roster [--json] [--color=...]` | Allowed | public roster status; add/remove retain conductor admission |
@@ -573,6 +573,7 @@ and so is every help entry the gate has no class for yet;
573
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
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
575
  | `ahub room list [--json]`, `ahub room status [<name>] [--json]` | Allowed | room metadata only; every other `room` form, the inbox included, stays Console |
576
+ | `ahub study show [<id> \| --after <id>] [--json] [--color=...]` | Allowed | the same read as `hub_study_show`: a private campaign, task or source shows ids, counts and states only; `study draft\|revise\|approve\|approve-protocol\|close` stay Console (#930) |
576
577
 
577
578
  `backlog list`, `release list` and `sdlc costs|metrics` retain their existing
578
579
  planner/person policy. Their CLI forms stay Console for ordinary agents; a
@@ -926,6 +927,37 @@ Incompatible or unverified processes are shown explicitly and are never killed b
926
927
  PID-name matching. See the operations guide and the
927
928
  [multi-project specification](specs/2026-09-19-multi-project-design.md).
928
929
 
930
+ ## acon console plugin (#935, #1012)
931
+
932
+ `ahub setup` registers agent-hub as a plugin of the family console
933
+ ([agent-console](https://github.com/STAIxBWLB/agent-console), `acon ui`). It writes
934
+ `$AGENTHUB_HOME/console/plugins/ahub.json` (default `~/.agenthub/console/plugins/ahub.json`;
935
+ the directory 0700, the file 0600, replaced atomically) naming the installed module
936
+ `@staix/agent-hub/ui-plugin` and its sha256, and prints `acon plugin registered`, `acon plugin
937
+ up to date` or why it was not registered. acon reads the file at its next start and marks the
938
+ plugin stale when the module changed after setup, so run `ahub setup` again after each upgrade.
939
+
940
+ - **Panels.** The plugin's panels are the dashboard's seven views, and the panel is the `ahub ui`
941
+ page itself, following acon's theme. It reaches project hubs only through the manager's own
942
+ listing and forwarding (`ahub ui --all`): registered project and instance ids, the manager's
943
+ closed action list and the seven view names. There is no new protocol or action.
944
+ - **Inbox.** acon's inbox lists each running project's pending approvals (Allow once / Deny,
945
+ answered with the hub's own `permit` and an option the request offers) and in-review tasks whose
946
+ reviewer is you (Open board). A local-worker or Pi approval stays deny-only there, as on the
947
+ dashboard: answer it with `ahub permit` after `ahub tail`.
948
+ - **Confirmation.** `stop`, `permit`, `send`, the settings writes (`setting`, `setting_undo`,
949
+ `setting_reload`, `config_edit_apply`), `start_peer`, `backlog_split` and the inbox's Allow once
950
+ and Deny answer `needsConfirm` with a preview first; acon runs them after you confirm.
951
+ Previews and the other dashboard actions run directly, as on the page.
952
+ - **Space.** The plugin feeds acon's Space view from the dashboard's office model
953
+ (`src/ui/office-space.ts`): peers are actors (state from condition, approvals and review, with
954
+ approval, queue, paused and mode marks), the office rooms are queue, work, review and done zones,
955
+ tasks are cards, and overlap, conflict and cohort links and recent message arcs are links. Ids
956
+ are project-prefixed, every running project's scene is merged within acon's caps (32 actors, 200
957
+ cards, 64 links, newest links kept), and a project that has not answered within about 3 s is left
958
+ out of that poll. Only labels and states reach the scene, never message bodies, task details,
959
+ approval titles or reasons; a PII task's card shows the `[pii]` stub.
960
+
929
961
  ## Install and start
930
962
 
931
963
  Use Bun 1.4.2 or newer. Source checks and CI use the version pinned in
@@ -943,7 +975,8 @@ ahub up
943
975
  `ahub init` writes the project configuration and managed instruction blocks.
944
976
  Run it after an upgrade when those blocks need refreshing. `ahub setup`
945
977
  installs or updates the Claude plugin and a project-neutral PreToolUse hook in each
946
- Claude configuration home in use (see "Machine autonomy and Claude homes"). Keep `ahub console` open when a local worker
978
+ Claude configuration home in use (see "Machine autonomy and Claude homes"), and
979
+ registers the acon console plugin (see "acon console plugin"). Keep `ahub console` open when a local worker
947
980
  may request an approval.
948
981
 
949
982
  `.agenthub/config.json` can be committed and shared. The fields that choose
@@ -1135,6 +1168,19 @@ and start): a committed file must not choose commands the hub runs. Keys that
1135
1168
  are not task classes are ignored with a log line. A hub stopped mid-check leaves
1136
1169
  the task in progress (`check interrupted`); the owner marks it done again.
1137
1170
 
1171
+ The owner may call `hub_task_done` again while the task is in review, or approved with its PR not merged, with a
1172
+ different full lowercase commit SHA in `refs.commit` (#1000, #1021, #1047). It replaces the
1173
+ delivered artifact, not a verdict: the reviewer gets a fresh request for the new
1174
+ head, the old CI watch is replaced, stale observed, reviewed and merge evidence is
1175
+ cleared, and open Important findings, spec acceptance and the rejection count stay.
1176
+ A configured completion check first returns the task to in progress until it passes.
1177
+ The same head, a malformed SHA, another owner or a running check refuse without
1178
+ effect, and a verdict started on the old request no longer applies. Reopening an
1179
+ approval first reads the PR state with `gh`: a merged PR, an unreadable state or a
1180
+ private task with a linked PR refuses without effect, and an approved task with no
1181
+ reviewer goes to the person. The reviewer may instead withdraw an approval of an
1182
+ unmerged PR with `hub_review` `changes_requested`, which returns the task to its owner.
1183
+
1138
1184
  While the project root checkout is on a branch other than its default
1139
1185
  (`origin/HEAD`, else `main`), finishing a task adds `the main checkout is on
1140
1186
  <branch>; move the work to a worktree under .worktrees/` to the owner's result
@@ -1427,6 +1473,61 @@ ahub research backfill # build this project's records once from even
1427
1473
  ahub task label 12 reverted # a later verdict (ok, regressed, reverted, incomplete, wrong, abandoned)
1428
1474
  ```
1429
1475
 
1476
+ ## Research campaigns: `ahub study` (#930)
1477
+
1478
+ A research campaign splits one question on an approved parent task into bounded units, each a task with
1479
+ its own sources and required output. Nothing happens until a campaign is drafted, and the `ahub research`
1480
+ telemetry above is unrelated. The contract, its bounds and the slices are in the
1481
+ [research campaigns spec](specs/2026-11-01-research-campaigns-design.md); the worked three-source example is in
1482
+ [profiles](profiles.md).
1483
+
1484
+ ```bash
1485
+ ahub study draft 12 contract.json # store revision 1 as a draft; creates no task
1486
+ ahub study revise rc-0123456789ab 1 next.json # store the next draft; a stale revision refuses
1487
+ ahub study approve rc-0123456789ab 2 # person only: create the unit tasks as Backlog cards
1488
+ ahub study show [rc-0123456789ab] # list campaigns (50 per page, --after continues) or one projection
1489
+ ahub study close rc-0123456789ab 2 --reason "answered"
1490
+ ahub study approve-protocol <64-hex hash> # person only: approve one exact experiment protocol
1491
+ ```
1492
+
1493
+ - **Contract.** The JSON names the question, scope, exclusions, registered source ids, 1 to 32 units
1494
+ (round, question, perspective, required output, sources, dependencies and a purpose: discovery,
1495
+ independent check, analysis or synthesis), deliverable sections, a stop policy (`maxUnits`,
1496
+ `maxFollowUpRounds` up to 5, a deadline at most 90 days after approval) and an optional execution budget.
1497
+ A contract over its bounds refuses whole, naming the field.
1498
+ - **Who does what.** Draft and revise are the person's (`ahub study`) or a configured planner's
1499
+ (`hub_study_draft`, `hub_study_revise`); they store drafts only. Approve, approve-protocol and close are
1500
+ console commands. Approval is the only step that creates unit tasks: they go to Backlog and the planner
1501
+ offers them through its ordinary pull; the campaign never assigns owners or approves a task. Re-running
1502
+ approve on an approved revision creates nothing.
1503
+ - **Dispatch gate.** Before a unit task is offered, assigned, pulled or handed to another owner the hub
1504
+ re-reads the campaign and holds the task with a reason when its revision is no longer the latest approved
1505
+ one, the deadline has passed or the stop policy is exceeded. A newer draft holds older bound work until it
1506
+ is approved, and an owner offer held this way is re-offered after approval, across a restart too. A hold
1507
+ never stops a native turn already running.
1508
+ - **Handoffs and coverage.** The current in-progress owner of a unit records `hub_study_handoff`: evidence per
1509
+ source, unresolved questions, contradictions and limitations, or `insufficientEvidence`. `ahub study show` and
1510
+ `hub_study_show` report per unit its round, task state, handoff completeness, covered sources and duplicate
1511
+ discovery assignments, the budget support label per resource (`enforceable`, `advisory`, `unknown` or
1512
+ `unavailable`) and why dispatch is held. A follow-up round needs every discovery unit of the previous round
1513
+ settled by a complete handoff or `insufficientEvidence`.
1514
+ - **Budgets.** A contract's `budget: { id, hardCap: [...] }` names an existing execution budget. With a non-empty
1515
+ `hardCap`, approval refuses unless the budget is enabled, every capped resource has a limit and is not
1516
+ `tokens`, and at least one of its peers is instrumented (today `pi` and `local`); every later dispatch and
1517
+ owner change rechecks that same budget. The ledger is shared with the peers' other work.
1518
+ - **Experiments.** Protocols, comparison plans and attempts are stored with their provenance (`declared`,
1519
+ `host-observed` from a check receipt or a hub-hashed deliverable), and `ahub study approve-protocol` binds an
1520
+ approval to one exact protocol hash and its evaluator; a changed protocol needs its own approval. The hub
1521
+ never ranks results. This release has no hub tool or CLI that records experiments; they are host storage
1522
+ for the slices that follow.
1523
+ - **Privacy.** A campaign whose parent task is PII, or that names a private source, is private: every view keeps
1524
+ ids, counts, states and provenance and stubs its text as `[pii]`. Study tool arguments and results are kept out
1525
+ of hub-managed memory capture.
1526
+ - **Upgrades.** The daemon advertises `research_records`, `research_dispatch`, `research_dropped_offers`,
1527
+ `research_experiments`, `research_comparisons` and `research_runners`. While the matching rows exist (for
1528
+ `research_runners`, stored runner receipts or any `research.runners` block), a restart, upgrade or
1529
+ recovery onto a release without the feature is refused.
1530
+
1430
1531
  ## Hub memory
1431
1532
 
1432
1533
  The hub opens the selected amem memory profile under `memoryHome()` (default `~/.agenthub/memory/`), with no claude-mem worker dependency ([how it works](memory.md)): notes saved
@@ -1442,7 +1543,7 @@ ahub memory forget 1a2b3c4d # a person's; also promote, demote, sup
1442
1543
 
1443
1544
  `"memory": { "native": false }` in `.agenthub/config.json` turns it off for a project.
1444
1545
 
1445
- The exact-pinned `@staix/agent-mem` 0.15.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.
1546
+ The exact-pinned `@staix/agent-mem` 0.18.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.
1446
1547
 
1447
1548
  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).
1448
1549
 
@@ -2050,6 +2151,7 @@ A guarded file edit that the daemon keeps at its accepted value remains visible
2050
2151
  | Permissions | `permission_modes.<peer>` (the default mode) | `config.local.json` | next peer start |
2051
2152
  | Start | `peers.pi.start_mode`, `peers.codex.start_mode`, `peers.kimi.start_mode`, `peers.jcode.start_mode` (`tui` or `headless`) | `config.local.json` | that peer's next start |
2052
2153
  | Start | `pi.auto_start` | `config.local.json` | next startup/recovery auto-start decision; never starts a peer merely on reload |
2154
+ | Start | `start.agents` (peer list; `none` for an empty list) | `config.local.json` | next use; the next person's `ahub up` reads it |
2053
2155
  | Routing | `routing.stay_switch`, `routing.classes.<class>.peers`, `.escalate_to`, `.route`, `.pi_backend` | `routing.local.toml` | at once |
2054
2156
  | Switches | `research.enabled`, `approvals.notify`, `snapshots.enabled` | `config.local.json` | live, without disturbing an active turn |
2055
2157
  | Switches | `coordination` | `config.local.json` | newly created tasks only |
@@ -2929,6 +3031,95 @@ Agent CLIs update themselves often, and a hub release cannot follow every one. O
2929
3031
 
2930
3032
  ## Upgrade and crash recovery
2931
3033
 
3034
+ ### Side by side project versions (#1039)
3035
+
3036
+ `ahub --project <path|id> upgrade --to <version>` reviews and changes only that
3037
+ project. With no selector, the current project is selected. Its recovery hold is
3038
+ per project, so another project can keep working, start, or perform its own
3039
+ upgrade. The registry and manager still serialize their short ownership writes.
3040
+ Legacy unscoped recovery receipts retain their machine-wide hold and original
3041
+ shared installation steps; finish or dispose those before a new project upgrade.
3042
+
3043
+ From 0.26.0, `ahub up`, manager Start and dashboard Start honor the selected
3044
+ project's `<state dir>/runtime.json`. It binds the project identity to a verified
3045
+ runtime version, root and capture digest. A caller at that version or later uses
3046
+ the retained runtime; an older caller refuses with `this project runs X; use X or
3047
+ later / ahub upgrade`. A scoped upgrade changes only that project's selection and
3048
+ does not refresh the shared manager. The daemon records its claimed instance,
3049
+ and readiness confirms the same selection before returning.
3050
+
3051
+ With no runtime record, the first managed startup captures the invoking package
3052
+ and its exact first-party memory core under
3053
+ `~/.agenthub/releases/<VERSION>/source-<digest>/`. Existing records with invalid
3054
+ bindings or missing/changed captures refuse rather than selecting another
3055
+ package. Runtime selection is machine-local state: a Git-tracked runtime record
3056
+ or indeterminate Git tracking check refuses before launch. Each startup checks
3057
+ only the runtime file's containing paths in Git once, with a bounded deadline;
3058
+ unrelated index entries are not enumerated. Plain directories without a
3059
+ repository remain supported. Capture reuse does not depend on a mutable global installation. Scoped
3060
+ upgrade targets use the same version directory, with the verified registry
3061
+ install kept separately in `registry/`. Recovery uses its authority receipt's
3062
+ exact target path and records that selection through the updated startup path.
3063
+
3064
+ This selector/refusal guarantee starts with 0.26.0. Published CLIs older than
3065
+ 0.26.0 ignore the runtime record. After a scoped upgrade, invoke `ahub up` with a
3066
+ 0.26.0-or-later CLI that is at least the selected project's version; keeping an
3067
+ older global CLI does not retrofit this protection. No database schema or
3068
+ bootstrap floor is raised for runtime selection. Tests of the updated resolver
3069
+ are not receipts from those older published binaries.
3070
+
3071
+ After a confirmed new startup, a separate, rate-limited maintenance worker keeps
3072
+ the newest three source captures per version plus every registered runtime, live
3073
+ manager runtime and retained recovery reference. Metadata snapshots and atomic
3074
+ quarantine use short registry writer transactions. Only removal candidates are
3075
+ hashed; package verification and recursive deletion run outside that lock and
3076
+ outside the startup result. Active recovery, uncertain owners, exhausted
3077
+ inventory budgets or an unreadable/incomplete inventory defer deletion.
3078
+ Only verified versioned `source-<digest>` directories are eligible;
3079
+ registry installs, unversioned coordinator sources, unknown layouts and symlinks
3080
+ remain. Reserved `.runtime-prune-*` quarantine paths cannot launch a managed
3081
+ runtime. Changed or unknown quarantined contents remain undeleted for inspection.
3082
+ Admission is home-wide, at most once per hour with at most three removals per
3083
+ run. `~/.agenthub/runtime-prune.db` records admission and the bounded outcome.
3084
+ Admission stores the worker's PID and process signature. The next admission
3085
+ atomically replaces a worker proven gone; live or uncertain owners remain
3086
+ fenced. Worker completion uses the normal database wait bound, while startup
3087
+ admission keeps its 1 ms contention bound. `ahub doctor` names an uncertain or
3088
+ stuck ledger and leftover quarantine paths without changing either. Leftover
3089
+ containers remain available for inspection even after a dead worker is replaced.
3090
+ Startup still succeeds when maintenance defers. A damaged runtime
3091
+ record/capture requires inspection or restoration.
3092
+ Archiving the record deliberately resets first-use selection; archived copies
3093
+ are not active pruning references.
3094
+
3095
+ A scoped upgrade leaves the global CLI and shared Claude plugin unchanged.
3096
+ Update the CLI separately with `bun add --global @staix/agent-hub@<version>`, then
3097
+ run `ahub setup` for the plugin. `ahub doctor` reports either tool when its known
3098
+ version is older than the selected running daemon; it installs nothing.
3099
+
3100
+ | Runtime | Control protocol window | Legacy status field |
3101
+ | --- | --- | --- |
3102
+ | 0.12.20 through 0.25.0 | 16 only | `protocol: 16` |
3103
+ | 0.26.0 | 16 through 17 | `protocol: 16`, `protocolMin: 16`, `protocolMax: 17` |
3104
+
3105
+ New clients choose the highest common protocol. The runtime supports N-1, so the
3106
+ published protocol-16 client and channel plugin remain usable; unrelated feature
3107
+ capabilities still require their own labels. A disjoint window refuses with 4426.
3108
+ The compatibility-floor `protocol` field remains readable by old clients.
3109
+
3110
+ Guard-aware runtimes read `hub.db` format metadata before startup effects and
3111
+ before writable database opens. A newer incompatible format refuses with
3112
+ `this project was opened by agent-hub X; use X or later`. The format counter
3113
+ advances for incompatible storage changes, not every package release. Controlled
3114
+ recovery refuses a target without `schema_guard` before source replacement. A
3115
+ fully committed legacy receipt continues from retained target identity evidence;
3116
+ new staging gates are not applied retroactively to a target already started.
3117
+ Published binaries from before this guard cannot enforce it; use their matching CLI for bootstrap and never open
3118
+ a newer database directly with an unguarded old binary. New scoped receipts use
3119
+ schema 3 so an older recovery coordinator refuses them rather than applying its
3120
+ machine-wide upgrade steps.
3121
+
3122
+
2932
3123
  On a terminal, one command reviews, applies and follows an upgrade:
2933
3124
 
2934
3125
  ```bash
@@ -2946,8 +3137,8 @@ section is what they run.
2946
3137
 
2947
3138
  Upgrade running projects with the target release's own coordinator. It accepts
2948
3139
  a running source on control protocol 9 (0.6.x), 10 (0.7.0 through 0.12.0),
2949
- 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
2950
- a target on its own protocol, so the target's coordinator fits every supported
3140
+ 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.25.0), plus the current 17/16 window, and only
3141
+ a target with a common protocol and the required recovery capabilities, so the target's coordinator fits every supported
2951
3142
  source and carries every recovery fix released up to it. Protocol 8 and older
2952
3143
  (0.5.x and earlier) are refused as `manual-bootstrap-required`. Run from the
2953
3144
  project directory, without replacing the global CLI first:
@@ -2960,7 +3151,7 @@ bunx --package @staix/agent-hub@0.12.43 ahub upgrade --to 0.12.43 --yes
2960
3151
  | Running now | Coordinator to use |
2961
3152
  | --- | --- |
2962
3153
  | 0.6.x (protocol 9) | the target's, through `bunx` as above |
2963
- | 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 |
3154
+ | 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.25.0 (protocol 16) | the target's, through `bunx` as above |
2964
3155
  | any supported source, with the installed CLI already at the target | `ahub upgrade` below, which is the same coordinator |
2965
3156
  | 0.5.x or earlier (protocol 8 and older) | not supported: bootstrap by hand with the matching CLI |
2966
3157
 
@@ -2984,11 +3175,10 @@ one queued); and one with the 0.11.0 coordinator from a 0.10.0 hub with
2984
3175
  `local.bash_network` on.
2985
3176
 
2986
3177
  The coordinator verifies and retains the exact target package, preserves its
2987
- own source, and promotes the global CLI only after restored projects pass
2988
- readback.
3178
+ own source, and verifies the selected project before releasing its hold. Legacy
3179
+ unscoped receipts additionally promote the global CLI after project readback.
2989
3180
 
2990
- Once the installed CLI matches the target release, review the current project or all registered
2991
- projects first:
3181
+ Once the installed CLI matches the target release, review the selected project first:
2992
3182
 
2993
3183
  ```bash
2994
3184
  ahub restart --dry-run
@@ -3011,8 +3201,8 @@ ahub recovery dispose [<operation-id>] --stop-and-archive --reason <text>
3011
3201
  `--yes` needs `--to`; a dry run without `--to` plans the latest release. A target
3012
3202
  newer than the CLI is handed to that release's coordinator with the same flags,
3013
3203
  and the command is printed on stderr first. The `recovery` commands take the
3014
- operation that holds the machine's lock when the id is left out, and say `no
3015
- recovery operation holds the lock` when none does.
3204
+ operation that holds the selected project's lock (or a legacy machine hold) when
3205
+ the id is left out, and report no operation when neither exists.
3016
3206
 
3017
3207
  The coordinator commits only once the source is quiet: no turn running, no
3018
3208
  approval pending, no completion check queued or running. It waits up to 10
@@ -4323,6 +4513,22 @@ A bundled adapter module is authorized by the person with `adapter_modules` (at
4323
4513
  - Refresh instructions with init and restart the daemon/MCP clients to apply their resolved contracts. Existing flow-role and executable-check authority remains in `roles`/trusted `checks`. Native model/account evidence remains unverified.
4324
4514
 
4325
4515
 
4516
+ ## Calendar schedules: `cron` and `tz` (#1017)
4517
+
4518
+ A `schedules` entry in `.agenthub/config.local.json` takes either `every` (an interval, `<n>m|h|d`, 15 minutes at least) or `cron`, never both. `cron` ties the run to the clock:
4519
+
4520
+ ```json
4521
+ { "schedules": [{ "id": "digest", "cron": "30 7 * * 1-5", "tz": "Asia/Seoul", "class": "plan", "title": "Morning digest" }] }
4522
+ ```
4523
+
4524
+ - **The expression** is the standard five fields, minute, hour, day of month, month, day of week, with numbers, lists (`1,15`), ranges (`1-5`) and steps (`*/15`, `10-50/10`). Day of week 0 and 7 are Sunday. There are no seconds, no year, no `@daily` macros and no names (`MON`, `jan`). When both day fields are restricted a day matches if either does (the classic rule); a field that begins with `*` leaves the other to decide.
4525
+ - **`tz`** is an IANA zone name (`Asia/Seoul`, `America/New_York`) and applies to `cron` only. Without it the machine's own zone is used. An unknown zone is refused with the entry named.
4526
+ - **Daylight saving, one rule.** A local time the clock skips runs at the next valid minute of that day; a local time the clock repeats runs once, at its first occurrence.
4527
+ - **Too frequent.** An expression that can fire more often than every 15 minutes (`* * * * *`, `*/5 * * * *`, `0,10 7 * * *`) is refused when the config loads, naming the schedule, as is one that never fires (`0 0 31 2 *`).
4528
+ - **Everything else is as for `every`.** A schedule never runs at first sight; a hub that was down or held through several fire times proposes one task when it can, not one per missed fire; a budget pause or hold skips and counts the tick; no second task while the first is open. `ahub schedule list`, `ahub status` and the dashboard show the expression and the next run in the schedule's zone, for example `2026-11-02 07:30 Asia/Seoul`.
4529
+ - **Upgrades.** The daemon advertises `calendar_schedules`. While a schedule uses `cron` or `tz`, a restart, upgrade or recovery onto a release without it is refused, naming the reason.
4530
+ - Out of scope: holiday and exchange calendars. A task whose first step finds a non-working day can end itself.
4531
+
4326
4532
  ## Staging work in Backlog (#263)
4327
4533
 
4328
4534
  Use `ahub task propose implement "Later work" --backlog --owner codex` to stage
@@ -4941,7 +5147,7 @@ Native launcher connections retry transport timeouts/unclean closes with bounded
4941
5147
 
4942
5148
  ## amem runtime (agent-mem#13 P4)
4943
5149
 
4944
- The hub exact-pins @staix/agent-mem 0.15.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.
5150
+ The hub exact-pins @staix/agent-mem 0.18.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.
4945
5151
 
4946
5152
  ## Backlog issue identity and notes
4947
5153
 
@@ -5334,3 +5540,47 @@ Lists are complete up to 1,100 rows. If an all-room list exceeds that limit, the
5334
5540
  CLI explicitly asks you to choose a room name; it never silently truncates.
5335
5541
  ponytail: pagination is later work; a filtered room's 100 unread plus 1,000
5336
5542
  settled stubs fit the current response ceiling.
5543
+
5544
+ ## Handing work to another project (#604, agent-hub#1030)
5545
+
5546
+ Work that belongs to another repository that runs agent-hub on this machine goes onto that repository's board, not into this session. Its own roles plan, implement and review it, and each state change it reaches is reported back here. The first case: after a library you maintain publishes a release, raising its exact pin in a consumer belongs on the consumer's board.
5547
+
5548
+ ```
5549
+ ahub task handoff --project <root|id> [<class>] <title...> [--path <p>]... [--issue <n>] [--task <n>] [--detail <text>]
5550
+ ```
5551
+
5552
+ - `--project` is the other project's canonical root or its id from `ahub projects`. It must be registered and its hub must be running (`ahub up` in that project). Nothing is queued: a hub that is not running is refused with that action.
5553
+ - A feature or behavior change first gets a spec issue in the other repository: pass its number as `--issue`. A mechanical chore (an exact version pin, a regenerated file) goes straight to a task.
5554
+ - `--path` names files in the other project, repository-relative (no absolute path, `~` or `..`). `--task` names your own task this comes from, so a `[pii]` one is refused.
5555
+ - `ahub task handoff` is the person's command (an agent's shell is refused it as a console command). Agents use `hub_task_handoff` (the same fields) when they hold the planner or conductor role; `hub_task_handoffs` and `ahub board --handoffs` list what this project handed over and the last state each target reported.
5556
+
5557
+ What the other project sees: an ownerless proposed task, made by its hub, whose detail ends with where it came from; `ahub board` and `ahub task show` there name the origin project and peer. Its own routing offers it as it would a local proposal. The requesting peer has no identity or authority there: it cannot accept, review, approve, assign or cancel.
5558
+
5559
+ What comes back: each change is recorded in `ahub board --handoffs`, and an agent that handed the task over also gets one `kind: task` message per change, `agent-hub task 123 accepted`, `in review`, `approved: PR #7 at <sha>`, `declined` or `cancelled` (a handoff you made from the terminal sends you no message: read the list). It carries an event name, a PR number and a full head, never the other project's text. If this project's hub was stopped meanwhile, it asks each running target hub where its open handoffs stand when it starts again; a target that is down then is asked at the next start.
5560
+
5561
+ Refusals, each named with the action: an unregistered target, this very project, a hub that is not running, a request over 8 KiB or with an unknown field, a caller that is not a planner, the conductor or the person, a private (PII) turn, a `[pii]` task named with `--task`, text that matches the target's PII patterns or that the target's screen cannot clear, a target hub in recovery or shutting down (hand it off again after that ends), and an older target hub (it lacks the `task_handoff` feature: run `ahub upgrade` in that project).
5562
+
5563
+ Recovery: `task_handoff` is its own recovery label. A project that holds a handoff row, in either direction, refuses a recovery target that lacks it.
5564
+
5565
+ ## The agents `ahub up` starts (#608, agent-hub#1040)
5566
+
5567
+ `ahub up` starts the hub, then asks it to start this project's default agents: `claude` and `codex` unless you chose others. Each goes through the same hub start as the dashboard's Start button and `ahub <peer>` from the console, so the peer's own start mode applies (`peers.<id>.start_mode`), the terminal comes from `terminal.open` or Orca, and a peer that is already attached answers `already attached`. There is no headless fallback: a peer whose TUI cannot be opened is refused with the command to run by hand.
5568
+
5569
+ ```
5570
+ ahub up # the hub, then one line per agent
5571
+ ahub up --no-agents # the hub alone, for this run
5572
+ ahub setup --agents kimi,codex # this machine's choice, once
5573
+ ahub settings set start.agents claude,codex,kimi # this project's own list; none for no agent; inherit removes it
5574
+ ```
5575
+
5576
+ ```
5577
+ claude: started tui (terminal.open), it attaches when its TUI is ready
5578
+ codex: already attached
5579
+ kimi: not started: kimi starts in its TUI and the hub could not open a terminal for it (...). Run ahub kimi in a terminal
5580
+ ```
5581
+
5582
+ - **Where the list comes from, in order:** `start.agents` in the project's `.agenthub/config.local.json` (an empty list starts none), else the machine default `start.agents` in `~/.agenthub/defaults.json`, else `["claude", "codex"]`. `ahub setup` asks once ("Agents to start with ahub up"); `--agents <a,b>` answers it, `--yes` keeps the current choice or the default, and a run with no terminal and neither flag keeps the default and says so. `ahub init` of a new project copies the machine default into its `config.local.json`; a project that has its own value keeps it, and nothing is written into a file git tracks.
5583
+ - **Machine-local and trust tier:** `start.agents` is in `ALL_MACHINE_LOCAL`: a committed `.agenthub/config.json` that sets it is ignored with a notice, because a clone must not open terminals on someone else's machine. It is a settings row in the Start group that raises what the hub does on its own, so it needs a settings session (`ahub ui --settings`) or a person's terminal. `defaults.json` stays owner-only (mode 0600, outside every project root); a `start` entry that is not an object with an `agents` list of allowed peers makes the whole file ignored with the existing notice.
5584
+ - **Who and when:** only a person's own `ahub up` and the dashboard manager's "start" (which counts as one). An agent's `ahub up` is refused by the CLI before this runs. A daemon restart, a crash recovery, a config reload (`start` applies at the next `ahub up`) and the `ahub up` that an upgrade or recovery relaunches (it carries `AGENTHUB_RECOVERY_OPERATION`) start nothing from the list, and the running hub never reads it. `--live-test` starts none either.
5585
+ - **What can be listed:** `claude`, `codex`, `kimi`, `pi`, `local` and `jcode`. `qwen` is refused when saved ("the hub never opens its TUI"), as are `agy`, a declared instance, a Claude channel extra, an unknown id and a duplicate, each with a named reason; the loader and the settings writer say the same words. Pi in the list is the user-facing switch; `pi.auto_start` keeps working for configs that have it.
5586
+ - **Failure is local to one agent:** a refusal, a terminal provider that fails or a request that times out prints its own line and never stops the hub or the other agents. The lines come from the hub's own reply, so a provider's error text stays in `hub.log`.
package/docs/profiles.md CHANGED
@@ -21,6 +21,8 @@
21
21
  - Configure an explicit machine-local `checks.fact_check` command if desired. Alias checks take precedence over a base `checks.test`; no check runs merely because a profile was selected.
22
22
  - Example deterministic check: `bun /path/to/agent-hub/scripts/check-citations.ts report.md references.bib`. It checks bracketed Markdown `[@key; @other]` keys against BibTeX entry keys, with 256 KiB/4 MiB input caps. It performs no fetch, validates no source quality and is not a full Markdown/BibTeX parser. A missing key exits 1. A resolved bibliography is one check, not proof that claims are supported.
23
23
  - Keep a person's sign-off as the final review. The live three-source/two-agent example remains unverified and was not run during solo implementation.
24
+ - A multi-unit study (#930, #601) goes through `ahub study`: the person (or a configured planner with `hub_study_draft` / `hub_study_revise`) stores a contract as a draft, only the person approves it with `ahub study approve <id> <revision>`, which creates one task per unit, and anyone reads coverage with `ahub study show [<id>]` or `hub_study_show`. A unit owner records `hub_study_handoff` with evidence for every source the unit names, or `insufficientEvidence`; a done task is not a handoff, and an incomplete handoff does not close a discovery round. A private campaign, task or source shows ids, counts and states only.
25
+ - The deterministic three-source, two-peer example (`test/study-daemon.test.ts`, "three-source, two-peer example"): one contract names sources S0, S1 and S2 and two discovery units, `alpha` (S0 and S1, owned by codex) and `beta` (S2, an independent check, owned by kimi). After `ahub study approve` both units read `none` and `0/2 covered`, `0/1 covered`. A handoff for S0 only moves `alpha` to `incomplete` with `1/2 covered`. A handoff for both S0 and S1, and one for S2, make both `complete` and the discovery round complete. Another peer's handoff for `alpha` is refused. This runs with fake peers: it shows the hub's rules, not that a native model follows the guidance.
24
26
 
25
27
  # General project example
26
28