Repository navigation
feat(items): idempotency keys for patch writes (TASK-3585) - #2062
Merged
Merged
Conversation
…resolves only through its own tab (TASK-3585) A patch may carry an idempotency key (Idempotency-Key header, or op_key). The key is claimed per (workspace, user, key) before any write-path guard, with a lease, and its answer is recorded inside the write's own transaction, guarded by the claim token: done holds exactly when the write committed, so an expired claim committed nothing and may be taken over. A retry of the same request replays the answer (Idempotent-Replayed); a key reused for another request is 422; a live claim is 409 in flight; a refused write releases the key. When an open editor received the patch and never confirmed it, the key records the patch id and a boot-scoped reference to that connection. A retry goes only to that connection, whose tab remembers the patches it applied and re-acks one it is sent again; with it gone the answer is patch_outcome_unknown with the diff since, and nothing is re-planned, since an insert-shaped edit would keep its anchor elsewhere. The CLI sends a key per invocation and retries once with it on a transport error or 5xx. MCP 0.90 adds op_key and three codes. Claude-Session: https://claude.ai/code/session_01Q9WssCjFuHjSfLABoW4tmS
…ency_keys (TASK-3585) An older server ignores op_key, so the CLI's single same-key retry of a 5xx could apply a patch twice against a server that serves patch but not keys. The retry now requires the idempotency_keys capability. Claude-Session: https://claude.ai/code/session_01Q9WssCjFuHjSfLABoW4tmS
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
TASK-3585: idempotency keys for patch writes
As ruled on TASK-3585 (lead, day 91: all six asks, plus the §5 correction). Patch first; create, append, copy and push follow in their own PRs.
Mechanism
Idempotency-Keyheader, orop_keyin the body (excluded from the request hash).doneand the answer are written INSIDE the write's own transaction (ItemUpdate.AfterWriteTx), guarded by the claim token.donetherefore holds exactly when the write committed. A claim whose lease expired committed nothing, so a retry takes it over with a new token, and the stale writer's late completion matches no row, which rolls its write back.Idempotent-Replayed: true;idempotency_key_reused;idempotency_in_flightwithRetry-After;The unknown outcome (lead's §5 correction)
patch_idand a boot-scoped reference to that connection. Connection ids restart with the process, so a bare id could name another tab after a restart.ApplyExternalPatchOn). Its tab remembers the last 32 patch ids it applied, recorded even when its own ack was too late, and re-acks a repeat instead of applying it again.patch_outcome_unknownwithseq,base_seqand the diff since. Nothing is re-planned, because an insert-shaped edit keeps its anchor elsewhere.Storage
idempotency_keys:key,responseandbase_bodyare NUL-protected; ScanNUL figures 142/27;Surfaces
--op-keyoverrides), and ONE same-key retry on a transport error or 5xx, never on a 4xx.op_key, plus three allow-listed codes with hints shared and parity-tested. A reused key comes back as its own code, notvalidation_failed.idempotency_keys. Docs: api.md, mcp.md, instructions.md, both SKILL.md copies.Tests (each shown red by a mutant)
Gates (local, on the pushed head 8fc941e, rebased onto 78729db)
make lint: 0 issues.internal/serverfull: ok (579s, under the lock, before the rebase; the rebase brought in only fix(editor): bold, italic and strike always serialize to markdown that reads back (BUG-3583) #2057/fix(collab): a patch whose applier times out is not re-sent to another tab (BUG-3586) #2059/test(collab): the unstable-round-trip case no longer borrows a fixed serializer bug (main red) #2061/feat(items): pad item patch --undo, anchored on each edit's context (PLAN-3578 step 3) #2058, all already in this branch's stack).https://claude.ai/code/session_01Q9WssCjFuHjSfLABoW4tmS