{"record":{"id":"f525adfcb2ea5ef4","repo":"JuliusBrussee/caveman","slug":"file-changed-after-setup-refusing-to-overwrite","errorCode":null,"errorMessage":"${file} changed after setup; refusing to overwrite user skill edits","messagePattern":"(.+?) changed after setup; refusing to overwrite user skill edits","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"packages/cli/src/index.ts","lineNumber":2550,"sourceCode":"  if (status.state !== \"installed\") repairNativeAgent(agent);\n}\n\nfunction preflightAgentNativeBundleComponents(\n  agent: \"claude\" | \"codex\",\n  existingBundle: AgentNativeBundleJournal | null,\n): void {\n  const previousSkills = new Map(existingBundle?.skills.map((skill) => [skill.file, skill]) ?? []);\n  for (const { file, body } of agentNativeSkillFiles(agent)) {\n    const current = fileBytes(file);\n    if (!existingBundle) {\n      if (current && !current.equals(Buffer.from(body))) {\n        throw new Error(`${file} already exists with non-canonical content; refusing to overwrite an unjournaled skill`);\n      }\n      continue;\n    }\n    const previous = previousSkills.get(file);\n    if (current && !current.equals(Buffer.from(body)) && (!previous || bytesHash(current) !== previous.after_sha256)) {\n      throw new Error(`${file} changed after setup; refusing to overwrite user skill edits`);\n    }\n  }\n  if (agent === \"claude\") {\n    const actual = claudeMcpRegistration(\"caveman-cloud\");\n    const marker = readMcpServerMarker(\"claude\", \"caveman-cloud\");\n    if (actual.present && !marker) {\n      throw new Error(\"Claude caveman-cloud MCP exists but is not Caveman-journaled; refusing overwrite\");\n    }\n    if (actual.present && marker && !agentNativeCloudMcpMatches(\"claude\", marker)) {\n      throw new Error(\"Claude caveman-cloud MCP changed after setup; refusing to overwrite user fields\");\n    }\n    return;\n  }\n  const config = fileBytes(join(homedir(), \".codex\", \"config.toml\"))?.toString(\"utf8\") ?? \"\";\n  if (!config.includes(\"[mcp_servers.caveman-cloud]\")) return;\n  if (!readMcpServerMarker(\"codex\", \"caveman-cloud\")) {\n    throw new Error(\"[mcp_servers.caveman-cloud] exists but is not Caveman-journaled; refusing overwrite\");\n  }","sourceCodeStart":2532,"sourceCodeEnd":2568,"githubUrl":"https://github.com/JuliusBrussee/caveman/blob/27d5a3981a347890211bb1bf2439e5c821a63bc9/packages/cli/src/index.ts#L2532-L2568","documentation":"With a committed bundle journal present, a skill file whose bytes differ from the canonical body AND whose hash does not match the journal's after_sha256 for that file (or has no journal entry at all) is a post-setup user edit. Setup refuses to overwrite it — the user's edits win over the canonical update.","triggerScenarios":"Upgrading caveman (new embedded skill bodies) while the user has edited one of the installed skill files since the last setup; or a skill file was added to the suite that now overlaps a user-created file at the same path.","commonSituations":"User tweaked a Caveman-installed SKILL.md; a pixel convert pass rewrote a body; agent tooling reformatted the file; partial journal missing an entry for a newly introduced skill path.","solutions":["Back up and re-apply your edits: move the edited file aside, re-run setup, then port your changes into the new canonical body","Or keep your version permanently by renaming the skill directory so future setups treat it as foreign but non-colliding","Diff your file against the journal's after_sha256/canonical body to confirm which changes are yours"],"exampleFix":null,"handlingStrategy":"validation","validationCode":"function skillUnedited(file: string, journalSha: string | undefined, canonicalBody: Buffer): boolean {\n  let cur: Buffer;\n  try { cur = readFileSync(file); } catch { return true; }\n  if (cur.equals(canonicalBody)) return true;\n  const h = createHash(\"sha256\").update(cur).digest(\"hex\");\n  return journalSha !== undefined && h === journalSha;\n}","typeGuard":"function isUserEditedSkillError(e: unknown): boolean {\n  return e instanceof Error && e.message.includes(\"changed after setup; refusing to overwrite user skill edits\");\n}","tryCatchPattern":"try {\n  runCavemanSetup(); // upgrade path\n} catch (e) {\n  if (isUserEditedSkillError(e)) {\n    // back up the file, let setup overwrite, re-apply edits to the new body\n  } else throw e;\n}","preventionTips":["Fork caveman skills under new names instead of editing them in place","Re-apply customizations after each caveman upgrade rather than blocking it"],"tags":["skills","upgrade","safety","agent-native"],"backgroundTag":null,"analyzedSha":"27d5a3981a347890211bb1bf2439e5c821a63bc9","analyzedAt":"2026-08-15T09:26:11.751Z","schemaVersion":2},"datasetVersion":"2026-08-15T17:31:12.345Z"}