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>/
        ├── goal.md
        ├── session.json
        ├── state.json
        ├── events.jsonl
        ├── control.json
        ├── updates_from_user.md
        ├── children.json
        ├── child_requests/
        ├── children/
        ├── 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

The children/ directory is populated only when the session dispatches child work; these files are covered on Child Sessions.

Session files

goal.md — The exact resolved goal text for this session. The rendered Goal and this session-local copy are canonical for active work, including child goals and --goal-file overrides.

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

state.json — Coordinator-owned durable dispatch state, history, stop state, active-child pointer, and usage ledger. This is the source of truth behind the best-effort event stream.

events.jsonl — A best-effort, append-only stream of versioned lifecycle events. It is written after the durable state mutation, so consumers must tolerate gaps, duplicates, and a torn final line; state.json remains the source of truth. Tail it with loopy events --follow.

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. The rendered Goal and session-local goal.md are canonical for the active session (especially for a child or --goal-file override); 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>/

children.json, child_requests/, and children/child_requests/ is the inbox for pending requests, children/ holds dispatched child sessions, and children.json indexes their durable status and outcomes. While a child is active, the parent's state.json carries active_child_session_id, which is the pointer followed during restart. A request filename suppresses redispatch only while its child record is running or dispatching; once that child is complete, the filename may be reused for new work. Final child records include whole-subtree usage for the parent's ledger and cost budget.

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.

salvage.json — Written into the interrupted iteration's directory when crash recovery processes at least one tracked harness run. It records reaper reports so the provenance of surviving working-tree edits is auditable rather than a mystery diff. The task's result.json never existed and is never fabricated; abandonment consumes a turn, then normal scheduling continues only if no stop condition fires. When any orphan settled, history carries error="abandoned_after_<policy>" instead of plain "abandoned". For an identity-tracked run, a non-zero unsettled_workers means some orphan may still be running and replacement dispatch is refused (HTTP 409). Remote or otherwise unreachable processes can fall back to legacy abandonment without that local proof.

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.