@staix/agent-research 0.0.0-stage → 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (52) hide show
  1. package/CHANGELOG.md +21 -0
  2. package/LICENSE +21 -0
  3. package/README.md +572 -2
  4. package/bin/ares.js +11 -0
  5. package/package.json +35 -4
  6. package/src/cli.ts +295 -0
  7. package/src/commands-table.ts +72 -0
  8. package/src/commands.ts +520 -0
  9. package/src/console/registry.ts +97 -0
  10. package/src/exp/ablate.ts +140 -0
  11. package/src/exp/artifacts.ts +229 -0
  12. package/src/exp/attempts.ts +475 -0
  13. package/src/exp/executor.ts +207 -0
  14. package/src/exp/final.ts +197 -0
  15. package/src/exp/ideas.ts +117 -0
  16. package/src/exp/investigate.ts +182 -0
  17. package/src/exp/logs.ts +63 -0
  18. package/src/exp/metrics.ts +132 -0
  19. package/src/exp/protocol.ts +145 -0
  20. package/src/exp/selection.ts +53 -0
  21. package/src/exp/snapshot.ts +147 -0
  22. package/src/http.ts +62 -0
  23. package/src/index.ts +4 -0
  24. package/src/lit/bibtex.ts +61 -0
  25. package/src/lit/candidates.ts +148 -0
  26. package/src/lit/cards.ts +21 -0
  27. package/src/lit/cite.ts +26 -0
  28. package/src/lit/sources.ts +122 -0
  29. package/src/lit/verify.ts +110 -0
  30. package/src/lit/watch.ts +109 -0
  31. package/src/manifest.ts +38 -0
  32. package/src/mcp-tools.ts +200 -0
  33. package/src/mcp.ts +111 -0
  34. package/src/memory/amem.ts +97 -0
  35. package/src/memory/archive.ts +63 -0
  36. package/src/memory/lessons.ts +88 -0
  37. package/src/memory/skills.ts +99 -0
  38. package/src/paper/build.ts +70 -0
  39. package/src/paper/check.ts +180 -0
  40. package/src/paper/placeholders.ts +116 -0
  41. package/src/paper/review.ts +115 -0
  42. package/src/plan/controller.ts +192 -0
  43. package/src/plan/definition.ts +156 -0
  44. package/src/plan/hub.ts +56 -0
  45. package/src/plan/state.ts +234 -0
  46. package/src/playbook.ts +129 -0
  47. package/src/scope.ts +53 -0
  48. package/src/setup.ts +296 -0
  49. package/src/study.ts +296 -0
  50. package/src/ui/panel.css +11 -0
  51. package/src/ui/panel.js +156 -0
  52. package/src/ui/plugin.ts +361 -0
package/README.md CHANGED
@@ -1,3 +1,573 @@
1
- # Temporary Holding Version
1
+ # agent-research (`ares`)
2
2
 
