{"record":{"id":"e1524104633175d7","repo":"zeroclaw-labs/zeroclaw","slug":"completed-run-has-no-step-results-to-distill","errorCode":null,"errorMessage":"completed run has no step results to distill","messagePattern":"completed run has no step results to distill","errorType":"validation","errorClass":"anyhow::Error","httpStatus":null,"severity":"error","filePath":"crates/zeroclaw-runtime/src/sop/procedural_memory.rs","lineNumber":97,"sourceCode":"        rollback_path: None,\n    };\n    engine.save_proposal(&proposal)?;\n    Ok(proposal)\n}\n\npub fn capture_successful_run(\n    engine: &SopEngine,\n    run_id: &str,\n    requested_by: Option<String>,\n) -> Result<ProposalRecord> {\n    let run = engine\n        .get_run(run_id)\n        .ok_or_else(|| anyhow::Error::msg(format!(\"SOP run not found: {run_id}\")))?;\n    if run.status != SopRunStatus::Completed {\n        bail!(\"only completed SOP runs can be captured\");\n    }\n    if run.step_results.is_empty() {\n        bail!(\"completed run has no step results to distill\");\n    }\n    if run\n        .step_results\n        .iter()\n        .any(|step| matches!(step.status, super::types::SopStepStatus::Failed))\n    {\n        bail!(\"failed step output is not captured into procedural memory\");\n    }\n\n    let sop = engine\n        .get_sop(&run.sop_name)\n        .ok_or_else(|| anyhow::Error::msg(format!(\"SOP not loaded: {}\", run.sop_name)))?;\n    let manifest_toml = read_or_default_manifest(sop)?;\n    let procedure_markdown = append_captured_notes(sop, run_id, &run.step_results)?;\n    create_proposal(\n        engine,\n        ProposalDraft {\n            sop_name: sop.name.clone(),","sourceCodeStart":79,"sourceCodeEnd":115,"githubUrl":"https://github.com/zeroclaw-labs/zeroclaw/blob/88bb9c8533fc57ed7a03e36ca7c9ed2bf8336dcc/crates/zeroclaw-runtime/src/sop/procedural_memory.rs#L79-L115","documentation":"A defensive guard in capture_successful_run: the run reached Completed but its step_results slice is empty, so there is nothing to distill into a procedure. Completed runs are expected to carry at least one step result; an empty set means the SOP effectively executed zero recorded steps (or the run record is degenerate).","triggerScenarios":"Capturing a run of a zero-step or placeholder SOP that 'completes' without executing any steps; a run record reconstructed or trimmed in a way that dropped step results.","commonSituations":"Authoring scaffolding SOPs with no steps and running them as a smoke test; test fixtures that mark a run Completed manually without step results.","solutions":["Verify the SOP actually has steps before running and capturing (validate authoring with validate_sop_strict).","Check engine.get_run(run_id).step_results before capture and skip degenerate runs.","If a real run shows Completed with no results, investigate the engine's step recording for that run."],"exampleFix":"// before: capture any completed run\nlet p = capture_successful_run(&engine, run_id, None).await?;\n\n// after: require at least one recorded step\nlet run = engine.get_run(run_id).context(\"run missing\")?;\nif run.status == SopRunStatus::Completed && !run.step_results.is_empty() {\n    let p = capture_successful_run(&engine, run_id, None).await?;\n}","handlingStrategy":"validation","validationCode":"let Some(run) = engine.get_run(run_id) else {\n    anyhow::bail!(\"SOP run not found: {run_id}\");\n};\nanyhow::ensure!(\n    run.status == SopRunStatus::Completed && !run.step_results.is_empty(),\n    \"run has nothing to distill\"\n);","typeGuard":"fn has_distillable_steps(run: &SopRun) -> bool {\n    !run.step_results.is_empty()\n}","tryCatchPattern":"match capture_successful_run(&engine, run_id, None).await {\n    Err(e) if e.to_string().contains(\"no step results to distill\") => {\n        // degenerate run: verify the SOP has steps, then re-run it\n    }\n    other => other?,\n}","preventionTips":["Reject zero-step SOPs at authoring time.","Pre-check step_results before capture.","Investigate any Completed run with no recorded results as an engine anomaly."],"tags":["procedural-memory","capture","empty-results"],"backgroundTag":"empty-payload-rejected","analyzedSha":"88bb9c8533fc57ed7a03e36ca7c9ed2bf8336dcc","analyzedAt":"2026-08-23T01:07:41.857Z","schemaVersion":2},"datasetVersion":"2026-08-23T08:06:27.607Z"}