Block Design
A block is the unit of work in LegoFlow. Every stage of the pipeline — data curation, trajectory generation, training, and evaluator measurement — is a block with a shared shape and a familiar skill lifecycle: :setup, :check, and :run. This page explains the design behind that shape: what lives inside a block, how blocks pass data to each other, and where runs are recorded.

The diagram shows the block boundary. A block is not just a script directory: it is a package around one unit of work. config.yaml is the declaration layer. It names the block, pins code repositories, declares child blocks and dependencies, and separates runtime inputs from runtime outputs. The rest of the block follows that declaration.
Inside the block, repos/ holds pinned code dependencies, plugin skills expose setup/check/run operations, and memory/ keeps the context an agent needs while operating the block. scripts/ contains concrete dry-run, smoke-test, and run entrypoints. dashboard/ turns logs, metrics, and state into an inspectable surface. blocks/ lets the same shape nest, so a block can be a leaf task or a parent pipeline.
The side panels are part of the contract too. Code repos are pinned outside the block logic but referenced by config; the config schema declares what the block is allowed to depend on and publish. A run then produces artifacts/: logs, archives, cache, output, and environment snapshots. That gives the root block something stable to inspect before execution and something durable to review after the run finishes.
Block
A self-contained directory that owns one stage of the pipeline. Every block has the same shape: a config.yaml for inputs and outputs, a scripts/ set for execution, an artifacts/ tree for results, an optional repos/ for vendored dependencies, and a CLAUDE.md that documents the agent contract.
Root and Child Blocks
The repo is a tree of blocks. The repo root is the root block; every directory under blocks/ is a child block. A parent block lists its children under meta_info.blocks with a short role description. Each child declares its own upstream and downstream handoffs in meta_info.dependencies through from and to. Children can have children, so the abstraction nests.
Config File
Every block's config.yaml is one-shot per run: every key is configuration. Live state — running, completed, failed — does not live here. It lives in artifacts/index.yaml. See Reference / config.yaml for the full schema.
Runtime Inputs
The values a block needs that originate outside the block tree: API keys, GitHub tokens, model names, human decisions. These are the only values you fill by hand.
Runtime Outputs
The values a block publishes once it has run, for downstream blocks to consume — e.g. curator.output.swe_tasks_dir. Downstream blocks reference these through dependencies.from, never by hand.
Dependency
meta_info.dependencies has two keys, from and to — a block shows both directions it participates in, from its own file. from is wired by the consumer: the key is the dot-path in that block's runtime_info.input that receives the value, and the value is <source_block>.output.<key> (with optional when:/required: modifiers for conditional or optional hand-offs). to is the mirror, wired by the producer: the key is one of its own runtime_info.output keys, the value is <consumer_block>.input.<path>. The same edge is declared on both ends; scripts/validate_config.py — run by every dryrun and by /root:check — resolves every dependency at preflight time and warns if the two sides disagree; an unresolvable dependency fails the check before anything heavy runs.
Repo
A vendored, pinned dependency under a block's repos/ directory (e.g. tracer/repos/harbor/). Pinned by commit SHA in config.yaml under meta_info.repositories.<name>. Editing files under repos/ is forbidden — auth/env fixes belong in scripts/ or config.yaml.
Resources
meta_info.resources.ip declares where a block runs. local (or null) means the current host. A real remote IP means the agent SSHes into that node and runs inside a tmux session, with meta_info.resources.directory as the working directory.
Archive
Every run is archived to artifacts/archives/run_NNN/: a snapshot of the config, the scripts as they were, the repo SHAs, and a metadata.yaml with timestamps and exit code. The EXIT trap installed by scripts/start.sh fires archive_run.sh regardless of how the run ended, so the timeline is always complete. See Reference / Artifacts.
Live State
The newest entry of artifacts/index.yaml. Status is one of completed | failed | interrupted, derived from scripts/start.sh's exit code. Always read live state from here — never from config.yaml.
Root Plugin
The Claude Code plugin at .claude/plugins/root-plugin/ operates the whole block tree. It gives users a guided way to create, set up, check, run, and inspect blocks without memorizing every script:
/root:create— scaffold a new block with the full directory tree wired to the contract./root:setup— prepare shared tooling and optionally recurse into each block's setup flow./root:check— recursively sanity-check schema, inputs, dependencies, remote-resource reachability, and LLM endpoints. Read-only./root:run— preflight, then executescripts/start.sh(locally, or in tmux over SSH) and archive the result./root:dashboard— open or start the tree-wide dashboard for run state, metrics, and artifacts.
Both /root:check and /root:run take a free-form argument. The agent resolves the target block by name or unambiguous paraphrase; ambiguity triggers a clarification question rather than a guess.