@staix/agent-hub 0.12.36 → 0.12.37
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 +11 -0
- package/README.md +13 -8
- package/docs/agent-notes/adapters.md +7 -3
- package/docs/agent-notes/budget.md +2 -1
- package/docs/agent-notes/daemon.md +5 -1
- package/docs/agent-notes/tasks.md +5 -2
- package/docs/agent-notes/tests.md +2 -2
- package/docs/memory.md +3 -1
- package/docs/operations.md +42 -10
- package/docs/quickstart.md +7 -7
- package/docs/sdlc.md +29 -3
- package/docs/security.md +6 -2
- package/docs/smoke.md +18 -2
- package/docs/specs/2026-09-19-agent-hub-design.md +34 -4
- package/docs/verification/2026-10-19-0.12.37.md +60 -0
- package/docs/verification/2026-10-19-jcode-0.98.1.md +30 -0
- package/docs/verification/2026-10-19-reviewer-isolation.md +119 -0
- package/docs/verified.json +18 -14
- package/package.json +2 -2
- package/plugins/agent-hub/.claude-plugin/plugin.json +1 -1
- package/plugins/agent-hub/server.js +3091 -2236
- package/src/adapters/acp.ts +3 -2
- package/src/adapters/claude-print.ts +18 -156
- package/src/adapters/codex-review-isolation.ts +276 -0
- package/src/adapters/definitions/builtin-metadata.ts +4 -1
- package/src/adapters/definitions/native-factories.ts +17 -0
- package/src/adapters/definitions/native-policy-aux.ts +1 -1
- package/src/adapters/definitions/reviewer-variants.ts +32 -0
- package/src/adapters/jcode.ts +20 -5
- package/src/adapters/registry-types.ts +7 -3
- package/src/adapters/registry.ts +17 -5
- package/src/adapters/reviewer-print.ts +319 -0
- package/src/cli/help.ts +2 -0
- package/src/cli/identity.ts +1 -1
- package/src/cli/main.ts +19 -0
- package/src/cli/memory.ts +13 -12
- package/src/hub/board.ts +34 -6
- package/src/hub/config-apply.ts +3 -2
- package/src/hub/control-client.ts +1 -1
- package/src/hub/daemon.ts +101 -27
- package/src/hub/envelope.ts +1 -1
- package/src/hub/host-executable.ts +1 -1
- package/src/hub/hub-tools.ts +5 -0
- package/src/hub/instances.ts +13 -3
- package/src/hub/memory-profile.ts +67 -11
- package/src/hub/planning.ts +2 -0
- package/src/hub/release-admission.ts +64 -0
- package/src/hub/reviewer-pool-config.ts +11 -0
- package/src/hub/reviewer-pool.ts +70 -0
- package/src/hub/routing.ts +42 -3
- package/src/hub/sdlc-release.ts +113 -0
- package/src/hub/tasks.ts +254 -36
package/CHANGELOG.md
CHANGED
|
@@ -2,6 +2,17 @@
|
|
|
2
2
|
|
|
3
3
|
Issue and pull request numbers in the entries for 0.7.7 and earlier refer to the previous repository, archived on 2026-09-30 when this repository's history was rewritten; the one exception is the open smoke-check issue, formerly #12, which moved here as #1. Numbers in newer entries refer to this repository.
|
|
4
4
|
|
|
5
|
+
## 0.12.37
|
|
6
|
+
|
|
7
|
+
Hub memory follows agent-mem 0.4.0 onto per-project shards (#534, #537), reviews rotate across fresh Claude and Codex sessions (#519), and `ahub jcode` starts again on current jcode releases (#529). Before running `amem migrate --shards`, upgrade every hub that shares the memory home to 0.12.37 or stop it.
|
|
8
|
+
|
|
9
|
+
- Reviews rotate across fresh Claude and Codex reviewer sessions (#519). A declared Codex reviewer instance opts in with `review_mode: "fresh"` and runs `codex exec` under a pinned isolation policy: plugins, hooks, ChatGPT Apps and web search disabled, a native read-only sandbox, denied credential roots, and only `hub_review` and `hub_task_show` from the hub. The start index follows the PR or task id, the implementer's family is skipped while another family can run (a separate fresh same-family instance is the last fallback, and a re-review keeps the family that requested changes), and an unavailable reviewer moves the walk on; it never counts as a pass. When no pool is configured and no fresh reviewer can run, the planner reviews as before; an explicitly required pool blocks for the person. Existing reviewer-role instances keep their native transport. Kimi is not offered yet, and agy is an implementer peer (#485), not a reviewer.
|
|
10
|
+
- Hub memory binds the alias-resolved project key that the `amem` CLI uses, so after `amem alias add` the daemon and `ahub memory` read and write the same shard and rows as standalone amem. A dangling or unreadable `shards.json` now counts as a split marker: new opens refuse with the resume remedy and an open unsplit binding latches unavailable. An alias added while a hub runs latches that hub's memory unavailable until it restarts (#237 review).
|
|
11
|
+
- Plan release trains through the board's release assignments, move missed work with a reason, and freeze approved scope through host resource admission and fresh peer quota checks. Hotfix causes and cut/shipment history are recorded; person upgrades retain original recovery readiness and train target compatibility (#481 S11).
|
|
12
|
+
- `ahub jcode` starts again: the adapter accepts any jcode release from 0.98.0 up to, not including, 1.0.0 (the assumed breaking version), so a jcode minor update no longer refuses the peer; releases before 0.98.0 are refused. The owned daemon's private socket is now named `j.sock`: jcode derives its debug socket by replacing `.sock` in the name, so the old name `s` made both binds one path and `serve` exited on EADDRINUSE with every jcode version. Every owned jcode process (the doctor version probe, `serve` and `acp`) now runs with `JCODE_NO_TELEMETRY=1`: jcode 0.98.1's telemetry policy can report the provider account's email. Verified live on 0.94.0 and 0.98.1 (#529).
|
|
13
|
+
- Hub memory opens a split memory home (agent-mem#15 T2, `amem migrate --shards`) through the project's own shard, with the user shard as its overlay. Before this, a hub pinned agent-mem 0.3.1, which does not detect a split and keeps using the old layout. An inactive split refuses with the `amem migrate --shards --resume` remedy, and an unsplit home opens exactly as before. The exact `@staix/agent-mem` pin rises to 0.4.0, the first release with T2 (agent-mem#15).
|
|
14
|
+
- CI: the macOS check gate gets a 960 s hang bound inside a 20-minute job ceiling; its test step had grown to 650-808 s (#538).
|
|
15
|
+
|
|
5
16
|
## 0.12.36
|
|
6
17
|
|
|
7
18
|
Reviews get their own reviewers and a hub-built brief: a headless Claude reviewer runs a fresh session per review (#450, #223), every review request carries a review brief with a coverage gate (#520), and a second Claude session can join as a reviewer or planner (#450 I3). The planner reviews plans before work starts, WIP admission applies in every lifecycle mode, a person accepts a task's spec issue (#481 S1, S9), and review and CI costs are governed, with identical-tree CI reuse (#481 S10). A Claude session with a transcript over 32 MiB no longer stays busy forever (#517). Restart Claude sessions after upgrading: each keeps the plugin server it started with.
|
package/README.md
CHANGED
|
@@ -5,7 +5,7 @@ Codex, Kimi Code, Pi and a hub-owned local worker (Qwen Code and Jcode optional)
|
|
|
5
5
|
share a message bus and a task board per project and one memory on the machine,
|
|
6
6
|
each attached through its own native interface.
|
|
7
7
|
|
|
8
|
-
Status: 0.12.
|
|
8
|
+
Status: 0.12.37, control protocol 16. Bun + TypeScript, one loopback daemon per
|
|
9
9
|
project. Site: [agent-hub.staix.net](https://agent-hub.staix.net).
|
|
10
10
|
|
|
11
11
|
## What it does today
|
|
@@ -77,24 +77,29 @@ cd <your project> && ahub init && ahub up
|
|
|
77
77
|
|
|
78
78
|
After upgrading from 0.12.30 or earlier, run `ahub jcode --mode headless` once in each
|
|
79
79
|
project that uses Jcode to refresh its owned MCP entry before conductor or dashboard
|
|
80
|
-
starts of Jcode. The entry's format is unchanged in 0.12.
|
|
80
|
+
starts of Jcode. The entry's format is unchanged in 0.12.37; a daemon start that still
|
|
81
81
|
reports the entry missing or stale is fixed by the same command, which since 0.12.36
|
|
82
|
-
writes the running hub's own plugin bundle (#502).
|
|
82
|
+
writes the running hub's own plugin bundle (#502). Since 0.12.37 the hub accepts jcode
|
|
83
|
+
0.98.0 or later, below 1.0.0, and no longer 0.94.0 (#529).
|
|
83
84
|
Cancellation needs a daemon advertising `task_cancel`; controlled downgrades
|
|
84
85
|
refuse while cancelled rows exist. For local/Pi implementation, opt in to an
|
|
85
86
|
available `checks.runner = "container"`; issue-backed owners also need authenticated
|
|
86
87
|
spec access. PII work and designated reviewers retain their separate exemptions.
|
|
87
|
-
See [release verification](docs/verification/2026-10-19-0.12.
|
|
88
|
+
See [release verification](docs/verification/2026-10-19-0.12.37.md) for the live legs
|
|
88
89
|
still open, including #418 AC8, #438 AC7, the native console settings leg (#439 AC5),
|
|
89
90
|
real upgrades with reattach (#463) and with a managed terminal and cancelled tasks
|
|
90
|
-
(#478), a live headless Claude review (#223),
|
|
91
|
-
(#
|
|
91
|
+
(#478), a live headless Claude review (#223), a Claude session over 32 MiB (#517), a
|
|
92
|
+
live fresh Codex review (#519) and a live split of a real memory home (#536).
|
|
92
93
|
|
|
93
94
|
Before migrating a memory home with `amem`, upgrade every hub using that home to
|
|
94
95
|
0.12.30 or later, check `ahub memory profile-check`, then quiesce and close all memory
|
|
95
96
|
consumers. Shared v1 remains the default; installing this release does not migrate
|
|
96
|
-
data. The curated shard migration
|
|
97
|
-
|
|
97
|
+
data. The curated shard migration (`amem migrate --shards`, agent-mem 0.4.0) needs
|
|
98
|
+
every hub using that home to run 0.12.37 or later, which opens memory through shards
|
|
99
|
+
(#534). An older hub (0.12.36 or earlier, agent-mem 0.3.1) does not detect the
|
|
100
|
+
split: it keeps reading and writing the old layout, and its writes reach the shards
|
|
101
|
+
only through `amem migrate --shards --resume`. Upgrade or stop every such hub before
|
|
102
|
+
splitting. `ahub memory profile-check` is not split-aware yet (#536). See [memory profiles and rollout](docs/memory.md#selected-memory-profile).
|
|
98
103
|
|
|
99
104
|
## Documentation
|
|
100
105
|
|
|
@@ -33,7 +33,7 @@ Scope: the Codex, ACP and Pi adapters and the Pi extension (turn `generation` an
|
|
|
33
33
|
- Pi's bridge executes `/tool` only for the verified owner (`verifiedOwner()`: the claimed pid with its `processSignature`; a headless owner is pinned from the spawned process once `get_state` proved its session) with the current `sessionId` and turn `generation`, and admits `/budget` only for that owner and generation, checked again after the awaited admission. Refuse anything else before execution. `tool_abort` aborts that pending call, or fences its id when it arrives before the call (64 ids a turn, then every further call of that turn). A person's `!command` (`purpose: "idle_user_bash"`) is taken only in TUI mode from an idle owner on its one-use reservation, and it alone skips `piPermit`: never widen that to a model's bash call (#253).
|
|
34
34
|
- Pi and local approvals (#253): `permit` answers a `ToolApproval` (contract: `local-worker.md`) and `requestPermission` (`src/hub/daemon.ts`) tells the adapter's `ApprovalObserver` what settled it. An expiry counts toward the stop only as `source: "expired"` with `eligibleExpiry` not false (no person answered a request of that peer while it was pending); `person` resets the count, `automatic` (a mode grant, Pi's `always`) never does, and a new turn starts it over. The second one ends the turn, never the peer: local throws `ApprovalWaitStop`, Pi sends `abort_budget` with `cause: "approval"`, and both settle the delivery `completed` with the reason; a Pi whose extension did not announce `approvalTurnAbort` goes offline with the delivery held. `requestPermission` deletes a request from `permissions` before it runs an observer, resolves or reports, and an aborted signal closes it as `cancelled`: a late answer must find nothing to grant, and a failed event, notice or console push never strands the caller.
|
|
35
35
|
- `stop(reason?)` (`src/hub/peers.ts`): a reason marks a person's requested stop (`peer_stop`, rule: `daemon.md`). Fence before the first await (the adapter's stopping flag, or `offline`) so `deliver()` and buffered native callbacks do nothing more, hold the in-flight delivery as `needs_review` with that reason, and end `offline`. For ACP without a journal, return ordinary in-flight envelopes once through `onFailed` before a requested stop invalidates the turn; never return the watchdog-cancelled original prompt. A requested stop never completes, escalates or reports the turn it cut (`AcpPeer` bumps `turn`; `PiPeer.fail` skips the failure message and `onTurnFailure` while `requestedStopReason` is set). `PiPeer.stop()` waits for aborted tools only up to `stopGraceMs`: a tool past it never blocks the teardown, and the stop throws `PiToolSettlementFailure` once the owner is down.
|
|
36
|
-
- A terminal the hub opens for a person (`src/hub/terminal-open.ts`, #269) gets `personEnv`, which strips every agent marker; never `peerChildEnv`, which sets one. Adding a native peer means registering its `AdapterDefinition` in `ADAPTERS` and listing its id in `KNOWN_PEERS` or `OPTIONAL_PEERS` and `ROLE_ORDER`, all in `src/adapters/registry.ts` (`isSupportedPeer`, `isKnownPeer` and `defaultPeerRoles`
|
|
36
|
+
- A terminal the hub opens for a person (`src/hub/terminal-open.ts`, #269) gets `personEnv`, which strips every agent marker; never `peerChildEnv`, which sets one. Adding a native peer means registering its `AdapterDefinition` in `ADAPTERS` and listing its id in `KNOWN_PEERS` or `OPTIONAL_PEERS` and `ROLE_ORDER`, all in `src/adapters/registry.ts` (the registry's `isSupportedPeer` answers from `ADAPTERS`, `isKnownPeer` and `defaultPeerRoles` from `definitionOf`; `STARTABLE_PEERS` reuses `SUPPORTED_PEERS`); if it has both a TUI and a headless form, also add it to the registry's `START_MODE_PEERS`, with its fixed words in its definition's `cli.startWords`. Mirror `KNOWN_PEERS`, `OPTIONAL_PEERS` and `START_MODE_PEERS` in the literal cross-release copies in `src/hub/peer-catalog.ts` and `src/hub/start-mode.ts` (`ROLE_ORDER` has none). Adding a peer is the one change that updates their pinned counts in `test/registry-guard.test.ts` and the literal in `test/adapter-catalog-a2.test.ts`; say so in the PR as an expected test change.
|
|
37
37
|
- Spawn a native peer with `peerChildEnv(peer, source)` so its own `AGENTHUB_PEER_ID` replaces inherited agent markers. Sandbox tool environments carry their executing peer too. Preserve the native shell environment policy; Codex's `CODEX_THREAD_ID` supplies the fallback after filtering. Before changing marker detection or launcher environments, read `docs/verification/2026-10-09-agent-shell-t0.md` for pinned source and live-probe limits.
|
|
38
38
|
- Emit Pi's ceiling event at the pre-effect rejection boundary (`src/pi/ceiling.ts`), before `agent_end`. Accept only a valid current-session/current-generation signal during a running turn; retain the first signal and attach it to that turn's failure. Never classify a ceiling from free text.
|
|
39
39
|
|
|
@@ -49,10 +49,14 @@ Scope: the Codex, ACP and Pi adapters and the Pi extension (turn `generation` an
|
|
|
49
49
|
- Pi TUI hub prompts (#379; an idle snapshot ACK arriving before a personal start inserted a hub prompt into the person's turn): attribute a start, end or settle to a hub turn only by the command's `promptId`, which lasts until the logical `agent_settled`; treat any other start while a hub prompt is pending or running as personal activity that detaches Pi. Settle `failed_safe` only on a `prompt_not_started` refusal bound to the verified owner, session, command id and generation; everything else stays `needs_review`. Refuse roster return without `fencedPrompt`.
|
|
50
50
|
|
|
51
51
|
- Adapter registry (#451): kind-specific policy lives in that peer's `AdapterDefinition` under `src/adapters/definitions/` (runtime definitions `NATIVE_BUILTIN_DEFINITIONS_WITHOUT_QWEN` in `builtin-metadata.ts` plus `qwen.ts`; the daemon constructs every native driver through `start.create` and `DAEMON_PORTS`; `BUILTIN_DEFINITIONS_WITHOUT_QWEN` is the frozen A1 export, read by no runtime consumer). Outside `src/adapters/`, read a semantic field of `definitionOf(id)`: never a raw peer-id comparison, a resolved kind (`kindOf`, `definitionOf(...).kind`) or a constant alias compared with a native name, a vendor-keyed object, a peer-name array, or a closed-list import from `peer-catalog.ts`, `start-mode.ts` or `permission-mode.ts`; the guard resolves aliases and import/re-export bindings. `test/registry-guard.baseline.json` is `[]` and stays empty: a new site moves into a definition, never into the baseline. The only exceptions are the cross-release literals `KNOWN_PEERS` (5), `OPTIONAL_PEERS` (2) and `START_MODE_PEERS` (3), pinned separately in `test/registry-guard.test.ts`. `LaunchContext.nativeCommand` carries only validated config argv (or its redacted preview), never model input; a definition's `permission.grants` text is what `permissionGrant` shows, ahead of the built-in wording. Keep native mode-id maps in `definitions/*-native.ts`, free of registry factories (`test/adapter-import-order.test.ts`).
|
|
52
|
-
- Registry load runs `assertDaemonEngine` and `assertNativeCapabilityScope` before publication. A definition that is not a fixed builtin (object identity in `fixedBuiltinDefinitions`, never a kind name) may not declare `start.acpCommand`, `attach.admission`, `budget.resetSoftTokensOnResume`, `session.crashIdentity`, a `pi-rpc` daemon port, `routing` or `
|
|
52
|
+
- Registry load runs `assertDaemonEngine` and `assertNativeCapabilityScope` before publication. A definition that is not a fixed builtin (object identity in `fixedBuiltinDefinitions`, never a kind name) may not declare `start.acpCommand`, `attach.admission`, `budget.resetSoftTokensOnResume`, `session.crashIdentity`, a `pi-rpc` daemon port, `routing`, `reviewerVariant` or `reviewer`. Only `definitionOf(id, instances)` picks a kind's `reviewerVariant`, for a declared extra with `start_mode: "headless"`, roles exactly `["reviewer"]`, and either a variant whose `reviewer.declaration` is `"automatic"` (Claude, agy) or `review_mode: "fresh"` (Codex, Kimi); without that opt-in a reviewer-only Codex or Kimi instance keeps its native adapter. A declaration-only definition (`AGY_DEFINITION`) is registered for `definitionOf` but stays out of `ADAPTERS`, `KNOWN_PEERS` and `OPTIONAL_PEERS`. A new field that depends on fixed-builtin assumptions or host-global native state joins that list (`test/adapter-capability-scope.test.ts`). `trust.onPrem` admits PII work (rule: `tasks.md`).
|
|
53
53
|
- `validatePiSession` (`src/pi/session.ts`) throws `PiSessionUnresumable` only on positive evidence (ENOENT, a file outside the managed session directory, an unparsable or mismatched header) and `PiSessionBookmarkMismatch` only when a valid header names another session id; every other read failure is rethrown unchanged, because the daemon retires a Pi continuity bookmark only on those two classes (#463). `PiPeer` rethrows a mismatch with the neutral header-id message and never claims a bookmark exists (`test/pi-session-validation.test.ts`).
|
|
54
54
|
|
|
55
55
|
- Spawn with `peerChildEnv(<instance id>)`, not the kind name. An ACP `kind` comes only from the host declaration, never from the id suffix or agent text.
|
|
56
56
|
|
|
57
57
|
- TUI launchers (Kimi, Jcode) write the plugin bundle the hub reports in `status` into their MCP entry, and the CLI's own copy only when the hub reports none (#502). Only the Jcode start path checks the entry against the daemon's own `SERVER_JS` (`jcodeMcpReady`); Kimi TUI admission does not compare it.
|
|
58
|
-
-
|
|
58
|
+
- Jcode (#529): accept the native version only through `jcodeVersionSupported` (from `JCODE_MIN_VERSION` up to, not including, `JCODE_BREAKING_VERSION`), in both the version probe and ACP `expectedAgent`; never pin the minor, and lower the breaking version when a release breaks the contract. Every owned jcode process (probe, serve, ACP) gets `JCODE_OWNED_ENV`, and the private socket name ends in `.sock`.
|
|
59
|
+
- `ReviewerPrintPeer` (`src/adapters/reviewer-print.ts`, #223, #519; `ClaudePrintPeer` is its Claude wrapper) runs one fresh child per review and clears `reviewing` at the leader's exit and first in `kill()`. A stop or watchdog settles `needs_review` at once, before the kill; `completed`, `idle` and `offline` come only after the whole process group is confirmed gone, and a failed cleanup keeps the child handle and stays `busy`. The daemon's `reviewAuthority` compares `reviewToken`: never widen it.
|
|
60
|
+
- A reviewer backend is a fixed builtin `ReviewerBackend` in a definition's `reviewer`, never config. Its `prepare` must return a complete isolation receipt (matching backend id, binary, version, profile, evidence, `readOnly`, `strictMcp` and `ephemeral` true) before every child, and `launch.current()` is rechecked right before spawn; a missing or failed proof refuses, never a weaker launch or a continuing native session. The daemon puts the reviewer on the bus only after `start()` proved isolation; a failed start or a `start_failed` report marks it in `reviewerUnavailable`, which routing reads as `failing`. A placeholder (`reviewer.unavailable`: Kimi, agy) has no backend or launch code; `startPeerBody` refuses it before preflight.
|
|
61
|
+
- Report reviewer failures (`onUnavailable`: `start_failed`, `quota`, `no_verdict`) only after the child's group is confirmed gone and only while its generation is current: a person's stop owns its hold and never reroutes. `quota` comes only from the backend's structured `failure(event)`, never from vendor text. An NDJSON frame over 256 KiB or more than 4 MiB of output aborts the review as `needs_review`.
|
|
62
|
+
- Codex reviewer isolation (`src/adapters/codex-review-isolation.ts`): accept only the pinned `codex-cli` version; prove model-free that Apps, plugins and hooks are disabled (`features list`), ambient MCP servers are replaced by disabled stubs, and the hub server alone is enabled with exactly `hub_review` and `hub_task_show`; run the native sandbox fixture probe before every launch; hash the binary once and fence binary and config files by metadata. Credential denies come only from the bounded `HOST_CREDENTIAL_PATHS` and `HOST_CREDENTIAL_ENV` lists, denied at the lexical and canonical path; refuse glob syntax or a root inside the checkout. Never pass the control token in a reviewer child's argv, env or MCP config: keep the hub state dir in its `privatePaths`.
|
|
@@ -19,4 +19,5 @@ Scope: quota and native context readings, checkpoints, pause, handoff and resume
|
|
|
19
19
|
- Quota handoff context reads `QUOTA_HANDOFF_PLATFORMS` (`src/adapters/registry.ts`, each definition's `hooks.recallPlatform`: claude, codex, kimi, qwen, jcode). It is independent of `hooks.recallInjected`: a handoff platform never joins the injected-platform exclusion in recall (`test/adapter-handoff-platforms.test.ts`).
|
|
20
20
|
- `Budget.report` skips `user`, `hub` and a registered kind with no `budget` block or with `budget.quotaExempt` (only local); an external id keeps quota accounting. `budget.instrumented` (execution budget) never exempts quota.
|
|
21
21
|
|
|
22
|
-
- Key quota readings, pause records, overrides, checkpoints and handoffs by the canonical account (`hooks.account`, idempotent); telemetry keeps the reporter id and native context occupancy stays per instance. `instanceBudgetHooks` fans pause, resume, checkpoint and handoff out to every member, and `canHandOff` excludes the paused account's siblings. Startup normalization merges old alias pause rows without dropping a row or marking an unfinished handoff done. A manual pause stays per member.
|
|
22
|
+
- Key quota readings, pause records, overrides, checkpoints and handoffs by the canonical account (`hooks.account`, idempotent); telemetry keeps the reporter id and native context occupancy stays per instance. `instanceBudgetHooks` fans pause, resume, checkpoint and handoff out to every member, and `canHandOff` excludes the paused account's siblings. Startup normalization merges old alias pause rows without dropping a row or marking an unfinished handoff done. A manual pause stays per member. Reviewer variants keep their base kind's `budget.account`.
|
|
23
|
+
- Release-cut quota admission (`releaseQuotaSnapshot` in `src/hub/daemon.ts`, #481 S11): an account has a quota source only when one of its members' definitions has `budget.reader`, or `budget.resetSoftTokensOnResume` with `kimi_tokens_5h > 0`. Without a source a registered account is reported `no quota source` and never defers; with one, an unknown, stale or below-gate reading defers. Manual and budget-record pauses defer for every attached peer.
|
|
@@ -5,7 +5,7 @@
|
|
|
5
5
|
|
|
6
6
|
- Console attention navigation selects a delivery only by an exact visible row id. A missing id or row prints read-only guidance; never select by peer or fall back to the first Queue row.
|
|
7
7
|
|
|
8
|
-
Scope: the operator console, control WS and its protocol, state files, the dashboard listener and settings, the Claude channel's reconnect, `ahub setup`, git snapshots, upgrade and recovery. Read before editing `src/adapters/definitions/native-recovery*.ts`, `src/adapters/definitions/native-policy-aux.ts`, `src/adapters/definitions/native-config-*.ts`, `src/adapters/definitions/native-cli-*.ts`, `src/cli/console.ts`, `src/cli/console-state.ts`, `src/cli/attention.ts`, `src/ui/needs.ts`, `src/hub/daemon.ts`, `src/hub/ui.ts`, `src/hub/settings.ts`, `src/hub/control-client.ts`, `src/adapters/claude-channel.ts`, `src/hub/restart.ts`, `src/hub/recovery-store.ts`, `src/hub/snapshots.ts`, `src/hub/manager.ts`, `src/hub/lifecycle.ts`, `src/hub/crash.ts`, `src/hub/start-mode.ts`, `src/hub/terminal-open.ts`, `src/hub/recovery-error.ts`, `src/hub/db-timeout.ts`, `src/hub/config-reload.ts`, `src/hub/config-apply.ts`, `src/hub/recovery-continuity.ts`, `src/cli/recovery-presentation-options.ts`, `src/cli/setup.ts`, `src/cli/upgrade*.ts`, `src/cli/terminal-recovery.ts`, `src/cli/recovery-package.ts`, `src/cli/facts-hook.ts` or the instance modules (`src/hub/instances.ts`, `src/hub/instance-config.ts`, `src/hub/instance-execution.ts`), or before adding a native peer.
|
|
8
|
+
Scope: the operator console, control WS and its protocol, state files, the dashboard listener and settings, the Claude channel's reconnect, `ahub setup`, git snapshots, upgrade and recovery. Read before editing `src/adapters/definitions/native-recovery*.ts`, `src/adapters/definitions/native-policy-aux.ts`, `src/adapters/definitions/native-config-*.ts`, `src/adapters/definitions/native-cli-*.ts`, `src/cli/console.ts`, `src/cli/console-state.ts`, `src/cli/attention.ts`, `src/ui/needs.ts`, `src/hub/daemon.ts`, `src/hub/ui.ts`, `src/hub/settings.ts`, `src/hub/control-client.ts`, `src/adapters/claude-channel.ts`, `src/hub/restart.ts`, `src/hub/recovery-store.ts`, `src/hub/snapshots.ts`, `src/hub/manager.ts`, `src/hub/lifecycle.ts`, `src/hub/crash.ts`, `src/hub/start-mode.ts`, `src/hub/terminal-open.ts`, `src/hub/recovery-error.ts`, `src/hub/db-timeout.ts`, `src/hub/config-reload.ts`, `src/hub/config-apply.ts`, `src/hub/recovery-continuity.ts`, `src/cli/recovery-presentation-options.ts`, `src/cli/setup.ts`, `src/cli/upgrade*.ts`, `src/cli/terminal-recovery.ts`, `src/cli/recovery-package.ts`, `src/cli/facts-hook.ts`, `src/hub/usage.ts`, `src/hub/memory-profile.ts` or the instance modules (`src/hub/instances.ts`, `src/hub/instance-config.ts`, `src/hub/instance-execution.ts`), or before adding a native peer.
|
|
9
9
|
|
|
10
10
|
- The plugin bundle is installed apart from the daemon. Breaking a control WS message contract bumps `PROTOCOL` in `control-client.ts`; a new request answered as unknown or an optional field preserves it, as does additive optional public task metadata in `ui_snapshot` that older clients ignore (such as `deps` in #246).
|
|
11
11
|
- `peer_stop` is console-only and stops only a hub-owned headless ACP, Pi or local runtime through its adapter. Refuse native-terminal peers and recovery-held mutations; serialize with starts and permission changes, and refuse a recovery `prepare` while a stop runs. An admitted stop holds delivery through `BusOptions.deliveryHeld` before any teardown await (never a console pause that resume can undo, or a global recovery hold that blocks unrelated peers) and withdraws the peer's pending approvals before the adapter stops. A requested stop holds an in-flight delivery as `needs_review` with an ids-only stop reason, suppresses restart/escalation, and never resolves that delivery automatically.
|
|
@@ -16,11 +16,14 @@ Scope: the operator console, control WS and its protocol, state files, the dashb
|
|
|
16
16
|
- A copy of the git index keeps the original's mtime (`snapshot()`): git's racy-entry check compares entries with the index file's time in whole seconds, and a fresh copy makes a same-size edit look clean.
|
|
17
17
|
- State files that clients read (`status.json`, `control-token`) are written after the port is bound, and `status.json` via temp file + rename.
|
|
18
18
|
- Open native memory through the shared profile-aware helper with an explicit current selector binding; never fall back to shared v1 after invalid/missing generation metadata. Check that binding at hub call-site entry and before candidate/counter writes, including callbacks retained across awaits; do not intercept store methods with proxies/private hooks or claim atomic in-flight cutover. Quiesce and close consumers before migration/selection changes, then reopen through the supported lifecycle. Keep the sidecar at the independent memory root, not inside the selected generation. Report memory availability separately from bus health; never hot-rebind or migrate as a startup repair (`docs/memory.md` §Selected memory profile).
|
|
19
|
+
- Take the memory project only from `memoryProjectFor` (the alias-resolved `memoryProjectKey`) in the daemon, `ahub memory` and `ahub goal`; never use a raw `memoryProjectKey` as the project of a store, screen or raw command. The daemon resolves it once at start (falling back to the raw key, so the memory home never blocks startup) and passes both `project` and the raw `projectKey`, so the binding latches unavailable once the alias log maps the key elsewhere; a restart binds the new key.
|
|
20
|
+
- Detect a curated split by `lstat` of `shards.json` (`splitMarked`), never `existsSync`. An inactive, malformed, dangling or unreadable marker throws `MEMORY_SPLIT_INCOMPLETE` before any shard read, and callers show that message instead of the generic profile refusal. On a split home open only through `openShardStore` with the canonical memory dir and a project key; never a plain `MemoryStore`, and refuse without a key.
|
|
19
21
|
- The channel's reconnect loop stops on closes a retry cannot fix (`TERMINAL_CLOSES` in `claude-channel.ts`); a new daemon close code that means "do not come back" belongs there, or two clients fight over it forever.
|
|
20
22
|
- Close 4000 is the one close that ends on its own, so it is not in `TERMINAL_CLOSES`: the session stands by and reconnects only once `status.json` reports the peer offline and not `claiming`. One exception (#205, `peerHeld` in `claude-channel.ts`): a session with channel pushes also reconnects when the holder is reported `toolsOnly` and not `claiming`, and takes the peer; the daemon refuses the reverse with 4000. Reconnecting blind would evict whoever holds the id now, and the two would trade it forever. `claim()` happens at hello and `attach()` only after the preface, so the peer reads as offline in between: the daemon writes the status file at the claim and the flag covers that window.
|
|
21
23
|
- Adding a native peer requires testing the command emitted by the actual recovery driver, not only its inspection or launch helpers. Never let a generic non-Codex branch treat a new peer as Claude.
|
|
22
24
|
- Cross-version recovery must use the authenticated source protocol for prepare/commit/abort and the target protocol after startup. Prove the transition against a real prior release before claiming compatibility.
|
|
23
25
|
- Preserve `task_cancel` target-feature preflight before source holds, capture or commit. Reuse stored target features only after verifying the same retained root and digest; legacy receipts still capable of commit must obtain feature proof. Do not apply pre-commit compatibility checks to sources already past commit. Every prepare, including the lease renewal before a terminal close and after a resume, sends the operation's stored `targetFeatures` (#478).
|
|
26
|
+
- A recovery target-feature floor (`task_cancel`, `peer_instances`, `release_trains` while a configured train is active or frozen, `reviewer_pool` while a fresh reviewer or `review.pool` is configured) lives in `incompatibleRecoveryTarget`, which every existing check site calls, and its name in `DAEMON_FEATURES`; never check a floor at one site only.
|
|
24
27
|
- Defer hub notices only for an authenticated recovery `prepare` or `commit` whose target features include `recovery_deferred_hub`, or for a restored daemon. Otherwise `setRecoveryHold(true)` moves the deferred notices into queues before commit records expected integrity, and a bare `fenceRecovery` defers nothing. Abort and lease expiry release the bus hold before clearing the operation; if that release fails, keep the operation and let expiry retry on its 1 s timer (#478).
|
|
25
28
|
- Resume a prepared `commitSent` source that is unavailable or database-busy inside the existing bounded shutdown poll; never re-send commit, re-prepare or close terminals again on that path. Require confirmed `stopped` before target startup, refuse a replacement instance, and retain the prepared receipt on timeout.
|
|
26
29
|
- Keep restoration receipts explicitly tagged as pending, waiting with origin/binding, done with binding, or failed. Project waiting origins as `waiting:pending` and `waiting:failed`; preserve the chosen binding and origin across interruption instead of inferring completion from an object.
|
|
@@ -59,6 +62,7 @@ Scope: the operator console, control WS and its protocol, state files, the dashb
|
|
|
59
62
|
- Keep full memory home, generation ID, selector revision and malformed-row totals in authenticated person status/CLI receipts, never disk status.json or agent CLI output; low-privilege receipts use a generation prefix and omit home, selector revision and malformed-row totals. Emit typed unbound disabled/unavailable receipts with no profile when no memory handle opened; profile-check may treat an authenticated same-home generation-capable unbound daemon as memory-quiesced, never a stale bound handle. Scope profile-check readiness to its named registry inventory; old, unknown or unreadable receipts refuse readiness, and other custom registries need separate checks.
|
|
60
63
|
|
|
61
64
|
- Bind instance declarations once at startup through the daemon-local `definitionOf` closure and the `extraIds` catalogs; never publish them to the global registry or take them from a request. `instances` is restart-class, and its settings rows come only from `projectSettings(declarations)`.
|
|
65
|
+
- `validateInstances` admits `review_mode: "fresh"` only on a headless instance whose roles are exactly `["reviewer"]` and whose kind declares a fresh reviewer; a fresh reviewer refuses `worktree` and `permission_mode` (variant selection: `adapters.md`). `review.pool` and `review.rerotate` are validated by `reviewerPoolConfig` at startup and in the config schema: 1 to 16 distinct kinds from `REVIEW_POOL_KINDS`.
|
|
62
66
|
- Every start resolves `resolveInstanceExecutionCwd` and passes that `executionCwd` as adapter cwd, sandbox, grant and Pi session root; set `executionOwners` only for the adapter actually started. Extra-instance native state is per instance, while its MCP binding keeps the canonical hub state dir.
|
|
63
67
|
- Recovery: record an extra Codex launch row only after the start reply supplies `executionCwd`; keep `projectRoot`, `executionCwd` and `terminalRoot` separate and never bind an extra by kind metadata. Abort and archive validate receipts without current declarations; restore, fresh and reattach require them; refuse before effects when `peer_instances` is missing. Extra-instance turns write `turn_workspaces` in the `turns` transaction and leave the legacy snapshot fields unknown; undo requires the same trusted declaration and validated root.
|
|
64
68
|
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# Task board and hub tools
|
|
2
2
|
|
|
3
|
-
Scope: the task flow, assignment, the state machine and the hub's MCP tools. Read before editing `src/hub/tasks.ts`, `src/hub/board.ts`, `src/hub/routing.ts`, `src/hub/task-sweep.ts`, `src/hub/settled-roles.ts`, `src/hub/conductor.ts`, `src/hub/supervision.ts`, `src/hub/tool-refusal.ts`, `src/hub/hub-tools.ts` or `src/hub/
|
|
3
|
+
Scope: the task flow, assignment, the state machine and the hub's MCP tools. Read before editing `src/hub/tasks.ts`, `src/hub/board.ts`, `src/hub/routing.ts`, `src/hub/task-sweep.ts`, `src/hub/settled-roles.ts`, `src/hub/conductor.ts`, `src/hub/supervision.ts`, `src/hub/tool-refusal.ts`, `src/hub/hub-tools.ts`, `src/hub/review-brief.ts`, `src/hub/reviewer-pool.ts`, `src/hub/reviewer-pool-config.ts` or `src/hub/release-admission.ts`.
|
|
4
4
|
|
|
5
5
|
- Tool callers are models: MCP `inputSchema` is not enforced on the way in. Normalize at the boundary (`cleanRefs`) before anything reaches the board, and never throw after a board write: a publish or notify that follows a `review()` or `done()` board write goes through the one `releaseNotice` guard, which turns a failed publish into a console notice, drops a failed notify and never retries (#231, #243). An assignment's delivery once its board write is saved (#276, #297): the move is fact, the failed publish is not retried in place (the journal's own recovery covers a storage error, and a retry may duplicate a publish that actually left), and a person looks for a queued or needs_review delivery with `ahub queue list --peer <owner>` (then `ahub queue show <id>`, or the accepted entry in `ahub task show <id>` if the journal is down) before re-sending with `ahub task assign <id> <owner>`. The distinction is made once, in `assignOwner`: a failure after the write prints the one console notice from `undeliveredNotice` right there, once, and throws `AssignmentUndeliveredError` carrying the saved task; callers (`review()`'s repeated rejection, `escalate` and its callers, the ready offer, both relays, the idle sweep, rescreen) decide control flow by the type and print nothing more, and `propose`, `assignTo` and `decline` additionally let the error reach the caller in its turn, which is a person or peer that can act. Two exceptions get no notice, by construction: a row superseded meanwhile, meaning a later history entry that moves or settles the task (another assignment, an unassign, the owner's accept, done, approval, close), while entries that do neither (the hub's quiet bookkeeping: answers, reviewer changes, idle sweeps, check results) leave the assignment undelivered and the notice stands, so the original error goes to the caller as before only on supersession; and a claim's own accepted write and "claimed by" line stay outside the delivery rule (a claim is its own hand-over; its overlap and routing announcements to earlier owners are deliveries and share the wrapper, so a failure there does read as one). A failure before the write stays a plain `Error` (#254), a failed write leaves the ready offer unconsumed (#231), and the notices before the write (waits, no-owner, the reserved-owner line) go through `releaseNotice` too, so those pre-write notices cannot fail their caller.
|
|
6
6
|
- Post-await assignment publication is fenced by current role authority: supersession returns the current task without a saved-delivery notice; reviewer-only changes refresh current metadata and do not suppress a valid owner delivery. A replacement notice goes only to a role that actually received the request, not to an unread-only queue.
|
|
@@ -46,5 +46,8 @@ Scope: the task flow, assignment, the state machine and the hub's MCP tools. Rea
|
|
|
46
46
|
|
|
47
47
|
- `Tasks.review` captures `reviewAuthority` once and rechecks it in `valid()` after every await, right before each write; a headless reviewer's verdict holds only while the same child runs for that task. Never cache the answer (#223).
|
|
48
48
|
- The review coverage gate (#520) pins `refs.commit` and the sdlc receipt and rechecks them in `valid()`; an unavailable brief is never an empty one (enforce refuses, advise notes). Read brief attributes and rules at the merge base (`--attr-source`, `check-attr --source`, `git show <base>:`), never from the head or the checkout.
|
|
49
|
-
- A `routing.reviewOnly` definition is refused as owner inside `assign()`'s eligibility check, for every class, nomination and reservation; keep it there, not at call sites.
|
|
49
|
+
- A `routing.reviewOnly` definition is refused as owner inside `assign()`'s eligibility check, for every class, nomination and reservation; keep it there, not at call sites. A definition with `reviewer.unavailable` is refused as reviewer there too (code `policy`); unavailable never means pass.
|
|
50
|
+
- Reviewer pool (#519): `selectReviewerPool` is pure and called only inside `assign()`, which builds the candidates (declared reviewer-only `routing.reviewOnly` instances) and their blocked reasons; Tasks passes only `reviewPool` (`pool`, `rerotate`, `failed`, `required`). With no fresh reviewer declared and no configured `review.pool`, routing stays the legacy path. The seed is the linked PR number, else the task id; the implementer's family is skipped while another can run, and a delta review keeps the last `changes_requested` family unless `rerotate`. `required` (an explicit `review.pool`) exhaustion goes to the person with `reviewBlocked`; only the optional default pool may fall back to an eligible planner, other family first. Record the choice through `reviewMetadata` (`reviewerFamily`, `reviewerReason`, including `same-family fallback`), never in note text alone. PII tasks, `review`-class tasks and a reviewer a person named (`reviewer changed`, reason `manual`) stay outside the pool.
|
|
51
|
+
- `Tasks.retryReviewer` is host-only (the daemon's reviewer `onUnavailable`), never a tool or CLI path. It acts only while the failed peer is still the reviewer of an `in_review`, non-PII, not manually named task, with the same review head, the request's `reviewRequestRevision` and a current child generation; `valid()` rechecks all of it after every await and right before the write, which also passes the flow revision. Each failed peer is tried once per head and round (`reviewerFailure` history). `reviewRequestRevision` counts only role events (`done`, `reviewer changed`, `reviewer unavailable`, ownership), so bookkeeping never fences a request; keep `refs.reviewRevision` and `refs.commit` on the review envelope.
|
|
52
|
+
- Release trains (#481 S11): change trains only through `Tasks.newRelease`, `configureRelease`, `assignRelease` and `cutRelease` (planner or person), and refuse every `hub_release_*` in a PII turn. `assignRelease` writes only `release` and refuses a frozen, shipped or dropped release on either side; private or pinned scope is person-only. One active or frozen train at a time. A cut freezes only through `Board.freezeRelease` after a fresh `releaseWindow` (host lease from `readReleaseHostAdmission`, quota, `current()`, at most 5 s old); unknown evidence defers, and the window is closed in `finally`. A cut never publishes, tags or upgrades, and upgrade and recovery never consult `releaseWindow`. Keep two of `RELEASE_TRAIN_HISTORY_LIMIT` slots for cut and ship; a configuration no-op appends nothing. An S9 plan receipt survives only contiguous planning revisions that changed exactly `["release"]`.
|
|
50
53
|
- `Board.update`'s untouched-return exception is the only way owned or accepted work returns to backlog: keep every condition (exact event, owner and reviewer null, state, `expectedFlowRevision`, non-empty note, no work evidence) and reach it only through `Tasks.returnUntouchedToBacklog` (#481 S9).
|
|
@@ -16,7 +16,7 @@ Scope: tests, fakes and the test gate. Read before adding or changing a test or
|
|
|
16
16
|
- Run `bun scripts/seeded-check.ts` before reporting the seeded-guard gate complete. Keep the `scripts/seeds.json` cases sequential; require the same named test green before its seeded assertion fails. Seed rot, survivors, setup/compiler failures, timeout/watchdog termination and process leaks fail the gate. Never run the full seed runner from a unit test or mutate the source checkout; inspect preserved failed fixtures before removing them. CI runs it as four sequential Linux shards (`--shard i/4`) beside the ordinary check, separately from `scripts/check.sh`; the required `seeded guards` job passes only when every shard passed, or when the decision job reused a verified identical-tree full gate (`admission == 'reuse'`). Keep that job name: branch protection and `scripts/ci-gates.mjs` read it (#457).
|
|
17
17
|
|
|
18
18
|
- `scripts/check.sh` holds the repo's heavy-job slot (`<git common dir>/ahub-heavy.lock`, #398) for the whole run, so a test never runs the real gate; `test/check-lock.test.ts` runs a copy in a scratch repo.
|
|
19
|
-
- The whole-suite watchdog is `AHUB_CHECK_HANG_S` (default in `scripts/check.sh`, hosted macOS value in `.github/workflows/check.yml`; #378, macOS CI hit it while tests still passed): raise it only when the suite was still progressing at the cutoff, never for a hung test, and keep the per-test 20 s timeout.
|
|
19
|
+
- The whole-suite watchdog is `AHUB_CHECK_HANG_S` (default in `scripts/check.sh`, hosted macOS value in `.github/workflows/check.yml`; #378, macOS CI hit it while tests still passed): raise it only when the suite was still progressing at the cutoff, never for a hung test, and keep the per-test 20 s timeout. The check job's `timeout-minutes` must leave room above that value for setup: raise them together.
|
|
20
20
|
- A seed's named test must run on Linux, where the seeded job runs: a darwin-only test reports no result there and fails the job (#382). Cover a macOS-only protection with its regression test instead.
|
|
21
21
|
|
|
22
22
|
- Keep CI's Bun version sourced from `.bun-version` through the pinned setup-bun action; update the version file rather than adding per-job version literals.
|
|
@@ -25,4 +25,4 @@ Scope: tests, fakes and the test gate. Read before adding or changing a test or
|
|
|
25
25
|
|
|
26
26
|
- Keep the CI admission lock around the whole controller workflow, not just its decision job. Preserve successful identical-tree full proof and required job names; a skipped job, pending controller or unavailable active inventory is never a successful full gate. CI reuse tests use fixed metadata fixtures and never dispatch real duplicate workflows.
|
|
27
27
|
|
|
28
|
-
- Headless-reviewer tests run `test/fakes/fake-claude-print.ts` through `command` or a PATH stand-in
|
|
28
|
+
- Headless-reviewer tests run `test/fakes/fake-claude-print.ts` through `command` or a PATH stand-in, and fresh Codex reviewer tests run `test/fakes/fake-codex-reviewer.ts` through `codex_bin`; a test that reaches the daemon's reviewer start must never run the real `claude` or `codex`. The fake's sandbox markers test host admission only, never native isolation.
|
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.
|
|
3
|
+
Agent-hub consumes the exact-pinned first-party [`@staix/agent-mem`](https://github.com/STAIxBWLB/agent-mem) library (current pin `0.4.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. Optional claude-mem remains a separate capture and session-recall source.
|
|
4
4
|
|
|
5
5
|
## Portable core and data directory
|
|
6
6
|
|
|
@@ -12,6 +12,8 @@ The [agent-mem repository](https://github.com/STAIxBWLB/agent-mem) is the refere
|
|
|
12
12
|
|
|
13
13
|
The memory root `M` is independent of the project state directory. The package's `M/profile.json` selects shared v1 or an explicit generation `G`; an absent selector means shared v1. The hub opens the selected profile with its current privacy screen and configured TTL. A generation uses `M/generations/G`, reads legacy prefixes through the package's readonly bridge, and writes its own rows and sync history there. The bridge does not migrate legacy bytes or use the legacy index. Exact-entry prune remains the package's person-only operation; it adds no shared-v1 operation and gives hub tools no prune authority.
|
|
14
14
|
|
|
15
|
+
A curated split (agent-mem#15 T2, `amem migrate --shards`) writes `M/shards.json`. Once it is active, the hub opens memory through the package's `openShardStore` for the project's key: `memoryProjectKey` of the hub's project, resolved through the person-reviewed alias log of the memory home, the same key the `amem` CLI binds. The daemon and `ahub memory`/`ahub goal` read and write under that key, split or not. A running hub resolves the key once at start; when `amem alias add` later maps that key elsewhere, its binding latches unavailable at the next memory entry, and a restart binds the new key. That store is this project's shard, with the user shard as its overlay. Other projects' private notes are not in it. A project first seen after the split gets its own shard on first open. The binding compares the shard identity (key, sealed generation, selector revision) at every entry. A split marker that is not active (captured, malformed, or a dangling or unreadable `shards.json`) refuses with `amem migrate --shards --resume` as the remedy and is never read as an empty or legacy store. A marker that appears under an open unsplit binding latches that binding unavailable. Without a marker, the hub opens memory exactly as before. `ahub memory profile-check` is not split-aware yet: on a split home it compares running hubs against the legacy selector's profile and reports a mismatch for every correctly bound hub (#536).
|
|
16
|
+
|
|
15
17
|
All hub consumers share that binding: Tasks notes and lifecycle operations, leases, suggestions, briefs, recall, peer read tools, dashboard, ask and automatic sync. Standalone `amem` and `ahub memory`/`ahub goal` must resolve the same root and profile to see one another's writes. An invalid selector or missing/corrupt generation disables native memory without taking down the bus or task board; it never silently opens shared v1. The daemon reports its package/profile binding and availability separately from ordinary hub health.
|
|
16
18
|
|
|
17
19
|
Person-facing status and memory output show the profile and nonzero malformed-row count. Allowed agent-shell reads retain current row screening and receive only a short profile label; full generation IDs, the memory home and home-wide skipped totals are available through authenticated person status and person CLI receipts, never the shell-readable status.json file or agent CLI output. Domain JSON shapes are unchanged; the additive `memoryProfile` receipt belongs to status, profile-check and the person-only decay object. Profile-check reports the registry scope it inspected; inventory separate custom registries too before treating zero live claims as quiescence.
|
package/docs/operations.md
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# Operations guide
|
|
2
2
|
|
|
3
|
-
This guide describes ahub 0.12.
|
|
3
|
+
This guide describes ahub 0.12.37 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
|
|
@@ -971,7 +971,7 @@ ahub memory forget 1a2b3c4d # a person's; also promote, demote, sup
|
|
|
971
971
|
|
|
972
972
|
`"memory": { "native": false }` in `.agenthub/config.json` turns it off for a project.
|
|
973
973
|
|
|
974
|
-
The exact-pinned `@staix/agent-mem` 0.
|
|
974
|
+
The exact-pinned `@staix/agent-mem` 0.4.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.
|
|
975
975
|
|
|
976
976
|
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`; `amem review` on a generation is still refused. 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).
|
|
977
977
|
|
|
@@ -979,6 +979,8 @@ Run `ahub memory profile-check [--json]` from the person's terminal for the read
|
|
|
979
979
|
|
|
980
980
|
Before real migration, first upgrade every hub sharing the home to 0.12.30 or later. Obtain positive ready or unbound/quiesced receipts in every relevant registry; then quiesce consumers and close bound handles before changing the selector. A matching ready handle proves compatibility but is still an open handle. Verify synthetic cross-writes before the separately approved real migration, then reopen through the supported session-preserving lifecycle afterward. Merely installing this release does not authorize migration or provide live capture/device-sync evidence.
|
|
981
981
|
|
|
982
|
+
A curated split (`amem migrate --shards`) writes `M/shards.json`; from 0.12.37 the hub then opens this project's shard, with the user shard as its overlay, under the alias-resolved project key the `amem` CLI binds (#534, #537). Before splitting, upgrade or stop every hub using the home: a hub on 0.12.36 or earlier does not detect the split and keeps writing the old layout, which reaches the shards only through `amem migrate --shards --resume`. A split that is not active (captured, malformed, or a dangling or unreadable `shards.json`) makes native memory unavailable with that resume command as the remedy. An `amem alias add` that remaps a running hub's key latches its memory unavailable until a restart. `ahub memory profile-check` is not split-aware yet: on a split home it reports a mismatch for every correctly bound hub (#536). See [the shard binding](memory.md#selected-memory-profile).
|
|
983
|
+
|
|
982
984
|
Managed agents read through `hub_memory_search`, `hub_memory_show` and `hub_memory_graph`, with the server's project identity, the `memory_read` capability and current privacy screening. They cannot supply another project; private turns are refused. The tools bound UTF-8 input/output, screen before counts and truncation, and exclude their arguments/results from hub-managed capture. `hub_memory_correct` has its own `memory_correct` capability and records a screened correction candidate, not a note change. See [the complete memory contract](memory.md) for caps and refusal semantics.
|
|
983
985
|
|
|
984
986
|
Review candidates and rendered/delivered/cited counters stay in machine-local `M/candidates.db`, outside sync, import, recall, briefs, ask and graph evidence. Public changes-requested reviews with unmet items may propose a fail candidate; missing cause/retry details stay unknown. Shared-v1 acceptance is a person's `amem review` action with its TTY/policy checks; selected generations still refuse that command. Read-time `(stale?)` hints and plan-fail advice never change stored rows or task authority. Structured fail notes are validated against 4000 UTF-8 bytes before screening or writes, with no silent truncation.
|
|
@@ -1978,21 +1980,21 @@ section is what they run.
|
|
|
1978
1980
|
|
|
1979
1981
|
Upgrade running projects with the target release's own coordinator. It accepts
|
|
1980
1982
|
a running source on control protocol 9 (0.6.x), 10 (0.7.0 through 0.12.0),
|
|
1981
|
-
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.12.
|
|
1983
|
+
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.12.37), and only
|
|
1982
1984
|
a target on its own protocol, so the target's coordinator fits every supported
|
|
1983
1985
|
source and carries every recovery fix released up to it. Protocol 8 and older
|
|
1984
1986
|
(0.5.x and earlier) are refused as `manual-bootstrap-required`. Run from the
|
|
1985
1987
|
project directory, without replacing the global CLI first:
|
|
1986
1988
|
|
|
1987
1989
|
```bash
|
|
1988
|
-
bunx --package @staix/agent-hub@0.12.
|
|
1989
|
-
bunx --package @staix/agent-hub@0.12.
|
|
1990
|
+
bunx --package @staix/agent-hub@0.12.37 ahub upgrade --to 0.12.37 --dry-run
|
|
1991
|
+
bunx --package @staix/agent-hub@0.12.37 ahub upgrade --to 0.12.37 --yes
|
|
1990
1992
|
```
|
|
1991
1993
|
|
|
1992
1994
|
| Running now | Coordinator to use |
|
|
1993
1995
|
| --- | --- |
|
|
1994
1996
|
| 0.6.x (protocol 9) | the target's, through `bunx` as above |
|
|
1995
|
-
| 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.12.
|
|
1997
|
+
| 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.12.37 (protocol 16) | the target's, through `bunx` as above |
|
|
1996
1998
|
| any supported source, with the installed CLI already at the target | `ahub upgrade` below, which is the same coordinator |
|
|
1997
1999
|
| 0.5.x or earlier (protocol 8 and older) | not supported: bootstrap by hand with the matching CLI |
|
|
1998
2000
|
|
|
@@ -2024,14 +2026,14 @@ projects first:
|
|
|
2024
2026
|
|
|
2025
2027
|
```bash
|
|
2026
2028
|
ahub restart --dry-run
|
|
2027
|
-
ahub upgrade --to 0.12.
|
|
2029
|
+
ahub upgrade --to 0.12.37 --dry-run
|
|
2028
2030
|
```
|
|
2029
2031
|
|
|
2030
2032
|
Apply only after reviewing the plan:
|
|
2031
2033
|
|
|
2032
2034
|
```bash
|
|
2033
2035
|
ahub restart --yes
|
|
2034
|
-
ahub upgrade --to 0.12.
|
|
2036
|
+
ahub upgrade --to 0.12.37 --yes
|
|
2035
2037
|
ahub recovery status [<operation-id>]
|
|
2036
2038
|
ahub recovery resume [<operation-id>]
|
|
2037
2039
|
ahub recovery abort [<operation-id>]
|
|
@@ -3063,7 +3065,7 @@ that the file is untracked; a dropped value is reported by the settings result.
|
|
|
3063
3065
|
|
|
3064
3066
|
## SDLC artifact receipts and gates
|
|
3065
3067
|
|
|
3066
|
-
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). S2-S7
|
|
3068
|
+
The playbook lifecycle (#257, #481) is off without `.agenthub/sdlc.toml`. When enabled its stages are intent, spec, plan, build, test, review, merge, release and maintain, grouped as Plan, Design, Build, Test, Deploy and Maintain. Advise shows blockers; enforce checks implemented head-bound review and approval gates. Stored implement/verify receipts read as build/test; new enabled receipts use canonical ids. See [the SDLC contract](sdlc.md) for gates, required check names, local receipts, console-only readbacks and the S0-S8 delivery table. A person accepts a spec issue with `ahub sdlc accept <task> --issue <n>` (S1). The planner records plan reviews with review and CI scope (`hub_plan_review`, `ahub sdlc plan-review`) and grooms with `ahub backlog groom`; `ahub backlog return <task> <revision> <reason>` returns accepted, untouched work (S9). `ahub sdlc costs [owner/repo]` reports review rounds and CI runs of merged PRs without writing the board (S10). S11 release trains ship in 0.12.37 (see [release scheduling](#planner-release-scheduling)). S2-S7 and a full live advise/release cycle remain pending. Owners/reviewers record bounded identities through `hub_sdlc_record`; these grant no additional authority. Off output, receipt and schema shapes stay unchanged.
|
|
3067
3069
|
## Optional Qwen Code peer
|
|
3068
3070
|
|
|
3069
3071
|
Select Qwen per project with `ahub roster add qwen`; it defaults off and is absent
|
|
@@ -3096,7 +3098,7 @@ Use ahub roster add jcode, then ahub jcode --mode headless [--model <alias>] [--
|
|
|
3096
3098
|
After upgrading to the recipient-retry release, a Jcode project MCP entry written before `AGENTHUB_DELIVERY_ONLY` was added is intentionally stale. Once per project, the person runs `ahub jcode --mode headless` before using conductor/dashboard starts. The launcher rewrites only the owned entry with the delivery-only marker, preserving foreign entries and refusing collisions; recovery relaunches already use this CLI path. Readiness checks never rewrite the file, and an explicit false marker or any other drift remains refused. Until that rewrite, daemon starts keep their actionable stale-entry refusal rather than advertising a retry tool Jcode cannot use.
|
|
3097
3099
|
|
|
3098
3100
|
|
|
3099
|
-
Native policy is unmanaged and has NO hub sandbox; no permission_modes.jcode or runtime mode mapping is invented. Quota and consumption remain unknown.
|
|
3101
|
+
Native policy is unmanaged and has NO hub sandbox; no permission_modes.jcode or runtime mode mapping is invented. Quota and consumption remain unknown. Supported versions run from 0.98.0 up to, not including, 1.0.0 (#529). Every owned jcode process (version probe, daemon, ACP) runs with `JCODE_NO_TELEMETRY=1`. Native session/load (verified on 0.94.0 and 0.98.1) cannot prove identity, so attached Jcode blocks controlled recovery before effects and crash recovery asks for deliberate native reopening. A requested load without an echoed identity refuses; no fresh session is presented as restored. Provider/tool/native teardown receipts remain unverified in the pinned contract report.
|
|
3100
3102
|
|
|
3101
3103
|
# Headless ACP watch (#286)
|
|
3102
3104
|
|
|
@@ -3547,6 +3549,30 @@ A missing
|
|
|
3547
3549
|
`peer_instances` feature on an old daemon or upgrade target requires a supporting
|
|
3548
3550
|
version before launching or restoring extras.
|
|
3549
3551
|
|
|
3552
|
+
### Fresh reviewer pool (#519)
|
|
3553
|
+
|
|
3554
|
+
From 0.12.37 reviews can rotate across fresh headless Claude and Codex reviewers. Declare reviewer-only instances with explicit headless mode and the Codex fresh opt-in in `.agenthub/config.local.json`:
|
|
3555
|
+
|
|
3556
|
+
```json
|
|
3557
|
+
{
|
|
3558
|
+
"instances": {
|
|
3559
|
+
"claude-review": { "kind": "claude", "roles": ["reviewer"], "start_mode": "headless" },
|
|
3560
|
+
"codex-review": { "kind": "codex", "roles": ["reviewer"], "start_mode": "headless", "review_mode": "fresh" }
|
|
3561
|
+
},
|
|
3562
|
+
"review": { "pool": ["claude", "codex"], "rerotate": false }
|
|
3563
|
+
}
|
|
3564
|
+
```
|
|
3565
|
+
|
|
3566
|
+
Start them with `ahub claude --as claude-review` and `ahub codex --as codex-review`; stop with `ahub stop <id>`. Fresh variants share the project checkout and 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 #223 automatic headless-reviewer declaration. The agy metadata placeholder remains automatically unavailable. 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 as fixed configured kind slots; undeclared/unavailable slots are skipped after modulo without renumbering; `review.rerotate` defaults to false. Pool selection starts at PR number modulo the fixed configured kind count, 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.
|
|
3567
|
+
|
|
3568
|
+
Codex requires an installed 0.146.0 executable and a passing bounded runtime receipt before its model child starts. Use the existing `codex_bin` setting to select a supported host executable when PATH resolves another version. PATH 0.163.0 has no receipt in this slice and remains unavailable. The producer checks native binary/config stability, configured MCP inventory with only the two hub tools, effective Apps/plugins/hooks disablement 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.
|
|
3569
|
+
|
|
3570
|
+
Kimi and agy are metadata-only unavailable candidates, outside the default pool. agy will not become a reviewer (person decision, 2026-10-19); it is planned as an implementer peer under [#485](https://github.com/STAIxBWLB/agent-hub/issues/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 the unavailable Kimi/agy placeholders (a Kimi fresh declaration also needs `review_mode: "fresh"`); their reason appears as an unavailable skip and they never run or count as approval. Kimi launch/isolation work is deferred to [#533](https://github.com/STAIxBWLB/agent-hub/issues/533), hub task #234; agy code belongs in `src/adapters/agy/`. Do not substitute plan mode for isolation.
|
|
3571
|
+
|
|
3572
|
+
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 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. 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.
|
|
3573
|
+
|
|
3574
|
+
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 #223 Claude behavior has no new live proof here.
|
|
3575
|
+
|
|
3550
3576
|
An additional implementer must have an existing registered Git worktree and
|
|
3551
3577
|
attached branch at `.worktrees/<instance-id>`, named explicitly by `worktree` in
|
|
3552
3578
|
its declaration. The hub validates it and does not create it in this phase.
|
|
@@ -3569,3 +3595,9 @@ fields as unknown and cannot restore them into the main checkout. The supporting
|
|
|
3569
3595
|
CLI checks the saved workspace against the current declaration again before any
|
|
3570
3596
|
context change or file restoration. Removing the declaration intentionally
|
|
3571
3597
|
blocks that undo until the identity and workspace can be reviewed.
|
|
3598
|
+
|
|
3599
|
+
## Planner release scheduling
|
|
3600
|
+
|
|
3601
|
+
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.
|
|
3602
|
+
|
|
3603
|
+
The per-release "gates run" record is deferred to S6/S7. Person ruling 2026-10-19 supersedes the earlier S11 upgrade-admission ruling: upgrades use pre-S11 recovery readiness (no busy peers, pending approvals resolved, completion checks settled) and retain the existing operation checks. S11 adds no host-admission, quota or all-idle requirement to upgrade. An active/frozen configured train still requires recovery-target `release_trains` compatibility. Release-cut admission remains unchanged: follow its refusal's applicable exit by stopping or resuming the paused peer, adjusting `ahub budget set`, or using `ahub release drop`. Missing/shadowed `dot` requires the resource controller or a PATH correction for the cut; nested admission requires retrying the cut outside the outer lease.
|
package/docs/quickstart.md
CHANGED
|
@@ -17,7 +17,7 @@ ahub setup # installs the Claude Code channel plu
|
|
|
17
17
|
Or use the matching GitHub release:
|
|
18
18
|
|
|
19
19
|
```bash
|
|
20
|
-
bun add -g github:STAIxBWLB/agent-hub#v0.12.
|
|
20
|
+
bun add -g github:STAIxBWLB/agent-hub#v0.12.37
|
|
21
21
|
ahub setup
|
|
22
22
|
```
|
|
23
23
|
|
|
@@ -30,15 +30,15 @@ and upgrade or crash recovery, see the [operations guide](operations.md). To
|
|
|
30
30
|
move running projects to a newer release later, run `ahub upgrade` in a
|
|
31
31
|
terminal: it reviews the plan with you, applies it and follows it.
|
|
32
32
|
|
|
33
|
-
## Upgrading to 0.12.
|
|
33
|
+
## Upgrading to 0.12.37
|
|
34
34
|
|
|
35
|
-
Restart Claude sessions after upgrading: each keeps the plugin server it started with, and
|
|
35
|
+
Restart Claude sessions after upgrading: each keeps the plugin server it started with, and the planner gets the release-train tools (`hub_release_new`, `hub_release_assign`, `hub_release_configure`, `hub_release_cut`, `hub_release_report`); the person uses `ahub sdlc train report|configure|cut` (#481 S11). A due date asks for a cut review and never clears unfinished work; a cut freezes scope and publishes nothing. See [release scheduling](operations.md#planner-release-scheduling).
|
|
36
36
|
|
|
37
|
-
|
|
37
|
+
Reviews can rotate across fresh Claude and Codex reviewer sessions (#519). Declare headless reviewer-only instances in `.agenthub/config.local.json`; Codex joins only with `review_mode: "fresh"` and an installed Codex 0.146.0 (select it with `codex_bin` when PATH resolves another version). A headless reviewer instance without `review_mode` keeps its native behavior and stays out of the pool. Kimi has no fresh reviewer yet (#533). agy will not be a reviewer: it is planned as an implementer peer (#485) and is not in this release. Naming either in `review.pool` shows an unavailable skip in `ahub route explain`, never a review. Once a train is active or frozen, or `review.pool` is set or fresh reviewer instances are declared, `ahub upgrade` refuses a target release without that support, so an older release cannot take the project over. See [fresh reviewer pool](operations.md#fresh-reviewer-pool-519).
|
|
38
38
|
|
|
39
|
-
|
|
39
|
+
Jcode must be 0.98.0 or later, below 1.0.0; 0.94.0 is no longer accepted, so `ahub doctor` fails its Jcode row for an older install and the peer does not start (#529). Every owned jcode process runs with `JCODE_NO_TELEMETRY=1`.
|
|
40
40
|
|
|
41
|
-
|
|
41
|
+
The hub now pins `@staix/agent-mem` 0.4.0 and opens a split memory home through its shards (#534). Before anyone runs `amem migrate --shards` on a memory home, every hub using that home must run 0.12.37 or later: an older hub does not see the split and keeps writing the old layout. `ahub memory profile-check` is not split-aware yet and reports a mismatch for every correctly bound hub on a split home (#536). See [memory profiles and rollout](memory.md#selected-memory-profile).
|
|
42
42
|
|
|
43
43
|
From 0.12.30 or earlier, existing Jcode project MCP entries need one refresh before conductor or dashboard starts. Once per project, the person runs:
|
|
44
44
|
|
|
@@ -102,7 +102,7 @@ More: `ahub help`. How it is built and why: [`docs/specs/2026-09-19-agent-hub-de
|
|
|
102
102
|
|
|
103
103
|
## Shared memory
|
|
104
104
|
|
|
105
|
-
The hub exact-pins the first-party `@staix/agent-mem` 0.
|
|
105
|
+
The hub exact-pins the first-party `@staix/agent-mem` 0.4.0 core. Existing shared-v1 memory remains the default; upgrading does not migrate it. Managed agents use screened `hub_memory_search`, `hub_memory_show` and `hub_memory_graph` tools, or propose a correction with `hub_memory_correct`; only a person accepts candidates through the supported `amem review` path. Generation candidate acceptance is still refused.
|
|
106
106
|
|
|
107
107
|
Before any real generation migration, upgrade every hub sharing that memory home to 0.12.30 or later and check its authenticated binding with `ahub memory profile-check` in each custom registry. Ready bindings prove compatibility, not an atomic live cutover: quiesce consumers and close their handles before migrating, then reopen through the supported session-preserving lifecycle. Invalid or changed profiles disable native memory without falling back to legacy writes; the bus and board continue. See [memory operations](operations.md#hub-memory) for the remaining rollout gates.
|
|
108
108
|
|
package/docs/sdlc.md
CHANGED
|
@@ -64,7 +64,7 @@ The acceptance-criteria checklist belongs to the Compliance pass over the same b
|
|
|
64
64
|
|
|
65
65
|
## Delivery and verification state
|
|
66
66
|
|
|
67
|
-
Head-bound review/approval gates, bounded custom definitions, read-only merge/release reports, S0/S8 vocabulary/checklist migration, S1 spec acceptance and S9 planner admission and S10 cost governance are implemented in source. S2-S7 and
|
|
67
|
+
Head-bound review/approval gates, bounded custom definitions, read-only merge/release reports, S0/S8 vocabulary/checklist migration, S1 spec acceptance and S9 planner admission and S10 cost governance are implemented in source. S11 release-train governance ships in 0.12.37. S2-S7 and the native advise-through-merge/release cycle remain pending. Installation, native acceptance and release authorization require separate receipts.
|
|
68
68
|
|
|
69
69
|
## Initial commands
|
|
70
70
|
|
|
@@ -206,7 +206,7 @@ The connection idle setting is not a guaranteed 10-second deadline for an active
|
|
|
206
206
|
|
|
207
207
|
A configured planner reads `hub_planner_groom` (or the person runs `ahub backlog groom`) before choosing build work. The digest reports missing text-only dependencies, repository-scoped duplicate issues and path/symbol overlaps, over-WIP owners, recorded supersession, issue edit/closed-state evidence and capability, queue or offline blockers. It proposes no board mutation. Missing repository or issue evidence stays unknown; the planner still evaluates issue rulings and the person's priorities. Private task text and references are withheld.
|
|
208
208
|
|
|
209
|
-
The planner records `hub_plan_review` with the task id, current `planningRevision` and a reason. The person's CLI is `ahub sdlc plan-review <task> <revision> <reason...>`. Tasks records the actor, planning revision, observation time and host-read issue identity/edit time. The fixed GraphQL query requests metadata only, never an issue body or comments; comment-only activity does not invalidate the body-edit receipt. Caller-supplied timestamps are refused. A changed revision
|
|
209
|
+
The planner records `hub_plan_review` with the task id, current `planningRevision` and a reason. The person's CLI is `ahub sdlc plan-review <task> <revision> <reason...>`. Tasks records the actor, planning revision, observation time and host-read issue identity/edit time. The fixed GraphQL query requests metadata only, never an issue body or comments; comment-only activity does not invalidate the body-edit receipt. Caller-supplied timestamps are refused. A changed revision requires another review except for the complete contiguous release-only history described under S11; issue identity/edit and cost-scope checks still apply. An issue edit requires another review; unavailable metadata never counts as a current review. The native CLI cannot delegate this person-attributed write through `--live-test`.
|
|
210
210
|
|
|
211
211
|
For implementation/bulk-edit tasks and an explicit build-base stage, enforce refuses an offer or acceptance without a current receipt. Advise reports the gap and off performs no lifecycle gate. Spec acceptance and planner review are independent readbacks bound to one task snapshot before acceptance. The ordinary synchronous acceptance path cannot bypass the enforce readback. A self-claim can remain assigned and proposed while its prerequisites are missing, with no offer or accepted event. A saved planner review can release previously held proposed work; delivery failure leaves that saved receipt intact.
|
|
212
212
|
|
|
@@ -225,7 +225,7 @@ S9 ceilings: a private build task with an issue reference cannot satisfy enforce
|
|
|
225
225
|
|
|
226
226
|
At plan review, batch small related fixes into one area branch and PR; keep release preparation separate from feature work. A plan can name `review: {scope: "none" | "delta" | "full", reviewers: N}` and `ci: "focused" | "full" | "reuse"`. None requires zero reviewers; delta/full require one to eight. These are expected costs, not permission to bypass the task's reviewer, CI or privacy gates. Use one reviewer for a small change; split a large review by area with one receipt owner. After a fix, review the delta against the previous findings. S2 will compare planned scope with completed coverage.
|
|
227
227
|
|
|
228
|
-
The planner can provide scope with `hub_plan_review`; the person can run `ahub sdlc plan-review <task> <revision> <reason...> --review full --reviewers 1 --ci full`. Tasks records the explicit scope on the plan and its revision-bound receipt. A file-list update retains omitted cost fields; an explicit scope change cannot replace a reviewed scope during enforce acceptance. Old plans and receipts remain unspecified and are never filled by guessing. Advise reports missing scope. In enforce, every newly recorded receipt needs both scopes; an existing scope-less receipt remains valid until
|
|
228
|
+
The planner can provide scope with `hub_plan_review`; the person can run `ahub sdlc plan-review <task> <revision> <reason...> --review full --reviewers 1 --ci full`. Tasks records the explicit scope on the plan and its revision-bound receipt. A file-list update retains omitted cost fields; an explicit scope change cannot replace a reviewed scope during enforce acceptance. Old plans and receipts remain unspecified and are never filled by guessing. Advise reports missing scope. In enforce, every newly recorded receipt needs both scopes; an existing scope-less receipt remains valid until a non-release planning revision changes, with the same complete-history exception under S11. Enforce acceptance cannot add owner-supplied cost keys to that legacy receipt; the planner must record an explicit scope first.
|
|
229
229
|
|
|
230
230
|
A review request is premature if its task head does not match the PR head, configured required checks are not green, or a pending bot thread is unanswered. A resolved thread is settled; an unresolved one needs a user reply after its last bot comment. The hub reads identity, timestamps, authorship type and resolution only, never comment bodies. Without explicit required checks the native floor is both OS checks plus `seeded guards`. Advise reports the gap; enforce refuses before the requesting done/cohort writes. A private turn may finish its own PII task in advise with a generic unknown-readiness notice and no remote metadata read; the private-turn refusal for ordinary work remains. Delayed checks revalidate before completing. Background roster/quota role changes remain fact, but enforce holds a new review envelope without an admission readback; refresh it through a manual reviewer assignment after CI and replies are ready. The brief and verdict coverage belong to #520, separate from this admission.
|
|
231
231
|
|
|
@@ -234,3 +234,29 @@ CI controllers are serialized across the repository for the entire workflow, wit
|
|
|
234
234
|
`hub_sdlc_costs` and the person's `ahub sdlc costs [owner/repo]` report linked merged PRs without changing the board. A review round is a board done-to-verdict handoff, not each reviewer in a multi-reviewer round. An observed handoff still counts when its declared scope was none. The report counts distinct check-workflow run ids, extra attempts (`run_attempt - 1`), full re-reviews, delta rounds and summed review intervals, with GitHub review counts alongside them. Unspecified scope and unavailable evidence stay unknown/incomplete. Only current public links are projected; a private counterpart withholds the whole PR. Up to 16 linked PR reads share 20 seconds; each PR covers at most 100 commits, reviews and recent check-workflow runs, and the pure aggregator bounds 500 tasks, 200 PRs and 10,000 history entries. Truncation is explicit. S10 exposes pure band-breach proposal data; S7 owns rolling baselines, band configuration, automatic scheduling and board-card creation.
|
|
235
235
|
|
|
236
236
|
S10 policy ceilings are tracked in hub task #230: metadata failures remain fail-closed; the repository-wide queue can delay newer heads and releases; unknown thread author kinds remain unknown in enforce; and advise still retries concurrent state/authority changes. These policies are deferred, with no silent relaxation in this slice.
|
|
237
|
+
|
|
238
|
+
# Planner release trains (S11)
|
|
239
|
+
|
|
240
|
+
Release scope stays on each task's existing `release` planning field. `hub_release_new` creates a future planning row; `hub_release_configure` activates it, sets its planned cut date, or records an enumerated hotfix cause. Only one train may be active or frozen. Future planning rows remain available so unfinished work has a next-train destination.
|
|
241
|
+
|
|
242
|
+
`hub_release_assign` changes only a task's release, including approved or in-flight work. It retains execution state, owner, reservations and dependencies. It requires the current planning revision and a reason. Public reasons are screened; private or pinned work requires the person. A private reason stays on the board with a withheld history marker and never goes to the model screen, including when approved work is rolled out of a train. Public readiness/cut still refuses a scope containing private work; frozen or settled scope cannot move through this operation. Release-only planning revisions retain the S9 plan review only when the complete intervening planning history changes exactly the release field; other planning changes still invalidate it. The existing general backlog planning rules remain unchanged.
|
|
243
|
+
|
|
244
|
+
Use `ahub sdlc train report <release-id>` to see planned task identities, approval readiness, due trigger and recorded release metrics. `ahub sdlc release-assign <task> <revision> <release-id|none> <reason...>` is the person's CLI for assignment. A planner uses the corresponding hub tools under its own identity. `ahub sdlc train configure <release-id> <revision> '{"state":"active","cut":1792500000000}'` activates a future train and optionally sets the UTC epoch-millisecond cut date.
|
|
245
|
+
|
|
246
|
+
A train becomes due when its tasks are approved or its cut date arrives. A date never clears an unfinished task: move it to the next planning row with a reason before cutting. `hub_release_cut`, or the person's `ahub sdlc train cut <release-id> <revision>`, freezes a nonempty, fully approved scope. Scope and authority are checked again after cost/admission reads and in the final database transaction. Cancellation does not count as approval.
|
|
247
|
+
|
|
248
|
+
The cut obtains a short repository lease through the installed host `dot admit` controller, checks the shared gate lock, and retains the lease until scope freeze. The controller is resolved outside the project, its command is fixed, and missing/expired/unknown admission defers. The daemon checks fresh account quota headroom for attached peers with a real source (a registered `budget.reader`, or an enabled soft-token limit), including shared instance accounts; unknown/stale readings from those sources defer. Registered accounts without a configured source report `no quota source` rather than deferring on absent readings; an unknown external account still requires evidence. Budget-record and manual pauses still defer for every attached peer. This metadata lease does not authorize a build. Each later heavy preparation step needs its own admission. The cut neither publishes nor tags, and it never substitutes for S6's person authorization or native verification.
|
|
249
|
+
|
|
250
|
+
A hotfix records one cause and why it cannot wait: `loss`, `security_privacy`, `upgrade_start_blocked`, or `core_flow_no_workaround`. These mean data/message loss, a security/privacy defect, blocked upgrade/start, or a broken core flow without a workaround. Hotfixes keep every scope, quota, resource, review and CI gate. Their branch starts at main and contains the fix and regression only; unrelated work belongs in a future train.
|
|
251
|
+
|
|
252
|
+
Release history records the frozen task ids and actual cut/shipment times. CI resource counts read only the planned scope's PRs at cut (at most 16 distinct PRs in one repository, run attempts included); failed reads or a larger/mixed-repository scope leave the count unknown; board review verdict counts are recorded separately. Missing PR links, incomplete reads and legacy history remain unknown. Metrics derive shipment intervals, approval-to-shipment lead time, hotfix fraction among shipped releases, and recorded resource counts. They do not infer success from an absent record. Release-preparation PR costs and future S7 band automation are separate evidence, not guessed counts. The per-release "gates run" record is deferred to S6/S7; frozen scope and resource counts do not prove that gates ran.
|
|
253
|
+
|
|
254
|
+
Legacy release rows omit train metadata. The old off-mode planning APIs keep their shape; explicit train operations always enforce their stated board/resource rules. Enabled lifecycle freeze, and any configured train freeze, must use the admitted cut path. Ship/drop remain person-only. The existing person-selected upgrade/recovery operation and its readiness/completion checks remain separate from a cut; a source gate is not an upgrade receipt.
|
|
255
|
+
|
|
256
|
+
Person ruling 2026-10-19 supersedes the earlier S11 upgrade-admission ruling: upgrades remain outside S11 host-admission, quota and additional all-idle gates. Recovery uses its pre-S11 `recoveryReady()` checks: no busy peers, pending approvals resolved and completion checks settled, with the existing operation-identity and lifecycle fences. A quota/manual pause, missing or shadowed `dot`, or an outer same-repository admission lease adds no S11 upgrade refusal. The person-selected operation remains the confirmation; no extra receipt or agent upgrade authority is added. Release-cut host and quota admission remains unchanged.
|
|
257
|
+
|
|
258
|
+
Configured cut dates must be positive safe UTC millisecond timestamps; the train tool does not accept null to clear a date. A configured active/frozen train also requires a recovery target advertising `release_trains`, so an older daemon cannot silently drop the safeguards after restart. Target proof is checked before preparation and again before commit; queued future planning rows and legacy rows retain their existing compatibility.
|
|
259
|
+
|
|
260
|
+
Release-cut refusals name the applicable exits: stop or resume a paused peer, adjust `ahub budget set`, or drop the train with `ahub release drop`. Install the resource controller or fix PATH when `dot` is unavailable or shadowed; retry the cut outside an outer admission lease when nested admission is refused.
|
|
261
|
+
|
|
262
|
+
Train history retains at most 256 events. Configuration no-ops retain the revision and history; meaningful configuration reserves the final two slots for cut and shipment. Capacity refusals occur before effects; an already exhausted legacy train requires a person-selected replacement.
|