iOfficeAI / iOfficeAI/OfficeCLI

resident: flush silently overwrites external writes to the same file (lost update, no warning)

Open
#375 1 comment 0 reactions 0 assignees View on GitHub
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

Open the contributing 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

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.