Hmbown/CodeWhale · error

refusing to trust skill outside Codewhale-owned roots

Error message

refusing to trust skill outside Codewhale-owned roots

What it means

trust_skill() refuses to mark a skill as trusted when its root does not pass skill.root.is_writable_owned(), i.e. the skill lives outside roots that Codewhale owns and can write. Trust records are bound to the skill's on-disk digest, so trusting a skill outside managed roots would create a trust attestation the library cannot enforce or update. This is a trust-boundary guard, checked before the managed-source check and digest verification.

Solutions

  1. Move or reinstall the skill into a Codewhale-owned root (project or global skills dir) before trusting it
  2. Check skill.root.is_writable_owned() before calling trust_skill
  3. Update the skills-root configuration so the skill's directory is registered as an owned, writable root
  4. Trust the skill manually via the UI/configuration if supported, accepting it stays outside managed roots

Example fix

// before
ctx.trust_skill(skill_id, digest)?; // skill lives in ~/.my-skills
// after
// move the skill into the managed project skills dir first
// ~/.codewhale/skills/<name> or .codewhale/skills/<name>, then:
ctx.trust_skill(skill_id, digest)?;
Defensive patterns

Strategy: validation

Validate before calling

if !skill.root.is_writable_owned() { return Err(anyhow!("skill root is not owned; move it into a Codewhale skills dir")); }

Type guard

fn trustable_root(s: &Skill) -> bool { s.root.is_writable_owned() && matches!(s.source_kind, SkillSourceKind::CodeWhaleManaged) }

Prevention

When it happens

Trigger: Calling trust_skill (directly or via trust_writes_v2_digest_binding / execute_sync) with a skill_id whose skill.root is a non-owned (e.g. user home, external path, read-only mount) skill root.

Common situations: A user points Codewhale at skills stored in a shared or personal directory and then tries to trust one; or a symlink/config change moved a previously-owned skill outside managed roots.

Understand the failure class

Background: Permission denied / not authorized / 403 Forbidden: access-control rejections when the caller lacks the required role, grant, or ownership — this error's family across 18 libraries.

Related errors


AI-assisted analysis of Hmbown/CodeWhale@73e0f67d83 (2026-09-22). Data as JSON: /api/errors/f05ea47e09bee6a8. Report an issue: GitHub.

Appendix: source

Thrown at crates/tui/src/skills/mutation.rs:975

    Ok(SkillMutationReceipt {
        action: SkillActionKind::Remove,
        name: skill_id.canonical_name,
        scope,
        safe_target_path: safe_display_path(&path, Some(ctx.workspace), ctx.home),
        before_digest: before,
        after_digest: None,
        outcome: SkillMutationOutcome::Removed,
    })
}

fn trust_skill(
    skill_id: AuditedSkillId,
    expected_digest: String,
    ctx: &MutationContext<'_>,
) -> Result<SkillMutationReceipt> {
    let (skill, path) = find_audited_skill(ctx, &skill_id)?;
    if !skill.root.is_writable_owned() {
        bail!("refusing to trust skill outside Codewhale-owned roots");
    }
    if skill.source_kind != SkillSourceKind::CodeWhaleManaged {
        bail!("only Codewhale managed skills can be trusted");
    }
    validate_owned_skill_path(ctx, &skill, &path)?;
    let before = verify_expected_digest(&path, Some(&expected_digest))?;
    validate_owned_skill_path(ctx, &skill, &path)?;
    write_trust_v2(&path, &expected_digest)?;
    let scope = match skill.root.kind {
        SkillRootKind::CodeWhaleProject => SkillScope::Project,
        SkillRootKind::CodeWhaleGlobal => SkillScope::Global,
        _ => SkillScope::Logical,
    };
    Ok(SkillMutationReceipt {
        action: SkillActionKind::Trust,
        name: skill_id.canonical_name,
        scope,
        safe_target_path: safe_display_path(&path, Some(ctx.workspace), ctx.home),

View on GitHub (pinned to 73e0f67d83)