{"record":{"id":"95be4c86e86d16b4","repo":"Hmbown/CodeWhale","slug":"skill-content-changed-since-audit-expected-expec","errorCode":null,"errorMessage":"skill content changed since audit (expected {expected}, found {current}); re-review before mutating","messagePattern":"skill content changed since audit \\(expected (.+?), found (.+?)\\); re-review before mutating","errorType":"exception","errorClass":"anyhow::Error","httpStatus":null,"severity":"error","filePath":"crates/tui/src/skills/mutation.rs","lineNumber":561,"sourceCode":"        _ => None,\n    }\n    .filter(|name| !name.is_empty())\n    .ok_or_else(|| {\n        anyhow::anyhow!(\n            \"invalid on-disk package directory for skill '{}'\",\n            skill_id.canonical_name\n        )\n    })?;\n    Ok(name)\n}\n\nfn verify_expected_digest(path: &Path, expected: Option<&str>) -> Result<Option<String>> {\n    let current = package_digest::compute_package_digest(path)\n        .with_context(|| format!(\"cannot digest {}\", path.display()))?;\n    if let Some(expected) = expected\n        && expected != current\n    {\n        bail!(\n            \"skill content changed since audit (expected {expected}, found {current}); \\\n             re-review before mutating\"\n        );\n    }\n    Ok(Some(current))\n}\n\nfn ensure_remote_updatable(skill_dir: &Path) -> Result<()> {\n    let marker_path = skill_dir.join(install::INSTALLED_FROM_MARKER);\n    let body = fs::read_to_string(&marker_path)\n        .with_context(|| format!(\"failed to read {}\", marker_path.display()))?;\n    let value: serde_json::Value = serde_json::from_str(&body)\n        .with_context(|| format!(\"malformed {}\", install::INSTALLED_FROM_MARKER))?;\n    let spec = value\n        .get(\"spec\")\n        .and_then(|v| v.as_str())\n        .unwrap_or_default();\n    if !install::is_registry_updatable_spec(spec) {","sourceCodeStart":543,"sourceCodeEnd":579,"githubUrl":"https://github.com/Hmbown/CodeWhale/blob/0c42157ee52f9d55af2b506d71b46249910f77d3/crates/tui/src/skills/mutation.rs#L543-L579","documentation":"verify_expected_digest recomputes the package digest and compares it to the digest recorded at audit time (or supplied by the caller). This bail fires on mismatch: the skill's files changed on disk after the user reviewed them, so proceeding could execute unreviewed content. The modified skill package is the input at fault; this is a TOCTOU/consistency guard used by import, update, remove, and trust.","triggerScenarios":"Thrown at crates/tui/src/skills/mutation.rs:561 when the library encounters an invalid state.","commonSituations":"See trigger scenarios.","solutions":["Re-review the skill contents (the review gate) and retry the mutation with the fresh digest","Restore the package to its audited state before mutating","Re-run the skills scan to pick up the new digest as the audit baseline"],"exampleFix":null,"handlingStrategy":"validation","validationCode":null,"typeGuard":null,"tryCatchPattern":null,"preventionTips":[],"tags":[],"backgroundTag":null,"analyzedSha":"0c42157ee52f9d55af2b506d71b46249910f77d3","analyzedAt":"2026-08-20T21:50:45.477Z","contentChangedAt":null,"schemaVersion":2},"datasetVersion":"2026-09-14T00:17:10.932Z"}