loopy-loop

Session Layout

loopy-loop keeps its state on disk, not inside a chat transcript. One session directory is created per fresh coordinator run and reused for every iteration in that session. Everything you need to audit, pause, or resume a run lives under that directory. This page is the reference for its shape: the session id format, the session-level files, and the per-iteration files.

The session id

New session ids follow this format:

<YYYYMMDD>_<HHMMSS>_<goal_hash>_<random>

For example, 20260419_143022_71393ee22450_ab12cd34. The leading UTC timestamp means session directories sort chronologically by name. The goal_hash is derived deterministically from the goal text, so runs against the same goal are easy to spot and compare, and the trailing random suffix keeps ids unique.

Directory tree

A session directory looks like this:

.loopy_loop/
└── sessions/
    └── <session_id>/
        ├── session.json
        ├── events.jsonl
        ├── control.json
        ├── updates_from_user.md
        ├── project_state/
        │   └── finished.md
        ├── eval_checks/
        ├── eval_results/
        │   └── <eval_banana_run_id>/
        │       ├── report.json
        │       ├── report.md
        │       └── checks/
        ├── harness_outputs/
        │   └── 0001_planner/
        │       └── <team_harness_run_id>/
        └── iterations/
            ├── 0001_planner/
            │   ├── prompt.txt
            │   ├── result.json
            │   ├── result_text.txt
            │   ├── harness_run_id.txt
            │   └── pending_finished_request.json
            └── 0002_goal_check/
                ├── prompt.txt
                ├── result.json
                ├── result_text.txt
                ├── harness_run_id.txt
                └── goal_check.json

Sessions that spawn child work also grow a child_requests/ inbox and a children/ directory; those are covered on Child Sessions.

Session files

session.json — Session metadata, written once when the directory is created. It contains session_id, goal_hash, and created_at.

events.jsonl — A reserved append-only event log for diagnostics, created at session start in v1.

control.json — The session-scoped workflow stop switch, created with state: "running" when the session starts. Workflows update it only when they want the loop to stop, and the coordinator reads it only from the session directory. It is the single gate that ends a run — see Success & Control for the full contract and stop reasons.

updates_from_user.md — A human-writable inbox for requests that arrive after the session starts. The outer workflow treats non-empty content as highest-priority planning input, and clears the file only after reflecting the request into project_state/.

project_state/ — Optional workflow-owned durable markdown state for reusable workflows. loopy_loop_goal.txt remains the source of truth for the target, constraints, and completion intent; do not copy or restate the goal into project_state/. Common files include README.md, memory.md, current_state.md, what_we_have.md, decisions.md, eval_results.md, finished.md, and what_we_should_do/plan.md. The README.md should document ownership rules — memory.md for essential durable facts only, finished.md for outer-owned accepted completions only, eval_results.md for eval detail, and current_state.md for live status, the latest eval headline, and the next action. The coordinator does not parse any of these files; it only provides the paths.

project_state/finished.md — An append-only ledger for outer-verified completed work. Each entry should summarize the completed task and link to the relevant iteration and harness output files. For implementation work, entries should include delivery evidence for each changed repo: repo path or remote, branch, PR URL, merge status, merge commit when merged, and checks/CI status. The default implementation delivery is branch + PR + passing checks + merge, unless the task is session-state-only, eval-only, research-only, planning-only, or has no usable remote/auth.

eval_checks/ — Optional workflow-owned eval-banana checks for this session. A workflow can run just these checks and write the results into the session:

eval-banana run \
  --cwd . \
  --check-dir .loopy_loop/sessions/<session_id>/eval_checks \
  --output-dir .loopy_loop/sessions/<session_id>/eval_results

eval_results/ — Optional workflow-owned eval-banana run output. Each eval-banana run --output-dir .../eval_results creates a child <eval_banana_run_id>/ directory containing report.json, report.md, and per-check artifacts. project_state/eval_results.md should summarize and link these reports rather than copy the raw output, and project_state/current_state.md should carry only the latest eval headline and the next action.

harness_outputs/ — The root for team-harness coordinator and worker artifacts. Each iteration gets its own output root, keyed by iteration number and workflow id:

.loopy_loop/sessions/<session_id>/harness_outputs/<NNNN>_<workflow_id>/

team-harness then creates a child directory named with its own run id:

.loopy_loop/sessions/<session_id>/harness_outputs/<NNNN>_<workflow_id>/<team_harness_run_id>/

Iteration files

Each iteration gets its own directory under iterations/, named <NNNN>_<workflow_id> (for example, 0001_planner). It holds the record of that single assignment.

prompt.txt — The rendered assignment prompt sent to TeamHarness.run(task=...). This is exactly what the workflow was asked to do, with all session paths already substituted in.

result.json — loopy-loop's normalized result payload for the iteration:

{
  "success": true,
  "text": "final response",
  "error": null,
  "harness_run_id": "run-123",
  "harness_output_dir": ".loopy_loop/sessions/<session_id>/harness_outputs/0001_planner/run-123"
}

Remember that success here means the harness ran, not that the work is good — see Success & Control.

result_text.txt — A plain-text copy of the result text, or an empty string on failure.

harness_run_id.txt — The team-harness run id, which links this iteration to its output under harness_outputs/. Empty when a failure occurred before a run id existed.

pending_finished_request.json — A durable handoff record written after result.json and before the worker calls /finished. It is removed once /finished is acknowledged or once /register recovers it. If a worker exits in that handoff window, the next /register uses this file to record the completed task instead of marking it abandoned; if it is missing but result.json exists for the active task, the coordinator reconstructs the finished request from result.json. See HTTP Contract for the recovery rules.

children.json (parent sessions) — Index of child sessions dispatched by this session. Each record carries the child session_id, workflow_set, status, timestamps, stop_reason, and the originating request_file name, which makes the dispatch scan idempotent — a request whose filename already appears here is never dispatched twice, even if a crash left the file behind. While a child runs, the parent's state.json records active_child_session_id: the durable session-stack pointer a restarted coordinator follows to resume the child instead of orphaning it.

salvage.json — Written into the interrupted iteration's directory during crash recovery, when the coordinator drained or reaped agent processes a dead worker's harness run left behind (per recovery_policy). It records the reap reports so the provenance of any surviving working-tree edits is auditable rather than a mystery diff. The iteration is still re-run — its result.json never existed and is never fabricated — and when any orphan actually settled, its history entry carries error="abandoned_after_<policy>" (e.g. abandoned_after_drain) instead of plain "abandoned". A non-zero unsettled_workers in the record means some orphan may still be running; the coordinator refuses to dispatch replacement work (HTTP 409) until it is resolved.

goal_check.json — A per-iteration eval artifact, present only for the reserved goal_check workflow or a workflow configured with emits_goal_check: true. Its schema in v1:

{
  "goal_met": false,
  "reason": "CTA exists, but deployment docs are still missing.",
  "schema_version": 1
}

A valid goal_check.json is evidence, not a stop switch. A workflow that wants to stop the loop must update the session control.json.