{"record":{"id":"d6b1a9888bbf3478","repo":"Hmbown/CodeWhale","slug":"refusing-to-update-skill-outside-codewhale-owned-roots","errorCode":null,"errorMessage":"refusing to update skill outside Codewhale-owned roots","messagePattern":"refusing to update skill outside Codewhale-owned roots","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"crates/tui/src/skills/mutation.rs","lineNumber":864,"sourceCode":"            copy_dir_regular_files(&path, &target)?;\n        } else if meta.is_file() {\n            if name_str.starts_with('.') {\n                continue;\n            }\n            fs::copy(&path, &target)?;\n        }\n    }\n    Ok(())\n}\n\nasync fn update_skill(\n    skill_id: AuditedSkillId,\n    expected_digest: Option<String>,\n    ctx: &MutationContext<'_>,\n) -> Result<SkillMutationReceipt> {\n    let (skill, path) = find_audited_skill(ctx, &skill_id)?;\n    if !skill.root.is_writable_owned() {\n        bail!(\"refusing to update skill outside Codewhale-owned roots\");\n    }\n    if skill.source_kind != SkillSourceKind::CodeWhaleManaged {\n        bail!(\"only Codewhale managed skills can be updated\");\n    }\n    let skills_dir = validate_owned_skill_path(ctx, &skill, &path)?;\n    // Imported skills carry `import:…` provenance and must not hit the registry.\n    ensure_remote_updatable(&path)?;\n    let before = verify_expected_digest(&path, expected_digest.as_deref())?;\n    let scope = match skill.root.kind {\n        SkillRootKind::CodeWhaleProject => SkillScope::Project,\n        SkillRootKind::CodeWhaleGlobal => SkillScope::Global,\n        _ => SkillScope::Logical,\n    };\n\n    let package_name = on_disk_package_name(&skill_id)?;\n    validate_owned_skill_path(ctx, &skill, &path)?;\n    let outcome = install::update_with_registry(\n        package_name,","sourceCodeStart":846,"sourceCodeEnd":882,"githubUrl":"https://github.com/Hmbown/CodeWhale/blob/73e0f67d83c59909b571efdfc88c4bc28c309cb1/crates/tui/src/skills/mutation.rs#L846-L882","documentation":"`update_skill` only mutates skills living in roots marked writable/Codewhale-owned (`root.is_writable_owned()`). Bundled skills, external skills, and read-only locations are refused, since Codewhale must not edit content it does not own.","triggerScenarios":"Calling the `Update` mutation (`execute` → `update_skill`) for a skill whose root is not a writable owned root — a bundled skill, an unimported external skill, or an owned dir mounted read-only.","commonSituations":"Trying to update an external skill instead of importing it first; pointing the skills dir at a read-only path (NFS, container image layer); passing the ID of a bundled example skill.","solutions":["Import the external skill into an owned scope first, then update the imported copy.","Target a skill that lives under a writable Codewhale-owned skills directory.","Fix the skills directory configuration/permissions so the root is writable and recognized as owned."],"exampleFix":"// before\nmutation.execute(Mutation::Update { skill_id: bundled_skill_id, .. })?; // rejected\n// after\nmutation.execute_sync(Mutation::ImportExternal { source_id: external_id, target, conflict_policy })?;\nmutation.execute(Mutation::Update { skill_id: imported_id, .. })?;","handlingStrategy":"validation","validationCode":"let (skill, _) = find_audited_skill(ctx, &skill_id)?;\nif !skill.root.is_writable_owned() {\n    return Err(anyhow!(\"skill root is not a writable owned root; import first\"));\n}","typeGuard":"fn updatable(skill: &AuditedSkill) -> bool {\n    skill.root.is_writable_owned() && skill.source_kind == SkillSourceKind::CodeWhaleManaged\n}","tryCatchPattern":null,"preventionTips":["Only update skills shown as managed in the audit listing.","Ensure the configured skills directory is writable.","Import external skills before attempting updates."],"tags":["skills","permissions","update"],"backgroundTag":"permission-denied","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"}