Item Delete Bug #782 -- PROPAGATE Fork Fix
Status: In Progress
Ticket: Arda-cards/management#782
Repository: operations
Branch: jmpicnic/management-782
Symptom
Section titled “Symptom”A PUT /v1/item/item/{eId} with the default PROPAGATE qualifier creates a forked bitemporal version chain when the item has two or more supplies. The fork makes the item non-deterministic to read and permanently blocks deletion with the error:
400: Record[ITEM] cannot be deleted because it has a successor
Root Cause
Section titled “Root Cause”ItemService.update() creates the updated item record once, then iterates over the item’s supplies. Each updateFixed() call ends with parentUniverse.touch(), which reads the item tip and inserts a new record pointing to it as previous. Within the same database transaction, all touch() calls read the same pre-loop tip (because recorded_as_of = NOW() is identical for all records in the transaction) and insert separate successors pointing to the same parent — creating a diamond fork.
For an item with two supplies, three INSERT INTO item statements are generated in a single request. Two of them point to the same previous record, producing the fork.
Fix Design
Section titled “Fix Design”Add updateSupplyOnly() to ItemSupplyUniverse — identical to updateFixed() but omitting the final parentUniverse.touch(). Implement a private updateSupplyOnly() on ItemService.Impl that delegates to the universe method. Change the PROPAGATE supply loop in ItemService.update() to call updateSupplyOnly() instead of updateFixed().
The parent item record is already created correctly by updater() before the supply loop begins. The supply loop only needs to persist the individual item_supply child records.
Task Breakdown
Section titled “Task Breakdown”| # | Description | Status |
|---|---|---|
| 1 | Set up worktree and branch | Planned |
| 2 | Write regression tests (PROPAGATE fork detection + delete blocked) | Planned |
| 3 | Run regression tests, confirm expected failures | Planned |
| 4 | Implement production code fix | Planned |
| 5 | Run regression tests, confirm they pass | Planned |
| 6 | Full build | Planned |
| 7 | API tests | Planned |
| 8 | Commit, push, PR | Planned |
| 9 | Monitor CI | Planned |
In scope: updateSupplyOnly() method, ItemService.update() supply loop change, regression tests.
Out of scope: Changing recorded_as_of DEFAULT NOW() behavior in common-module; data cleanup of the existing forked item (handled by separate SQL scripts on the ticket); frontend changes.
Data Cleanup
Section titled “Data Cleanup”The affected tenant and item are identified in the ticket. Cleanup SQL scripts were posted as a ticket comment and are separate from the code fix.
Copyright: © Arda Systems 2025-2026, All rights reserved