Summary
The post-tar archive integrity cross-check in backupCreateCommand fails on Windows when exclude patterns are active (two-step archive path). The check compares buildBackupArchivePath() output (forward-slash POSIX paths) against listArchiveEntries() output, but on Windows the paths don't match — causing all backup-exclude integration tests to fail on Windows CI shard 1.
Reproduction
Consistently reproduces on checks-windows shard 1 in CI. All tests using smartExclude: true or --exclude fail with:
Error: Archive integrity check failed: missing payload for asset
"C:\Users\runneradmin\AppData\Local\Temp\openclaw-backup-exclude-test-PC5aD4\.openclaw"
(expected "2026-03-12T00-00-01.000Z-openclaw-backup/payload/windows/C/Users/runneradmin/AppData/Local/Temp/openclaw-backup-exclude-test-PC5aD4/.openclaw")
8 tests fail in src/infra/backup-exclude.integration.test.ts (lines 124, 193, 225, 279, 309, 408, 491, 542).
Root Cause
The post-tar cross-check at src/infra/backup-create.ts:488-505 computes expected archive paths via buildBackupArchivePath(archiveRoot, asset.sourcePath) and compares them against listArchiveEntries() output. On Windows, there is a mismatch between what onWriteEntry writes into the tar (POSIX forward-slash paths) and what listArchiveEntries reads back, likely due to node-tar path normalization on Windows during read.
Relevant code:
- Cross-check:
src/infra/backup-create.ts:488-505
- Path encoding:
src/commands/backup-shared.ts:129-145 (encodeAbsolutePathForBackupArchive / buildBackupArchivePath)
- Entry remapping:
src/infra/backup-create.ts:284-294 (remapArchiveEntryPath)
- Entry listing:
src/commands/backup-shared.ts:49-59 (listArchiveEntries)
Possible Fix
Normalize both sides of the comparison to forward slashes before comparing, e.g.:
const normalizedEntries = archiveEntries.map(e => e.replaceAll("\\", "/"));
Or normalize inside listArchiveEntries itself since tar archives canonically use forward slashes.
CI Evidence
Affected Tests
All tests in src/infra/backup-exclude.integration.test.ts that exercise the two-step archive path (any test with exclude patterns active):
--smart-exclude excludes venvs/, models/, logs/, completions/
--exclude *.log excludes log files from archive
--exclude-file loads and applies patterns to real archive
--json output does not include excluded[], only excludedStats
excludedStats.totalBytes is correct
verify passes when smart-excluded paths are absent from archive
in-archive manifest contains excludedStats (not excluded[]) with accurate data
archive with excludes passes backup verify (excludedStats-only manifest)
Summary
The post-tar archive integrity cross-check in
backupCreateCommandfails on Windows when exclude patterns are active (two-step archive path). The check comparesbuildBackupArchivePath()output (forward-slash POSIX paths) againstlistArchiveEntries()output, but on Windows the paths don't match — causing all backup-exclude integration tests to fail on Windows CI shard 1.Reproduction
Consistently reproduces on
checks-windowsshard 1 in CI. All tests usingsmartExclude: trueor--excludefail with:8 tests fail in
src/infra/backup-exclude.integration.test.ts(lines 124, 193, 225, 279, 309, 408, 491, 542).Root Cause
The post-tar cross-check at
src/infra/backup-create.ts:488-505computes expected archive paths viabuildBackupArchivePath(archiveRoot, asset.sourcePath)and compares them againstlistArchiveEntries()output. On Windows, there is a mismatch between whatonWriteEntrywrites into the tar (POSIX forward-slash paths) and whatlistArchiveEntriesreads back, likely due tonode-tarpath normalization on Windows during read.Relevant code:
src/infra/backup-create.ts:488-505src/commands/backup-shared.ts:129-145(encodeAbsolutePathForBackupArchive/buildBackupArchivePath)src/infra/backup-create.ts:284-294(remapArchiveEntryPath)src/commands/backup-shared.ts:49-59(listArchiveEntries)Possible Fix
Normalize both sides of the comparison to forward slashes before comparing, e.g.:
Or normalize inside
listArchiveEntriesitself since tar archives canonically use forward slashes.CI Evidence
9a2ed86) — shard 1: 8 failuresecc2017) — shard 1: 9 failures (same root cause, pre-existing)Affected Tests
All tests in
src/infra/backup-exclude.integration.test.tsthat exercise the two-step archive path (any test with exclude patterns active):--smart-exclude excludes venvs/, models/, logs/, completions/--exclude *.log excludes log files from archive--exclude-file loads and applies patterns to real archive--json output does not include excluded[], only excludedStatsexcludedStats.totalBytes is correctverify passes when smart-excluded paths are absent from archivein-archive manifest contains excludedStats (not excluded[]) with accurate dataarchive with excludes passes backup verify (excludedStats-only manifest)