@staix/agent-hub 0.26.0 → 0.27.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +7 -0
- package/README.md +4 -3
- package/docs/agent-notes/daemon.md +1 -1
- package/docs/agent-notes/tasks.md +2 -2
- package/docs/dogfood.md +3 -1
- package/docs/operations.md +31 -5
- package/docs/profiles.md +1 -0
- package/docs/quickstart.md +5 -1
- package/docs/sdlc.md +3 -3
- package/docs/security.md +1 -1
- package/docs/specs/2026-09-19-agent-hub-design.md +1 -1
- package/docs/specs/2026-11-01-research-campaigns-design.md +12 -1
- package/docs/verification/2026-11-01-0.27.0.md +49 -0
- package/docs/verified.json +12 -12
- package/package.json +1 -1
- package/plugins/agent-hub/.claude-plugin/plugin.json +1 -1
- package/plugins/agent-hub/server.js +9 -7
- package/src/cli/help.ts +2 -1
- package/src/cli/identity.ts +5 -5
- package/src/cli/main.ts +6 -1
- package/src/cli/study-output.ts +56 -0
- package/src/cli/study.ts +26 -3
- package/src/hub/action-policy.ts +5 -3
- package/src/hub/daemon.ts +21 -9
- package/src/hub/events.ts +2 -1
- package/src/hub/hub-tools.ts +7 -5
- package/src/hub/no-writes.ts +1 -1
- package/src/hub/project-runtime-state.ts +38 -13
- package/src/hub/sdlc-release-authorization.ts +11 -5
- package/src/hub/session-identity.ts +1 -1
- package/templates/presets/confirmation-matrix.json +5 -5
- package/templates/release-guard.block.md +2 -1
package/CHANGELOG.md
CHANGED
|
@@ -4,6 +4,13 @@ Issue and pull request numbers in the entries for 0.7.7 and earlier refer to the
|
|
|
4
4
|
|
|
5
5
|
## Unreleased
|
|
6
6
|
|
|
7
|
+
## 0.27.0
|
|
8
|
+
|
|
9
|
+
- Let the configured conductor record release consent on the person's explicit version-naming instruction (#1063). Receipt and audit retain the real asker; exact candidate/docs/origin bindings, restart revocation and live-test refusal remain.
|
|
10
|
+
- Record inert experiment protocols, plans, attempts, events and signed runner receipts through `hub_experiment_record` (#932, task 615). Existing Tasks authority and verification apply; PII turns refuse, raw arguments/results stay out of memory capture, and the relay starts no experiment.
|
|
11
|
+
- Concurrent `ahub up` cold starts tolerate an atomically replaced runtime record, while malformed, linked and oversized records still refuse (task 616).
|
|
12
|
+
- Read recorded research experiments (task 602, #932 T3): `ahub study experiments --campaign <id>` (or `--attempt`, or `--plan` with `--baseline` and `--variant`) and the read-only `hub_experiment_show` render a campaign's attempts and one comparison. Every value shows whether it is declared, host-observed, runner-attested or person-attested, failed and cancelled attempts stay in the counts, a repeat is never an independent reproduction, and an incompatible comparison is shown, not ranked. A synthetic CPU-only fixture (`test/research-experiments-fixture.test.ts`) covers baseline, variant, failure, changed-protocol, private-input and inaccessible-output cases, and `docs/operations.md` has a person-authored optional rerun checklist. Nothing here runs a command or records an experiment.
|
|
13
|
+
|
|
7
14
|
## 0.26.0
|
|
8
15
|
|
|
9
16
|
- Retain each project's verified runtime across stop/start, preserve other projects when upgrading one, prune unreferenced source captures with reference checks, and report stopped projects before source preservation (#1039 follow-up). Runtime selection protection begins with the 0.26.0 resolver.
|
package/README.md
CHANGED
|
@@ -5,7 +5,7 @@ Code, Codex, Kimi Code, Pi and a hub-owned local worker (plus Qwen Code, Jcode a
|
|
|
5
5
|
Antigravity if you want them) share a message bus, a task board and a memory, each
|
|
6
6
|
connected through its own native interface.
|
|
7
7
|
|
|
8
|
-
Status: 0.
|
|
8
|
+
Status: 0.27.0, control protocol 17 (accepts 16). Bun + TypeScript, one loopback daemon per
|
|
9
9
|
project. Site: [agent-hub.staix.net](https://agent-hub.staix.net).
|
|
10
10
|
|
|
11
11
|
## Quick start
|
|
@@ -64,7 +64,8 @@ become links. The scene carries labels and states only, never message bodies or
|
|
|
64
64
|
(a planner drafts it with `hub_study_draft`); only the person approves it, which
|
|
65
65
|
creates one task per unit, and `ahub study show` reads coverage per unit and
|
|
66
66
|
source. A unit is covered by its owner's `hub_study_handoff` with evidence, not by
|
|
67
|
-
a done task.
|
|
67
|
+
a done task. Experiment attempts are recorded with `hub_experiment_record` and read
|
|
68
|
+
with `ahub study experiments`, each value naming where it came from.
|
|
68
69
|
- **Shared memory.** Decisions, contracts, fails and findings are kept through the
|
|
69
70
|
first-party [`@staix/agent-mem`](https://github.com/STAIxBWLB/agent-mem) library,
|
|
70
71
|
in process with no claude-mem worker (short- and long-term tiers, a typed graph,
|
|
@@ -126,7 +127,7 @@ uses the home must run a version that understands the new layout.
|
|
|
126
127
|
[#258](https://github.com/STAIxBWLB/agent-hub/issues/258)).
|
|
127
128
|
|
|
128
129
|
Live checks that still need a real account or a person are listed in the
|
|
129
|
-
[release verification report](docs/verification/2026-11-01-0.
|
|
130
|
+
[release verification report](docs/verification/2026-11-01-0.27.0.md) and the
|
|
130
131
|
[smoke checklist](docs/smoke.md).
|
|
131
132
|
|
|
132
133
|
## Documentation
|
|
@@ -139,7 +139,7 @@ Scope: the operator console, control WS and its protocol, state files, the dashb
|
|
|
139
139
|
- Campaign dispatch (#597): keep `research_dispatch` additive to `research_records`; any stored campaign requires it, because old campaign stop policies also need current dispatch enforcement. Preserve the conditional dispatch metadata/handoff integrity digest and every other recovery feature guard. A study-dropped owner offer (#603) is a `research_dropped_offers` row written through `Board.recordResearchDroppedOffer` from `Tasks.relevant`, never an in-memory map: a restart between the drop and the approval must still re-offer. It joins `researchDispatchIntegrity` only when rows exist (so a project without one keeps its digest), requires a `research_units` row for its task and a peer-shaped owner, and `studyReoffer` clears it only after the offer is published or the record is stale.
|
|
140
140
|
|
|
141
141
|
- Experiment storage and runner registration (#598, #600): preserve `research_experiments`, its conditional integrity digest and every earlier research/calendar/autonomy guard. Read check evidence only through this project's host reader; no record input creates a measured field. Accept `research.runners` only in untracked `.agenthub/config.local.json`; refuse shared or tracked raw keys even when empty. Keep registration person-only, with whole-block tier2 structural edits and atomic scoped reload; never admit conductor writes. Consume registrations through `TasksDeps.researchRunners`, revalidating the active accepted configuration and current source trust each call; never adopt raw disk edits through that getter. While stored runner state or an active/raw runner key exists (empty counts), require `research_runners`. Include `researchRunners` in release-equality integrity only when stored receipts exist, using their count and stored-row digest alone; never bind accepted registrations or raw config presence to board-state equality across restart.
|
|
142
|
-
- `ahub study` (#601, `src/cli/study.ts`, `src/cli/study-output.ts`): `study.ts` is pure argv parsing (ids `rc-` and twelve hex digits, revisions as positive integers, `--reason` takes the rest of the words) and runs before any connection; `main.ts` reads the contract file against `invokedFrom` with a 64 KiB bound and sends it to the hub, which validates it. `show`
|
|
142
|
+
- `ahub study` (#601, `src/cli/study.ts`, `src/cli/study-output.ts`): `study.ts` is pure argv parsing (ids `rc-` and twelve hex digits, revisions as positive integers, `--reason` takes the rest of the words) and runs before any connection; `main.ts` reads the contract file against `invokedFrom` with a 64 KiB bound and sends it to the hub, which validates it. `show` and `experiments` (#602: `--campaign`, `--attempt` or `--plan --baseline --variant`, the same read as `hub_experiment_show`) are `allowed` in `classifyPeerCommand` through `peerReadForms`; draft, revise, approve, approve-protocol and close are `console`, and the audit label names at most the subcommand (approve-protocol is labelled `study`; a campaign id, a protocol hash and a contract path are the person's text). The default output is for a person and `--json` prints the hub's text byte for byte (`test/output-cli.test.ts`); a new `study` form needs its `--json` branch and a renderer, never a `TEXT_DEBT` entry.
|
|
143
143
|
|
|
144
144
|
- Experiment comparisons (#599): preserve the separate conditional `research_comparisons` floor and integrity for plans, exact protocol/evaluator approvals and causal protocol-context rows. Never backfill legacy order from timestamps or carry approval to a changed hash. Keep the approval op under the console-only `study_` dispatch guard.
|
|
145
145
|
- Doctor memory capture (#1008, #1025): the `memory capture: <tool>` rows come only from `amem setup <tool> --json`, a preview that writes nothing (`readMemorySetup` in `src/cli/main.ts`: no shell, 10 s, 256 KiB), judged by the pure `nativeMemoryCaptureRow` (`src/adapters/definitions/native-cli-doctor.ts`). A plan not in the documented shape, a non-zero exit, a timeout or unparsable output is unknown, never ready; never read dotfiles text or a client's config, and do not scrub the caller's identity before the call.
|
|
@@ -78,7 +78,7 @@ Scope: the task flow, assignment, the state machine and the hub's MCP tools. Rea
|
|
|
78
78
|
- `review.same_family = "never"` (#820) is read only inside `assign()` through `reviewPool.sameFamily`, which `Tasks.health()` and `reviewPoolOptions` pass from `d.review.same_family` (absent for the default `"fallback"`, so default routing stays byte-identical). It removes every same-family reviewer: the selector's last-resort loop (`selectReviewerPool` `sameFamily`), the legacy `automatic` fallback, a named same-family reviewer (refused with code `family`, `same family as <owner>; review.same_family is never`) and the planner escape unless the planner holds the reviewer role and is of another family. Nobody left means `reviewer = "user"` with `reviewBlocked`, never `sameFamilyFallback`, and that holds for any automatic exhaustion (all offline, no candidate, no declared instance), never `reviewer: none, done will approve directly`; the person fallback is keyed on the setting, not on an eligible same-family candidate. Keep it out of call sites: `Tasks` never tests the family itself.
|
|
79
79
|
- `Tasks.retryReviewer` is host-only (the daemon's reviewer `onUnavailable`), never a tool or CLI path. It acts only while the failed peer is still the reviewer of an `in_review`, non-PII task, with the same review head, the request's `reviewRequestRevision` and a current child generation; `valid()` rechecks all of it after every await and right before the write, which also passes the flow revision. A manually named reviewer's `no_verdict` and a no-verdict with no declared pool go only to the person; other manually named failures keep explicit person control. Confirm fallback only after accepted reviewer publication or a person offer and a fresh binding/generation check; failed publication or notification never confirms settlement. Each failed peer is tried once per head and round (`reviewerFailure` history). `reviewRequestRevision` counts only role events (`done`, `reviewer changed`, `reviewer unavailable`, ownership), so bookkeeping never fences a request; keep `refs.reviewRevision` and `refs.commit` on the review envelope.
|
|
80
80
|
- 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"]`.
|
|
81
|
-
- Release authorization (#481 S6, `src/hub/sdlc-release-authorization.ts`):
|
|
81
|
+
- Release authorization (#481 S6, `src/hub/sdlc-release-authorization.ts`): the console user or the one configured conductor through `sdlc authorize-release` creates one under the same admission as `sdlc merge` (tools role, matching configured conductor, no `toolsOwner`, private turn or caller PII context). Keep the actual asker in receipt `by` and the audit, and the cooperative explicit-person-instruction rule in AGENTS.md Releasing. Keep consent bound to the current HEAD commit, its committed package version, the origin identity and a fresh, error-free docs verdict, all reread after the docs check. It lives in daemon memory only: a restart revokes it and the event is audit, never restored. `release-guard` and `release-guard-hook` are refused in a private turn; a tag push needs a matching receipt, and npm publish also a clean tracked worktree and the same docs digest. Never accept authorization from a tool, a task receipt or a file.
|
|
82
82
|
- `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).
|
|
83
83
|
- The main-checkout note (`mainCheckoutWarning`, `src/hub/main-checkout.ts`, #572) is advisory text in the `hub_task_done` result, the review request and status, never a refusal. A branch name or other metadata the hub reads from outside the board reaches task text only after `Tasks.publicMetadataText` (PII patterns, secret shapes, private paths) and a plain ref-name check; otherwise use the generic sentence that carries none of it.
|
|
84
84
|
|
|
@@ -110,7 +110,7 @@ Scope: the task flow, assignment, the state machine and the hub's MCP tools. Rea
|
|
|
110
110
|
- Schedules (#512a, `src/hub/schedules.ts`, `Tasks.proposeFromSchedules`): the hub proposes a scheduled task only through `Tasks.propose` as `HUB`, never `Board.propose`, so the PII screen, class validation, triage and routing apply. "Its previous task is open" is read from the board (`openScheduled`: first history entry by the hub, detail starting `Schedule key: <id>`, state not closed), never from `schedule_runs`. A run restarts the period at `now`; a hold or an open task counts a skip and proposes nothing, so a long hold ends in one task. Status rows carry ids, times and a state, never the title or detail. A schedule is `every` or `cron` (+ `tz`), never both (#1017): every consumer of its period goes through `scheduleNext(entry, anchor)` and `scheduleZone`, never `parseEvery(entry.every)` (`every` is absent on a cron entry), and a view carries `cron` and `zone` instead of `every`. The cron core is pure and takes its time as an argument; keep the clock and the machine zone out of it, and keep the daylight-saving rule (a skipped time runs at the next valid minute, a repeated one once) in `instantOf` alone. No third-party cron or time-zone library.
|
|
111
111
|
- Research campaign dispatch (#930, #597; `docs/specs/2026-11-01-research-campaigns-design.md` "T2 dispatch and coverage implementation boundary"): a task bound to a campaign unit reaches an owner only through the paths that call `studyDispatchReason` / `studyDispatchCheck` (offer, assign, planner pull, owner change, acceptance, and a queued owner offer re-checked at delivery). Each reads the stored campaign, its deadline and stop limits, and for a hard cap the named budget, synchronously, and `assignOwner` asks again immediately before the owner write with nothing awaited between that read and the write; a new owner write asks the same read there rather than checking earlier. An explicit request refuses before any effect; the hub's own offer holds with `holdReady`. Budget enforcement is the named budget by id (`ExecutionBudget.covers`), never `applies` (any budget), and it only reads: a campaign never resets, copies or writes a counter, and a `kind: "run"` budget stays peer-wide. A read that fails holds, never dispatches, and a hold never touches a native turn already running. `studyShow` returns ids, enums, counts and times, and strips source ids from a private campaign. A later slice that exposes a new dispatch or wire requirement adds its own `DAEMON_FEATURES` label and recovery guard.
|
|
112
112
|
- Campaign storage (#930 T1, #596; spec "T1 implementation boundary"): every study write runs inside `studyWrite` (counted by `studiesPending`, which `recoveryReady` and the shutdown drain read; `stopStudies` fences new ones) and asks `requestCheck(by, () => this.studyHold())` again after every await and right before the board write. `studyApprove` screens and builds units outside any transaction, re-reads the latest hash and references, then writes once through `Board.activateCampaign`; never move a screen, source lookup or file read into that transaction. A replay creates nothing (it may only tighten privacy), a stable unit id keeps its task only for an identical definition (changed work takes a new id), and a private campaign never retains a public task. `research-campaign.ts` validates only inert plain data (plain prototype, no proxy, accessor or symbol key; a caller's `toJSON` never runs) before canonical hashing. A receipt carries ids, revision, status and counts; no raw contract or close reason enters parent history or memory.
|
|
113
|
-
- Experiments (#932, #598, #599; spec "Experiment T1 implementation boundary" and "T2 comparison implementation boundary"): `Tasks.experimentRecord` and `experimentShow`
|
|
113
|
+
- Experiments (#932, #598, #599; spec "Experiment T1 implementation boundary" and "T2 comparison implementation boundary"): `hub_experiment_record` and `hub_experiment_show` are thin callers of `Tasks.experimentRecord` and `experimentShow`; the person alone approves through `study_approve_protocol`. Forward the complete record envelope and authenticated actor unchanged, keep record calls out of read-only publication, refuse all PII-turn contexts, and exclude their arguments/results from capture. Only the existing receipt kind admits a person relay; ordinary records keep the current in-progress unit-owner check. New forms need their own admission and recovery review. A writer is the current in-progress owner of a unit task in the protocol's current approved campaign revision (`Board.researchDispatchBinding`), rechecked after every screen, receipt read and hash. A host measurement is its own `research_attempt_observations` row, never a rewrite of a declared event, and no path upgrades a provenance level. Output-path privacy uses `matchesPolicy` with `failClosedOnDiscoveryLimit` (`experimentOutputPrivate`); keep that opt-in to experiments, never the default for other callers. Approval binds the exact protocol hash and evaluator hash, never carries to a changed hash, and a comparison never ranks.
|
|
114
114
|
- Registered runner receipts (#1018): admit a new signed receipt only through `Tasks.experimentRecord`, with current task/source/campaign/privacy authority and fresh trusted registrations rechecked after screening. Keep declared, host-observed and runner-attested evidence separate; a deliverable protocol never derives a code match from a signed commit claim. Identical canonical receipt/signature replay returns stored proof without registration verification or writes, even after revocation; changed bytes or a new revoked/unknown-key receipt refuse. Validate the whole linked attestation event and absence of host observations on every stored receipt read, including paged views that omit that event. Store receipt/event and advance format 2 in the same transaction; keep new/unused databases at `HUB_DB_BASE_SCHEMA` (1). Preserve task, privacy and source fences and show current key status separately from as-of-signing provenance. No public recording surface runs or contacts a runner; keep relay arguments/results out of memory capture.
|
|
115
115
|
- Study readback (#930 T3, #601): `ahub study draft|revise|approve|close` and the hub tools are thin callers of `Tasks.study*` and add no board write. The daemon's person ops are `study_draft|revise|approve|approve_protocol|close` (`taskOpBody`), refused on any non-console connection by an explicit `study_` check in the `task` control case before `Tasks` is asked; agents use `hub_study_draft` and `hub_study_revise` (configured planner through `planningAuthority`, never approve), `hub_study_show` (read-only, also in `READ_ONLY_HUB_TOOLS`) and `hub_study_handoff` (the bound in-progress owner). Pass the tool's arguments to `Tasks` whole: `studyFields` refuses an unknown field, and re-picking fields in the daemon would silently drop it (mutation-checked in `test/study-daemon.test.ts`). `STUDY_TOOLS` keeps the four tools' arguments and results out of memory capture. An implementer child gets `hub_study_show` and `hub_study_handoff` only. `Tasks.studyList` returns one page `{ campaigns, next? }` of receipts (ids, revision, status, counts), never question, scope or sources, at most 50 per call: the bound is the `LIMIT` in `Board.researchCampaignPage` (observed on its own SQL in `test/research-campaign-projection.test.ts`), `after` is the cursor, and a caller cannot raise the page size. `studyShow` blanks source ids for a private campaign and carries `budgetNamed`, so a named budget keeps every resource row with `-` for an absent limit: do not add text to either. `liveTestRefusal` refuses every `study` form except `show`, so an agent shell cannot delegate a study write to the person through `--live-test`; the contract file is read through one opened regular-file descriptor, at most 64 KiB plus one byte.
|
|
116
116
|
- Folder triggers (#512c, `src/hub/folder-trigger.ts`, `Tasks.runFolderTriggers`): the folder pass shares `schedulesRunning` with the schedules pass and `ahub schedule run`, so it is serialized with them and counted by `schedulesPending`; keep it so, and never start it from a path that does not take the guard. A file is recorded in `folder_seen` only AFTER its task exists, and the pass stops at the first failure so order is kept; do not record first (a failure would lose the file) or continue past a failure. A task made for a file names the source id and never the file's name or path, because both can be private. A trigger's baseline (the files there when it starts, possibly over several polls) proposes nothing, and a refused folder never begins the trigger; do not make either propose. The scan must keep no file's bytes (it hashes in chunks under a file and byte budget and never cuts the listing); `registerScanned` re-reads the one file being registered and refuses it unless it still hashes to what the scan saw. Ask `isNew` for each file inside the loop, not only when building the list, and `observe` (never skip) every file whose content is already known, in the list and in the loop, so it is found by `known` next time and cannot starve a new file of the hash budget. `folder_triggers` stays in `MACHINE_LOCAL` and `NEVER_EDITABLE`.
|
package/docs/dogfood.md
CHANGED
|
@@ -85,7 +85,7 @@ agent-hub is developed with agent-hub. This page lists every feature, whether it
|
|
|
85
85
|
| Merge-time PR readback, release metrics and security-scan baseline (#481 S7, AC12) | source and fixture coverage with a fake gh and real train transitions; the readback is on wherever auto merge is `on`, release metrics need shipped trains with recorded history, and the security scan and its bands stay off here until `[security_scan]` and `[bands.security_open_alerts]` are set | `cost` on merge records, `ahub sdlc metrics` review/CI-at-merge rows, the releases row of `ahub sdlc release`, `security_scans` rows and `security_open_alerts` cards |
|
|
86
86
|
| Required checks | advise (`check (ubuntu-latest)`, `check (macos-latest)`, `seeded guards`) | first-pass CI rate, same-head reruns (`hub_sdlc_costs`) |
|
|
87
87
|
| Seed anchor diagnostics (#684) | on in the seeded checker; exact-match failure remains mandatory | missing anchors report the base commit, enclosing symbol and current file/line or explicit unknown; `test/seeded-check.test.ts` |
|
|
88
|
-
| Release authorization (#481 S6) |
|
|
88
|
+
| Release authorization (#481 S6, #1063) | fixture coverage; delegated live consent awaits release and project upgrade | `test/sdlc-release-authorization.test.ts` and flow fixtures retain exact candidate/docs/origin/restart bindings and record the actual user/conductor actor; agent-read and floor fixtures refuse other peers, private turns and live-test; no fixture authorizes a live release |
|
|
89
89
|
| SDLC metrics and control bands (#481 S7) | `ahub sdlc metrics` on; bands off until `[bands]` is set in `.agenthub/sdlc.toml`, and they write cards only in advise or enforce (in off the report still shows tiers). Completion checks are off here, so `first_pass_check` and the PR-based figures read unknown | its own report; breach episodes per week by tier, and band cards the planner accepted rather than dropped |
|
|
90
90
|
| Fresh headless reviewers (#223, #519) | configured here (`codex-review`); per-tool approval correction and no-verdict diagnostics/re-offer (#783) take effect after release and upgrade | reviews by fresh sessions, findings they add; a per-review start failure names a closed cause class (#722): `ahub status` reviewer line and hub.log `[cause: ...]`, with the share of failures that are `unknown`; stale admission skips and current-policy `no_verdict` reroutes leave the reviewer available (#781); a real `hub_review` verdict per verified backend/version, beyond inventory-only proof; bounded refusal diagnostics, confirmed re-offers without queue holds, and uncertain fallback holds |
|
|
91
91
|
| Completion checks per class | off: the implementer already runs `scripts/check.sh`; a hub-run copy would double the heavy-slot use | time to verdict when on |
|
|
@@ -149,7 +149,9 @@ agent-hub is developed with agent-hub. This page lists every feature, whether it
|
|
|
149
149
|
| Study CLI, tools and readback (#930 T3, #601) | Fake peers and a real daemon over its control socket, plus the real `ahub` process against that daemon; no native peer or live model has run a campaign here, so fake versus native evidence stays separate (the `research-live-example` claim in docs/profiles.md is still open) | `test/study-daemon.test.ts` (person's draft, approve and close; no agent reaches an approve or close op; planner drafts and cannot approve; read-only show; a private source shows no source id, question, scope or path; handoff only from the bound owner with every missing field named; the three-source, two-peer example moving from none to incomplete to complete), `test/output-cli.test.ts` (parse before connecting, `--json` byte for byte), `test/study-guidance.test.ts` (guidance names only published tools) |
|
|
150
150
|
| Research dispatch and coverage (#930 T2) | Source/isolated fixtures only; no live research campaign | `test/research-dispatch.test.ts` and projection/recovery fixtures: no expired/revised dispatch, named-budget coverage, handoffs and shared ledger |
|
|
151
151
|
| Experiment records (#932 T1) | Source and isolated fixtures only; no live research attempt | Schema, storage, Tasks and recovery fixtures: declared/host provenance stays distinct, all attempts counted, replay and privacy fences |
|
|
152
|
+
| Experiment record relay (task 615, #932) | Fixture only; available after the next release and upgrade, with no live experiment or runner started here | `test/research-experiment-record-daemon.test.ts` exercises authenticated owner/person relay, role and PII refusals, idempotent replay and inert payloads; capture/publication fixtures exclude raw receipt/signature data |
|
|
152
153
|
| Experiment comparisons and protocol approval (#932 T2) | Fixture only; no scientific reproduction claimed on this repository | `test/research-comparison.test.ts`, `test/research-comparison-tasks.test.ts`, `test/research-protocol-cli.test.ts` and recovery tests verify incompatibility, fresh exact-hash approval, causal post-hoc context and restart identity; failed/cancelled attempts stay counted. |
|
|
154
|
+
| Experiment readback and receipt fixture (#932 T3, #602) | Fixture only: the readback (`ahub study experiments`, `hub_experiment_show`) is on after release and upgrade, but no live research attempt has been recorded here, and no native model or account has used it | `test/research-experiments-fixture.test.ts` (baseline, variant, failed and changed-protocol cases, a private input, a missing and a denied output, retry versus repeat; every value's source shown, every status counted, nothing run) and `test/study-guidance.test.ts` (who may call the read tool); the real use signal is `ahub study experiments` output on a campaign that has recorded attempts |
|
|
153
155
|
| Registered runner receipts (#1018) | source and test-key fixtures only; no external runner or live scientific attempt has been admitted here | `test/research-runner-tasks.test.ts` covers signed binding, immutable replay, revocation/readback, privacy and schema-2 rollback; parser/projection fixtures cover missing fields and sealing negatives; config/recovery fixtures cover person-only trust roots, raw-key floors and integrity. `evidence.runnerAttested` stays separate from declared/host-measured values and reports current key status |
|
|
154
156
|
| Dirty-submodule notices (#798) | source/fixture coverage; the live writing/general pilot (a workspace with 26 submodules) remains unverified | `ahub turns` shows `<path> (changed, contents unobserved)` for a turn that only edited a submodule; the D1 notice at `hub_task_propose` and `hub_task_accept`; `ahub undo` names what it cannot restore; a project without `.gitmodules` makes the same git calls per turn (counting-shim test) |
|
|
155
157
|
| Profile scorecard (#799) | source/fixture coverage; live writing/general/research pilot remains unverified | ahub report --dogfood profile rows; pure and reader dev byte goldens, private-text exclusion, research declarations/unknown verdicts and per-source unknown tests; anonymous weekly receipt templates |
|
package/docs/operations.md
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# Operations guide
|
|
2
2
|
|
|
3
|
-
This guide describes ahub 0.
|
|
3
|
+
This guide describes ahub 0.27.0 and control protocols 16 through 17. Live verification
|
|
4
4
|
results and remaining prerequisites are recorded separately in [the smoke ledger](smoke.md).
|
|
5
5
|
|
|
6
6
|
## Agent-operated live verification
|
|
@@ -1488,6 +1488,7 @@ ahub study approve rc-0123456789ab 2 # person only: create the unit task
|
|
|
1488
1488
|
ahub study show [rc-0123456789ab] # list campaigns (50 per page, --after continues) or one projection
|
|
1489
1489
|
ahub study close rc-0123456789ab 2 --reason "answered"
|
|
1490
1490
|
ahub study approve-protocol <64-hex hash> # person only: approve one exact experiment protocol
|
|
1491
|
+
ahub study experiments --campaign rc-0123456789ab [--after 50] # read the recorded attempts (also --attempt ra-... or --plan <hash> --baseline ra-... --variant ra-...)
|
|
1491
1492
|
```
|
|
1492
1493
|
|
|
1493
1494
|
- **Contract.** The JSON names the question, scope, exclusions, registered source ids, 1 to 32 units
|
|
@@ -1518,8 +1519,33 @@ ahub study approve-protocol <64-hex hash> # person only: approve one exact ex
|
|
|
1518
1519
|
- **Experiments.** Protocols, comparison plans and attempts are stored with their provenance (`declared`,
|
|
1519
1520
|
`host-observed` from a check receipt or a hub-hashed deliverable), and `ahub study approve-protocol` binds an
|
|
1520
1521
|
approval to one exact protocol hash and its evaluator; a changed protocol needs its own approval. The hub
|
|
1521
|
-
never ranks results.
|
|
1522
|
-
|
|
1522
|
+
never ranks results. The current in-progress owner of a bound unit task records inert data through
|
|
1523
|
+
`hub_experiment_record`: `kind` is `protocol`, `plan`, `attempt`, `event` or `receipt`, using the existing
|
|
1524
|
+
protocol/attempt/event/plan envelope or `{ kind: "receipt", receipt, signature }` for a signed runner receipt.
|
|
1525
|
+
The person may relay a signed receipt through the authenticated console connection; this does not grant
|
|
1526
|
+
agents person authority or allow a person to bypass the current task/source/campaign checks. Every PII turn
|
|
1527
|
+
refuses the tool. It returns only record metadata (kind, ids/hashes, sequence and receipt id as applicable),
|
|
1528
|
+
and neither starts an experiment nor signs or verifies a receipt in the relay. Existing host evidence and
|
|
1529
|
+
signature verification remain inside `Tasks`. Read records with `ahub study experiments` or
|
|
1530
|
+
`hub_experiment_show` (see Experiment readback below).
|
|
1531
|
+
- **Experiment readback (#602, #932 T3).** `ahub study experiments --campaign <id>` lists a campaign's attempts
|
|
1532
|
+
(50 rows a page, `--after <cursor>` continues), `--attempt <ra-id>` shows one, and `--plan <hash> --baseline
|
|
1533
|
+
<ra-id> --variant <ra-id>` shows one comparison under a recorded plan. `hub_experiment_show` takes the same three
|
|
1534
|
+
forms and is a read for every local peer. It runs no command and reads no file; it renders what
|
|
1535
|
+
`Tasks.experimentShow` already returns. Every value says where it came from: `declared` (the agent's own word),
|
|
1536
|
+
`host-observed` (a check receipt or a hub-hashed deliverable), `runner-attested` (a signature-verified runner
|
|
1537
|
+
receipt) or `person-attested`. A declared value is never shown as observed, a metric or output the hub could not
|
|
1538
|
+
read is `unknown`, sealing is `declared` and `isolation unverified` unless a runner receipt supplies the
|
|
1539
|
+
evidence, and failed, cancelled and unknown attempts stay in the counts at the top. A repeat is never an
|
|
1540
|
+
independent reproduction, and a comparison whose fixed inputs, evaluator, split or metric units differ, or that
|
|
1541
|
+
changes an undeclared factor, reads `no: <field> differs` and is shown, never ranked. A private campaign, task or
|
|
1542
|
+
source shows ids, counts and states only. **Optional rerun checklist** (written for a person, never run by the
|
|
1543
|
+
hub): a person who wants stronger evidence reruns the protocol themselves, then attaches it through the existing
|
|
1544
|
+
paths and no new one: a check on the task (`hub_check_start`, #513 and the remote runner #793) makes the end
|
|
1545
|
+
event's exit status `host-observed`; an output file named in an `output` event is hashed by the hub (#509) and
|
|
1546
|
+
stays `unknown` when it is missing or denied; a registered runner's signed receipt (#1050) is the only source of
|
|
1547
|
+
`runner-attested` sealing and isolation. Without one of these the experiment is reported as declared and
|
|
1548
|
+
unverified, which is not the same as wrong.
|
|
1523
1549
|
- **Privacy.** A campaign whose parent task is PII, or that names a private source, is private: every view keeps
|
|
1524
1550
|
ids, counts, states and provenance and stubs its text as `[pii]`. Study tool arguments and results are kept out
|
|
1525
1551
|
of hub-managed memory capture.
|
|
@@ -3100,7 +3126,7 @@ version is older than the selected running daemon; it installs nothing.
|
|
|
3100
3126
|
| Runtime | Control protocol window | Legacy status field |
|
|
3101
3127
|
| --- | --- | --- |
|
|
3102
3128
|
| 0.12.20 through 0.25.0 | 16 only | `protocol: 16` |
|
|
3103
|
-
| 0.26.0 | 16 through 17 | `protocol: 16`, `protocolMin: 16`, `protocolMax: 17` |
|
|
3129
|
+
| 0.26.0 through 0.27.0 | 16 through 17 | `protocol: 16`, `protocolMin: 16`, `protocolMax: 17` |
|
|
3104
3130
|
|
|
3105
3131
|
New clients choose the highest common protocol. The runtime supports N-1, so the
|
|
3106
3132
|
published protocol-16 client and channel plugin remain usable; unrelated feature
|
|
@@ -5092,7 +5118,7 @@ blocks that undo until the identity and workspace can be reviewed.
|
|
|
5092
5118
|
|
|
5093
5119
|
## Release approval and optional tag guard
|
|
5094
5120
|
|
|
5095
|
-
Run `ahub sdlc authorize-release <version>` from the running daemon's release-candidate checkout
|
|
5121
|
+
Run `ahub sdlc authorize-release <version>` from the running daemon's release-candidate checkout after committed documentation is fresh. The person may run it directly; the unique configured conductor may run it only on the person's explicit instruction in its own session naming the release. A hub message, task text, review note, file or peer relay never qualifies. Other peers, child `toolsOwner` sessions, private or PII turns, and delegated `--live-test` calls cannot authorize. The receipt binds commit, package version, remote identity and the docs verdict; its `by` and the audit name the actual asker. The daemon keeps approval only in memory; restart requires a new approval. The read-only `ahub sdlc release <owner/repo>` report shows its receipt, retaining native-verification blockers.
|
|
5096
5122
|
|
|
5097
5123
|
`ahub init --release-guard` installs a managed Git pre-push guard and release instructions (`--dry-run --json` previews them). It preserves an existing default hook as `pre-push.agent-hub-original` and passes original argv/stdin to it only after approval matches. Unknown or mismatched `v*` tag pushes fail closed, including when the daemon is down. Non-version branch pushes remain independent of the daemon. Custom `core.hooksPath` and linked/hardlinked hook files require manual integration. Re-run init after changing the Bun/package location. The person's `git push --no-verify` remains an explicit bypass; agents are instructed never to use it.
|
|
5098
5124
|
|
package/docs/profiles.md
CHANGED
|
@@ -22,6 +22,7 @@
|
|
|
22
22
|
- Example deterministic check: `bun /path/to/agent-hub/scripts/check-citations.ts report.md references.bib`. It checks bracketed Markdown `[@key; @other]` keys against BibTeX entry keys, with 256 KiB/4 MiB input caps. It performs no fetch, validates no source quality and is not a full Markdown/BibTeX parser. A missing key exits 1. A resolved bibliography is one check, not proof that claims are supported.
|
|
23
23
|
- Keep a person's sign-off as the final review. The live three-source/two-agent example remains unverified and was not run during solo implementation.
|
|
24
24
|
- A multi-unit study (#930, #601) goes through `ahub study`: the person (or a configured planner with `hub_study_draft` / `hub_study_revise`) stores a contract as a draft, only the person approves it with `ahub study approve <id> <revision>`, which creates one task per unit, and anyone reads coverage with `ahub study show [<id>]` or `hub_study_show`. A unit owner records `hub_study_handoff` with evidence for every source the unit names, or `insufficientEvidence`; a done task is not a handoff, and an incomplete handoff does not close a discovery round. A private campaign, task or source shows ids, counts and states only.
|
|
25
|
+
- Experiment attempts recorded on a study unit are read with `ahub study experiments --campaign <id>` (or `--attempt`, or `--plan` with `--baseline` and `--variant`) and `hub_experiment_show`. Each value shows its source: `declared` is the agent's own word, and only `host-observed`, `runner-attested` or `person-attested` rows are evidence, none of which proves the science. A rerun by a person attaches through an existing check, a hashed output file or a registered runner's signed receipt, as the checklist in `docs/operations.md` ("Experiment readback") says; without one the experiment stays declared and its sealing unverified. The synthetic CPU-only fixture is `test/research-experiments-fixture.test.ts`; no account, GPU or native model has run an experiment here.
|
|
25
26
|
- The deterministic three-source, two-peer example (`test/study-daemon.test.ts`, "three-source, two-peer example"): one contract names sources S0, S1 and S2 and two discovery units, `alpha` (S0 and S1, owned by codex) and `beta` (S2, an independent check, owned by kimi). After `ahub study approve` both units read `none` and `0/2 covered`, `0/1 covered`. A handoff for S0 only moves `alpha` to `incomplete` with `1/2 covered`. A handoff for both S0 and S1, and one for S2, make both `complete` and the discovery round complete. Another peer's handoff for `alpha` is refused. This runs with fake peers: it shows the hub's rules, not that a native model follows the guidance.
|
|
26
27
|
|
|
27
28
|
# General project example
|
package/docs/quickstart.md
CHANGED
|
@@ -17,7 +17,7 @@ ahub setup # installs the channel plugin and a pr
|
|
|
17
17
|
Or use the matching GitHub release:
|
|
18
18
|
|
|
19
19
|
```bash
|
|
20
|
-
bun add -g github:STAIxBWLB/agent-hub#v0.
|
|
20
|
+
bun add -g github:STAIxBWLB/agent-hub#v0.27.0
|
|
21
21
|
ahub setup
|
|
22
22
|
```
|
|
23
23
|
|
|
@@ -31,6 +31,10 @@ move a running project to a newer release later, run `ahub upgrade` in a
|
|
|
31
31
|
terminal in that project: it reviews the plan with you, applies it and follows
|
|
32
32
|
it; other projects keep their version.
|
|
33
33
|
|
|
34
|
+
## Upgrading to 0.27.0
|
|
35
|
+
|
|
36
|
+
Nothing changes until you use something new. Release consent can now come from your explicit instruction to the conductor: say in its own session which release to ship (for example "release 0.27.0"), and the configured conductor runs `ahub sdlc authorize-release <version>` for you, under the same admission as `sdlc merge`; you can still run it yourself. The approval keeps every binding it had (exact commit, version, origin and fresh docs; a restart revokes it), its record names who ran it, and a hub message, task text, review note, file or another peer's relay never counts as the instruction (#1063, #1066). Research experiments gain a read and a write: `ahub study experiments` and `hub_experiment_show` read a campaign's attempts, one attempt or one comparison (#602), and an owner records protocols, plans, attempts, events and receipts with `hub_experiment_record`, refused during a private turn (#615). Concurrent starts of one project no longer fail on a `runtime.json` replaced while it was being read (#616). As for 0.26.0, update the global CLI and the plugin yourself afterwards: `bun add -g @staix/agent-hub@0.27.0`, then `ahub setup`.
|
|
37
|
+
|
|
34
38
|
## Upgrading to 0.26.0
|
|
35
39
|
|
|
36
40
|
`ahub upgrade` now upgrades one project: the one in the current directory, or the one `--project <path|id>` names; other projects keep their running version (#1039, #1045). From a 0.25.0 CLI, run it in the project directory, since 0.25.0 refuses `--project` for upgrade; it hands over to the 0.26.0 coordinator as before. It no longer updates the global CLI or the shared Claude plugin: run `bun add -g @staix/agent-hub@0.26.0` and then `ahub setup` yourself, and `ahub doctor` warns while either is older than a running daemon. The daemon speaks control protocol 17 and still accepts 16, so a 0.25.0 plugin keeps connecting. Each project keeps its verified runtime across stop and start; start it with a 0.26.0 or later CLI, because an older CLI ignores that record (#1052). A daemon refuses a `hub.db` in a newer format and names the version it needs. `ahub up` now also starts `claude` and `codex` (see [First session](#first-session)): `ahub setup` asks once which agents (`--agents <a,b>`, `none` for no agent), `ahub settings set start.agents <a,b>` changes one project, and `ahub up --no-agents` skips them for one run; a restart, recovery or upgrade relaunch starts none (#1046). An owner can re-file `hub_task_done` on an approved, unmerged task with a new head, which sends it back to review, and a reviewer can withdraw an approval with `changes_requested` (#1049). Signed external runner receipts for research attempts are opt-in: the person registers a runner under `research.runners` in untracked `.agenthub/config.local.json` (#1050). Restart Claude sessions so they load the 0.26.0 plugin.
|
package/docs/sdlc.md
CHANGED
|
@@ -389,7 +389,7 @@ A private (PII) task reads no eval metadata and records none.
|
|
|
389
389
|
|
|
390
390
|
## Person release authorization (#481 S6)
|
|
391
391
|
|
|
392
|
-
From the running daemon's candidate checkout, the person runs `ahub sdlc authorize-release <version
|
|
392
|
+
From the running daemon's candidate checkout, the person or the one configured conductor runs `ahub sdlc authorize-release <version>` (#1063). The conductor acts only on the person's explicit instruction in its own session naming that release. A hub message, task text, review note, file or another peer's relay is never that instruction; the daemon cannot verify this cooperative rule. A conductor request uses the tools role and refuses a child `toolsOwner`, private turn or PII context, exactly like `sdlc merge`. Other peers and `--live-test` refuse. Consent binds the exact current commit, its committed package version, origin fetch/push identity and fresh committed documentation evidence, all reread after the docs check. Receipt `by` and the append-only metadata audit identify the actual asker alongside the approval id, commit, version and docs digest/count. The authoritative ledger is daemon-owned memory: restart revokes every approval; audit history cannot restore one.
|
|
393
393
|
|
|
394
394
|
`ahub sdlc release <owner/repo>` shows a matching or stale authorization receipt. Authorization removes only the person-decision blocker. Native acceptance remains unverified and `ready` remains false until separately supported. No tag, workflow or publication is changed by authorization or readback.
|
|
395
395
|
|
|
@@ -403,13 +403,13 @@ Tracked npm cleanliness compares the cached index to HEAD and raw tracked blob b
|
|
|
403
403
|
|
|
404
404
|
## Release preparation and the guarded tag (#787 P3b)
|
|
405
405
|
|
|
406
|
-
Release preparation is an agent task; the release decision
|
|
406
|
+
Release preparation is an agent task; the release decision remains the person's. Consent may be recorded by the person or by the configured conductor on the explicit version-naming instruction above; preparation alone grants no consent.
|
|
407
407
|
|
|
408
408
|
`bun scripts/release-prepare.ts [--version X.Y.Z] [--dry-run]` is the mechanical half of AGENTS.md "Releasing" steps 1 and 2. On a `release/<version>` branch (it refuses `main`, a detached HEAD, a dirty tree and any other branch name) it reads the commits since the last `v*` tag, applies the rule (any `feat` subject bumps the minor, otherwise the patch; a version below the rule or not newer than the last tag is refused, so is a missing or empty `## Unreleased`), and writes `package.json`, the README `Status:` line, the CHANGELOG section (the Unreleased entries move under the version, Unreleased stays empty), the bundle (`bun run build`) and a verification report skeleton `docs/verification/<date>-<version>.md`. The skeleton lists the pull requests the commits name and the native acceptance receipts, which it reads from `docs/verification/` files added since the tag, never from what an agent writes. It leaves `TODO(agent)` markers for what only a review can state: each PR's review and CI, the document review, the checks and the known gaps.
|
|
409
409
|
|
|
410
410
|
It restamps nothing by itself. A stamp in `docs/verified.json` records that someone read what changed under a document, so `bun scripts/release-prepare.ts restamp <document>... --reviewed` sets `verifiedAgainst` to HEAD for the named documents only, refuses without `--reviewed`, refuses an unnamed or unknown document and refuses with staged changes. The agent runs it per document after reading the source commits, commits `docs/verified.json`, opens the release PR and merges it with a merge commit (never squash).
|
|
411
411
|
|
|
412
|
-
`bun scripts/release-tag.ts <version> [--execute]` is the one guarded tag run. Every run repeats the whole preflight and reports every refusal at once: a valid release version; `package.json`, the README Status line and a CHANGELOG section at HEAD all naming it; HEAD on `main`, clean, and the merge commit of a `release/<version>` PR (a squash is refused); HEAD an ancestor of `origin/main` (fetched, not assumed); no `v<version>` tag locally or on the remote; `scripts/check-docs.mjs --require-fresh --committed` clean; and exactly one `docs/verification/<date>-<version>.md` with no `TODO(agent)` left. Without `--execute` it only reports, and whether the person's consent matches (exit 2 when it does not). With `--execute` it reads the consent through `ahub sdlc release-guard -- git push <remote> <commit>:refs/tags/v<version>` (read only; the ledger `ahub sdlc authorize-release` fills
|
|
412
|
+
`bun scripts/release-tag.ts <version> [--execute]` is the one guarded tag run. Every run repeats the whole preflight and reports every refusal at once: a valid release version; `package.json`, the README Status line and a CHANGELOG section at HEAD all naming it; HEAD on `main`, clean, and the merge commit of a `release/<version>` PR (a squash is refused); HEAD an ancestor of `origin/main` (fetched, not assumed); no `v<version>` tag locally or on the remote; `scripts/check-docs.mjs --require-fresh --committed` clean; and exactly one `docs/verification/<date>-<version>.md` with no `TODO(agent)` left. Without `--execute` it only reports, and whether the person's consent matches (exit 2 when it does not). With `--execute` it reads the consent through `ahub sdlc release-guard -- git push <remote> <commit>:refs/tags/v<version>` (read only; the ledger `ahub sdlc authorize-release` fills from the person or the explicitly instructed configured conductor, revoked by a hub restart), repeats the preflight, checks the commit did not move, reads the consent once more, and then makes the single push of that exact commit. A rejected push is reported and never retried, and the script passes neither `--force` nor `--no-verify`; the pre-push hook from `ahub init --release-guard` still runs on that push. The push creates a lightweight tag (`<commit>:refs/tags/v<version>`), the exact form `ahub sdlc release-guard` parses and the consent receipt binds; the repository's tags are mixed (v0.19.0 and v0.18.0 are annotated, v0.17.0 is not) and `.github/workflows/release.yml` accepts either (`v*`, then `--verify-tag`), so this is a convention difference, not a gap. If the project wants annotated release tags, create the tag object first and push the tag ref, which the guard also accepts (its source object is peeled to the commit); that is a later change to this script. `AHUB_RELEASE_ROOT` points both scripts at a checkout other than the one they live in (the tests use it so nothing outside a scratch repository is touched). Neither script calls `authorize-release`: the report only names the command; the person or the explicitly instructed configured conductor records consent separately. Publication (npm, the GitHub release) stays with the tag-triggered workflow, which has its own gate (`.github/workflows/release.yml`).
|
|
413
413
|
|
|
414
414
|
The scripts and their pure helpers live in `scripts/release-prepare.ts`, `scripts/release-tag.ts` and `scripts/lib/release-plan.ts`; they are maintainer tooling and are not part of the published package. The deterministic recovery barriers of #787 AC3 are not needed by either leg and are not part of this change.
|
|
415
415
|
|
package/docs/security.md
CHANGED
|
@@ -819,7 +819,7 @@ The review brief (#520) withholds private changes whole (both sides of a move) a
|
|
|
819
819
|
|
|
820
820
|
Deliverable records (#509a). `hub_task_done` and `hub_sdlc_record` name files as `{path, format, version?}`; the hub hashes them itself and refuses a claimed `sha256`, `hash` or `bytes` and any field it does not own. Paths resolve under the owner's execution root with `realPath` and `guardPath` in read mode: outside the project, the secrets denylist, the hub's own state (`.git`, `.agenthub`), a dangling link, a non-regular file and more than 256 MiB each or 512 MiB together are refused, with the path as given and never a resolved one. The file is opened without following a final link and its device, inode, size and time are compared with what was checked, before and after the read, so a swapped or changing file is refused rather than recorded half-read; a bad call writes nothing. A declared worktree instance whose root is not proven refuses, and a later read of it reports `unknown`. No hash of hub state is ever taken, and a file the hub cannot read or vouch for is `unknown`, never `unchanged`. A reviewer's approval re-hashes the files and is refused when one changed, went missing or is unknown; only the person may approve the current bytes, which are then measured and bound. For a PII task, a file under `pii.paths` or a path matching a PII pattern, `publicDeliverables` is the one projection every peer view, review request, status and log uses: id, hash and size only. The board keeps the path, as it keeps a PII task's text. `ahub deliverables` (#509b) from an agent shell lists only the tasks `hub_task_show` would show that agent (#933), and `--live-test` cannot delegate it. It lists the board's records through `publicDeliverables` and opens a file only with `--check`, through the same reader and limits. The dashboard's Deliverables chip and task group use the same projection of the same records and never read a file on the poll; a `changed` mark there is only as fresh as the last live read in this daemon.
|
|
821
821
|
|
|
822
|
-
Person release authorization (#481 S6). `ahub sdlc authorize-release <version>`
|
|
822
|
+
Person-directed release authorization (#481 S6, #1063). `ahub sdlc authorize-release <version>` accepts the console user or the one configured conductor under the same admission as `sdlc merge`: tools role, matching configured conductor, no child `toolsOwner`, private turn or PII context. It remains refused under `--live-test`. The conductor may record consent only on the person's explicit instruction in its own session naming the release; a hub message, task text, review note, file or peer relay is never that instruction. The daemon cannot verify that cooperative prerequisite. Receipt `by` and the audit identify the actual asker. It binds the daemon checkout's commit, package version, push remote identity and a fresh-docs verdict, lives only in daemon memory (a restart revokes it) and is logged with its commit, version and docs digest. `ahub sdlc release-guard -- <argv>` is a read-only preflight that agents may run, except during a private or PII turn; it never executes the command and refuses ambient target selectors (`GIT_DIR`, `GH_REPO`, npm prefix and registry settings and the like). The pre-push hook that `ahub init --release-guard` installs refuses an unauthorized or unknown `v*` tag push, also with the daemon down, and passes other pushes through. It and the release block in `AGENTS.md` are a cooperative guard, not an operating-system boundary: `git push --no-verify`, a custom `core.hooksPath` or another Git invocation bypasses the hook, and `gh release create` and `npm publish` are checked only when the preflight is run (tasks 312 and 313).
|
|
823
823
|
|
|
824
824
|
SDLC metrics and control bands (#481 S7). `ahub sdlc metrics` and `hub_sdlc_metrics` are planner or person only, read-only, and refused during a private or PII turn. A private task is counted as withheld and read for nothing else; the report calls no model and makes no GitHub read. Band breaches are written only by the daemon's evaluator through Tasks, never while it is stopping, recovering or has a storage error. A `diagnose` or `propose` card is a backlog task (`triage` or `plan`) whose text is fixed words, numbers and task ids, never task text; it passes the PII signal check and the candidate-text screen before it is written, and no GitHub issue is created. A `log` breach is one hub notice, remembered in the board's `band_notices` table as a marker (`<metric>:#<task>:log`, checked against a fixed pattern) and a time, nothing else.
|
|
825
825
|
|
|
@@ -5888,7 +5888,7 @@ Order: 2a first (it measures everything else). 2b and 2c are independent. 2d aft
|
|
|
5888
5888
|
|
|
5889
5889
|
### S6 person release authorization boundary (#481)
|
|
5890
5890
|
|
|
5891
|
-
- `ahub sdlc authorize-release <version>`
|
|
5891
|
+
- `ahub sdlc authorize-release <version>` accepts the console user or the one configured conductor, never live-test delegated (#1063). Conductor admission matches `sdlc merge`: tools role, `conductorPeer(config.roles) === c.peer`, no `toolsOwner`, private turn or caller PII context. The conductor records consent only on the person's explicit instruction in its own session naming the release; a hub message, task text, review note, file or another peer's relay is never that instruction. This instruction-origin condition is cooperative, not verified by the daemon. Receipt `by` and the metadata audit identify the actual asker (`user` or conductor peer). The daemon binds consent to its candidate checkout's exact HEAD, committed package version, origin fetch/push identity and fresh committed-doc verdict. Caller-supplied expected HEAD must match. Head and origin are rechecked after the bounded package-owned documentation checker. Only ids, hashes, version, timestamp and counts enter the receipt/audit event; no project script or release operation runs.
|
|
5892
5892
|
- Authoritative receipts live only in the current daemon generation's bounded memory ledger (at most 64 candidates). An additive metadata event audits consent but cannot recreate it. Restart revokes all approvals. The release report shows matching/stale consent without certifying native acceptance, changing the tag workflow or publishing.
|
|
5893
5893
|
- Optional `ahub init --release-guard` installs a managed executable Git pre-push hook plus instructions. A regular existing hook is renamed, preserving its inode/mode, and chained with exact argv/stdin after successful guard readback. Linked/hardlinked hooks, occupied backups and custom hooksPath refuse before project writes. Re-init retains the guard; the wrapper pins Bun/package paths and needs re-init if they move.
|
|
5894
5894
|
- Actual pushed `v*` refs must have a valid version and captured source-object/commit matching consent. Missing/mismatched commit/version/remote, unreachable daemon, deletion, unknown evidence or restart refuses the version-tag push. Branch-only pushes work offline. `--no-verify` remains the person's explicit bypass and is forbidden to agents; this is cooperative Git enforcement, not a sandbox.
|
|
@@ -276,7 +276,7 @@ All new tables live in `hub.db`, created in the `Board` constructor with `CREATE
|
|
|
276
276
|
|
|
277
277
|
| Surface | Name | Who |
|
|
278
278
|
|---|---|---|
|
|
279
|
-
| CLI | `ahub study draft <parent> <contract.json>`, `study revise <id> <revision> <contract.json>`, `study approve <id> <revision>`, `study close <id> <revision> --reason`, `study show [<id> | --after <id>] [--json]`, `study experiments
|
|
279
|
+
| CLI | `ahub study draft <parent> <contract.json>`, `study revise <id> <revision> <contract.json>`, `study approve <id> <revision>`, `study close <id> <revision> --reason`, `study show [<id> | --after <id>] [--json]`, `study experiments (--campaign <id> [--after <n>] | --attempt <ra-id> | --plan <hash> --baseline <ra-id> --variant <ra-id>) [--json]`, `study attest <attempt> <field> <value>`, `study receipt <file>` | approve, attest and receipt are person only; draft and revise are person or planner; show and experiments are anyone. `experiments` ships in #602 in exactly those three closed forms; `attest` and `receipt` are not built yet (see the #602 row) |
|
|
280
280
|
| Hub tools | `hub_study_draft` and `hub_study_revise` (configured planner only, through `planningAuthority`; they store drafts and never approve), `hub_study_show` (read-only; no id lists this project's campaigns as receipts, one page of at most 50 as `{ campaigns, next? }`, and `after` continues from the `next` of the previous page), `hub_study_handoff` (bound task owner), `hub_experiment_record` (`kind`: `protocol`, `plan`, `attempt`, `event` or `receipt`; task owner), `hub_experiment_show` (read-only) | none of them approves or attests |
|
|
281
281
|
| CLI (person only) | `ahub study approve-protocol <hash>` | person |
|
|
282
282
|
| Control | `study` (console role, CLI only) | person |
|
|
@@ -299,6 +299,17 @@ Each slice amends this spec in its own PR if it must differ, and updates `docs/d
|
|
|
299
299
|
| #601 | #930 T3, AC5 and AC6 | `ahub study` draft, revise, approve, close and show; `hub_study_draft`, `hub_study_revise`, `hub_study_show` and `hub_study_handoff`; the privacy stubs; role guidance in `templates/`; the three-source, two-peer example |
|
|
300
300
|
| #602 | #932 T3, AC5 and AC6 | `ahub study experiments`, `attest` and `receipt`; `hub_experiment_show`; the synthetic CPU-only fixture; the rerun checklist in `docs/profiles.md` |
|
|
301
301
|
|
|
302
|
+
**As built in the #602 PR (amended here, because the build differs from this row).** Built: `ahub study experiments` in its three closed forms, the read-only `hub_experiment_show` (a read for every local peer, in `STUDY_TOOLS`, the read-only tool lists and `SESSION_READ_OPS`), the synthetic CPU-only fixture `test/research-experiments-fixture.test.ts`, and the person-authored rerun checklist, which lives in `docs/operations.md` ("Experiment readback") with a pointer from `docs/profiles.md`. Not built, and not needed for AC5 or AC6: `ahub study attest`, `ahub study receipt` and the `hub_experiment_record` tool. `Tasks.experimentRecord` and its receipt kind exist and are tested, but no tool, daemon case or CLI calls them yet, so until that slice lands the readback shows only what a test or a later slice recorded. Each of those writers needs its own authority review (person-only attest and receipt, task-owner record) and is a separate change.
|
|
303
|
+
|
|
304
|
+
**Task 615 record relay.** `hub_experiment_record` passes the whole inert envelope to
|
|
305
|
+
`Tasks.experimentRecord` with the authenticated caller identity. Its five existing kinds are `protocol`,
|
|
306
|
+
`plan`, `attempt`, `event` and `receipt`. Ordinary records require the current in-progress owner of a bound
|
|
307
|
+
unit in the current approved campaign; the person may relay a signed runner receipt through the existing
|
|
308
|
+
`USER` branch. This is not a new attestation or approval authority. All PII turns refuse before dispatch,
|
|
309
|
+
read-only callers cannot mutate, and arguments/results are excluded from hub-managed memory capture.
|
|
310
|
+
The relay returns the method's bounded record metadata only and performs no execution, signing or
|
|
311
|
+
verification itself. `ahub study attest` and `ahub study receipt` remain separate, unimplemented forms.
|
|
312
|
+
|
|
302
313
|
## Departures from the issues
|
|
303
314
|
|
|
304
315
|
- Runner registration is person only in V1. #1018 proposed "the person or conductor". A public key is a trust root, so the conductor path is deferred until a reviewed change gives it the same confirmation as other trust settings.
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
# 0.27.0 release verification
|
|
2
|
+
|
|
3
|
+
This record covers release preparation and source review. It claims no installation, no running-hub upgrade to 0.27.0 and no live TUI launch.
|
|
4
|
+
|
|
5
|
+
## Candidate
|
|
6
|
+
|
|
7
|
+
Version 0.27.0, prepared from main de08b8e1 (4 commits since v0.26.0, 3 of them `feat`; bump rule: minor). The release commits carry the version, the CHANGELOG section, the bundle and the documentation stamps.
|
|
8
|
+
|
|
9
|
+
## Reviews
|
|
10
|
+
|
|
11
|
+
Pull requests named by the commits since v0.26.0: #1066, #1062, #1061, #1059.
|
|
12
|
+
|
|
13
|
+
Each merged after a claude `hub_review` approval with exact-head CI green, no unresolved review thread and no pending change request:
|
|
14
|
+
|
|
15
|
+
- #1059 (task 602, experiment readback): approved at c10a509f, exact-head CI run 41313240639. One nit (the spec's as-built note could name task 615) left open; #1062 then named it.
|
|
16
|
+
- #1061 (task 616, runtime.json read retried only when the file was replaced): approved after exact-head CI run 41318247416 at 636fe45f. The board bound the approval to 6c6dfeff, because hub 0.23.1 refuses an in_review re-file; the verdict note names the one-line CHANGELOG delta to 636fe45f.
|
|
17
|
+
- #1062 (task 615, `hub_experiment_record`): approved at 66b76a8e, run 41322472076.
|
|
18
|
+
- #1066 (task 617, conductor-recorded release consent, #1063): approved at 6f3b2dfc, run 41341091037. The first head 5f12dec3 failed CI on two assertions of wording #1063 changed (run 41339855105); the reviewer ruled that exactly those two be updated to the new text, and the delta to 6f3b2dfc is those two lines.
|
|
19
|
+
|
|
20
|
+
## Native acceptance receipts
|
|
21
|
+
|
|
22
|
+
Receipts added under `docs/verification/` since v0.26.0: none.
|
|
23
|
+
|
|
24
|
+
This list is read from the repository, never written by hand. Native acceptance and the person's release consent are separate records: consent is `ahub sdlc authorize-release 0.27.0`, recorded by the person or the unique configured conductor only on the person's explicit release instruction naming the release in its own session (never a hub/task/review/file/peer relay), and is not recorded in this file. The tag run refuses without it.
|
|
25
|
+
|
|
26
|
+
## Document review
|
|
27
|
+
|
|
28
|
+
Stale at preparation (`node scripts/check-docs.mjs`): README.md, docs/agent-notes/{adapters,budget,bus,daemon,tasks,tests}.md, docs/operations.md, docs/quickstart.md, docs/security.md and src/cli/help.ts; docs/agent-notes/README.md became stale after the daemon.md edit. The conductor read each against the diff of its covered sources since v0.26.0 (#1059, #1061, #1062, #1066) and found:
|
|
29
|
+
|
|
30
|
+
- src/cli/help.ts: `ahub study experiments` (three closed forms) shipped in #1059 without a help row; added. Every other form in `ahub help` still matches `src/cli/main.ts`, `src/cli/study.ts` and `src/cli/launch.ts`; the `sdlc authorize-release` row was already updated by #1066.
|
|
31
|
+
- docs/agent-notes/daemon.md: the `ahub study` note now names `experiments` as an allowed read form beside `show`.
|
|
32
|
+
- docs/quickstart.md: the v0.27.0 install tag and a new "Upgrading to 0.27.0" section (instructed release consent, experiment read and record, the runtime.json race fix, and updating the global CLI and plugin by hand as in 0.26.0).
|
|
33
|
+
- README.md: the report link, and one sentence on experiment record and readback under Research campaigns.
|
|
34
|
+
- docs/operations.md: the guide header (0.27.0) and the protocol-window table row (0.26.0 through 0.27.0, unchanged window 16 through 17). The experiment and release-approval sections were already updated by #1059, #1062 and #1066.
|
|
35
|
+
- docs/security.md, docs/agent-notes/{tasks,tests}.md: already current (#1066 updated the release-authorization paragraphs; tasks.md names both experiment tools). adapters.md, budget.md, bus.md and agent-notes/README.md: stale only through covered daemon.ts or note paths; nothing in them describes the changed behavior.
|
|
36
|
+
|
|
37
|
+
All twelve were restamped with `--reviewed` at the release branch head that holds these edits.
|
|
38
|
+
|
|
39
|
+
## Checks
|
|
40
|
+
|
|
41
|
+
- `node scripts/check-docs.mjs --require-fresh`: `docs: OK (0 stale documents)` on the release branch.
|
|
42
|
+
- `bun test test/help.test.ts test/completion.test.ts` after the help row: 24 pass, 0 fail.
|
|
43
|
+
- `bun x tsc --noEmit`, `bun scripts/build.mjs --check` and `node scripts/check-package.mjs`: run by the release PR's exact-head CI, together with the full suite on ubuntu and macOS and the seeded guards (heavy checks run only on CI or dgx02 on this machine).
|
|
44
|
+
|
|
45
|
+
## Known gaps, not blocking
|
|
46
|
+
|
|
47
|
+
- No installation and no running-hub upgrade to 0.27.0 is claimed here. The running hub on this machine is 0.23.1, so the conductor-recorded consent of #1063 is not live yet; this release's consent is the person's own `ahub sdlc authorize-release 0.27.0`.
|
|
48
|
+
- No live receipt for `hub_experiment_record` or the instructed-consent path; both are covered by daemon-level tests only.
|
|
49
|
+
- Issue #418 was closed with its managed-Pi live leg waived (owner decision 2026-11-02); a post-close Pi smoke on hub 0.23.1 (tasks 618, 619) passed spec read, memory read and one remote isolated check, Linux evidence only.
|
package/docs/verified.json
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
"schemaVersion": 1,
|
|
3
3
|
"documents": {
|
|
4
4
|
"README.md": {
|
|
5
|
-
"verifiedAgainst": "
|
|
5
|
+
"verifiedAgainst": "c3f379ce1ce1c4c1f7b5442c78fd8c534ff12ec0",
|
|
6
6
|
"paths": [
|
|
7
7
|
"package.json",
|
|
8
8
|
"src/",
|
|
@@ -11,14 +11,14 @@
|
|
|
11
11
|
]
|
|
12
12
|
},
|
|
13
13
|
"docs/security.md": {
|
|
14
|
-
"verifiedAgainst": "
|
|
14
|
+
"verifiedAgainst": "c3f379ce1ce1c4c1f7b5442c78fd8c534ff12ec0",
|
|
15
15
|
"paths": [
|
|
16
16
|
"src/",
|
|
17
17
|
"templates/"
|
|
18
18
|
]
|
|
19
19
|
},
|
|
20
20
|
"docs/operations.md": {
|
|
21
|
-
"verifiedAgainst": "
|
|
21
|
+
"verifiedAgainst": "c3f379ce1ce1c4c1f7b5442c78fd8c534ff12ec0",
|
|
22
22
|
"paths": [
|
|
23
23
|
"package.json",
|
|
24
24
|
"src/",
|
|
@@ -35,7 +35,7 @@
|
|
|
35
35
|
]
|
|
36
36
|
},
|
|
37
37
|
"docs/quickstart.md": {
|
|
38
|
-
"verifiedAgainst": "
|
|
38
|
+
"verifiedAgainst": "c3f379ce1ce1c4c1f7b5442c78fd8c534ff12ec0",
|
|
39
39
|
"paths": [
|
|
40
40
|
"package.json",
|
|
41
41
|
"src/",
|
|
@@ -43,7 +43,7 @@
|
|
|
43
43
|
]
|
|
44
44
|
},
|
|
45
45
|
"docs/agent-notes/adapters.md": {
|
|
46
|
-
"verifiedAgainst": "
|
|
46
|
+
"verifiedAgainst": "c3f379ce1ce1c4c1f7b5442c78fd8c534ff12ec0",
|
|
47
47
|
"paths": [
|
|
48
48
|
"src/adapters/",
|
|
49
49
|
"src/pi/",
|
|
@@ -93,7 +93,7 @@
|
|
|
93
93
|
]
|
|
94
94
|
},
|
|
95
95
|
"docs/agent-notes/budget.md": {
|
|
96
|
-
"verifiedAgainst": "
|
|
96
|
+
"verifiedAgainst": "c3f379ce1ce1c4c1f7b5442c78fd8c534ff12ec0",
|
|
97
97
|
"paths": [
|
|
98
98
|
"src/hub/budget.ts",
|
|
99
99
|
"src/cli/statusline-tee.ts",
|
|
@@ -117,7 +117,7 @@
|
|
|
117
117
|
]
|
|
118
118
|
},
|
|
119
119
|
"docs/agent-notes/bus.md": {
|
|
120
|
-
"verifiedAgainst": "
|
|
120
|
+
"verifiedAgainst": "c3f379ce1ce1c4c1f7b5442c78fd8c534ff12ec0",
|
|
121
121
|
"paths": [
|
|
122
122
|
"src/hub/bus.ts",
|
|
123
123
|
"src/hub/envelope.ts",
|
|
@@ -132,7 +132,7 @@
|
|
|
132
132
|
]
|
|
133
133
|
},
|
|
134
134
|
"docs/agent-notes/daemon.md": {
|
|
135
|
-
"verifiedAgainst": "
|
|
135
|
+
"verifiedAgainst": "c3f379ce1ce1c4c1f7b5442c78fd8c534ff12ec0",
|
|
136
136
|
"paths": [
|
|
137
137
|
"src/hub/daemon.ts",
|
|
138
138
|
"src/hub/control-client.ts",
|
|
@@ -243,7 +243,7 @@
|
|
|
243
243
|
]
|
|
244
244
|
},
|
|
245
245
|
"docs/agent-notes/tasks.md": {
|
|
246
|
-
"verifiedAgainst": "
|
|
246
|
+
"verifiedAgainst": "c3f379ce1ce1c4c1f7b5442c78fd8c534ff12ec0",
|
|
247
247
|
"paths": [
|
|
248
248
|
"src/hub/tasks.ts",
|
|
249
249
|
"src/hub/board.ts",
|
|
@@ -291,7 +291,7 @@
|
|
|
291
291
|
]
|
|
292
292
|
},
|
|
293
293
|
"docs/agent-notes/tests.md": {
|
|
294
|
-
"verifiedAgainst": "
|
|
294
|
+
"verifiedAgainst": "c3f379ce1ce1c4c1f7b5442c78fd8c534ff12ec0",
|
|
295
295
|
"paths": [
|
|
296
296
|
"test/",
|
|
297
297
|
"scripts/check.sh",
|
|
@@ -320,7 +320,7 @@
|
|
|
320
320
|
]
|
|
321
321
|
},
|
|
322
322
|
"docs/agent-notes/README.md": {
|
|
323
|
-
"verifiedAgainst": "
|
|
323
|
+
"verifiedAgainst": "c3f379ce1ce1c4c1f7b5442c78fd8c534ff12ec0",
|
|
324
324
|
"paths": [
|
|
325
325
|
"src/cli/console-state.ts",
|
|
326
326
|
"src/cli/attention.ts",
|
|
@@ -329,7 +329,7 @@
|
|
|
329
329
|
]
|
|
330
330
|
},
|
|
331
331
|
"src/cli/help.ts": {
|
|
332
|
-
"verifiedAgainst": "
|
|
332
|
+
"verifiedAgainst": "c3f379ce1ce1c4c1f7b5442c78fd8c534ff12ec0",
|
|
333
333
|
"paths": [
|
|
334
334
|
"src/cli/main.ts",
|
|
335
335
|
"src/cli/launch.ts",
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "agent-hub",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.27.0",
|
|
4
4
|
"description": "Channel between Claude Code and the agent-hub daemon: peer messages from Codex, Kimi and the local worker arrive as channel events; hub_send replies.",
|
|
5
5
|
"author": {
|
|
6
6
|
"name": "Young Joon Lee",
|