{"record":{"id":"3d2a6b655828129c","repo":"zeroclaw-labs/zeroclaw","slug":"sop-rejected","errorCode":null,"errorMessage":"SOP rejected: {}","messagePattern":"SOP rejected: (.+?)","errorType":"validation","errorClass":"anyhow::Error","httpStatus":null,"severity":"error","filePath":"crates/zeroclaw-runtime/src/sop/mod.rs","lineNumber":978,"sourceCode":"        }\n        for bullet in render_step_bullets(step) {\n            out.push_str(&format!(\"   - {bullet}\\n\"));\n        }\n    }\n    out\n}\n\n/// Persist an SOP to `<sops_dir>/<name>/` as `SOP.toml` + `SOP.md`.\n/// Normalizes step numbers first, then rejects the write entirely if\n/// strict validation finds blocking problems; nothing touches disk on\n/// failure.\npub fn save_sop(sops_dir: &Path, sop: &Sop) -> Result<()> {\n    let mut sop = sop.clone();\n    normalize_step_numbers(&mut sop);\n    let sop = &sop;\n    let validation = validate_sop_strict(sop);\n    if !validation.is_ok() {\n        anyhow::bail!(\"SOP rejected: {}\", validation.blocking.join(\"; \"));\n    }\n\n    let sop_dir = resolve_sop_dir(sops_dir, &sop.name)?;\n    std::fs::create_dir_all(&sop_dir)?;\n\n    let manifest = SopManifest::from_sop(sop);\n    let toml_content = toml::to_string_pretty(&manifest)?;\n    std::fs::write(sop_dir.join(\"SOP.toml\"), toml_content)?;\n    std::fs::write(sop_dir.join(\"SOP.md\"), render_steps(&sop.steps))?;\n\n    Ok(())\n}\n\n// ── Validation ──────────────────────────────────────────────────\n\n/// Validate a loaded SOP and return a list of warnings.\npub fn validate_sop(sop: &Sop) -> Vec<String> {\n    let mut warnings = Vec::new();","sourceCodeStart":960,"sourceCodeEnd":996,"githubUrl":"https://github.com/zeroclaw-labs/zeroclaw/blob/88bb9c8533fc57ed7a03e36ca7c9ed2bf8336dcc/crates/zeroclaw-runtime/src/sop/mod.rs#L960-L996","documentation":"save_sop clones the SOP, normalizes step numbers, then runs validate_sop_strict; if validation reports blocking findings they are joined with '; ' into this error and nothing is written to disk. Blocking findings include an empty SOP name, a step with an empty title, duplicate step numbers, and malformed step bindings or step-reference diagnostics.","triggerScenarios":"Saving an SOP whose steps have empty titles, duplicate step numbers that normalization cannot disambiguate, malformed binding syntax, or broken step references; the bail happens after normalize_step_numbers but before any directory is created, so no partial SOP lands on disk.","commonSituations":"Hand-edited SOP.md files with repeated '1.' list markers; generated SOPs where a template field was left blank; refactoring steps and renumbering by hand; bindings referencing steps by an invalid form.","solutions":["Read the message: every blocking finding is listed; fix each one (titles non-empty, unique step numbers, well-formed bindings).","Run validate_sop_strict(&sop) yourself before save to get findings without attempting a write.","Dry-run save into a temp dir when validating authored content in tests or importers.","Re-check numbering after hand edits, or let normalize_step_numbers handle sequencing by avoiding explicit conflicting numbers."],"exampleFix":"// before: save fails with a joined list of blocking findings\nsave_sop(&sops_dir, &sop)?;\n\n// after: validate first, surface each finding, only then save\nlet validation = validate_sop_strict(&sop);\nif !validation.is_ok() {\n    for issue in &validation.blocking {\n        eprintln!(\"blocking: {issue}\");\n    }\n    anyhow::bail!(\"SOP authoring failed validation\");\n}\nsave_sop(&sops_dir, &sop)?;","handlingStrategy":"validation","validationCode":"let validation = validate_sop_strict(&sop);\nif !validation.is_ok() {\n    for issue in &validation.blocking {\n        eprintln!(\"blocking: {issue}\");\n    }\n    anyhow::bail!(\"SOP failed strict validation; nothing written\");\n}\nsave_sop(&sops_dir, &sop)?;","typeGuard":null,"tryCatchPattern":"match save_sop(&sops_dir, &sop) {\n    Err(e) if e.to_string().starts_with(\"SOP rejected:\") => {\n        // parse the '; '-joined findings and surface each authoring error\n    }\n    other => other?,\n}","preventionTips":["Run validate_sop_strict in authoring tooling and CI before save.","Dry-run save_sop into a temp dir when importing SOP packs.","Give every step a non-empty title and unique number before submitting."],"tags":["sop","validation","authoring","strict"],"backgroundTag":"schema-validation-failed","analyzedSha":"88bb9c8533fc57ed7a03e36ca7c9ed2bf8336dcc","analyzedAt":"2026-08-23T01:07:41.857Z","schemaVersion":2},"datasetVersion":"2026-08-23T08:06:27.607Z"}