Appearance
Backend PR: https://github.com/snag-tcg/tcg-collector-backend/pull/495 Issue: https://github.com/snag-tcg/tcg-collector-backend/issues/494
The new authenticated POST /collection/mutations lets a client retry collection autosave after a timeout or lost response without adding the same copies twice. The request carries an owner-scoped operationId, a type (add, patch, delete, restore), and the existing holding/patch payload or item UUID. Successful replies are 200 { item, replayed }; delete returns null, the other types return the existing holding format. Repeated normalized requests replay the original response; conflicting key reuse returns 409.
PATCH accepts market condition and variant annotations in addition to quantity, favourite, tags and note. Condition uses the existing enum and accepts null to clear; variant uses the existing printing enum and cannot be null. Omitted fields are preserved. The shared legacy item PATCH route also accepts these annotations.
One transaction locks the user's row, checks the receipt and quota, changes the holding, and records a real event with private receipt metadata. A failed receipt rolls back every holding change. Existing endpoints, collection identity, stacking, soft deletion/restoration and plan limits are preserved. Receipt metadata never appears in collection history. There is no schema migration or dependency change.
Verification
Observed individual TDD Red/Green cycles cover validation, initial persistence, replay, conflicting reuse, simultaneous retries, stacking, each mutation type, quotas, foreign-key errors and private history metadata. Six overlapping retries produced six writes before serialization and one write plus five replays afterwards.
- Condition and variant parser tests first failed with unknown patch keys; their PostgreSQL tests then observed the old values until the SQL assignments were added.
- Node 24.19.0: 1,191 API tests and 553 ingest tests passed, without skips.
- Dedicated PostgreSQL 16 integration: 29 tests passed, including a lost real HTTP response after commit, rollback on all write paths, concurrent same/conflicting keys, cross-user same-key isolation, quotas, market annotation validation/replay and legacy endpoint compatibility.
- API production build and TypeScript checks for production/tests/scripts passed.
- New test files are explicitly wired into the unit and E2E scripts.
- Full Docker E2E/Compose was unavailable locally because the Docker daemon pipe was missing. The feature's integration tests ran on a temporary loopback-only PostgreSQL instance outside the repository, using disposable schemas.
- GitHub CI passed all six jobs on final backend commit
0b1f376, including the full API E2E suite with the 29 mutation integration tests, ingest integration and Compose smoke test (run39049481693).
Scope And Retry Rules
Mobile persistence and strict startup loading are implemented separately. The operation must be stored against its owner before sending. Network errors, timeouts and 5xx retain the original operation ID because commit may already have succeeded. 429 backs off with the same key; 401 can resume after authentication. Invalid input, quota rejection and a rolled-back transaction do not reserve the operation ID.
New clients must use mutations consistently to obtain serialization guarantees. Legacy write routes remain compatible. Receipt lookup uses the existing owner index and scans that owner's history, with no TTL. Future retention changes must preserve receipts for pending retries. A replay may contain an older snapshot and must not overwrite newer local state.
Implementation and tests did not access the VPS or perform a production migration or user-data operation. Both PRs target staging. Any subsequent API-only deployment requires separate explicit approval and preserves the existing runtime configuration.