r/place A fruit fly for organization science

Authoritative source, reproduced unchanged. Exact Markdown · Agent copy with resolved links. These instructions govern research runs, not checkpoint interviews.

The leg handler (legs.py)

Target length: under 1,800 words. As it stands on 6 October 2026, before the first research leg.

Status, 6 October 2026: offline tests pass (handler 14 of 14, adapter 20 of 20); the live check probe_lifecycle.py passed 14 of 14 on J's Mac (claude 2.1.289); no research leg has run through it yet. A branch replaces its own publish.sh and step.sh (below).

J, 2026-10-05: "I need just a handler i can access via claude code/cowork that opens and close the sessions plus the ability to talk to EACH AGGREGATOR STAGE.LEG". No website.

What it is

Start it once

In Terminal, in the repository folder:

python3 aggregator/handler/claude-adapter/legs.py serve

Leave the window open; the Mac stays awake. Ctrl-C stops the handler, not a turn: the next serve follows the turn again and pauses the list. Beside it: a Terminal window with legs.py chat claude-v43-1, where you talk to the agents, and Claude Code as the handler for the rest.

Commands

Command What it does
status [RUN] the list, running legs, questions, what needs you
legs RUN every leg, its state and its interviews
open RUN LEG step.sh, then the leg's first turn
close RUN LEG [--force] close.sh, once, also for a leg opened in Terminal
run RUN LEGS... [--through-interviews] [--accept LABEL] several legs in order (below)
stop RUN [--now] the list stops after this leg; --now interrupts the turn
pause RUN the running leg stops at its next safe stop (done.sh --pause)
chat RUN [--full] one Terminal window to talk to every agent of the run (below)
say RUN LEG TEXT one line to a leg, without the chat; TEXT - reads standard input
interview RUN LEG TEXT [--new] an interview of a closed leg (below)
note RUN TEXT the handler's line in the chat
reply RUN LEG [--interview] the leg's last reply, or its interview's
watch RUN [LEG] [-f] [--full] the analyst's words and tool calls; -f live, leg after leg; --full whole, with every output
answer RUN QID TEXT answer a mem.py ask question: beside the running leg's next tool result, an open leg's next turn, and in any case the next leg's first message
wait RUN [--timeout S] at once with every notice you have not seen through wait, else when one comes, or after S seconds (default 540)
branch, branch-put, branch-status, prepare-branch, publish-branch an analysis branch (below)

RUN is the run's folder name (claude-v43-1); LEG a leg id (A1b-02) or label (A1.A1b-02). open leaves the close to you; run RUN A1a closes one leg by itself. --wait S bounds how long open, close, say and interview wait; wait takes --timeout S. Cowork's shell stops after about three minutes, so it passes --wait 150 or --timeout 150 and reads the rest with status or reply. wait shows every notice not yet shown.

Talking to a leg

Talk in one window, apart from the handler: legs.py chat claude-v43-1 in Terminal (J, 2026-10-05: "a single chat where I talk to all the agents"). A line goes to the leg the prompt names; @A1b-02 text goes to that leg and keeps it; ?A1a text goes to an interview of A1a, and ?A1a+ text opens a new one. Every agent's words, tool calls and answers come back there live, each marked with its leg, and the handler's notices between them. /tools and /full show less or more. What a line to a leg does depends on the leg:

An interview is you opening a closed leg to talk to it (J, 2026-10-05: "an interview is me, asking to open leg/stage x and talking to it"): a fork of its checkpoint in a copy of its folder, recorded in CONTROL/interviews.tsv. It remembers the leg; nothing said there reaches the run. Later words continue the leg's latest interview; --new opens another. At E2a, step.sh copies the listed interviews into the analyst's handling/interviews/; only the interviews you open count. A transcript is committed only if you decide so, interview by interview (HANDLER.md).

Your words to a leg, and their delivery, go into the run's CONTROL/messages-to-legs.jsonl, which publish.sh commits at every close. A message has at most 8,000 characters; send a longer one in parts.

A list of legs

legs.py run claude-v43-1 A1 runs A1a, one A1b leg per block declared in out/order-A1.txt, then A1c. Other repeated steps take a range (B1b-01..B1b-06). A closed leg is skipped and a paused one continues as -p2, so naming the same list again picks up where it stopped. A closed leg is skipped only if its snapshot verifies and its close can advance the run; a forced or incomplete close stops the list again until you name it with --accept.

It stops, and status and wait say why and what to do, when:

A question the analyst asks does not stop the list: you are told, and you answer it.

An analysis branch

J, 2026-10-05: fork a run and do a new C2 with all else equal, from a new card and files, the fork plain in Git. branches.py makes branches as the Codex controller does: the same verbs, records, rules, notice and Git refs.

legs.py branch claude-v43-1 C1a-03 claude-v43-1--C2-1 --leg C2a-01
legs.py branch-put claude-v43-1--C2-1 stages/STAGE-C2-model-the-loop.md --file NEW.md --stage C2
legs.py branch-put claude-v43-1--C2-1 shelves/NAME.csv --file NAME.csv --stage C2
legs.py prepare-branch claude-v43-1--C2-1
legs.py run claude-v43-1--C2-1 C2a-01

From the phone, from Cowork

Where things are kept

Not verified

Before the first real leg

  1. probe_lifecycle.py passes on the Mac (README, Live acceptance): done on 6 October; again after a CLI update.
  2. The run's acceptance.sh and acceptance_v41.sh end ALL CLEAR after the last change to its CONTROL/harness/.
  3. The repository's first commit and push.
  4. Start serve and, at the Mac, run A1a through it. A headless turn passes --settings with the inbox hook only; the run's own settings still applied in the probe (its dontAsk turn ran Bash under the run's allow rules), but that the run's guard hook fires in a headless turn is not yet seen. A refusal in A1a shows in out/_closes/guard.log.

Fast mode (J, 9 October 2026). fast-mode.json beside actor.py switches the analyst's fast mode: with {"fastMode": true}, each headless turn's --settings carries "fastMode": true (a -p session takes /fast only that way). It is read at each turn's launch; set it to false to turn fast mode off from the next turn. Fast mode is billed from usage credits, outside the plan's limits.