Session Format

This document provides a complete reference for the codeloops session file format.

Overview

Sessions are stored as JSONL (JSON Lines) files. Each line is a valid JSON object representing one event in the session.

Location: ~/.local/share/codeloops/sessions/

Filename format: <timestamp>_<hash>.jsonl

Example: 2025-01-27T15-30-45Z_a3f2c1.jsonl

  • timestamp: ISO 8601 format with hyphens replacing colons (filesystem-safe)
  • hash: First 6 characters of SHA256(prompt)

Line Types

Every line has a type field indicating its kind:

TypeOccurrenceDescription
session_startExactly onceFirst line, session metadata
iterationZero or moreOne per actor-critic cycle
session_endZero or onceLast line, final outcome

SessionStart

The first line of every session file.

Schema

{
  "type": "session_start",
  "timestamp": "<ISO 8601 datetime>",
  "prompt": "<string>",
  "working_dir": "<path>",
  "actor_agent": "<string>",
  "critic_agent": "<string>",
  "actor_model": "<string | null>",
  "critic_model": "<string | null>",
  "max_iterations": "<integer | null>"
}

Fields

FieldTypeRequiredDescription
typestringYesAlways "session_start"
timestampstringYesISO 8601 datetime when session started
promptstringYesFull task prompt text
working_dirstringYesAbsolute path to working directory
actor_agentstringYesActor agent name (e.g., "Claude Code")
critic_agentstringYesCritic agent name (e.g., "Claude Code")
actor_modelstring/nullYesActor model name or null if not specified
critic_modelstring/nullYesCritic model name or null if not specified
max_iterationsinteger/nullYesIteration limit or null if unlimited

Example

{
  "type": "session_start",
  "timestamp": "2025-01-27T15:30:45Z",
  "prompt": "Add input validation to the user registration endpoint.\n\nRequirements:\n- Email must be valid format\n- Password must be at least 8 characters",
  "working_dir": "/home/user/projects/myapp",
  "actor_agent": "Claude Code",
  "critic_agent": "Claude Code",
  "actor_model": "sonnet",
  "critic_model": null,
  "max_iterations": 10
}

Iteration

One line per actor-critic cycle. Sessions may have zero iterations (if the actor fails immediately).

Schema

{
  "type": "iteration",
  "iteration_number": "<integer>",
  "actor_output": "<string>",
  "actor_stderr": "<string>",
  "actor_exit_code": "<integer>",
  "actor_duration_secs": "<float>",
  "git_diff": "<string>",
  "git_files_changed": "<integer>",
  "critic_decision": "<string>",
  "feedback": "<string | null>",
  "timestamp": "<ISO 8601 datetime>"
}

Fields

FieldTypeRequiredDescription
typestringYesAlways "iteration"
iteration_numberintegerYes1-indexed iteration number
actor_outputstringYesActor's stdout output
actor_stderrstringYesActor's stderr output
actor_exit_codeintegerYesActor's process exit code
actor_duration_secsfloatYesActor execution time in seconds
git_diffstringYesUnified diff of changes
git_files_changedintegerYesNumber of files modified
critic_decisionstringYes"DONE", "CONTINUE", or "ERROR"
feedbackstring/nullYesCritic feedback (null for DONE)
timestampstringYesISO 8601 datetime when iteration completed

Critic Decision Values

ValueMeaning
DONETask complete, no more iterations
CONTINUEMore work needed, feedback provided
ERRORError occurred, recovery suggestion provided

Example (CONTINUE)

{
  "type": "iteration",
  "iteration_number": 1,
  "actor_output": "I've added email validation using a regex pattern. The validation is now in place for the registration endpoint.",
  "actor_stderr": "",
  "actor_exit_code": 0,
  "actor_duration_secs": 45.2,
  "git_diff": "diff --git a/src/api/users.rs b/src/api/users.rs\nindex 1234567..abcdefg 100644\n--- a/src/api/users.rs\n+++ b/src/api/users.rs\n@@ -10,6 +10,12 @@ pub async fn register(data: Json<RegisterRequest>) {\n+    if !is_valid_email(&data.email) {\n+        return Err(ApiError::BadRequest(\"Invalid email format\"));\n+    }\n",
  "git_files_changed": 1,
  "critic_decision": "CONTINUE",
  "feedback": "Email validation is implemented correctly. However, password validation is missing. Please add:\n1. Minimum 8 character length check\n2. Error message for invalid passwords",
  "timestamp": "2025-01-27T15:31:30Z"
}

Example (DONE)

{
  "type": "iteration",
  "iteration_number": 2,
  "actor_output": "I've added password validation with minimum length check. Both email and password are now validated.",
  "actor_stderr": "",
  "actor_exit_code": 0,
  "actor_duration_secs": 32.1,
  "git_diff": "diff --git a/src/api/users.rs b/src/api/users.rs\n...",
  "git_files_changed": 1,
  "critic_decision": "DONE",
  "feedback": null,
  "timestamp": "2025-01-27T15:32:02Z"
}

Example (ERROR)

