{"record":{"id":"18741fd1b37a29fd","repo":"farion1231/cc-switch","slug":"mcode-skill-dir-name","errorCode":null,"errorMessage":"MCode Skill 与托管副本内容不同，拒绝替换链接: {dir_name}","messagePattern":"MCode Skill 与托管副本内容不同，拒绝替换链接: (.+?)","errorType":"exception","errorClass":"anyhow::Error","httpStatus":null,"severity":"error","filePath":"src-tauri/src/services/skill.rs","lineNumber":2162,"sourceCode":"            // 解析元数据\n            let skill_md = dest.join(\"SKILL.md\");\n            let (name, description) = Self::read_skill_name_desc(&skill_md, &dir_name);\n\n            // 其他应用保存用户选择；Pi 的 exists=active 必须直接来自原生目录。\n            let mut apps = selection.apps;\n            apps.pi = Self::skill_exists_in_app(&dir_name, &AppType::Pi);\n\n            // An explicitly imported MCode link must point at the managed copy\n            // so subsequent toggles and updates can verify its ownership.\n            if apps.mcode {\n                let projection = (|| -> Result<()> {\n                    let native = Self::get_distinct_app_skills_dir(&ssot_dir, &AppType::Mcode)?\n                        .join(&dir_name);\n                    if Self::is_symlink(&native) && !Self::paths_alias(&native, &dest) {\n                        if Self::compute_pi_deployment_hash(&native)?\n                            != Self::compute_pi_deployment_hash(&dest)?\n                        {\n                            return Err(anyhow!(\n                                \"MCode Skill 与托管副本内容不同，拒绝替换链接: {dir_name}\"\n                            ));\n                        }\n                        if let Some(deployment) =\n                            Self::inspect_pi_skill_destination(&native, &native, &dir_name)?\n                        {\n                            Self::refresh_pi_skill_destination(\n                                &dest,\n                                &native,\n                                &dir_name,\n                                &deployment,\n                            )?;\n                        }\n                    } else {\n                        Self::preflight_install_destination(&dest, &dir_name, &AppType::Mcode)?;\n                    }\n                    if !native.exists() {\n                        Self::sync_to_app_dir(&dir_name, &AppType::Mcode)?;","sourceCodeStart":2144,"sourceCodeEnd":2180,"githubUrl":"https://github.com/farion1231/cc-switch/blob/06082e189d65e6d6dbadc35dacdac1ce6c79d89a/src-tauri/src/services/skill.rs#L2144-L2180","documentation":"When syncing a skill into the MCode (MiniMax Code) app directory, the code finds the native destination is a symlink that does not alias the intended target. It then compares content hashes of the linked (managed) copy and the new source; if they differ, replacement is refused to avoid clobbering a symlink whose pointed-to content diverged from the deployed skill. This guards against replacing a link that no longer matches managed content.","triggerScenarios":"Deploying/importing a skill where the app's skills dir entry for dir_name is a symlink pointing somewhere other than the SSOT path, AND compute_pi_deployment_hash(native) != compute_pi_deployment_hash(dest) — i.e. the linked copy's files were modified after deployment so it no longer matches the incoming skill content.","commonSituations":"A user manually edited files inside the symlinked skill directory; the skill was previously linked to a different/custom skill source; an older deployment left a stale symlink after the SSOT content was updated; switching between managed and hand-crafted versions of the same skill name.","solutions":["Diff the content at the native symlink target against the new skill source; if the local edits are unwanted, delete the divergent content and re-run the sync so the link is replaced.","If the local modifications are wanted, rename/backup the divergent directory, then re-run deployment to establish a fresh managed link.","Verify the symlink actually points at the intended managed copy (paths_alias check) — a wrong symlink target is often the root cause.","Re-import or re-hash the skill so its deployment hash matches, then retry."],"exampleFix":"// before: sync fails because the linked copy was hand-edited\nsync_skill_to_mcode(dir_name)?;\n// after: back up the divergent copy and re-sync\nfs::rename(&native, native.with_extension(\"bak\"));\nsync_skill_to_mcode(dir_name)?;","handlingStrategy":"validation","validationCode":"fn is_replacable(native: &Path, dest: &Path) -> bool {\n    if !is_symlink(native) || paths_alias(native, dest) { return true; }\n    compute_pi_deployment_hash(native) == compute_pi_deployment_hash(dest)\n}","typeGuard":null,"tryCatchPattern":"if let Err(e) = sync_skill_to_mcode(dir_name) {\n    if e.to_string().contains(\"拒绝替换链接\") {\n        backup_and_remove_link(native)?;\n        sync_skill_to_mcode(dir_name)?;\n    } else { return Err(e); }\n}","preventionTips":["Don't hand-edit files inside symlinked managed skill directories","Verify symlink targets point at the SSOT before deploying","Back up local modifications before re-running skill sync","Keep deployment hashes in sync by re-importing after content changes"],"tags":["symlink","skill-sync","content-mismatch","conflict"],"backgroundTag":"checksum-mismatch","analyzedSha":"06082e189d65e6d6dbadc35dacdac1ce6c79d89a","analyzedAt":"2026-09-19T08:37:02.504Z","contentChangedAt":"2026-09-19T08:37:02.504Z","schemaVersion":2},"datasetVersion":"2026-09-23T08:17:48.524Z"}