Skip to content

fix: preserve ordinary POSIX mode when Code Collab saves text - #604

Open
ladydd wants to merge 1 commit into
LodyAI:mainfrom
ladydd:fix/preserve-code-collab-save-mode
Open

ladydd wants to merge 1 commit into
LodyAI:mainfrom
ladydd:fix/preserve-code-collab-save-mode

Conversation

@ladydd

@ladydd ladydd commented Sep 11, 2026

Copy link
Copy Markdown
Contributor

Related issue

Closes #603

Problem / pressure

Code Collab saveText reports success and then replaces the file with a new inode whose mode is 0666 & ~umask. Saving a 0755 script drops +x (EACCES on the next run). A 0600 file is widened to 0664/0644. Digest conflicts already skip the write; the successful path does not keep the ordinary mode.

Summary

writeFileAtomically still writes a temp file and renames it over the original path. On non-Windows it now lstats an existing regular file, chmods the temp path with mode & 0o777 before rename, and leaves conflict / file_deleted returns on the existing no-write paths. Adapter/core gitlinks are unchanged.

Visual explanation

writeFileAtomically(path, bytes)
  mode = lstat(path).mode & 0o777   # skip win32 / missing / non-file
  writeFile(temp, bytes, mode 0666) # still masked by umask
  if mode known: chmod(temp, mode)  # not masked by umask
  rename(temp, path)                # atomic replace

Conflict and deleted-file checks still run in saveText before this helper. A digest mismatch or missing file returns without creating the temp file.

Before / after

Before After
Save 0755 under umask 0022 → 0644, next exec is EACCES Save keeps 0755; script still runs
Save 06000664/0644 Save keeps 0600
Digest conflict / deleted file Unchanged: no write, no recreate
Windows Unchanged: no chmod (untested)

Test plan

  • apps/cli Vitest src/lib/code-collab/code-collab-v2-service.test.ts: 48 passed on Linux (Node 24.15.0). New coverage: umask 0022 + 0755 remains executable, 0600 stays 0600, 0644 stays 0644, digest conflict leaves 0755, deleted file returns file_deleted and is not recreated. Existing save/conflict tests still pass.
  • oxlint on the two changed TypeScript files: 0 warnings / 0 errors.
  • pnpm run docs check: 0 new errors (pre-existing AGENTS.md size warnings only).
  • check:code-collab-imports, check:platform-boundaries, check:public-boundary: passed.
  • Not run: packaged Electron click-save, macOS, Windows, ACL/xattr/setuid. Isolated profile; the developer's existing Electron instance was not stopped or replaced.

Context handoff

Instructions for reviewing agents

  • Review focus: writeFileAtomically in apps/cli/src/lib/code-collab/code-collab-v2-service.ts and the POSIX save tests; chmod must land on the temp path before rename, and conflict/deleted returns must still skip the write.
  • Decisions to challenge: Copying only mode & 0o777 (dropping setuid/setgid/sticky), skipping Windows chmod, and not restoring ACL/xattr on the new inode.
  • Plausible failures / evidence gaps: Packaged Electron click-save was not run; macOS/Windows untested; a delete between the digest check and rename can still recreate the file (pre-existing TOCTOU).

Authoring context

  • User goal / directives: Fix Code Collab save dropping ordinary POSIX mode, then open a main-repo Issue and PR; do not change adapter/core or fold this into Fix/electron local file browser #38/fix: pin Codex compact-kill waiter rejection #551.
  • Constraints / non-goals: Keep atomic rename and digest/deleted conflict behavior; do not claim ACL, special bits, Windows, macOS, or GUI coverage.
  • Risk-bearing decisions: chmod the temp file with the previous ordinary mode before it becomes visible, so umask cannot strip bits after writeFile.
  • Destructive or irreversible behavior: A successful save still replaces the file inode via rename; failed writes still unlink the temp path. Conflict and deleted-file paths do not write.
  • Deliberately not done or tested: No adapter/core changes; no Electron GUI click-through; no Windows/macOS/ACL/xattr/setuid tests.
  • Unknowns / confidence: Linux service tests pass for 0755/0600/0644 plus conflict/deleted protection. Residual risk is untested desktop GUI and non-Linux permission models.

Original user prompt

Code Collab text save should keep the file's original ordinary POSIX permission bits.

…#603)

writeFileAtomically created the replacement at mode 0666, so rename
left 0666 & ~umask and dropped +x on scripts. chmod the temp file
with the original ordinary bits before rename.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[Bug] Code Collab save replaces POSIX mode with umask defaults

1 participant