| name | investigate |
| description | Run a research question as a swarm. Frame it (or a /study gap) into falsifiable hypotheses, test each as a contained cxc agent in its own git worktree (swarm wt), verify outcomes adversarially against their evidence, and render one fixed-template HTML investigation to ~/.anu/atlas/investigations/<slug>. Launcher investigate with a live investigate watch cockpit. Composes trail, swarm, box. Use when the user runs /investigate or asks to test / pursue / run experiments on / settle a question. |
/investigate: run a research question as a swarm
The third stage of the research arc: find (/prior-art), understand
(/study), do (this), show (/present). /study reads a question into a
dossier; /investigate runs it: frame falsifiable hypotheses, test each as a
contained agent, judge the evidence, synthesize one investigation, the lab
notebook of an autonomous run. The page is the artifact; keep chat minimal.
Two substrates, both already in anu:
- execution = the swarm: one contained
cxc agent per hypothesis, each in its
own worktree, visible as a pane, running experiments for as long as it takes.
- decision record =
trail: each agent records Hypothesis:/Predicts:/
Alternatives:/Resolves:/Outcome:/Evidence: as commit trailers, so the
reasoning reconstructs from git log with no LLM. This skill drives the loop
that fills the trail; the trail is the decision-graph companion to the report.
Run each stage as its own step so you can check with the user between them. The
swarm is expensive, and the framing has to be right before it spends.
1. Frame: question to hypotheses (cheap; needs sign-off)
$ARGUMENTS is a research question, or a path/id of a /study dossier
(~/.anu/atlas/papers/<id>/study.json) whose gap[] seeds the hypotheses (the
study→investigate seam). Turn it into:
- a Goal: the one question the whole run serves (becomes the
Goal: trailer).
- 2-5 falsifiable hypotheses, each with: a one-line claim (its stable slug),
a prediction (the observable that would confirm it), alternatives (named
rivals you won't test first), and a minimal experiment (the cheapest thing
that could refute the prediction).
A good hypothesis can come out false. If you can't say what would refute it,
it is untestable; downgrade it to a question. Show the plan (the goal, the
hypotheses table, how many agents) and get sign-off before spending the swarm.
STAMP=$(date '+%Y-%m-%d %H:%M'); TS=$(date +%Y%m%d-%H%M%S)
ROOT=$(git rev-parse --show-toplevel)
2. Bench: spin up the contained swarm
One contained agent per hypothesis, each in its own worktree:
swarm wt <N> cxc
Anchor the goal on the base branch (so the trail attaches to it), brief each agent
with its hypothesis (one swarm send each), then open the live cockpit in its
own pane and watch it converge:
git commit --allow-empty --no-gpg-sign -m "Investigate: <question>" -m "Goal: <the question>"
swarm send agent-1 "$(brief for hypothesis 1)"
investigate watch
trail swarm watch
Each brief = the hypothesis, its prediction, its alternatives, the minimal
experiment, and the instruction: follow the running-experiments skill: design
the cheapest test, run it in your box, record the trail trailers, resolve forward
with real evidence. Give a status line ("Deploying N contained agents, one per
hypothesis"). Default workers to cxc (unattended autonomy with the blast radius
of one worktree); keep the conductor on the host.
Redundancy mode: when one tricky claim matters more than breadth, give the
same hypothesis slug to K agents instead of N different ones. trail swarm then
flags where they disagree: the adversarial cross-check, agents adjudicating agents.
3. Run + monitor
The swarm runs (the long part). Watch it: the investigate watch cockpit (each
agent's hypothesis opening → resolving with its verdict, live), swarm status
(busy/waiting/done), agentlog agent-i (one agent's reasoning). Nudge a stalled
agent with swarm send; an open hypothesis far past the typical resolve time shows
⚠ stuck in the trail. Proceed when the frontier is resolved, or you call a
checkpoint.
4. Evaluate + synthesize (the Workflow)
Collect the agent branches and run the synthesis workflow. It reads each agent's
trail + evidence, adversarially verifies every outcome (does the Evidence:
actually support the Outcome:?), and synthesizes the report. Call the Workflow
tool with:
scriptPath: ~/.local/share/anu/plugins/investigate/skills/investigate/workflow.js (expand ~)
args: {"question": "...", "root": "<ROOT>", "generatedAt": STAMP, "agents": [{"label":"agent-1","branch":"<branch>","hypothesis":"<slug>"}, …]}
Get the branches from git -C "$ROOT" worktree list --porcelain (or swarm status).
The workflow returns the investigation JSON.
5. Persist + render
SLUG=$(printf '%s' "<question>" | tr '[:upper:]' '[:lower:]' | tr -cs 'a-z0-9' '-' | sed 's/^-//;s/-$//' | cut -c1-60)
OUT="$HOME/.anu/atlas/investigations/$SLUG"; mkdir -p "$OUT/history"
[ -f "$OUT/investigation.json" ] && mv "$OUT/investigation.json" "$OUT/history/$TS.json"
Write the returned JSON to $OUT/investigation.json, then render the report and the
trail companion (the decision graph / agent × hypothesis matrix) into the same
folder, so the investigation is self-contained: index.html links to ./trail.html.
Render the companion before tearing the swarm down (build.py --swarm reads the
live worktrees):
python3 ~/.local/share/anu/plugins/investigate/skills/investigate/render.py "$OUT/investigation.json" "$OUT/index.html"
TRAIL="$HOME/.local/share/anu/plugins/trail/skills/trail"
python3 "$TRAIL/build.py" --swarm "$ROOT" "$OUT/trail.json"
python3 "$TRAIL/render.py" "$OUT/trail.json" "$OUT/trail.html"
open "$OUT/index.html"
(For a single-line investigation with no swarm, drop --swarm to render the decision
graph + tempo of the current branch instead.) Tear the swarm down only after the
companion is rendered: swarm kill.
6. Hand back + loop
Report: the page path, the verdict (where the question stands now), the single
sharpest thing learned, and the frontier (open hypotheses awaiting a human, the
bottleneck). Then offer the next turn:
- expand the frontier: open the next hypotheses from
frontier[].
- chase a refutation: a refuted hypothesis already named its alternatives;
branch from its fork (
gwa <try-alt> <fork-sha>) and test the road not taken.
- re-run to diff:
history/ holds prior runs; a re-run shows how the question moved.
Loop until the goal is settled, the frontier is dry, or the user stops.
Rules
- Never edit
template.html / render.py per run; fixed by design, all per-run
content lives in investigation.json. Same contract as /map and /study.
- Don't fabricate evidence. A refuted or inconclusive hypothesis is a real
result; report it plainly. The synthesis only believes outcomes whose evidence it
re-checked; a confirmed-but-unupheld hypothesis is flagged, not hidden.
- Frame findings as bounded ("tested X under Y, observed Z"), never a blanket
claim beyond what ran. Coverage honesty is part of the result.
- Keep the conductor on the host; workers
cxc (contained). Contained agents commit
locally; push from the host if publishing the trail.
- History accumulates in
$OUT/history/; never delete it. It is how an investigation
is diffed over time.