{"record":{"id":"a99ceaf92d0c3f97","repo":"Hmbown/CodeWhale","slug":"owned-skill-root-escapes-anchor","errorCode":null,"errorMessage":"owned skill root {} escapes anchor {}","messagePattern":"owned skill root (.+?) escapes anchor (.+?)","errorType":"exception","errorClass":"anyhow::Error","httpStatus":null,"severity":"error","filePath":"crates/tui/src/skills/mutation.rs","lineNumber":216,"sourceCode":"\n    let skills_exists = checked_real_directory(skills_dir)?;\n    if !skills_exists {\n        if require_existing {\n            bail!(\"owned skill root {} does not exist\", skills_dir.display());\n        }\n        return Ok(());\n    }\n\n    let canonical_anchor = fs::canonicalize(anchor)\n        .with_context(|| format!(\"failed to resolve owned anchor {}\", anchor.display()))?;\n    let canonical_skills = fs::canonicalize(skills_dir).with_context(|| {\n        format!(\n            \"failed to resolve owned skill root {}\",\n            skills_dir.display()\n        )\n    })?;\n    if !canonical_skills.starts_with(&canonical_anchor) {\n        bail!(\n            \"owned skill root {} escapes anchor {}\",\n            skills_dir.display(),\n            anchor.display()\n        );\n    }\n    Ok(())\n}\n\nfn create_owned_directory(path: &Path) -> Result<()> {\n    match fs::create_dir(path) {\n        Ok(()) => {}\n        Err(err) if err.kind() == ErrorKind::AlreadyExists => {}\n        Err(err) => {\n            return Err(err).with_context(|| format!(\"failed to create {}\", path.display()));\n        }\n    }\n    if !checked_real_directory(path)? {\n        bail!(\"failed to create owned skill directory {}\", path.display());","sourceCodeStart":198,"sourceCodeEnd":234,"githubUrl":"https://github.com/Hmbown/CodeWhale/blob/0c42157ee52f9d55af2b506d71b46249910f77d3/crates/tui/src/skills/mutation.rs#L198-L234","documentation":"After canonicalization, validate_owned_target_chain checks that the real (symlink-resolved) skills root is still inside the real anchor. This bail fires when they diverge — meaning some component (usually a symlink introduced after the lexical checks) redirects the skills root outside the anchor. It is a defense-in-depth traversal check: the canonical skills path escaping the canonical anchor is the fault condition.","triggerScenarios":"Thrown at crates/tui/src/skills/mutation.rs:216 when the library encounters an invalid state.","commonSituations":"See trigger scenarios.","solutions":["Look for symlinks introduced inside .codewhale or skills and remove them","Restore the canonical layout: anchor/.codewhale/skills as real directories","Re-scan skills after repairing the directory structure"],"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-14T05:17:10.506Z"}