iOfficeAI / iOfficeAI/OfficeCLI
resident: flush silently overwrites external writes to the same file (lost update, no warning)
- Dominant language
- C#
- Stars
- 30.7k
- Forks
- 2.1k
- Avg merge
- 9d 8h
- Merged PRs (30d)
- 5
Description
**Version:** 1.0.148 (macOS arm64)
### Summary
While a resident holds a document, any change another program makes to that file on disk is silently discarded by the resident's next flush (`save` / `close` / idle auto-flush). No warning, no error, no non-zero exit — the external write just disappears.
The docs cover the *read* side of this well (`save`: "a direct disk reader sees the pre-edit file until a flush") and `create` already refuses a resident-held file with `file_locked`. The *write* side has no equivalent protection.
### Repro
```bash
officecli create b.docx
officecli close b.docx
officecli open b.docx
officecli add b.docx /body --type p --prop text="FROM_OFFICECLI"
# an external program edits the same file on disk while the resident is live
python3 - <<'PY'
import zipfile, shutil
zin = zipfile.ZipFile('b.docx'); zout = zipfile.ZipFile('b.tmp', 'w', zipfile.ZIP_DEFLATED)
for it in zin.infolist():
d = zin.read(it.filename)
if it.filename == 'word/document.xml':
d = d.decode().replace('', 'FROM_EXTERNAL').encode()
zout.writestr(it, d)
zout.close(); zin.close(); shutil.move('b.tmp', 'b.docx')
PY
python3 -c "import zipfile;x=zipfile.ZipFile('b.docx').read('word/document.xml').decode();print('before flush -> external:',x.count('FROM_EXTERNAL'))"
officecli close b.docx
python3 -c "import zipfile;x=zipfile.ZipFile('b.docx').read('word/document.xml').decode();print('after flush -> officecli:',x.count('FROM_OFFICECLI'),' external:',x.count('FROM_EXTERNAL'))"
```
### Actual
```
before flush -> external: 1
after flush -> officecli: 1 external: 0
```
### Expected
The flush should notice the file changed underneath it and refuse, rather than overwrite.
### Why this one hurts
An agent that repairs a document with its own script (dedup, search/replace, XML surgery) verifies the result on disk, sees it is correct, and reports success — then the resident flushes and the repair is gone. There is no signal it could have checked.
Combined with the `raw-set` rollback issue this cost us a user's document: the dedup script ran correctly, and the flush put the duplicated section straight back.
### Suggested fix
Capture mtime/size/hash at open, and on flush refuse with a clear error when the file changed underneath — the same protective instinct `create` already has with `file_locked`. A `--force` style opt-out would cover the cases where clobbering is intended.
Contributor guide
Research direction
Reproduce the loss with the resident open/add and close commands, then trace the resident flush path and the existing create handling for file_locked. The change is done when a disk modification made after open is detected during save, close, or idle flush, produces a clear failure, and does not overwrite the external file.
Written by the indexing model from the issue text.
Assessment
- Tech stack
- csharp
- Domain
- cli
- Issue type
- Bug
- Difficulty
- 4/5
- Estimated time
- 3-5 days
- Activity status
- Active
- Clarity
- Mostly clear
- Newbie friendliness
- 55/100