anthropics / anthropics/claude-code
Projects: concurrent sessions silently clobber each other's project_write — three losses on one doc in ninety minutes
- 主要言語
- Python
- スター
- 145k
- フォーク
- 23.1k
- PR マージ指標
- PR 指標を取得中
説明
## Summary
`project_write` is a whole-document replace with no compare-and-swap. When two or more sessions work the same project at once — the normal case for agent workloads — each reads, composes, and writes back its whole copy, and whichever lands last silently deletes everything the others added in between. Every session gets a success response. None of them can tell.
This is distinct from #87117 (closed as not planned), which was about one session composing an incomplete write. This is about **individually correct** writes destroying each other.
## What happened
On the night of 9–10 September 2026, one project doc — `claude/open-tasks.md`, a handover doc several sessions read and update — was rewritten whole **three times in about ninety minutes** by different sessions on the same account. Each rewrite was good and current on its own. Each deleted content added minutes earlier by another session: a live-source sweep recording which mailboxes, drafts, chat threads and scheduled tasks had been read, and when.
The document's uuid changed on each write (`f0793cd2` → `f8de655a` → `7a066ec8`), so nothing about the result looked like an overwrite.
It was found only because one session ran a custom check that greps that doc for those recorded reads. Without it the next session would have re-read four live sources for nothing — or, worse, assumed the unrecorded ones were quiet.
## Why client-side rules cannot close this
Every mitigation available to a session protects only the session using it:
- Reading immediately before writing narrows the window; it does not close it.
- A read-splice-PATCH inside a single call (what we do) is safe for that writer and invisible to a whole-file writer landing on top of it.
- An out-of-band mutex (we keep one in a GitHub repo) works only if every session opts in, and unattended runs cannot take it at all, because connector writes there block on an approval prompt nobody answers.
Nothing in the API lets a session say "write this only if the document is still what I read."
## Requested changes
1. **Compare-and-swap on write.** Return a version token from `project_read`, accept it as `if_version` on `project_write`, reject with a conflict when it no longer matches. This closes the class. The memory tool in this same product already works exactly this way.
2. **An append mode** (`project_append`, or `mode: "append"`), making the most common write — adding to a running log or handover doc — non-destructive by construction.
3. **A shrink/delta signal in the write response** (`bytes_before`, `bytes_after`), so a model can see it just removed 60% of a document and repair it in the next turn.
(1) alone would have prevented all three losses. (3) would have made all three visible immediately instead of by luck.
## Impact
Project docs are the cross-session memory Projects tells users to rely on, and cross-session concurrency is the workload agents are built for. Together they produce silent data loss on well-formed calls, with no conflict, no history and no recovery.
コントリビューションガイド
このリポジトリのコントリビューションガイドは索引されていません
調査の方向性
The issue names no implementation files or tests; start by locating the project_read and project_write entry points and their existing tests, then inspect the memory tool's version-token behavior. Done should be demonstrable through tests covering stale-version conflicts, append behavior, and write responses exposing bytes_before and bytes_after.
索引モデルが issue の本文から書いたものです。
評価
- 領域
- backend-api-design
- issue の種類
- バグ
- 難易度
- 5/5
- 見積もり時間
- 1週間以上
- 活発さ
- 活発
- 明瞭さ
- おおむね明確
- 初心者へのやさしさ
- 35/100