fix(backup): dereference archive hardlinks#83938
Conversation
|
Codex review: needs maintainer review before merge. Workflow note: Future ClawSweeper reviews update this same comment in place. How this review workflow works
Summary Reproducibility: yes. The current-main source plus node-tar 7.5.15 source show a high-confidence reproduction path with hardlinked files in a backup source: node-tar can emit Link entries while OpenClaw remaps only PR rating Rank-up moves:
What the crustacean ranks mean
Shiny media proof means a screenshot, video, or linked artifact directly shows the changed behavior. Runtime, network, CSP, and security claims still need visible diagnostics. Real behavior proof Risk before merge
Maintainer options:
Next step before merge Security Review detailsBest possible solution: Land the focused backup fix if maintainers accept the larger-archive tradeoff, keeping the hardlink regression tests as the contract for future tar upgrades. Do we have a high-confidence way to reproduce the issue? Yes. The current-main source plus node-tar 7.5.15 source show a high-confidence reproduction path with hardlinked files in a backup source: node-tar can emit Link entries while OpenClaw remaps only Is this the best way to solve the issue? Yes. Disabling the node-tar hardlink cache for backup creation and validating any remaining Link targets during verification is a narrow fix for the restore-safety bug; the main tradeoff is larger hardlink-dense archives. Label changes:
Label justifications:
Acceptance criteria:
What I checked:
Likely related people:
Codex review notes: model gpt-5.5, reasoning high; reviewed against a00e7d3898cf. |
|
@clawsweeper re-review |
|
🦞🧹 I asked ClawSweeper to review this item again. Re-review progress:
|
|
@clawsweeper re-review |
|
🦞🧹 I asked ClawSweeper to review this item again. Re-review progress:
|
|
ClawSweeper PR egg ✨ Hatched: 🥚 common Gilded Lint Imp Rarity: 🥚 common. What is this egg doing here?
|
|
Landed via temp rebase onto main.
Thanks @jason-allen-oneal! |
Summary
Verification
Not Run
Real behavior proof
53802c6a93f6; real filesystem hardlink created withfs.link(...)inside an isolatedOPENCLAW_HOME/OPENCLAW_STATE_DIR.git fetch origin pull/83938/head:refs/heads/proof-83938andgit worktree add /tmp/openclaw-proof-83938 proof-83938.node --import /home/rev/projects/openclaw/node_modules/tsx/dist/loader.mjs /tmp/openclaw-proof-83938-smoke.mjs.tar -tvf /tmp/openclaw-backup-hardlink-proof-zGSJ13/out/hardlink-proof.tar.gz.hardlink-alias.datandhardlink-original.datas-rw-r--r--regular file entries with size 35, with nolink tohardlink entry.nlink=2. The archive contained no tar hardlink entries, both source names were archived as regularFileentries with no linkpath, OpenClaw backup verification passed, and extraction produced equal file contents but separate inodes withnlink=1.--include-workspacecustom workspace assets, concurrent live writers, or very large backup payloads.Fixes #54242
Current-head proof refresh (2026-05-19)
Reran the hardlink backup proof at current PR head
04ee028b52077175c3d732e70e1574d45bd9ef6c.Real hardlink smoke output summary:
{ "pr": 83938, "head": "04ee028b52077175c3d732e70e1574d45bd9ef6c", "hardlinkFixture": { "sameDevice": true, "sameInode": true, "nlinkTarget": 2, "nlinkHardlink": 2 }, "archive": { "totalEntries": 15, "linkEntryCount": 0, "esbuildPayloadEntries": [ { "path": "payload/.../node_modules/esbuild/bin/esbuild", "type": "File", "linkpath": null }, { "path": "payload/.../node_modules/@esbuild/darwin-arm64/bin/esbuild", "type": "File", "linkpath": null } ] }, "verification": { "ok": true, "reason": null } }Commands run at current head:
node --import tsxusingcreateBackupArchive(...)+backupVerifyCommand(...)-> source paths were true hardlinks (sameInode: true,nlink: 2), archive emitted 0 Link entries, both payload entries were regular File entries, verification passed.node scripts/run-vitest.mjs src/infra/backup-create.test.ts src/commands/backup-verify.test.ts src/commands/backup.test.ts src/commands/backup.create-verify.test.ts src/commands/backup.restore.test.ts-> 4 test files passed, 45 tests passed.git diff --check-> passed.Not run: live macOS bsdtar restore smoke; this host is Linux.