{
  "type": "iteration",
  "iteration_number": 1,
  "actor_output": "",
  "actor_stderr": "error[E0433]: failed to resolve: use of undeclared crate or module `validator`",
  "actor_exit_code": 101,
  "actor_duration_secs": 12.5,
  "git_diff": "",
  "git_files_changed": 0,
  "critic_decision": "ERROR",
  "feedback": "The actor encountered a compilation error. The `validator` crate is not in Cargo.toml. Please either:\n1. Add `validator = \"0.16\"` to Cargo.toml, or\n2. Implement validation manually without the external crate",
  "timestamp": "2025-01-27T15:31:00Z"
}

SessionEnd

The last line of a completed session. May be absent if the session is still in progress or crashed.

Schema

{
  "type": "session_end",
  "outcome": "<string>",
  "iterations": "<integer>",
  "summary": "<string | null>",
  "confidence": "<float | null>",
  "duration_secs": "<float>",
  "timestamp": "<ISO 8601 datetime>"
}

Fields

FieldTypeRequiredDescription
typestringYesAlways "session_end"
outcomestringYesSession outcome (see values below)
iterationsintegerYesTotal number of iterations completed
summarystring/nullYesTask completion summary (for success)
confidencefloat/nullYesConfidence score 0.0-1.0 (for success)
duration_secsfloatYesTotal session duration in seconds
timestampstringYesISO 8601 datetime when session ended

Outcome Values

ValueDescription
successCritic returned DONE, task complete
failedUnrecoverable error occurred
interruptedUser pressed Ctrl+C
max_iterations_reachedHit iteration limit without completion

Example (success)

{
  "type": "session_end",
  "outcome": "success",
  "iterations": 2,
  "summary": "Input validation has been added to the user registration endpoint. Email addresses are validated using RFC 5321 compliant regex. Passwords require minimum 8 characters. Appropriate error messages are returned for invalid inputs.",
  "confidence": 0.95,
  "duration_secs": 89.4,
  "timestamp": "2025-01-27T15:32:14Z"
}

Example (max_iterations_reached)

{
  "type": "session_end",
  "outcome": "max_iterations_reached",
  "iterations": 10,
  "summary": null,
  "confidence": null,
  "duration_secs": 482.3,
  "timestamp": "2025-01-27T15:38:45Z"
}

Complete Example

A full session file:

{"type":"session_start","timestamp":"2025-01-27T15:30:45Z","prompt":"Fix the typo in greeting.rs","working_dir":"/home/user/myapp","actor_agent":"Claude Code","critic_agent":"Claude Code","actor_model":null,"critic_model":null,"max_iterations":null}
{"type":"iteration","iteration_number":1,"actor_output":"I found and fixed the typo. 'Helo' is now 'Hello'.","actor_stderr":"","actor_exit_code":0,"actor_duration_secs":23.4,"git_diff":"diff --git a/src/greeting.rs b/src/greeting.rs\n--- a/src/greeting.rs\n+++ b/src/greeting.rs\n@@ -1 +1 @@\n-println!(\"Helo, World!\");\n+println!(\"Hello, World!\");","git_files_changed":1,"critic_decision":"DONE","feedback":null,"timestamp":"2025-01-27T15:31:08Z"}
{"type":"session_end","outcome":"success","iterations":1,"summary":"Fixed the typo in greeting.rs. Changed 'Helo' to 'Hello'.","confidence":1.0,"duration_secs":23.4,"timestamp":"2025-01-27T15:31:08Z"}

Parsing Sessions

Using jq

# Get session prompt
head -1 session.jsonl | jq -r '.prompt'

# Get all critic decisions
jq -r 'select(.type == "iteration") | .critic_decision' session.jsonl

# Get final outcome
tail -1 session.jsonl | jq -r '.outcome'

# Count iterations
jq -s '[.[] | select(.type == "iteration")] | length' session.jsonl

Using Python

import json

def parse_session(filepath):
    with open(filepath) as f:
        lines = [json.loads(line) for line in f]

    start = lines[0]
    iterations = [l for l in lines if l.get("type") == "iteration"]
    end = lines[-1] if lines[-1].get("type") == "session_end" else None

    return {
        "start": start,
        "iterations": iterations,
        "end": end,
    }

Using Rust

#![allow(unused)]
fn main() {
use codeloops_sessions::{SessionStore, Session};

let store = SessionStore::new()?;
let session: Session = store.load_session("2025-01-27T15-30-45Z_a3f2c1")?;

println!("Prompt: {}", session.start.prompt);
println!("Iterations: {}", session.iterations.len());
if let Some(end) = session.end {
    println!("Outcome: {}", end.outcome);
}
}

Session Discovery

Sessions are stored in a flat directory. To discover sessions:

# List all session files
ls ~/.local/share/codeloops/sessions/*.jsonl

# Find sessions by date
ls ~/.local/share/codeloops/sessions/2025-01-27*.jsonl

# Find sessions by prompt hash
ls ~/.local/share/codeloops/sessions/*_a3f2c1.jsonl

File Integrity

Sessions are written in append-only mode:

  • Lines are written atomically (full line or nothing)
  • No line is ever modified after writing
  • Crashes leave the file in a consistent state

If a session file is missing session_end, the session was either:

  • Interrupted before completion
  • Crashed unexpectedly
  • Still in progress