anthropics / anthropics/claude-code
[BUG] Account Skills have no read-back and no compare-and-swap, so concurrent sessions silently destroy each other's edits
- Lingua principale
- Python
- Stelle
- 145k
- Fork
- 23.1k
- Metriche di merge delle PR
- Metriche PR in attesa
Descrizione
## Summary
Updating an account Skill is a whole-file replace with **no way to read the stored body back** and **no concurrency control**. Two sessions editing the same skill on the same day will silently overwrite one another, with no error on either side and no way to notice except by luck.
This happened **three times in two days** on my account (25–26 Aug 2026), on two different skills, to sessions that were each individually doing everything correctly.
## Steps to reproduce
1. Session A copies the synced skill body, adds a section, uploads it (via the claude.ai skills endpoints — `rename-skill` aside, `upload-skill`, `delete-skill` the aside copy, since there is no update verb).
2. Session A verifies: the skill is listed, `updated_at` is fresh, the synced mirror matches byte for byte. Correct at that moment.
3. **Two hours later**, Session B — which took its base *before* A's upload — adds its own section and uploads.
4. A's section is gone. No error is surfaced to either session. Nothing on the account records that a version ever existed with A's text in it.
## What makes it undetectable rather than merely racy
- **No read endpoint.** `GET skills/{id}`, `skills/get-skill`, `skills/download-skill`, `skills/{id}/files` all 404. The account holds no readable copy of a skill's body, so a session cannot diff its intended write against what is actually stored — not before writing, and not after.
- **No compare-and-swap.** `upload-skill` takes no `sha`, `if-match`, `base_version` or equivalent. There is no way to say "write this only if the stored copy is still the one I edited".
- **No version history.** Once overwritten, the previous body is unrecoverable from the platform.
- **The local synced mirror is not a substitute.** It lags by minutes (sometimes much longer), and on some surfaces it is a session-start snapshot that never refreshes, so a stale read there is indistinguishable from a lost write.
- **`updated_at` moving proves a write landed, not whose.** Both sessions see a fresh timestamp after their own upload.
## Why this matters more for skills than for ordinary files
Skills are the mechanism by which an agent's own operating rules persist. A silently lost skill edit is a corrected behaviour that quietly reverts — the failure resurfaces later as the agent repeating a mistake it had already been taught not to make, with nothing in the record explaining why.
## What would fix it, in order of preference
1. **A read endpoint** returning the stored `SKILL.md` (and ideally the bundled `references/`, `scripts/`). This alone makes the problem detectable, which is most of the value.
2. **Compare-and-swap on upload**: accept an opaque version token from `list-skills` and reject the write with `409` if the stored version moved. GitHub's `sha` parameter on contents writes is the exact shape.
3. **Version history**, so an overwrite is recoverable rather than terminal.
4. **A true update verb.** Today the only route is rename-aside → upload → delete, which widens the window and strands a `-prev` copy whenever a session dies mid-sequence.
## Current workaround, for anyone hitting this
An external mutex (a lock file in a private repo, created without a `sha` so creation is an atomic test-and-set), plus re-reading the synced mirror immediately before packaging, plus a content check after upload — and when the check fails, rebasing onto the other session's version rather than re-uploading, since re-uploading turns one lost edit into two. It narrows the window to seconds; it cannot close it, because the platform exposes nothing to close it with.
## Related
- #87071 — no in-session way to save/update a skill, and no confirmation that a save happened. Neighbouring but distinct: that one is about the *absence of a save path*; this one is about the save path that does exist being unsafe under concurrency and unverifiable afterwards.
Guida per i contributori
Nessuna guida per i contributori indicizzata per questo repository
Direzione di ricerca
Start by reviewing the reported claude.ai skills endpoints: upload-skill, rename-skill, delete-skill, and the listed read routes that return 404. Compare the requested read-back, compare-and-swap, version-history, and update-verb options, then define which concurrency guarantee and observable success or conflict behavior would count as done.
Scritto dal modello di indicizzazione a partire dal testo della issue.
Valutazione
- Ambito
- api, backend
- Tipo di issue
- Bug
- Difficoltà
- 5/5
- Tempo stimato
- Più di una settimana
- Stato di attività
- Attiva
- Chiarezza
- Specificata chiaramente
- Idoneità per principianti
- 35/100