The handler's folder

aggregator/handler/ holds what the handler of a run needs beyond the harness: its protocol, and the scripts it runs before and around the analyst's work. The harness (step.sh, close.sh, dispatch.sh and the rest) runs each leg, typed by the PI or driven by the leg handler below; the scripts here the handler runs itself.

Start with HANDLER.md: who decides what, the rules that do not bend, how the handler remembers across sessions, its duties before and during the run, and how the instrument is changed. The coder side's handler has its own contract, CONTRACT-scheduler.md.

The scripts

Every script finds the repository's own folders from where it sits. Sources outside the repository come from paths.py: set them once in aggregator/handler/paths.json (copy paths.example.json), or as RPLACE_<KEY> in the environment. python3 aggregator/handler/paths.py lists every key, its value and whether it exists.

Script When What it reads What it writes
paths.py first paths.json, the environment nothing; prints where each source is
extract_legacy.py before the build, once the batch archives of rooms coded before the yard (layer_a) each room's coder output, as written, into aggregator/handler/_work/legacy/
import_rooms.py before the build; again when rooms finish the yard's verified rooms (yard_rooms), aggregator/handler/_work/legacy/, the coder-version table (layer_a) aggregator/input/coders/rooms/<sub>/ in the one layout every room has; forests re-renders every forest with one tree builder
build_outcome.py before the build the placeAtlas 2017 file, ROOMS.tsv case_facts_outcome.jsonl: six canvas fields per room (entries, area, centre, ring, artwork, source)
build_canvas.py before the build the pixel record per artwork (c5_data), the hourly series (hourly_pixels, from data/pixels-hourly/) aggregator/input/canvas/: artworks.jsonl and pixels.csv
check_quotes.py before stage E1 a seeded sample of rooms and the record each coder read (yard_rooms, aggregator/handler/_work/legacy/) RUN/DROPS/E1/quote-check-E1.json, which E1 reads
gather_collection.py when the collection code changes the layers' code folders, given on the command line a folder you name with INVENTORY.tsv and CANDIDATES.md
bundle_history.py once, then frozen earlier instrument folders, given on the command line archive/instrument-history.tar.gz and its manifest

The leg handler and the Claude adapter

aggregator/handler/claude-adapter/ runs the analyst's legs headless through the harness and lets the PI talk to any leg (LEGS.md, README.md). It sits beside the harness, outside the run's fingerprint.

File What it does
legs.py serve on the Mac owns the sessions; open, close, run, status, wait, chat, say, answer, interview, pause, stop, and the branch commands
actor.py one leg or interview as an actor: prepare, one headless turn, messages mid-turn, save, the close through close.sh
stream.py, inbox_hook.py the CLI's stream turned into events; the hook that hands the PI's words to the analyst beside its next tool result
branches.py analysis branches: a new run from one verified close, on its own Git branch
probe.py, probe_lifecycle.py live checks of the CLI and of the actor, in a synthetic run

Two more live with what they serve: shelves/make_meta.py writes a metadata page for every shelved work, from Crossref and the PDFs; tools/version_log.py keeps every earlier version of an instrument file before a change lands.

Why a handler has its own files

The analyst is fenced: it sees one folder, cannot reach the web or the handler's files, and forgets everything between legs except what it wrote. Everything around it, from assembling the rooms to checking quotes before the audit, is the handler's, and a run is only as reproducible as that work. Until 26 September 2026 the handler's scripts sat among its working files, with paths fixed to one machine, and its duties were spread across notes. They are here now, so another team's handler, on another account, starts from the same protocol and the same scripts.