(run: Option<String>)
| 399 | } |
| 400 | |
| 401 | fn cmd_reply(run: Option<String>) -> Result<()> { |
| 402 | let root = trace_root_or_bail()?; |
| 403 | let raw = reply::read_stdin_to_string()?; |
| 404 | let payload = reply::parse_payload(&raw)?; |
| 405 | let step_id: Option<String> = payload.step_id().map(String::from); |
| 406 | |
| 407 | // Every cited evidence path must exist on disk at call time. |
| 408 | let run_id = resolve_run(&root, run.as_deref())?; |
| 409 | let cited = payload.evidence_paths(); |
| 410 | for p in &cited { |
| 411 | let abs = flowtrace_core::state::run_dir(&root, &run_id).join(p); |
| 412 | if !abs.exists() { |
| 413 | anyhow::bail!( |
| 414 | "evidence path `{}` not found on disk (resolved to {}) — write the file before calling `flowtrace reply`", |
| 415 | p, |
| 416 | abs.display() |
| 417 | ); |
| 418 | } |
| 419 | } |
| 420 | |
| 421 | let (seq, _at) = reply::append_reply(&root, &run_id, payload)?; |
| 422 | |
| 423 | // Stage explicitly: the new reply file + every cited evidence path. |
| 424 | // No `add -A`; commits should contain only what the payload declares. |
| 425 | let reply_rel = format!("runs/{}/replies/{:04}.json", run_id, seq); |
| 426 | let cited_rel: Vec<String> = cited |
| 427 | .iter() |
| 428 | .map(|p| format!("runs/{}/{}", run_id, p)) |
| 429 | .collect(); |
| 430 | let mut files: Vec<&str> = Vec::with_capacity(1 + cited_rel.len()); |
| 431 | files.push(reply_rel.as_str()); |
| 432 | for c in &cited_rel { |
| 433 | files.push(c.as_str()); |
| 434 | } |
| 435 | git::commit_files(&root, &git::reply_commit_msg(step_id.as_deref(), seq), &files)?; |
| 436 | |
| 437 | match step_id { |
| 438 | Some(s) => println!("ok: run={} step={} seq={:04}", run_id, s, seq), |
| 439 | None => println!("ok: run={} seq={:04}", run_id, seq), |
| 440 | } |
| 441 | Ok(()) |
| 442 | } |
| 443 | |
| 444 | fn cmd_completion(shell: &str) -> Result<()> { |
| 445 | use clap_complete::Shell; |
no test coverage detected
searching dependent graphs…