Reference solution
The findings below are ordered by priority.
Findings
1. Merge discards concurrent additions instead of combining them
Location: src/replicated-cart.ts:50-59
The merge picks one materialized row by wall-clock timestamp. If two offline devices each add quantity, only the later clock's total survives instead of the two operation deltas commuting.
Persist and merge immutable operations by ID, then derive quantity from the union under the causal remove rules.
2. Removal leaves no tombstone and deleted products can resurrect
Location: src/replicated-cart.ts:21-24,52-59
Removing an item simply splices the local row. A remote replica with an older materialized row later contributes it back because there is no retained remove operation or observed vector to suppress it.
Persist the remove and its causal context until replica acknowledgement, and materialize only additions not suppressed by that tombstone.
3. Device wall clocks decide conflicts
Location: src/replicated-cart.ts:54-57
updatedAtMs values come from independent devices and are not causally comparable. Clock skew can make stale state permanently beat later operations, even apart from the lost-increment behavior.
Eliminate wall-clock last-write-wins and use replica sequence vectors plus operation identity.
4. Deduplication is process-local and precedes durable state
Location: src/replicated-cart.ts:7-17
The module-level set is neither cart-scoped nor persisted atomically with the item and vector. Restart forgets applied IDs and replays additions; a persistence failure after adding the ID can suppress a needed retry. The vector assignment can also regress progress when an out-of-order sequence is applied.
Store applied operation identity with each cart transaction, validate per-replica sequence progress, and update the durable vector atomically without regression.
5. Replicated client prices become order prices
Location: src/replicated-cart.ts:25-38, src/cart-checkout.ts:18-23
An operation's client-provided price is materialized and copied directly into the order. An offline or malicious client can submit stale or arbitrary prices and determine the checkout amount.
Treat replicated prices as display hints and fetch authoritative server catalog prices for the frozen checkout lines.
6. Checkout ignores its causal consistency requirement
Location: src/cart-checkout.ts:13-24
requiredVector and synchronize are both unused. Checkout can freeze a local cart that has not received operations the initiating client already observed, producing an order with missing or resurrected items.
Synchronize until the cart vector includes the required vector, then atomically freeze that operation set, validate positive quantities, and price it.
Reasonable non-findings
- Taking the component-wise maximum is the correct way to merge version-vector progress, but it cannot replace merging operations.
- Copying item arrays during local materialization avoids direct input-array mutation.
- A causally later add may intentionally reintroduce a previously removed product.