diff --git a/src/agent.rs b/src/agent.rs index 888f1c0..44d409a 100644 --- a/src/agent.rs +++ b/src/agent.rs @@ -205,8 +205,12 @@ pub async fn run(cli: &Cli) -> Result<()> { let system = prompts::system_prompt(schema, claude.family()); info!("model family: {}", claude.family().name()); + // Resolve ledger path: explicit --ledger-file takes precedence, else /run_ledger.jsonl + let ledger_path = cli.ledger_file.clone().unwrap_or_else(|| cli.output_dir.join("run_ledger.jsonl")); + info!("ledger: {}", ledger_path.display()); + // Load prior runs from ledger and build cross-run context for iteration 1 - let prior_summary = load_prior_summary(&cli.output_dir, &swym).await; + let prior_summary = load_prior_summary(&ledger_path, &swym).await; // Agent state let mut history: Vec = Vec::new(); @@ -412,7 +416,7 @@ pub async fn run(cli: &Cli) -> Result<()> { info!(" condition audit: {}", serde_json::to_string_pretty(audit).unwrap_or_default()); } } - append_ledger_entry(&cli.output_dir, &result, &strategy); + append_ledger_entry(&ledger_path, &result, &strategy); results.push(result); } Err(e) => { @@ -599,7 +603,7 @@ async fn run_single_backtest( } /// Append a ledger entry for a completed backtest so future runs can learn from it. -fn append_ledger_entry(output_dir: &Path, result: &BacktestResult, strategy: &Value) { +fn append_ledger_entry(ledger: &Path, result: &BacktestResult, strategy: &Value) { // Skip nil run_ids (error placeholders) if result.run_id == Uuid::nil() { return; @@ -620,11 +624,10 @@ fn append_ledger_entry(output_dir: &Path, result: &BacktestResult, strategy: &Va return; } }; - let path = output_dir.join("run_ledger.jsonl"); if let Err(e) = std::fs::OpenOptions::new() .append(true) .create(true) - .open(&path) + .open(ledger) .and_then(|mut f| writeln!(f, "{}", line)) { warn!("could not write ledger entry: {e}"); @@ -634,8 +637,8 @@ fn append_ledger_entry(output_dir: &Path, result: &BacktestResult, strategy: &Va /// Load the run ledger, fetch metrics via the compare endpoint, and return a compact /// prior-results summary string for the initial prompt. Returns `None` if the ledger /// is absent, empty, or the compare call fails. -async fn load_prior_summary(output_dir: &Path, swym: &SwymClient) -> Option { - let path = output_dir.join("run_ledger.jsonl"); +async fn load_prior_summary(ledger: &Path, swym: &SwymClient) -> Option { + let path = ledger; let contents = std::fs::read_to_string(&path).ok()?; // Parse all ledger entries diff --git a/src/config.rs b/src/config.rs index a9ad403..5e4308c 100644 --- a/src/config.rs +++ b/src/config.rs @@ -118,6 +118,13 @@ pub struct Cli { #[arg(long, default_value = "./results")] pub output_dir: PathBuf, + /// Path to the run ledger JSONL file used for cross-run learning. + /// Defaults to /run_ledger.jsonl when not specified. + /// Pass a different path to seed a new run from a specific ledger + /// (e.g. a curated export from a previous campaign). + #[arg(long)] + pub ledger_file: Option, + /// Poll interval in seconds when waiting for backtest completion. #[arg(long, default_value_t = 2)] pub poll_interval_secs: u64,