{"record":{"id":"ea93c92c575de4b6","repo":"Hmbown/CodeWhale","slug":"audited-owned-root-does-not-match-mutation-targ","errorCode":null,"errorMessage":"audited owned root {} does not match mutation target {}","messagePattern":"audited owned root (.+?) does not match mutation target (.+?)","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"crates/tui/src/skills/mutation.rs","lineNumber":280,"sourceCode":"fn target_scope_for_root(root: &SkillRootDescriptor) -> Result<SkillTargetScope> {\n    if !root.is_writable_owned() {\n        bail!(\"refusing to mutate non-owned root {}\", root.path.display());\n    }\n    match root.kind {\n        SkillRootKind::CodeWhaleProject => Ok(SkillTargetScope::Project),\n        SkillRootKind::CodeWhaleGlobal => Ok(SkillTargetScope::Global),\n        _ => bail!(\"refusing to mutate non-owned root {}\", root.path.display()),\n    }\n}\n\nfn validate_owned_root_descriptor(\n    ctx: &MutationContext<'_>,\n    root: &SkillRootDescriptor,\n) -> Result<PathBuf> {\n    let target = target_scope_for_root(root)?;\n    let expected = resolve_owned_target(ctx.workspace, ctx.home, target)?;\n    if root.path != expected {\n        bail!(\n            \"audited owned root {} does not match mutation target {}\",\n            root.path.display(),\n            expected.display()\n        );\n    }\n    let anchor = owned_anchor(ctx.workspace, ctx.home, target)?;\n    validate_owned_target_chain(anchor, &expected, true)?;\n    Ok(expected)\n}\n\n/// Validate a real direct child of an already validated owned skills root.\n/// Returns false only when a missing child is permitted.\nfn validate_owned_child(skills_dir: &Path, child: &Path, require_existing: bool) -> Result<bool> {\n    if child.parent() != Some(skills_dir) {\n        bail!(\n            \"refusing to mutate path {} outside direct owned root {}\",\n            child.display(),\n            skills_dir.display()","sourceCodeStart":262,"sourceCodeEnd":298,"githubUrl":"https://github.com/Hmbown/CodeWhale/blob/73e0f67d83c59909b571efdfc88c4bc28c309cb1/crates/tui/src/skills/mutation.rs#L262-L298","documentation":"validate_owned_root_descriptor re-derives the expected owned skills root for the descriptor's scope (project or global) and compares it with the path recorded in the audited root descriptor. A mismatch means the audit snapshot is stale or points at a different location than the mutation would resolve, so the mutation is refused to avoid writing outside the audited location.","triggerScenarios":"Calling validate_owned_skill_path or update_skill where root.path != resolve_owned_target(workspace, home, scope): e.g. the workspace directory changed between audit and mutation, HOME moved, the project was relocated, or a stale AuditedSkill from an old session is reused.","commonSituations":"Renaming/moving the repo or changing the workspace root after listing skills; running with a different $HOME; stale cached audit results after a config change; passing a global-scope skill audit into a mutation with a different home.","solutions":["Re-run skill discovery/audit in the current workspace+home so the root descriptor path matches the resolved target, then retry the mutation.","Check that the workspace path and HOME are the same as when the skill was audited; correct the invocation's workspace/home context.","If config changed the skills root, reload config and rebuild the audited skill list before mutating."],"exampleFix":"// before\nlet audited = load_cached_audit(old_workspace);\nupdate_skill(&ctx, &audited.skill)?; // stale path\n// after\nlet audited = rediscover_and_audit(&ctx.workspace, ctx.home)?;\nupdate_skill(&ctx, &audited.skill)?;","handlingStrategy":"validation","validationCode":"fn audit_is_current(ctx: &MutationContext, root: &SkillRootDescriptor) -> bool {\n    match target_scope_for_root(root) {\n        Ok(scope) => resolve_owned_target(ctx.workspace, ctx.home, scope)\n            .map(|expected| expected == root.path)\n            .unwrap_or(false),\n        Err(_) => false,\n    }\n}","typeGuard":null,"tryCatchPattern":"// match on the error to distinguish stale-audit from other failures\nmatch update_skill(&ctx, &skill) {\n    Err(e) if e.to_string().contains(\"does not match mutation target\") => {\n        let fresh = rediscover_and_audit(&ctx.workspace, ctx.home)?;\n        update_skill(&ctx, &fresh.skill)?\n    }\n    other => other?,\n}","preventionTips":["Re-audit skills in the same process/workspace where you mutate; never persist AuditedSkill across moves.","Keep workspace and HOME stable between discovery and mutation.","Reload configuration after any change to skills root settings."],"tags":["skills","stale-state","path-safety","rust"],"backgroundTag":"internal-invariant-violation","analyzedSha":"73e0f67d83c59909b571efdfc88c4bc28c309cb1","analyzedAt":"2026-09-22T01:30:00.501Z","contentChangedAt":"2026-09-22T01:30:00.501Z","schemaVersion":2},"datasetVersion":"2026-09-23T08:17:48.524Z"}