Fix/web app duplicate task title - #12266
Conversation
POST /v1/action-items hashed the description as an idempotency key, so a second task with the same title returned the original document (wrong due date, gone after reload). Honor an optional Idempotency-Key instead; titles are not unique. Failure-Class: none Co-authored-by: Cursor <cursoragent@cursor.com>
Co-authored-by: Cursor <cursoragent@cursor.com>
|
Thanks @Yny4ii — this is a clean fix for a real correctness bug, and the verification section is exactly how I wish every PR reported testing. What I verified
One decision for maintainers Removing the server-side title-hash fallback changes the contract for every client, and only the web app sends the key today:
The hash was originally added (#7093) to stop flaky-network retry duplicates from the desktop client, so until desktop/Flutter adopt the header, a network-level retry on those clients can again produce a duplicate. On net this is still the right trade — current behavior silently returns the wrong task on same-titled creates — but whether to merge ahead of client adoption or together with it is a maintainer sequencing call. The regenerated clients already support the header, so a follow-up on the desktop side is straightforward. Minor, non-blocking: the db idempotency lookup filters CI note The failing checks are not from this branch: by AI on behalf of David — if you need David’s attention urgently, please @Git-on-my-level and escalate with |
kodjima33
left a comment
There was a problem hiding this comment.
Real bug (title-hash idempotency collision) and clear fix, but diff exceeds the confidence-gate size ceiling and CI has multiple failing required checks, so approve-only per policy.
|
Follow-up on my earlier review — the head is unchanged ( State check since that review:
Label updates: removed by AI on behalf of David — if you need David’s attention urgently, please @Git-on-my-level and escalate with |
What changed and why
POST /v1/action-itemshashed the task title as an idempotency key, so creating a second task named like an existing one (123) returned the original document (same due date, gone after reload). Create is now idempotent only when the client sendsIdempotency-Key; the web app sends a per-submit UUID, and the proxy forwards it.Product invariants affected
none
How it was verified
BACKEND_UNIT_TEST_FILE_LIST=tests/unit/test_action_item_idempotency.py BACKEND_PYTEST_XDIST=0 ./test.sh→ 11 passedNODE_OPTIONS=--no-experimental-webstorage bunx vitest run --config vitest.config.mts src/lib/__tests__/actionItemList.test.ts→ 2 passed/tasksagainst this backend: production still hashes titles until this change is deployed. Could not confirm two same-title tasks persist after reload on a real account.Tests
Regression:
backend/tests/unit/test_action_item_idempotency.py::test_router_does_not_hash_description_as_idempotency_key— two POSTs with the same description and no header must both insert.Web defensive list merge:
web/app/src/lib/__tests__/actionItemList.test.ts— if the API still returns an existing id, the list replaces that row instead of prepending a ghost duplicate.Failure class (fixes)
Failure-Class: none