{"record":{"id":"291cd5757927d26f","repo":"santifer/career-ops","slug":"safety-violation-violation-message-and-revert","errorCode":null,"errorMessage":"Safety violation (${violation.message}) and revert also failed (${revertErr.message})","messagePattern":"Safety violation \\((.+?)\\) and revert also failed \\((.+?)\\)","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"critical","filePath":"update-system.mjs","lineNumber":2450,"sourceCode":"      throw err;\n    }\n\n    if (violatedUserPaths.size > 0) {\n      console.error('Aborting: user files were touched. Rolling back system files...');\n      // Revert ONLY the system-layer updates — never `git checkout` the\n      // violated user paths back to HEAD. Doing so would overwrite the\n      // user's working-tree content (accumulated STAR+R stories, local\n      // edits) with whatever is committed upstream, causing data loss.\n      // The user files were flagged as touched by the update, not by the\n      // user; leaving them as-is is the safe choice — the user decides\n      // what to do with them.\n      const violation = new Error('Update aborted: user files were touched.');\n      try {\n        revertPaths([...updated], initialStatusPaths);\n      } catch (revertErr) {\n        // If the revert itself fails, don't lose the safety-violation\n        // diagnostic — chain it via `cause` so the user sees both.\n        throw new Error(\n          `Safety violation (${violation.message}) and revert also failed (${revertErr.message})`,\n          { cause: violation },\n        );\n      }\n      console.error(`User file(s) left as-is (your content was NOT overwritten):`);\n      for (const f of violatedUserPaths) console.error(`  ${f}`);\n      // `throw` (not `process.exit`) so the outer `finally` runs and\n      // .update-lock is removed. Exiting here would leak the lock and\n      // permanently block subsequent updates until the user deletes\n      // it manually.\n      throw violation;\n    }\n\n    // 5. Install any new dependencies\n    try {\n      execSync('npm install --silent', { cwd: ROOT, timeout: NPM_INSTALL_TIMEOUT_MS });\n    } catch {\n      console.log('npm install skipped (may need manual run)');","sourceCodeStart":2432,"sourceCodeEnd":2468,"githubUrl":"https://github.com/santifer/career-ops/blob/e7abd431fce9348a95261acac9e0c14779c35df8/update-system.mjs#L2432-L2468","documentation":"update-system.mjs enforces that an update never overwrites user-layer files. When the applied update is detected to have touched user files, it raises the safety violation, attempts to revert the touched paths, and if the revert itself fails throws this combined error (original violation chained via `cause`). Unlike the successful-abort path, here the worktree may retain modifications to user files that could not be rolled back.","triggerScenarios":"An apply run whose updated file set includes user-layer paths (violatedUserPaths), followed by revertPaths() throwing — e.g. git checkout failing due to local modifications, a lock, or permissions.","commonSituations":"The user edited a system-layer file so the update flow classified it as user-touched and the revert collided with uncommitted changes; .git/index.lock present; read-only checkout; file held open by a watcher on Windows.","solutions":["Inspect err.cause (the safety violation) and the listed user file(s); back up any local edits first.","Manually restore the touched files: `git checkout -- <paths>` after saving your changes.","Move your customizations into the user layer (cv.md, config/, modes/_profile.md, modes/_custom.md) so updates never touch them.","Re-apply the update cleanly with `node update-system.mjs apply --confirm` once user files are out of the blast radius."],"exampleFix":"// before: hand-edited modes/_shared.md (system layer)\ncp modes/_shared.md /tmp/my-shared-backup.md\ngit checkout -- modes/_shared.md\n// after: put the rule in the user layer instead\n# move the rule into modes/_custom.md, then:\nnode update-system.mjs apply --confirm","handlingStrategy":"validation","validationCode":"const USER_LAYER = [/^cv\\.md$/, /^config\\//, /^modes\\/_profile\\.md$/, /^modes\\/_custom\\.md$/, /^modes\\/_brief\\.md$/, /^data\\//, /^reports\\//, /^output\\//];\nconst userTouched = updatedPaths.filter(p => USER_LAYER.some(re => re.test(p)));\nif (userTouched.length) throw new Error(`user files in update set: ${userTouched.join(', ')}`);","typeGuard":null,"tryCatchPattern":"try {\n  applyUpdate();\n} catch (e) {\n  if (e.message.startsWith('Safety violation')) {\n    console.error('back up listed user files, git restore them, then re-apply');\n  } else throw e;\n}","preventionTips":["Keep personalizations only in user-layer files (cv.md, config/, modes/_profile.md, modes/_custom.md).","Never hand-edit system-layer files (modes/_shared.md, AGENTS.md, *.mjs).","Commit or back up local edits before updating.","Run verify-pipeline / doctor after updates to confirm the layer split."],"tags":["git","safety-guard","revert-failed","user-files"],"backgroundTag":"git-command-failed","analyzedSha":"e7abd431fce9348a95261acac9e0c14779c35df8","analyzedAt":"2026-09-16T06:35:29.214Z","contentChangedAt":"2026-09-16T06:35:29.214Z","schemaVersion":2},"datasetVersion":"2026-09-23T08:17:48.524Z"}