{"record":{"id":"ef92eda3bf3e9e7c","repo":"Yeachan-Heo/oh-my-codex","slug":"native-hook-transaction-rollback-preserved-artif-ef92ed","errorCode":null,"errorMessage":"Native hook transaction rollback preserved ${artifact.path} for manual recovery because ${artifact.label} no longer matches the expected restored version.","messagePattern":"Native hook transaction rollback preserved (.+?) for manual recovery because (.+?) no longer matches the expected restored version\\.","errorType":"exception","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"src/cli/setup.ts","lineNumber":1877,"sourceCode":"\t\t\t(stabilizedSnapshot) => {\n\t\t\t\tapplied.appliedSnapshot = stabilizedSnapshot;\n\t\t\t},\n\t\t\tassertRollbackState,\n\t\t);\n\t}\n\tconst restored = await assertNativeHookTransactionArtifactSnapshot(\n\t\tartifact.path,\n\t\tartifact.label,\n\t\tapplied.appliedSnapshot,\n\t\t\"rollback\",\n\t);\n\tif (\n\t\t!nativeHookTransactionSnapshotMatchesExpected(restored, {\n\t\t\tbytes: artifact.before.bytes,\n\t\t\ttopology: artifact.before.topology,\n\t\t})\n\t) {\n\t\tthrow new Error(\n\t\t\t`Native hook transaction rollback preserved ${artifact.path} for manual recovery because ${artifact.label} no longer matches the expected restored version.`,\n\t\t);\n\t}\n}\n\nasync function cleanupNativeHookTransactionStagedDeletions(\n\tapplied: readonly AppliedNativeHookTransactionArtifact[],\n\tancestorPrecondition: NativeHookTransactionAncestorPrecondition,\n\ttracker: RegularFileDurabilityTracker,\n\tpreconditions?: readonly NativeHookTransactionPrecondition[],\n\trollbackApplied?: readonly AppliedNativeHookTransactionArtifact[],\n): Promise<void> {\n\tfor (const entry of applied) {\n\t\tif (\n\t\t\t!entry.stagedDeletionPath ||\n\t\t\t!entry.stagedDeletionSnapshot ||\n\t\t\tentry.stagedDeletionCleaned\n\t\t) {","sourceCodeStart":1859,"sourceCodeEnd":1895,"githubUrl":"https://github.com/Yeachan-Heo/oh-my-codex/blob/3ad79a8a6fe6e95fdbb8c00e40716fffe4011ce2/src/cli/setup.ts#L1859-L1895","documentation":"After rollback writes the pre-transaction bytes back to artifact.path, setup re-reads the file and verifies it matches the recorded before-state (bytes and topology such as link count / symlink status). A mismatch means the restore did not land as expected, so the file is preserved for manual recovery instead of silently accepted.","triggerScenarios":"Post-restore verification where the file on disk differs from artifact.before — e.g. another writer touched it immediately after restore, or topology (symlink/hardlink) diverged.","commonSituations":"Racing processes, platforms where rename/replace semantics differ (Windows), or fault injection at before_rollback checkpoints.","solutions":["Manually compare artifact.path with the intended before-bytes recorded in the transaction log","Stop concurrent writers and re-run setup","Check for filesystem-level oddities (bind mounts, symlinks pointing at the path)","Verify no test injection hooks fired during restore"],"exampleFix":null,"handlingStrategy":"try-catch","validationCode":null,"typeGuard":null,"tryCatchPattern":"try { await tx.rollback(); } catch (e) { if (e instanceof Error && e.message.includes(\"no longer matches the expected restored version\")) { /* manually restore from backup root copy, then re-run setup */ } else throw e; }","preventionTips":["Prevent concurrent writers during the transaction window","Verify restored files byte-for-byte after any manual recovery","Re-run setup to confirm consistent state"],"tags":["transaction","rollback","verification","file-integrity"],"backgroundTag":"transaction-rollback-failed","analyzedSha":"3ad79a8a6fe6e95fdbb8c00e40716fffe4011ce2","analyzedAt":"2026-08-27T22:18:39.783Z","schemaVersion":2},"datasetVersion":"2026-08-28T00:17:15.603Z"}