Follow-up to #9. The backup transform s,^\./,workspace/, rewrites the workspace contents (./foo → workspace/foo) but leaves tar's root member as a bare ./. That ./ entry carries the source root's mode/mtime, so extracting the archive stamps them onto the extraction directory itself (e.g. an unexpected chmod/mtime on wherever the user unpacks it).
Fix
Match the leading . instead of ./ (s,^\.,workspace,), so tar's root member is renamed ./ → workspace. The archive then carries a proper workspace/ directory entry and no bare ./, and the extraction directory is left untouched. Contents, hidden files, excludes, symlink targets, and the flags=rh hardlink handling are all unchanged.
Verification (in-container, exact production script)
Archive top level is exactly workspace/ + home-claude/; no ./ member.
Extracting into a 0755 directory leaves it 0755 (previously the root entry could reset it).
node_modules/target still excluded (0 leaked).
home-claude/ still un-nested; workspace/.git history preserved.
Symlink targets preserved; intra-workspace hardlink pair shares one inode.
## Problem
Follow-up to #9. The backup transform `s,^\./,workspace/,` rewrites the workspace *contents* (`./foo` → `workspace/foo`) but leaves tar's root member as a bare `./`. That `./` entry carries the source root's mode/mtime, so extracting the archive stamps them onto the **extraction directory itself** (e.g. an unexpected chmod/mtime on wherever the user unpacks it).
## Fix
Match the leading `.` instead of `./` (`s,^\.,workspace,`), so tar's root member is renamed `./` → `workspace`. The archive then carries a proper `workspace/` directory entry and no bare `./`, and the extraction directory is left untouched. Contents, hidden files, excludes, symlink targets, and the `flags=rh` hardlink handling are all unchanged.
## Verification (in-container, exact production script)
- Archive top level is exactly `workspace/` + `home-claude/`; **no `./` member**.
- Extracting into a `0755` directory leaves it `0755` (previously the root entry could reset it).
- `node_modules`/`target` still excluded (0 leaked).
- `home-claude/` still un-nested; `workspace/.git` history preserved.
- Symlink targets preserved; intra-workspace hardlink pair shares one inode.
- `cargo check` passes.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
The transform used `s,^\./,workspace/,`, which rewrites the workspace
*contents* (`./foo` -> `workspace/foo`) but leaves tar's root member as a
bare `./`. That `./` entry carries the source root's mode/mtime, and on
extraction tar stamps them onto the extraction directory itself.
Match the leading `.` instead (`s,^\.,workspace,`) so the root member is
renamed `./` -> `workspace`, giving the archive a proper `workspace/`
directory entry and no bare `./`. The extraction directory is left
untouched. Contents, hidden files, excludes, symlink targets and the
`flags=rh` hardlink handling are unchanged.
Verified in-container: archive top level is exactly `workspace/` +
`home-claude/`, no `./` member, node_modules excluded, extraction into a
0755 dir leaves it 0755, workspace/.git preserved.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
jknapp
merged commit 59d89bcd1b into main2026-07-01 13:35:03 +00:00
Blocking a user prevents them from interacting with repositories, such as opening or commenting on pull requests or issues. Learn more about blocking a user.
Problem
Follow-up to #9. The backup transform
s,^\./,workspace/,rewrites the workspace contents (./foo→workspace/foo) but leaves tar's root member as a bare./. That./entry carries the source root's mode/mtime, so extracting the archive stamps them onto the extraction directory itself (e.g. an unexpected chmod/mtime on wherever the user unpacks it).Fix
Match the leading
.instead of./(s,^\.,workspace,), so tar's root member is renamed./→workspace. The archive then carries a properworkspace/directory entry and no bare./, and the extraction directory is left untouched. Contents, hidden files, excludes, symlink targets, and theflags=rhhardlink handling are all unchanged.Verification (in-container, exact production script)
workspace/+home-claude/; no./member.0755directory leaves it0755(previously the root entry could reset it).node_modules/targetstill excluded (0 leaked).home-claude/still un-nested;workspace/.githistory preserved.cargo checkpasses.🤖 Generated with Claude Code