Run History

Store, query, and export recipe run history

Run History CLI

Every recipe run is recorded automatically. Use recipe runs to see what happened, then feed a Run ID into recipe judge.

Diagram

on default

off

▶ recipe run

🧠 Execute

📦 RecipeResult

PRAISONAI_RECIPE_HISTORY?

💾 ~/.praison/runs

⏭ Skip

📋 recipe runs

⚖ recipe judge run-id

Quick Start

bash
# List recent runs (last 20)
praisonai recipe runs

# Filter by recipe and status
praisonai recipe runs ai-url-blog-generator --status failed

# Judge a run using its Run ID
praisonai recipe judge run-abc123

Commands

runs

List recipe run history.

bash
praisonai recipe runs [NAME] [OPTIONS]

Options:

OptionDescription
NAME (positional, optional)Recipe name to filter runs
--status <status>Filter by status (e.g. success, failed, policy_denied, timeout, missing_deps, dry_run)
--session, -s <id>Filter by session ID
--limit <n>Maximum results (default: 20)
--jsonOutput JSON format ({"runs": [...], "count": N})

The table shows Recipe · Version · Status · When · Session · Run ID, with color-coded status (success green, failed red, policy_denied/timeout yellow).

Examples:

bash
# List all recent runs
praisonai recipe runs

# Filter by recipe
praisonai recipe runs ai-url-blog-generator

# Filter by recipe and status
praisonai recipe runs ai-url-blog-generator --status failed --limit 10

# Filter by session
praisonai recipe runs --session session-abc123

# JSON output
praisonai recipe runs --json

If the history module can’t be imported, the command prints Run history unavailable: <error> and exits with code 1.

runs stats

Get storage statistics.

bash
praisonai recipe runs stats [--json]

Output:

Run History Stats:
  Total runs: 42
  Storage size: 1.5 MB
  Path: ~/.praison/runs

runs cleanup

Clean up old runs based on retention policy.

bash
praisonai recipe runs cleanup [--json]

export

Export a run for replay or debugging.

bash
praisonai recipe export <run_id> [options]

Options:

OptionDescription
-o, --output <path>Output file path
--jsonOutput JSON format

Examples:

bash
# Export to default filename
praisonai recipe export run-abc123

# Export to specific file
praisonai recipe export run-abc123 -o my-export.json

replay

Replay a run from an export bundle.

bash
praisonai recipe replay <bundle> [options]

Options:

OptionDescription
--compareCompare output with original
--jsonOutput JSON format

Examples:

bash
# Simple replay
praisonai recipe replay export.json

# Replay with drift detection
praisonai recipe replay export.json --compare

Export Format

json
{
  "format": "praison-run-export",
  "version": "1.0",
  "exported_at": "2024-12-29T12:00:00Z",
  "run": {
    "run_id": "run-abc123",
    "recipe": "support-reply",
    "version": "1.0.0",
    "status": "success",
    "input": {"ticket_id": "T-123"},
    "output": {"reply": "..."},
    "metrics": {"duration_sec": 2.5},
    "trace": {"session_id": "session-001"}
  }
}

Automatic History Persistence

Every praisonai recipe run (and every run_stream() execution) is recorded to history automatically. You don’t need --save for a run to appear in recipe runs.

BehaviorDetail
DefaultEnabled
Env varPRAISONAI_RECIPE_HISTORY
DisableSet to 0, false, no, or off
Failure modeHistory write errors log a warning; the recipe run still succeeds
Terminal statuses recordedsuccess, failed, missing_deps, policy_denied, dry_run, timeout

History is independent of the recipe registry: runs for a deleted or unknown recipe are still listed.

Disable persistence:

bash
export PRAISONAI_RECIPE_HISTORY=false
praisonai recipe run my-recipe   # not recorded

Re-enable (default):

bash
unset PRAISONAI_RECIPE_HISTORY
# or
export PRAISONAI_RECIPE_HISTORY=true

Storage Location

Run history is stored at ~/.praison/runs/.

~/.praison/runs/
├── index.json
└── run-abc123/
    ├── run.json      # Metadata
    ├── input.json    # Input data
    ├── output.json   # Output data
    └── events.jsonl  # Event stream

Data Policy

Runs respect the recipe’s data policy:

yaml
# In TEMPLATE.yaml
data_policy:
  retention_days: 30
  export_allowed: true
  pii:
    mode: redact

Python API

python
from praisonai.recipe.history import RunHistory, get_history

# Get default history
history = get_history()

# Store a run
from praisonai.recipe.models import RecipeResult, RecipeStatus

result = RecipeResult(
    run_id="run-abc123",
    recipe="my-recipe",
    version="1.0.0",
    status=RecipeStatus.SUCCESS,
    output={"result": "hello"},
)

history.store(result, input_data={"query": "test"})

# List runs
runs = history.list_runs(recipe="my-recipe", limit=10)

# Get specific run
run_data = history.get("run-abc123")

# Export
export_path = history.export("run-abc123")

# Stats
stats = history.get_stats()
print(f"Total runs: {stats['total_runs']}")

# Cleanup
deleted = history.cleanup(retention_days=30)

Thread- and process-safety

RunHistory is safe under concurrent use. Every read-modify-write of index.json in store(), delete(), and cleanup() is wrapped in:

  • A per-instance threading.RLock (in-process serialisation).
  • A POSIX fcntl advisory lock on a sidecar lock file (cross-process serialisation). On Windows this degrades to a no-op; the RLock still serialises threads in the same interpreter.
  • An atomic _save_index — writes go to a temp file in the same directory and os.replace over index.json, so a SIGKILL mid-write cannot leave a truncated index.

This matches the concurrency posture of recipe.arun_background() (default max_concurrent=5) and of multiple praisonai run invocations sharing ~/.praison/runs/.

get_history(path) returns the same RunHistory instance for the same resolved path across repeated calls, so multi-threaded stores that pass a path share the in-process lock — important on Windows where the advisory lock is a no-op. The default no-path singleton is unchanged.

python
from praisonai.recipe.history import get_history

# Same instance, same in-process lock — safe under threads on any platform.
h1 = get_history("/opt/agents/runs")
h2 = get_history("/opt/agents/runs")
assert h1 is h2

Next Steps