提交前确认 · Pre-submission checklist
问题类别 · Category
UI / 界面体验 · UI / UX
涉及的 Agent 框架 · Agent framework
ZCode Agent(自研)
严重程度 · Severity
影响体验 · Major (功能可用但体验受损 / works but degraded)
复现频率 · Reproducibility
必现 · Always
问题描述 · Description
Bug: Edit tool drops file permissions (executable bit) on edit
Task ID: sess_401e6b50-fd6b-411d-af7d-d006f1b3afa7
Workspace: /home/gr/supreme-mcp-tools
复现步骤 · Steps to reproduce
Issue
The Edit tool silently resets file permissions when modifying an existing file.
Editing a file with the executable bit (chmod +x, git mode 100755) produces
a non-executable file (100644) after the edit, with no warning. This breaks
executable scripts invoked directly via shebang.
Example (this task): I edited launchmcp.py (committed as 100755,
invoked as ./launchmcp.py from my startlauncher wrapper) and startlauncher
(also 100755). After the edits, both became non-executable, and
./startlauncher silently failed with "Permission denied". The user had to
diagnose and chmod +x by hand.
Reproduction:
$ echo '#!/bin/sh' > demo.sh && echo 'echo hi' >> demo.sh && chmod +x demo.sh
# edit demo.sh with ZCode Edit tool (any change), then:
$ ls -l demo.sh
-rw-r--r-- ... demo.sh # exec bit GONE
$ git ls-files -s demo.sh
100644 ... demo.sh # git sees mode change 100755 => 100644
Why it matters: Silent (no warning), breaks executable scripts, and the
regression can be committed to git permanently.
Likely root cause: The Edit tool appears to write content to a temp file
then atomically os.replace(tmp, target). I verified this mechanism reproduces
the symptom: os.replace replaces the target inode, so the target's existing
mode/owner/ACLs are lost — the file ends up with the temp file's mode. By
contrast, in-place rewrite (open(path,'w'), which truncates the existing
inode) preserves mode. Confirmed: os.replace drops 0o755→0o600; open(w)
keeps 0o755.
Suggested fix: Preserve the target's mode across the atomic write:
import shutil
shutil.copystat(target, tmp) # copy mode+xattrs to temp BEFORE replace
os.replace(tmp, target)
Affects any file whose mode differs from umask default (read-only 0o444,
group-writable, ACL-restricted), not just executable scripts.
Expected result
Editing a 100755 file should leave it 100755 — the executable bit (and all
file metadata) must survive edits. git diff --summary should show no
mode change after an edit, and the file should remain directly executable
with no manual chmod +x. If atomic temp+replace write is needed for crash
safety, call shutil.copystat(old, tmp) before the replace.
Env: Linux Debian 13, Python 3.13.5, umask 0002. Observed on two executable
scripts (launchmcp.py, startlauncher) committed as 100755, dropped to
100644/664 after one Edit each.
期望表现 · Expected behavior
When editing files, the file permissions should be kept as they are. Opencode, github copilot, codex and claude code do that.
实际表现 · Actual behavior
when editing files, file permissions are reset so an executable script is not longer executable (that's annoying)
ZCode 版本 · ZCode version
3.2.5
设备 / 系统 / 浏览器 · Device / OS / Browser
Linux
截图 / 录屏 / 日志 · Screenshots / Recordings / Logs
No response
提交前确认 · Pre-submission checklist
问题类别 · Category
UI / 界面体验 · UI / UX
涉及的 Agent 框架 · Agent framework
ZCode Agent(自研)
严重程度 · Severity
影响体验 · Major (功能可用但体验受损 / works but degraded)
复现频率 · Reproducibility
必现 · Always
问题描述 · Description
Bug: Edit tool drops file permissions (executable bit) on edit
Task ID: sess_401e6b50-fd6b-411d-af7d-d006f1b3afa7
Workspace: /home/gr/supreme-mcp-tools
复现步骤 · Steps to reproduce
Issue
The Edit tool silently resets file permissions when modifying an existing file.
Editing a file with the executable bit (
chmod +x, git mode100755) producesa non-executable file (
100644) after the edit, with no warning. This breaksexecutable scripts invoked directly via shebang.
Example (this task): I edited
launchmcp.py(committed as100755,invoked as
./launchmcp.pyfrom mystartlauncherwrapper) andstartlauncher(also
100755). After the edits, both became non-executable, and./startlaunchersilently failed with "Permission denied". The user had todiagnose and
chmod +xby hand.Reproduction:
Why it matters: Silent (no warning), breaks executable scripts, and the
regression can be committed to git permanently.
Likely root cause: The Edit tool appears to write content to a temp file
then atomically
os.replace(tmp, target). I verified this mechanism reproducesthe symptom:
os.replacereplaces the target inode, so the target's existingmode/owner/ACLs are lost — the file ends up with the temp file's mode. By
contrast, in-place rewrite (
open(path,'w'), which truncates the existinginode) preserves mode. Confirmed:
os.replacedrops0o755→0o600;open(w)keeps
0o755.Suggested fix: Preserve the target's mode across the atomic write:
Affects any file whose mode differs from umask default (read-only
0o444,group-writable, ACL-restricted), not just executable scripts.
Expected result
Editing a
100755file should leave it100755— the executable bit (and allfile metadata) must survive edits.
git diff --summaryshould show nomode changeafter an edit, and the file should remain directly executablewith no manual
chmod +x. If atomic temp+replace write is needed for crashsafety, call
shutil.copystat(old, tmp)before the replace.Env: Linux Debian 13, Python 3.13.5, umask 0002. Observed on two executable
scripts (
launchmcp.py,startlauncher) committed as100755, dropped to100644/664after one Edit each.期望表现 · Expected behavior
When editing files, the file permissions should be kept as they are. Opencode, github copilot, codex and claude code do that.
实际表现 · Actual behavior
when editing files, file permissions are reset so an executable script is not longer executable (that's annoying)
ZCode 版本 · ZCode version
3.2.5
设备 / 系统 / 浏览器 · Device / OS / Browser
Linux
截图 / 录屏 / 日志 · Screenshots / Recordings / Logs
No response