3
- This version is a temporary placeholder for this package. An operational version to replace this has been submitted for review and is awaiting a staged release.
3
+ Research engine for the agent family ([agent-hub](https://github.com/STAIxBWLB/agent-hub), [agent-mem](https://github.com/STAIxBWLB/agent-mem), [vault-mem](https://github.com/STAIxBWLB/vault-mem), [agent-console](https://github.com/STAIxBWLB/agent-console)). The plan and phases are in [#1](https://github.com/STAIxBWLB/agent-research/issues/1).
4
+
5
+ This checkout implements A1 literature studies, A2 container experiments and the A3 plan DAG: an append-only ledger, approval gates, verified reference identifiers, frozen candidate snapshots, separate evaluator containers, attempt records and a resumable controller with read-only hub task references. Investigation selection, idea decisions, winner/no-winner results, declared ablations, reserved-seed final evaluation and exploratory posthoc results are available. Paper check/build and build-bound review gates render evidence-backed Markdown. A5 adds amem lesson reads/archive and a reviewed skill proposal queue. A6 adds local client setup, registered-study console panels and confirmed decisions, and finite literature watch passes. Automatic idea generation/writing/reviewing remains later work.
6
+
7
+ ## Install
8
+
9
+ Requires Bun 1.4.2 or newer. After the first tagged release is published, install from npm:
10
+
11
+ ```sh
12
+ bun add -g @staix/agent-research
13
+ ares --help
14
+ ```
15
+
16
+ Or run from a checkout:
17
+
18
+ ```sh
19
+ bun install --frozen-lockfile
20
+ bun bin/ares.js --help
21
+ ```
22
+
23
+ In the examples below, `ares` means the executable `bin/ares.js`; use `bun bin/ares.js` from the checkout when it is not on PATH. Register its MCP entry with an agent client using a write-free preview, then apply only the inspected plan:
24
+
25
+ ```sh
26
+ ares setup all --json
27
+ ares setup all --apply --json
28
+ ares setup agy --apply --json
29
+ ares setup console --apply --json
30
+ ```
31
+
32
+ Targets are `claude`, `codex`, `kimi`, `pi`, `qwen`, `jcode`, `agy`, `all` and `console`. `all` covers the six user-level agent clients; it excludes workspace-local `agy` and the separate console registration. Run `setup agy` from its workspace. Only the ares MCP server entry is edited; instruction blocks remain dotfiles-owned. The command is `bun --smol <root>/bin/ares.js mcp`.
33
+
34
+ Setup is dry-run by default and lists config, receipt and lock paths plus the entry it would change. `--apply` writes a private receipt under `$AGENTHUB_HOME/research/setup/` before changing the client file, is idempotent, and preserves unrelated settings. `--undo` restores the prior entry only while the installed entry is unchanged; user edits are refused and retained. Config/receipt links and foreign-owned mutable paths are refused. A per-config exclusive lock serializes writers; a stale lock requires checking that its owner is no longer active before manual cleanup.
35
+
36
+ `--root ABS` selects and pins an existing agent-research release root, including its `package.json` and `bin/ares.js`; relative roots are refused. For example, preview `ares setup codex --root /absolute/releases/0.1.0 --json`, then add `--apply`. Console registration writes the API-1 `{api, tool, module, sha256}` manifest at `$AGENTHUB_HOME/console/plugins/ares.json`, or `ares@<version>.json` with `--root`. The module is the absolute `<root>/src/ui/plugin.ts` path. Re-run setup after a package change; use the same versioned root when undoing its console registration. The console loader verifies the module hash and trusted package tree. Setup starts no client or console process.
37
+
38
+ ## Literature workflow
39
+
40
+ ```sh
41
+ ares init study --question "How do research agents verify citations?"
42
+ ares scope study --file scope.json # question, subquestions, include, exclude, yearFrom, yearTo, maxCandidates
43
+ ares gate approve study scope
44
+ ares lit search study "citation verification agents"
45
+ ares lit list study
46
+ ares lit screen study <id>... --include --reason "on topic"
47
+ ares lit card study <id> --file card.json # summary, claims, methods, findings, limitations, relevance
48
+ ares lit verify study
49
+ ares gate approve study screen
50
+ ares lit bib study # writes study/refs/references.bib
51
+ ares cite check study draft.md # exit 1 on unknown or unverified [@keys]
52
+ ```
53
+
54
+ `ares playbook` prints the full standalone workflow and its rules. Study commands accept `--json`; see the command list for exact usage.
55
+
56
+ ## Literature watch workflow
57
+
58
+ A watch is a saved, bounded search pass for either a survey or an experiment study. It is not a resident watcher. Configure an initialized study with a scope first; in gates mode, approve its current scope before running:
59
+
60
+ ```json
61
+ {
62
+ "v": 1,
63
+ "queries": [
64
+ {"id": "recent", "query": "synthetic research topic", "source": "all", "yearFrom": 2020, "limit": 20}
65
+ ],
66
+ "rubric": "Display this context beside new candidates for the human screener."
67
+ }
68
+ ```
69
+
70
+ ```sh
71
+ ares lessons study --for "refresh the literature watch"
72
+ ares watch set study --file watch.json
73
+ ares watch run study --json
74
+ ares watch show study --json
75
+ ```
76
+
77
+ There are at most 20 uniquely named queries, each at most 500 characters, with source `openalex|s2|arxiv|all`, optional yearFrom and limit 1..50. The rubric is at most 8,000 UTF-8 bytes and is display-only: ares never scores against it. `watch set` writes bounded `literature/watch.json` and `watch.set`; identical stored bytes are a no-op. Valid input can repair corrupt JSON and malformed version values, including string "1", null, a missing v or fractional 1.5; the refusal says to set v to 1. Only a safe integer v greater than 1 is an unsupported future version, which remains unchanged. Read failures name the file and set/restore recovery.
78
+
79
+ `watch run` holds one study lock for the full pass, reusing the existing scope gate and `lit search` merge path for each query. Other mutating commands for that study, including another watch start, are refused until it finishes. At the maximum, sequential HTTP deadlines can total 20 minutes (20 queries x 3 sources x 20 seconds), plus parsing and local file work. Queries do not run in parallel and ares starts no timer, daemon or recurring process. Each query records `lit.search`, then one `watch.ran` records counts, source errors as host/status only, the stored config byte hash, and up to 500 surviving new candidate ids plus their full total. The rubric stays in config rather than being copied to every run. Aggregate `added` retains the per-query search counts, even when a later merge absorbs an earlier new record. CLI `watch run` waits for completion; a run where every source fails is still recorded and exits 1.
80
+
81
+ MCP `ares_watch_run` is always detached: after validating config, scope and query years under the study lock, it returns `{started:true,configSha256}`. This acknowledges admission, not completion. The same MCP process runs the finite pass while remaining responsive to ping and tool listing; no separate background daemon is spawned. Use `ares_watch_show` to inspect the eventual `watch.ran` outcome, including `ok:false` if every source failed. A second start while the study is locked refuses; wait for the recorded outcome before starting another pass.
82
+
83
+ Watch never includes, excludes or verifies candidates, and never approves the screen gate. New records remain `new`; existing screening states survive merges. Inspect `watch show`, then screen and verify through the normal literature workflow. Show is read-only, with current config/rubric, the ten most recent runs, and a bounded list of still-new candidates. The `watch.run` plan step takes `{}` and returns `{added}`; an all-source failure fails the node.
84
+
85
+ ## Scheduling watch passes
86
+
87
+ Recurrence belongs to the person. Ares does not register or enable a schedule. Choose one of these approaches and replace every absolute path, task class and peer with values for the intended project.
88
+
89
+ ### Agent-hub schedule
90
+
91
+ Merge an entry into that project's machine-local `.agenthub/config.local.json` (not committed `config.json`):
92
+
93
+ ```json
94
+ {
95
+ "schedules": [
96
+ {
97
+ "id": "ares-watch",
98
+ "every": "1h",
99
+ "class": "implement",
100
+ "title": "Run one saved literature watch",
101
+ "owner": "codex",
102
+ "queue": "ready",
103
+ "detail": "Run one pass: /absolute/path/to/bun --smol /absolute/path/to/agent-research/bin/ares.js watch run /absolute/path/to/study --json. Inspect new candidates; do not screen automatically."
104
+ }
105
+ ]
106
+ }
107
+ ```
108
+
109
+ Use an existing class and configured peer. Agent-hub requires intervals of at least 15 minutes; `every` accepts `<n>m`, `<n>h` or `<n>d`. This schedule proposes a board task for the peer, never executes the command itself. The peer must accept and perform the task through its normal workflow. A still-open task from the previous scheduled run prevents another proposal; held or missed periods do not replay as a burst. The person can inspect it with `ahub schedule list`. Only the person's local configuration enables this behavior.
110
+
111
+ ### User cron
112
+
113
+ A user crontab can run one pass directly, without agent-hub. The absolute Bun executable and ares entrypoint avoid cron's restricted PATH:
114
+
115
+ ```cron
116
+ 0 * * * * /absolute/path/to/bun --smol /absolute/path/to/agent-research/bin/ares.js watch run /absolute/path/to/study --json >> /absolute/path/to/ares-watch.log 2>&1
117
+ ```
118
+
119
+ Use the intended user's HOME/family configuration and required search environment, with the study scope already approved. This is a user crontab line, with no username column. Overlapping runs are refused by the study lock; cron does not bypass approval or screening. Neither recipe creates an ares resident watcher.
120
+
121
+ ## Experiment workflow
122
+
123
+ Requires Docker on the executor host and, for SSH execution, a configured SSH alias that works with `BatchMode=yes`. Images must already contain dependencies; ares does not build them and experiments have no network access. Survey is the default mode; mode is fixed at init, so create a separate experiment study.
124
+
125
+ ```sh
126
+ ares init experiment --question "Does this candidate improve the declared metric?" --mode experiment
127
+ # Write experiment/task/task.md and experiment/task/evaluator/ before setting the protocol.
128
+ ares protocol experiment --file protocol.json
129
+ ares executor set experiment --kind ssh --host dgx-h100-2 --workdir /private/owned/ares
130
+ # Or: ares executor set experiment --kind local --workdir /private/owned/ares
131
+ ares executor check experiment
132
+ ares gate approve experiment design
133
+ ares attempt run experiment --repo /absolute/candidate-repo --commit <full-40-hex-SHA> --seed 1 --run main --label baseline --detach
134
+ ares attempt reconcile experiment
135
+ ares attempt list experiment
136
+ ares attempt show experiment <id>
137
+ ares attempt logs experiment <id> --stage candidate --stream stderr --tail 16384
138
+ # To stop a running attempt:
139
+ ares attempt cancel experiment <id> --reason "stop this run"
140
+ ```
141
+
142
+ Executor hosts need `tar --no-xattrs` support (GNU tar >=1.27 or bsdtar). Unsupported implementations such as BusyBox tar fail with the generic "stage log collection failed" executor error; ares does not fall back to archives containing metadata.
143
+
144
+ Create the executor workdir on that host, owned by its user with mode `0700`. The path is required and absolute; a shared `/tmp` default is not used. `executor check` reports the Docker server version, image availability and non-root uid:gid, and may pull missing images. It stores the alias, never SSH credentials.
145
+
146
+ The task contains `task.md`, evaluator entrypoint/data under `evaluator/`, and optional `rubric.md`. Use regular files only, at most 500 files and 64 MiB total; symlinks are refused. A protocol has this shape (the pinned BusyBox image below can run the synthetic shell fixtures):
147
+
148
+ ```json
149
+ {
150
+ "v": 1,
151
+ "method": "Compare a frozen candidate using the declared evaluator",
152
+ "candidate": {
153
+ "image": "busybox@sha256:a7dbc319b1438115a207e11930dc4bb3e643dcb3ec0bdd694bb36cd8fad289ec",
154
+ "command": ["sh", "/workspace/candidate.sh"]
155
+ },
156
+ "evaluator": {
157
+ "image": "busybox@sha256:a7dbc319b1438115a207e11930dc4bb3e643dcb3ec0bdd694bb36cd8fad289ec",
158
+ "command": ["sh", "/eval/evaluate.sh"]
159
+ },
160
+ "metrics": [{"name": "score", "unit": "points", "direction": "max"}],
161
+ "seeds": [1],
162
+ "limits": {"cpus": 1, "memoryMb": 64, "pidsLimit": 64, "wallSeconds": 30, "gpus": 0},
163
+ "network": "none",
164
+ "budget": {"maxAttempts": 4, "maxWallHours": 1, "maxGpuHours": 0}
165
+ }
166
+ ```
167
+
168
+ Commands are argv arrays, never shell strings. An optional `candidate.env` accepts bounded strings; secret-like keys and `ARES_SEED`, `__proto__`, `constructor`, `prototype` are refused. The selected declared seed is supplied to both stages as `ARES_SEED`.
169
+
170
+ The candidate gets its frozen Git archive at `/workspace:ro` and writable `/out`, with no evaluator or `/result` mount. A different container gets `/eval:ro`, `/candidate:ro` and writable `/result`. Both use network none, read-only root filesystems, a temporary `/tmp`, dropped capabilities, no-new-privileges, protocol resource limits and the executor user's non-root uid:gid. The commit must be a full 40-hex SHA; the archive is capped at 256 MiB and ignores later HEAD/worktree edits.
171
+
172
+ Only the evaluator's `/result/metrics.json` supplies metrics: at most 64 KiB, exactly the declared names with finite numeric values, for example `{"score": 7}`. Units and directions come from the protocol. Missing, malformed or undeclared metrics fail at the evaluator stage; candidate-printed scores never become metrics.
173
+
174
+ Without `--detach`, `attempt run` reconciles until finished. Detached runs and MCP `ares_attempt_run` return while running; call `attempt reconcile` again to advance stages and collect results. States progress from running candidate to running evaluator, then succeeded, failed, cancelled or unknown. A gone container without an exit record becomes unknown (`lost`); reconcile never creates a new attempt. One attempt may be active per study. A host watchdog enforces each stage's wall-clock limit.
175
+
176
+ The same protocol hash, commit/tree, seed and run return the same attempt. Use `--retry <id>` for a linked new attempt; a different commit or seed is a new attempt, not a reproduction. Every status counts toward study budgets (attempt count, wall hours, GPU hours). `attempt list` returns summaries and whole-study totals for every status regardless of filters or page; default page size 50, maximum 200, with `--offset` for later pages. `attempt show` returns the full record.
177
+
178
+ Raw outputs and logs stay in the private executor attempt directory. The study holds output paths, sha256 and byte counts (regular files only, at most 500 files and 256 MiB total), plus each log stream's path, hash, bytes and truncation flag. Each stdout/stderr copy is capped at 16 MiB; excess log volume does not fail an attempt. `attempt logs` checks the recorded copies and prints tails without changing the study; `--tail` accepts 0..65536 bytes (default 16384), and omitted stage/stream filters return all recorded streams. No log content is persisted in the study.
179
+
180
+ ## Investigation workflow
181
+
182
+ Set the selection rule before running baseline and candidate attempts. It fixes the run, primary protocol metric, frozen baseline commit, minimum paired seeds and improvement margin. Save this as `selection.json`, replacing the synthetic baseline SHA with your full frozen commit:
183
+
184
+ ```json
185
+ {"v": 1, "run": "investigate", "metric": "score", "baseline": {"commit": "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"}, "minSeeds": 1, "minDelta": 0.01}
186
+ ```
187
+
188
+ Use the experiment setup above, then:
189
+
190
+ ```sh
191
+ ares selection experiment --file selection.json
192
+ ares gate approve experiment design
193
+ # Run the frozen baseline and each candidate with --run investigate and declared seeds.
194
+ ares attempt run experiment --repo /absolute/candidate-repo --commit <baseline-40-hex-SHA> --seed 1 --run investigate --label baseline --detach
195
+ ares attempt reconcile experiment
196
+ # After it stops, run candidate commits the same way, then reconcile each one.
197
+ ares investigate show experiment --json
198
+ ares investigate close experiment --json
199
+ ```
200
+
201
+ `metric` must be declared in the protocol; its unit and max/min direction come from there. `minSeeds` is 1..the number of declared seeds, and `minDelta` is finite and >=0 in that unit. A new or changed rule adds its raw file hash to the design seal and reopens approval. Identical validated bytes are a no-op, so they cannot erase earlier results by moving the cutoff. Protocol edits that invalidate an existing rule refuse before writing. Studies without selection retain the original design binding and approval.
202
+
203
+ `investigate show` is read-only. Counting uses the selected run, current protocol hash and `attempt.started` ledger order after the latest recorded `selection.set`. Only the earliest successful attempt per commit/seed counts. Later successes, other-protocol, before-selection, undeclared-seed and missing-start attempts are listed with reasons; failed, cancelled, unknown and excluded records remain in run totals. Labels do not choose the baseline, its commit in the rule does.
204
+
205
+ Each candidate compares its mean primary metric against the baseline on their shared successful seeds. It needs at least `minSeeds` pairs, a direction-adjusted delta strictly above zero and at least `minDelta`. Eligible candidates rank by paired delta, not raw mean, because paired seed sets can differ. An exact best-delta tie yields no winner. Other no-winner reasons are `no-baseline`, `baseline-seeds`, `no-candidate` and `below-margin`. Means are descriptive, with no significance test.
206
+
207
+ Close refuses while any attempt in the selected run is running, including excluded attempts. It writes `runs/<run>/investigate.json` and `investigate.closed` together. The timestamp-free result includes rule/input hashes, baseline/candidate rows, paired values and means, exclusions, totals and a winner or `null` with its reason. Closing unchanged inputs writes nothing. Show/close starts no attempt, Docker process or hub; the tests use synthetic offline records.
208
+
209
+ ### Idea journal
210
+
211
+ Ideas and their decisions live in ledger events, not a second file. An idea input for `ares idea add experiment --file idea.json` is:
212
+
213
+ ```json
214
+ {"id": "variant-1", "title": "Synthetic candidate", "hypothesis": "The frozen candidate improves the declared metric", "commit": "bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb"}
215
+ ```
216
+
217
+ Replace that synthetic commit with your candidate's frozen SHA. `id` and optional `parent` use the run-name rule (1..64 characters, alphanumeric first, then letters/digits/underscore/hyphen). Ids are unique, parent must exist, and each commit has exactly one idea owner. Limits are 1,000 ideas, 500-character titles and 4,000-character hypotheses. After inspecting terminal attempts from that idea's existing commits, record a decision:
218
+
219
+ ```sh
220
+ ares idea decide experiment variant-1 refine --reason "Test the next frozen candidate" --attempts <terminal-attempt-id> --commit <next-40-hex-SHA>
221
+ # To close a tested direction:
222
+ ares idea decide experiment variant-1 pivot --reason "Drop this direction" --attempts <terminal-attempt-id>
223
+ ```
224
+
225
+ Pivot optionally records `--cause` and `--retry-when` (at most 2,000 UTF-8 bytes each); refine refuses these fields. Missing pivot fields export as `not recorded`. Refine adds the uniquely owned commit. Pivot refuses `--commit`, closes further decisions and keeps historical attempts and totals. Evidence is at most 200 existing, non-running, same-study attempt ids from the idea's commits; reasons are at most 2,000 bytes. An untested idea may omit `commit` when added, then omit `--attempts` for a first `refine --commit` or a pivot before testing. Once it has commits, nonempty evidence is required. Investigation rows and winner references carry the owning idea id; journal changes join the result's input hash. Survey studies refuse all selection, idea and investigate commands.
226
+
227
+ A conductor can put a no-argument `investigate.close` step after its agent-work hub unit, then bind a local plan gate to that result:
228
+
229
+ ```json
230
+ {
231
+ "v": 1,
232
+ "nodes": [
233
+ {"id": "agent-work", "kind": "hub", "campaign": "rc-0123456789ab", "unit": "candidates"},
234
+ {"id": "close", "kind": "step", "command": "investigate.close", "args": {}, "needs": ["agent-work"]},
235
+ {"id": "review-result", "kind": "gate", "needs": ["close"], "binds": ["close"]}
236
+ ],
237
+ "edges": []
238
+ }
239
+ ```
240
+
241
+ Use your real campaign/unit ids, with unit completion covering all attempts reaching terminal states. The close step emits exactly `{sha256, winner}` for the saved result; `winner: null` is a valid completed outcome, and its file explains why. The plan gate binds that reference before any later research phase. Start/resume/approve the plan as below. Ares only observes hub work; it does not generate ideas, launch coding agents or perform hub writes. Live hub/executor DAG coverage has not been run for this investigation workflow.
242
+
243
+ ## Ablation, final and posthoc workflow
244
+
245
+ Before collecting results, reserve unseen seeds with `ares final seeds experiment --file final-seeds.json`, where the input is `{"v":1,"seeds":[3,4]}` and all seeds are declared in the protocol. Reserve at least one and at most half of the protocol seeds. New or changed reservations refuse seeds used by any existing attempt, across all runs and statuses; identical bytes are a no-op. The file joins the design seal, so approve design again after a change. Protocol edits cannot drop reserved seeds or leave more than half reserved. Only run `final` may use them; investigate and ablate report reserved samples as excluded. Selection run names `ablate`, `final` and `posthoc` are reserved phase names and refused.
246
+
247
+ After `investigate close` has a current winner, declare each ablation with `ares ablate add experiment --file ablation.json`: `{"id":"without-component","commit":"<full-40-hex-SHA>","removes":"Component removed"}`. Ids and commits are unique, up to 200 declarations; `removes` is at most 2,000 bytes. The winner and baseline cannot be ablation commits. Run each declared commit and the winner replication using non-reserved seeds and `--run ablate`, then reconcile them. `ares ablate show experiment` pairs each declaration against the winner's own ablate-run successes on shared seeds, using the current protocol and earliest success after the selection cutoff. It never reuses investigate samples. Every declaration appears even without data, with all statuses/exclusions and no ranking. `ares ablate close experiment` requires a current closed investigate winner and no running ablate attempts; it writes timestamp-free `runs/ablate/ablate.json` and `ablate.closed`. Unchanged inputs record nothing.
248
+
249
+ Approve the global final gate after investigation and, if ablations were declared, ablation closure:
250
+
251
+ ```sh
252
+ ares gate approve experiment final
253
+ # Repeat for every reserved seed and both the winner and baseline frozen commits:
254
+ ares attempt run experiment --repo /absolute/repo --commit <winner-or-baseline-SHA> --seed 3 --run final --detach
255
+ ares attempt reconcile experiment
256
+ ares final close experiment
257
+ ```
258
+
259
+ The final gate binds SHA-256 of the investigate result sha, the ablate result sha or none, and the design binding. Approval requires current closed upstream results. Changed content reopens it; re-close changed upstream inputs and re-approve before final close. Start checks require a reserved seed, the current winner or selection baseline, and final-gate authorization before any Git resolution, record or container. In auto autonomy, final approval is recorded only with a verified candidate start, so failed preparation records no automatic approval.
260
+
261
+ Final is one shot: the first ledger-started attempt for each winner/baseline commit and reserved seed is decisive, whatever its status. Retries stay visible and never replace it. Close refuses running final attempts or missing decisive pairs. It requires the final gate currently approved even for a negative result. Every decisive attempt must succeed and the direction-adjusted paired delta must be positive and at least `minDelta`; otherwise the result is `not_qualified` with a named reason. There is no significance test. Reapproval keeps existing data fixed; old seal/input bindings alone never disqualify it. A changed evaluation protocol gives `protocol-changed`. A winner change after any final attempt, including baseline-only trials, gives `winner-changed` without demanding new-winner pairs and blocks new final starts. Reason priority is winner change, protocol change, first decisive failure status, then below-margin. `runs/final/final.json` and `final.closed` are committed together; unchanged inputs record nothing, unsupported versions are preserved.
262
+
263
+ After a historical `final.closed` receipt exists, run additional non-reserved seeds/commits with `--run posthoc`. `ares posthoc show experiment` lists all posthoc statuses and metrics labeled `exploratory`. Posthoc work never changes final result bytes, the final input fingerprint or its close receipt, even while an exploratory attempt runs. These are experiment-only commands; survey studies refuse them on CLI and MCP.
264
+
265
+ A conductor can use no-argument `ablate.close` and `final.close` steps. Outputs are `{sha256,status}`, with the hash of the committed result bytes and status `closed` for ablate, or `qualified`/`not_qualified` for final. Both final outcomes complete the step normally. A downstream plan gate binds these references; the global `final` gate remains a separate prerequisite for final attempts and closure. Put attempt-completion dependencies before each close and approve the global final gate after ablate closure.
266
+
267
+ Final here means reserved seeds, the frozen Git snapshot and the sealed evaluator. Held-out evaluator data outside the study repository is deferred; data already in `task/` is not hidden from agents. Acceptance uses offline synthetic fixtures; no live hub campaign or live executor DAG coverage is claimed.
268
+
269
+ ## Paper and review workflow
270
+
271
+ A4 renders Markdown from recorded evidence; it does not write a manuscript or run reviewers for you. Write `paper/manuscript.md` (at most 2 MiB) and put measured values in `{{n:REF}}` placeholders, optionally `{{n:REF|.Nf}}` with N from 0 to 6. At most 5,000 numeric and literal placeholders are allowed together. Use `{{lit:TEXT}}` for a reviewer-visible literal such as a year or cohort label. Every literal is listed by the check; it is an explicit exception, not a measured-value match.
272
+
273
+ | Ref | Closed source |
274
+ | --- | --- |
275
+ | `final.status`, `final.reason`, `final.delta`, `final.mean`, `final.baselineMean`, `final.attempts.<status>` | current final result and all-final-attempt status counts |
276
+ | `investigate.winner`, `investigate.reason`, `investigate.winner.delta`, `investigate.winner.mean`, `investigate.baseline.mean`, `investigate.totals.<status>` | current closed investigate result |
277
+ | `ablate:<id>.delta`, `ablate:<id>.mean`, `ablate:<id>.referenceMean`, `ablate.totals.<status>` | current closed ablate result |
278
+ | `attempt:<id>.<metric>`, `posthoc:<id>.<metric>` | succeeded current-protocol attempt with a finite ares-observed metric; posthoc always appends ` (exploratory)` |
279
+
280
+ Status counts use `running`, `succeeded`, `failed`, `cancelled` or `unknown`. Text refs take no numeric format; an investigate winner renders as its 12-hex commit prefix. Missing, null, unclosed, stale or unsupported sources give `unresolved-ref`. Final refs also need the final gate currently approved. Metric names are exact declared names, including spaces and punctuation such as `accuracy (%)`; names containing `|` or `}` are unreferenceable in this grammar and need a new protocol/name. The ordinary `attempt:` prefix refuses posthoc records, and `posthoc:` refuses other runs.
281
+
282
+ ```sh
283
+ ares paper check experiment --json
284
+ ares paper build experiment --json
285
+ ```
286
+
287
+ In experiment prose, other digits give `bare-number`; matching a free number against an existing metric is never allowed. Digits are allowed in link targets, placeholder/literal payloads and line-start ordered-list markers. Citation keys may contain digits, but bracket prose is checked: only locator terms (`p.`, `pp.`, `sec.`, `§`, `ch.`, `chap.`, `fig.`, `tbl.`, `table`, `eq.`, `l.`, `ll.`, `para.`) followed by integers, ranges or lists are exempt. `[@key, improved accuracy by 12.5%]` still fails. Inline/fenced/indented code numbers are nonblocking but listed as `codeNumbers` (first 500 values with line/column) and `codeNumberCount` (total). Paragraph and list continuations are prose. Unknown or unverified citation keys fail, including citations inside literal escapes; verification means identifier existence plus title/year match, never support for a claim.
288
+
289
+ Negative results must be visible: when final.json exists, include `{{n:final.status}}`; when the closed investigation has no winner, include `{{n:investigate.reason}}`. Code examples, link targets, HTML comments and reference-definition metadata cannot satisfy this rule. Include a reason or numeric ref only when that source field exists; null values stay unresolved. The check is read-only and reports positions for every finding; a failing CLI check exits 1. Survey studies check citations without the experiment bare-number rule and refuse experiment placeholders with `unresolved-ref`; they can build citation-only manuscripts.
290
+
291
+ Build refuses every finding before writes. It commits `paper/build/manuscript.md` (rendered values), `paper/build/evidence.json` (`paperEvidence: 1`) and `paper.built` together. The evidence map keeps the input manuscript hash and each active placeholder's syntax, value, rendering, position, source file and raw source hash; literal sources point to the input manuscript. Outputs are bounded to 2 MiB rendered Markdown and 8 MiB evidence. Identical outputs with a matching receipt are a no-op. Changed provenance rebuilds evidence even if rendered text is identical; unknown evidence versions preserve original bytes. The returned `manuscriptSha256` is the rendered output hash; `evidenceSha256` is the review's build identity.
292
+
293
+ Have reviewers inspect the current output, evidence, literals and code-number list. Save each report and add it against the current evidence hash:
294
+
295
+ ```json
296
+ {
297
+ "v": 1,
298
+ "reviewer": {"id": "reviewer-a", "modelFamily": "family-a"},
299
+ "buildSha256": "<current-evidenceSha256>",
300
+ "checklist": {"negativeResults": true, "denominators": true, "limitations": true, "reproducibility": true},
301
+ "findings": []
302
+ }
303
+ ```
304
+
305
+ ```sh
306
+ ares review add experiment --file review-a.json
307
+ # Add a report from a second, distinct model family with the same current evidence hash.
308
+ ares review add experiment --file review-b.json
309
+ ares review show experiment --json
310
+ # For an existing finding, using the review id returned by add:
311
+ ares review resolve experiment <review-id>/<finding-id> --reason "How the issue was resolved"
312
+ ares gate approve experiment review
313
+ ```
314
+
315
+ A finding is `{id,kind,location,text,blocking}`; kinds are `contradiction`, `unsupported-claim`, `number`, `citation`, `method`, `clarity`, `other`. Contradictions are always blocking, even if submitted as false. A reasoned resolution records its actor; clean votes never resolve another review's finding. Reports use self-reported family labels, normalized for whitespace at the ends and case; ares neither launches nor attests to the reviewers. Limits: 100 reports per build, 200 findings per report, 4,000-byte texts/reasons, 128-byte reviewer/family/finding identifiers. The existing ledger event ceiling also limits a combined report to 60 KiB; shorten a report rather than raising the ledger bound.
316
+
317
+ The global `review` gate needs a clean current check/build, at least two distinct model families for that evidence hash, and no open blocking finding. Checklist answers are shown, including false answers, rather than adding a separate approval condition. Readiness names every unmet condition. Approval is checked in the shared command under the study lock; reject still needs a reason but no readiness. No current command auto-requires the review gate. The gate binds both output hashes and sorted added/resolved event hashes for that build. A changed build, added review or resolution reopens it; old reviews stay in the ledger but do not count toward a new build.
318
+
319
+ A no-argument `paper.build` conductor step emits `{sha256}` of the committed evidence file; a downstream `plan:<id>` gate binds it. That local gate does not replace the global manuscript `review` gate. MCP paper/review operations never start the controller loop. Claim-to-passage evidence and its contradictions await [agent-hub#931](https://github.com/STAIxBWLB/agent-hub/issues/931); readiness currently reports `claims: unavailable (STAIxBWLB/agent-hub#931)` without blocking on it. Claim verdicts stay outside ares. LaTeX/PDF container builds and venue templates are out of scope. Acceptance uses offline synthetic fixtures, not live hub/executor DAG proof.
320
+
321
+ ## Memory and skill proposal workflow
322
+
323
+ Use amem 0.20.3 or a compatible CLI. Start each phase with an intent-specific lesson read, then archive only at the end of the study:
324
+
325
+ ```sh
326
+ ares lessons study --for "compare candidate hypotheses" --half-life 180 --limit 50 --json
327
+ ares skill propose study --file proposal.json
328
+ ares skill list study --json
329
+ ares skill decide study reusable-procedure accept --reason "Reviewed against recorded evidence" --pr https://github.com/example/project/pull/1
330
+ ares archive study --json
331
+ ares archive study --confirm <fingerprint> --json
332
+ ```
333
+
334
+ `lessons` searches with `--for` text, otherwise with the study question, in the study's amem project. An intent also adds a typed `amem brief`. It reports age in fractional days (future timestamps clamp to zero), T1 relevance with amem recency removed, half-life-decayed score and 1-based rank. The formula is `score = relevance * 0.5^(age / halfLife)`; ties put fail notes first, then lexical id. Half-life defaults to 180 days, bounded to 1..3650; result limit defaults to 50, bounded to 1..50. The command is read-only and takes no study lock.
335
+
336
+ Archive derives fail notes from pivot decisions, findings from current closed investigation/final results and resolved review contradictions, and decisions from selection/final seeds. Fail fields use recorded cause/retryWhen or `not recorded`; outcomes retain negative reasons. Open contradictions stay out. Each note starts with study id/question; evidence uses study-scoped attempt/run refs, split into consecutive notes of at most 16 refs so none are dropped. Preview writes nothing. Its fingerprint binds the full canonical note list and stays valid across receipts. Confirmation writes at most the first 200 pending notes in stable order, reports `remaining`, and records returned ids/content hashes in one `archive.exported` event. Repeat the same confirmation for later batches; already-exported hashes are skipped. Partial failures commit successful receipts and explain how to continue. Changed source notes invalidate confirmation. Receipt capacity is checked before any outward write, and confirmed calls hold the study lock. Errors never include amem stdout/stderr. Note text travels on stdin, and `--`-leading option values get one leading space for the amem parser. Agent markers and home/config context are allowlisted; API credentials stay stripped.
337
+
338
+ A proposal input is `{name, summary, steps, evidence}`: a run-name `name`, nonblank summary, at least one step, and 1..64 evidence refs (32-hex amem ids or `ares:<study-id>/...`). Unknown fields are refused. Deterministic Markdown at `proposals/skills/<name>.md` is bounded to 16 KiB and bound to its `skill.proposed` hash receipt. At most 100 proposals, including terminal states, are retained. List verifies bounded file bytes against receipts and shows proposed/accepted/rejected/withdrawn. Accept requires an HTTP(S) PR URL without credentials; ares records the actor's reference without opening, checking or merging the PR. Every terminal state is final; withdraw is allowed only while proposed. Durable vault promotion remains amem's `vault-candidates` pipeline; ares never writes the vault.
339
+
340
+ Survey and experiment studies both support lessons, archive and all skill commands. Survey archives can export resolved review contradictions, and are empty when no eligible lessons exist. Selection, ideas and experiment-result operations remain experiment-only. All A5 acceptance tests use synthetic records and fake runners; no live amem or vault interoperability is claimed.
341
+
342
+ ## Console workflow
343
+
344
+ Register only studies the console should read; ares never scans the disk:
345
+
346
+ ```sh
347
+ ares console add /absolute/path/to/study
348
+ ares console list --json
349
+ ares setup console --json
350
+ ares setup console --apply --json
351
+ # To remove only the registration, including one whose study moved:
352
+ ares console remove /absolute/path/to/study
353
+ ```
354
+
355
+ The bounded version-1 registry is `$AGENTHUB_HOME/research/studies.json` (at most 200 absolute/canonical study directories, private 0600 file in a 0700 directory). Add requires a readable study; add/remove are idempotent. A moved or unreadable study remains registered and appears as `unavailable`, with its restore reason. Remove can use its listed path without reopening the study.
356
+
357
+ The API-1 plugin supplies `studies` and `study` panels, plus inbox items for pending gates, still-new recorded watch candidates with the current display rubric, proposed skills, open review findings, failed/unknown attempts and unavailable studies. A truncated watch receipt produces an event-timed overflow notice rather than guessing provenance for unrecorded ids. Partially unreadable records produce an alert instead of false zero counts; empty stores and unfinished optional phases are normal. The study view links only the fresh built manuscript (at most 2 MiB) as `text/plain`; ares generates no PDF or figures. Reads stay in process, with no search, container or plan-control subprocess.
358
+
359
+ Every console write first returns a write-free preview and fingerprint. Confirmation recomputes the selected study and bound content under the existing command lock and refuses drift. The wire ids are `gate.approve`, `gate.reject`, `screen.include`, `screen.exclude`, `skill.accept`, `skill.reject`, `skill.withdraw` and `archive.confirm`. The id selects the decision; there is no separate page-supplied decision field. Rejections and screening/skill decisions require reasons; skill acceptance also requires the reviewed HTTP(S) PR URL. The ledger actor is the person, with verified `meta.identity` when the console supplies it; page actor/meta fields and other unsupported action fields are refused. A read-only console (`acon serve --read-only`) refuses actions before invoking the plugin.
360
+
361
+ Console archive writes to amem only after confirmation and has a 25-second total child-process budget inside the console's 30-second provider ceiling. Returned note ids are receipted on partial failure; refresh and review a new console preview to continue, or inspect `ares archive` from the CLI. The final study step remains archive, after paper/review and skill decisions. No live amem/vault or deployed-console interoperability is claimed by the offline fixture tests.
362
+
363
+ ## Plan and conductor workflow
364
+
365
+ A plan connects existing commands, approval gates and read-only hub task references. It can close investigate, ablate and final results and bind their output references to downstream gates. Create a study first. This offline example checks two drafts, with an approval between them: put citation-free text in `plan-study/draft.md` and `plan-study/final.md`, and save this definition as `draft-plan.json`:
366
+
367
+ ```json
368
+ {
369
+ "v": 1,
370
+ "nodes": [
371
+ {"id": "check-draft", "kind": "step", "command": "cite.check", "args": {"draft": "draft.md"}},
372
+ {"id": "review", "kind": "gate", "needs": ["check-draft"], "binds": ["check-draft"]},
373
+ {"id": "check-final", "kind": "step", "command": "cite.check", "args": {"draft": "final.md"}, "needs": ["review"]}
374
+ ],
375
+ "edges": []
376
+ }
377
+ ```
378
+
379
+ ```sh
380
+ ares init plan-study --question "Are the draft citations ready for review?"
381
+ # Write the two draft files described above, then set the plan.
382
+ ares plan set plan-study --file draft-plan.json
383
+ ares plan show plan-study --json
384
+ # Terminal A: stays open, initially paused.
385
+ ares run plan-study --parallel 2
386
+ ```
387
+
388
+ In terminal B, use the revision from `plan show` (1 for this first definition):
389
+
390
+ ```sh
391
+ ares plan control plan-study resume --command-id resume-1 --expected-revision 1
392
+ ares gate list plan-study
393
+ # After plan:review is pending with complete upstream outputs:
394
+ ares gate approve plan-study plan:review
395
+ ares plan show plan-study --json
396
+ ```
397
+
398
+ `run` exits 0 when the plan is done. Each controller start restores an unfinished plan paused; resume it explicitly from another terminal or MCP client. SIGINT/SIGTERM stops admission, drains active command calls, records paused when still running, and releases the controller lock. Detached attempts stay alive for `attempt reconcile`. A live or foreign controller owner is refused; a dead same-host owner is recoverable by pid and process start identity, never by lock age. Waiting observations use 1 s to 30 s backoff, reset on progress, and unchanged polls write nothing.
399
+
400
+ Plan definitions accept at most 128 nodes, 512 explicit edges and 128-byte ids, with no cycles or missing dependency endpoints. `needs` and `edges` both supply dependencies; a gate's `binds` may name only upstream nodes. A step command is one of `lit.search`, `lit.verify`, `lit.bib`, `cite.check`, `attempt.run`, `investigate.close`, `ablate.close`, `final.close`, `paper.build`, `watch.run`. Close/build and `watch.run` steps take no args. `watch.run` uses the saved watch synchronously and emits the completed `{added}` count; every-source failure records the run and fails the node. Named `args` carry CLI options without `--`, including `year-from`; positional inputs are `query` for search, optional `ids` for verify, and `draft` for cite check. `attempt.run` needs `repo` and `commit`, always detaches, and accepts the existing seed/run/label/retry options. Steps refuse controller-owned `json` and `detach` arguments. Relative `repo` and `draft` resolve against the study directory. Scope, screen and design prerequisites still apply to the underlying commands.
401
+
402
+ `--parallel` admits 1..8 step nodes (default 1); the existing one-active-attempt-per-study rule still applies. Plan state is replayed from ledger events. `plan show` is lock-free and lists each node's current status, last observation, attempt/retry/task references, gate decisions, uncertain nodes and controller owner. It also shows `hubProject` when a hub read resolves a board.
403
+
404
+ Controls require a caller-chosen `--command-id` and the current definition's `--expected-revision`. Identical requests replay their original receipt, even after a definition change; reusing an id with another payload or using a stale revision for a new request refuses. Controls do not bump the definition revision. `pause` stops admission, `resume` allows it, `cancel <node>` stops that node's attempt when one exists, and `retry <node>` makes a failed, cancelled, uncertain, stale or completed node eligible for a fresh dispatch. Retry links an existing parent attempt; a predicted id with no attempt record is not a retry parent. `skip <node>` releases ordinary control dependencies without producing output, and never passes any ancestor gate. Hub cancellation is local only.
405
+
406
+ Changing a consumed node's definition or dependencies marks it and consumed descendants stale; never-run nodes remain eligible after their dependencies finish. Stale nodes need explicit retry. An unobservable interrupted step becomes uncertain and is never replayed automatically. A changed upstream output invalidates its plan gate approval; pending gates in auto autonomy are approved inside the controller's commit with actor `auto`. Which person or agent may approve a local plan gate remains workspace policy.
407
+
408
+ ### Hub references
409
+
410
+ A hub node has this shape, using the campaign/unit ids from an existing agent-hub campaign:
411
+
412
+ ```json
413
+ {"id": "variants", "kind": "hub", "campaign": "rc-0123456789ab", "unit": "variants", "needs": ["check-draft"]}
414
+ ```
415
+
416
+ The planner drafts that campaign and a person approves/activates it in agent-hub. Ares only reads: it never drafts, revises, approves or creates hub tasks. The controller host needs `ahub` on PATH. Its bounded `ahub --project <study-dir> status --json` and `study show <campaign> --json` calls resolve the nearest hub project within the Git boundary; `status.cwd` becomes `hubProject`. A hub node whose project cannot be resolved/reached stays dispatched with a named read error. Studies outside a hub project can still run plans with steps and local gates.
417
+
418
+ A ready hub node records its unit's child task id and waits for that task's approval. Pending campaign approval or activation waits without a manual retry. An initial read outage may leave the dispatch unbound until its first successful read; the task is then pinned for that plan revision, including across restarts and retries. Missing units/tasks, cancelled tasks and a different binding fail with the reason. A new plan revision can bind the unit's new task. Hub waits use the controller backoff, do not consume step slots, and do not redispatch agent work. Their completed output is an id/hash reference, so a downstream plan gate can bind to it.
419
+
420
+ ## What sealed means
421
+
422
+ The `design` gate binds the exact protocol bytes and the sorted `task/` file tree hashes, plus the raw selection file hash when present. Editing any sealed input reopens the gate. A budget-only change also reopens it, while the comparable attempt protocol hash excludes budget. The executor configuration is outside the seal; the attempt records the executor it used.
423
+
424
+ Sealing and metrics are **ares-observed**: ares builds the argv and checks the observed container configuration. They are never **host-observed** or **runner-attested**. Signed receipts await [agent-hub#1018](https://github.com/STAIxBWLB/agent-hub/issues/1018). This seal neither attests to an untrusted host nor establishes support for a research claim.
425
+
426
+ ## What the checks mean
427
+
428
+ - **verified**: the DOI exists at Crossref or DataCite, or the arXiv id exists at arXiv, and the registry's title and year match the candidate. It never means a paper supports a claim.
429
+ - **unknown**: the registry could not be reached or answered incompletely (network failure, rate limit, server error, error or truncated feed). Run `ares lit verify` again; unknown is never treated as missing.
430
+ - When a record shows that two candidates are the same work, an unscreened one is folded in; otherwise both are flagged `duplicates`, and `ares lit bib` refuses while both are included.
431
+ - Only included and verified papers enter `references.bib`. Citekeys never change once assigned.
432
+
433
+ ## Gates
434
+
435
+ `scope` binds to the content of `scope.json`; `screen` binds to the set of included papers; `design` binds the experiment protocol and task tree, plus selection/final-seeds files when present; `final` binds closed upstream results and design; `review` binds the current rendered/evidence hashes and current-build review/resolution event hashes; `plan:<node-id>` binds a plan gate to all upstream observed output references. Changing bound content returns its gate to pending. With `--autonomy auto` a pending gate is approved as `auto` when a command needs it; with `gates` (default) someone must run `ares gate approve`. The ledger records who approved: a person, an agent (detected from the agent environment) or `auto`. Which of them may approve is the workspace's policy, not the tool's. For experiments, automatic design and final approval is committed with a verified candidate start; a failed preparation records no approval. The review gate is approved explicitly after its readiness conditions pass; no current command auto-requires it.
436
+
437
+ ## Study files
438
+
439
+ | File | Content |
440
+ | --- | --- |
441
+ | `study.json` | question, mode, autonomy |
442
+ | `ledger.jsonl` | append-only events with actor and time, including plan state/dispatch/control receipts |
443
+ | `plan.json` | validated versioned plan definition and revision |
444
+ | `.ares.plan.lock` | controller pid, host, process start identity and ownership token |
445
+ | `scope.json` | the scope |
446
+ | `literature/watch.json` | bounded saved queries and display-only rubric |
447
+ | `literature/candidates.json` | merged candidates, screening and verification |
448
+ | `literature/cards/*.json` | extraction cards |
449
+ | `refs/references.bib` | generated bibliography |
450
+ | `task/task.md`, `task/evaluator/`, optional `task/rubric.md` | experiment task and evaluator files, written directly |
451
+ | `design/protocol.json` | validated experiment protocol |
452
+ | `design/selection.json` | sealed investigation rule, when set |
453
+ | `design/final.json` | sealed unused reserved seeds |
454
+ | `runs/ablate/ablate.json` | declaration rows paired with winner replication, without ranking |
455
+ | `runs/final/final.json` | one-shot qualification, decisive attempts and all final attempts |
456
+ | `runs/<run>/investigate.json` | deterministic winner/no-winner result, candidate rows and totals |
457
+ | `paper/manuscript.md` | source Markdown with numeric refs and literal escapes |
458
+ | `paper/build/manuscript.md` | rendered manuscript |
459
+ | `paper/build/evidence.json` | versioned placeholder values, source paths/hashes and input manuscript hash |
460
+ | `proposals/skills/<name>.md` | bounded versioned skill proposal, hash-bound to its ledger receipt |
461
+ | `executor.json` | executor kind, optional SSH alias and required workdir |
462
+ | `runs/<run>/attempts/<id>.json` | attempt identity, stage receipts, status, metrics and artifact/log metadata |
463
+
464
+ Experiment logs and outputs live on the executor at `<workdir>/<study-id>/<attempt-id>/`, not in the study. Existing A1 survey studies remain readable without migration. Family-local setup receipts and the study registry live under `$AGENTHUB_HOME/research/`; they are not copied into each study.
465
+
466
+ ## Commands
467
+
468
+ The implemented CLI surface below comes from `src/commands-table.ts`; `ares --help` prints current usage. Every operation exposing `--json` supports structured output.
469
+
470
+ ```text
471
+ ares skill propose <dir> --file PATH|- [--json]
472
+ ares skill decide <dir> <name> <accept|reject|withdraw> --reason TEXT [--pr URL] [--json]
473
+ ares skill list <dir> [--json]
474
+ ares lessons <dir> [--for TEXT] [--half-life DAYS] [--limit N] [--json]
475
+ ares archive <dir> [--confirm FINGERPRINT] [--json]
476
+ ares setup <claude|codex|kimi|pi|qwen|jcode|agy|all|console> [--apply|--undo] [--root ABS] [--json]
477
+ ares watch set <dir> --file PATH|- [--json]
478
+ ares watch run <dir> [--json]
479
+ ares watch show <dir> [--json]
480
+ ares console add <dir> [--json]
481
+ ares console remove <dir> [--json]
482
+ ares console list [--json]
483
+ ares manifest [--json]
484
+ ares init <dir> --question TEXT [--mode survey|experiment] [--autonomy auto|gates] [--json]
485
+ ares run <dir> [--parallel 1..8] [--json]
486
+ ares plan set <dir> --file PATH|- [--json]
487
+ ares plan control <dir> <pause|resume|retry|cancel|skip> [node] --command-id ID --expected-revision N [--json]
488
+ ares plan show <dir> [--json]
489
+ ares status <dir> [--json]
490
+ ares scope <dir> --file PATH|- [--json]
491
+ ares idea add <dir> --file PATH|- [--json]
492
+ ares idea decide <dir> <id> <refine|pivot> --reason TEXT [--attempts ID[,ID...]] [--commit SHA] [--cause TEXT] [--retry-when TEXT] [--json]
493
+ ares investigate show <dir> [--json]
494
+ ares investigate close <dir> [--json]
495
+ ares ablate add <dir> --file PATH|- [--json]
496
+ ares ablate show <dir> [--json]
497
+ ares ablate close <dir> [--json]
498
+ ares posthoc show <dir> [--json]
499
+ ares final close <dir> [--json]
500
+ ares final seeds <dir> --file PATH|- [--json]
501
+ ares selection <dir> --file PATH|- [--json]
502
+ ares protocol <dir> --file PATH|- [--json]
503
+ ares executor set <dir> --kind local|ssh [--host ALIAS] --workdir PATH [--json]
504
+ ares executor check <dir> [--json]
505
+ ares gate list <dir> [--json]
506
+ ares gate approve <dir> <scope|screen|design|final|review|plan:node-id> [--reason TEXT] [--json]
507
+ ares gate reject <dir> <scope|screen|design|final|review|plan:node-id> --reason TEXT [--json]
508
+ ares attempt run <dir> --repo PATH --commit SHA [--seed N] [--run NAME] [--label TEXT] [--retry ID] [--detach] [--json]
509
+ ares attempt list <dir> [--run NAME] [--status running|succeeded|failed|cancelled|unknown] [--limit N] [--offset N] [--json]
510
+ ares attempt show <dir> <id> [--json]
511
+ ares attempt cancel <dir> <id> [--reason TEXT] [--json]
512
+ ares attempt reconcile <dir> [--json]
513
+ ares attempt logs <dir> <id> [--stage candidate|evaluator] [--stream stdout|stderr] [--tail BYTES] [--json]
514
+ ares lit search <dir> <query> [--source openalex|s2|arxiv|all] [--limit 1..50] [--year-from YYYY] [--json]
515
+ ares lit list <dir> [--status new|included|excluded] [--limit 1..500] [--json]
516
+ ares lit screen <dir> <id>... --include|--exclude [--reason TEXT] [--json]
517
+ ares lit card <dir> <id> --file PATH|- [--json]
518
+ ares lit verify <dir> [<id>...] [--json]
519
+ ares lit bib <dir> [--json]
520
+ ares review add <dir> --file PATH|- [--json]
521
+ ares review resolve <dir> <review-id>/<finding-id> --reason TEXT [--json]
522
+ ares review show <dir> [--json]
523
+ ares paper build <dir> [--json]
524
+ ares paper check <dir> [--json]
525
+ ares cite check <dir> <draft> [--json]
526
+ ares playbook
527
+ ares mcp
528
+ ```
529
+
530
+ Memory tools are `ares_archive`, `ares_lessons`, `ares_skill_propose`, `ares_skill_decide` and `ares_skill_list`; `ares_idea_decide` accepts pivot cause/retryWhen. Archive may write external amem notes; lessons reads external amem. Both carry `openWorldHint`; skill operations stay local. MCP calls preserve agent attribution even without a native shell marker.
531
+
532
+ Watch tools are `ares_watch_set` (`{study, watch}`), `ares_watch_run` and `ares_watch_show` (`{study}`). `ares_watch_run` always returns the detached admission result `{started:true,configSha256}` after config/scope/year preflight under the study lock. Read the eventual completed result through `ares_watch_show`; `watch.ran` records an all-source failure as `ok:false`. The finite pass stays in the MCP process, which can still answer ping and `tools/list`; another start while locked refuses. CLI `watch run` and the `watch.run` plan step remain synchronous. Registry tools are `ares_console_add` and `ares_console_remove` (`{study}`), and `ares_console_list` (no arguments). These use the same shared operations as the CLI. Registry commands manage local registration only; they do not start a console.
533
+
534
+ MCP uses shared command handlers; `ares_setup` exposes the same registration preview/apply/undo operations. `init`, the controller loop `run`, and the server entrypoint `mcp` are CLI-only. Investigation tools are `ares_selection_set`, `ares_idea_add`, `ares_idea_decide`, `ares_investigate_show` and `ares_investigate_close`. Plan tools are `ares_plan_set`, `ares_plan_show` and `ares_plan_control`; `ares_gate` accepts `plan:<node-id>`. Setting, showing or controlling a plan never starts the controller loop. Start `ares run` in a separate terminal before resuming through MCP. Experiment tools are `ares_protocol_set`, `ares_executor_set`, `ares_executor_check`, `ares_attempt_run`, `ares_attempt_list`, `ares_attempt_show`, `ares_attempt_cancel`, `ares_attempt_reconcile` and `ares_attempt_logs`; `ares_gate` accepts `design`, `final` and `review`. Paper/review tools are `ares_paper_check`, `ares_paper_build`, `ares_review_add`, `ares_review_resolve` and `ares_review_show`. Phase tools include `ares_final_seeds`, `ares_ablate_add`, `ares_ablate_show`, `ares_ablate_close`, `ares_final_close` and `ares_posthoc_show`. Attempt run is always detached over MCP, and executor-reaching tools carry `openWorldHint`.
535
+
536
+ ## Version and format discovery
537
+
538
+ `ares manifest --json` (MCP `ares_manifest`, no arguments) reports the running package version, implemented command names/usages and the schema versions of family-readable records. It needs no study, network or executor. Current versions are `manifest`, `study`, `ledgerEvent`, `experimentProtocol`, `executor`, `experimentAttempt`, `studyPlan`, `experimentSelection`, `investigateResult`, `experimentFinalSeeds`, `ablateResult`, `finalResult` and `paperEvidence`, all version 1, carried by each record's `v` field. `skillProposal: 1` is also listed; proposal Markdown carries `skillProposal: 1` frontmatter and skill events carry that version in their data. The `ledgerEvents` list advertises `archive.exported`, `skill.proposed`, `skill.decided`, `watch.set` and `watch.ran` in the version-1 ledger envelope. The manifest also lists `studyRegistry: 1` and `watchConfig: 1`. Unsupported ledger/proposal/registry/watch versions are refused without rewriting their bytes. `mcp.tools` publishes `{name, readOnly}` for installers; grants are fail-closed for mutating or outside-reaching tools.
539
+
540
+ Within a supported schema, readers preserve additive fields on study/attempt records and keep ledger events with unknown kinds. Appending to the ledger leaves prior lines intact; updating an attempt keeps unrecognized fields and stage entries. Unknown stage entries remain readable, including logs and totals, but budget-gated starts refuse with an upgrade instruction because this version cannot account for their resource use. A future schema version or unknown execution status/stage is refused with an upgrade instruction, leaving the original data unchanged. Unknown plan versions or node kinds are refused without changing plan.json. Command input remains strict so unknown options or protocol fields cannot silently change an experiment.
541
+
542
+ The private scope/card/candidate storage and evaluator metrics object are not family interchange schemas. The console registration uses agent-console API 1 rather than an ares interchange format. Signed runner receipts, campaign contracts and vault notes are not implemented or advertised here. Setup and `--root` registration are available. See Release below for the first-publication procedure.
543
+
544
+ ## Environment
545
+
546
+ | Variable | Use |
547
+ | --- | --- |
548
+ | `OPENALEX_API_KEY` | OpenAlex key (without it, requests share a small daily budget per IP) |
549
+ | `S2_API_KEY` | Semantic Scholar key (without it, searches are often rate limited) |
550
+ | `ARES_CONTACT_EMAIL` | contact address added to the User-Agent |
551
+
552
+ Keys are sent with requests only; they are never written to the study.
553
+
554
+ ## Not implemented yet
555
+
556
+ Automatic idea generation, parallel attempts, held-out evaluator data, significance tests, signed executor receipts, automatic manuscript writing/reviewing, LaTeX/PDF builds and venue templates, claim-passage evidence pending agent-hub#931, vault-mem reference notes (after vault-mem#73), the `copilot` and `step` autonomy modes.
557
+
558
+ ## Release
559
+
560
+ A `v*` tag must equal `v` plus the `package.json` version and have a dated `## [VERSION] - YYYY-MM-DD` section in `CHANGELOG.md`. The release workflow runs validation on Ubuntu and macOS, verifies a staged tarball against its SHA-256 manifest, and installs it into a clean prefix for CLI version, manifest and MCP smoke checks. Publication uses the exact validated Linux tarball with provenance; a GitHub release follows with the dated changelog notes, tarball and hash manifest. Never move a release tag or reuse a published version.
561
+
562
+ Publication runs only when the repository variable `NPM_PUBLISH_ENABLED` is `true` and a reviewer approves the `npm-production` environment. It uses Node 24 and npm 11.5.1 or newer with [npm OIDC trusted publishing](https://docs.npmjs.com/trusted-publishers), without an npm token. After merge, the owner configures the npm trusted publisher for repository `STAIxBWLB/agent-research`, workflow `release.yml` and environment `npm-production`, enables the repository gate, and creates the release tag. These are owner steps; preparing the workflow does not establish that the package has been published.
563
+
564
+ Version 0.1.0 is the initial publication. There is no prior published minor line, so no N-1 compatibility claim is made yet for CLI, MCP tools or study/ledger formats. N-1 CI checks begin at the next minor release against the latest published patch of the preceding minor line.
565
+
566
+ ## Development
567
+
568
+ ```sh
569
+ bun install --frozen-lockfile
570
+ bun run check # typecheck, boundary check, tests (no network), package dry-run
571
+ ```
572
+
573
+ Live executor acceptance is separate from the offline gate. From a checkout with the configured SSH executor, run `bun scripts/live-exp.ts --host dgx-h100-2`. It uses a CPU-only synthetic Git repository, checks commit-specific scores, evaluator isolation, oversized logs, read-only log retrieval and archive containment, and prints a bounded summary. It removes its labeled containers and leaves private artifacts for inspection. The recorded A2 live proof is in [PR #14](https://github.com/STAIxBWLB/agent-research/pull/14); it covers that dgx Docker configuration, not every host or the local backend.
package/bin/ares.js ADDED
@@ -0,0 +1,11 @@
1
+ #!/usr/bin/env -S bun --smol
2
+ const version = /^(\d+)\.(\d+)\.(\d+)/.exec(process.versions.bun ?? "");
3
+ if (!version || Number(version[1]) < 1 || (Number(version[1]) === 1 && (Number(version[2]) < 4 || (Number(version[2]) === 4 && Number(version[3]) < 2)))) {
4
+ console.error("ares: Bun >=1.4.2 is required. Install Bun: https://bun.sh");
5
+ process.exit(1);
6
+ }
7
+ const { main, runMcp } = await import("../src/cli.ts");
8
+ if (process.argv.length === 3 && process.argv[2] === "mcp") {
9
+ try { await runMcp(); }
10
+ catch (error) { console.error(`ares mcp: ${error instanceof Error ? error.message : "failed"}`); process.exitCode = 1; }
11
+ } else process.exitCode = await main(process.argv.slice(2));