---
status: fixing
trigger: "Adding a non-serial product to a card that already has a serial unit causes the serial unit to lose its product image and serial number label."
created: 2026-04-13T00:00:00Z
updated: 2026-04-13T00:01:00Z
symptoms_prefilled: true
---

## Current Focus

hypothesis: CONFIRMED — `on_lookup_item_selected` optimistic update at line 4191 calls `build_item_squares_from_str_slice`, extracting only display_name strings from existing squares and rebuilding name-only, destroying serial_id/has_image data. The fix replaces the optimistic update with a SQLite-backed `build_item_squares_from_vm` rebuild (same pattern as `on_lookup_create_confirmed`).
test: Build succeeded (no errors). Awaiting human UAT verification.
expecting: After adding a non-serial product, serial unit retains image and serial label.
next_action: Human UAT verification.

## Symptoms

expected: When adding a non-serial product (e.g., "Restotama") to a card that already has a serial unit (e.g., "001" of "BS2H Prototype"), the serial unit should retain its product image and "001" serial label.
actual: After adding the product, the serial unit shows "BP" initials and "BS2H Pr..." text with no image. The serial_id and has_image fields become empty/false on the item square.
errors: No error messages — the rebuild silently produces wrong data.
reproduction: 1. Open lookup modal on Marty Aldridge's card (which has serial unit 001 of BS2H Prototype with product image). 2. Add "Restotama" product (a non-serial product). 3. Observe serial unit 001 loses its image and serial label.
started: Broken through multiple fix attempts.

## Eliminated

- hypothesis: apply_filters overwrites item squares with name-only data
  evidence: apply_filters reads from all_cards (in-memory CardData slice) and pushes it to window.set_cards — it does not rebuild item squares itself.
  timestamp: 2026-04-13T00:01:00Z

- hypothesis: EditCommand::AddItem / dispatch triggers a UI rebuild
  evidence: dispatch calls live_client.add_item which calls service::api::items::add_item_to_package — operates only on the legacy in-memory Repository, no UI model writes.
  timestamp: 2026-04-13T00:01:00Z

- hypothesis: on_lookup_create_confirmed is the broken path
  evidence: on_lookup_create_confirmed already does build_item_squares_from_vm correctly at line 4360. Bug is in on_lookup_item_selected.
  timestamp: 2026-04-13T00:01:00Z

## Evidence

- timestamp: 2026-04-13T00:00:00Z
  checked: All call sites of build_item_squares_from_product_names and build_item_squares_from_str_slice
  found: build_item_squares_from_str_slice called at line 4191 inside on_lookup_item_selected "optimistic update" block.
  implication: The optimistic update extracts only display_name strings from existing squares, appends the new product name, then rebuilds — destroying serial_id and has_image on all squares.

- timestamp: 2026-04-13T00:01:00Z
  checked: on_lookup_item_selected full flow (lines 4162-4229)
  found: After the optimistic update corrupts in-memory CardData, there is no subsequent build_item_squares_from_vm call to restore the rich data. The SQLite persist block correctly writes product_refs_json, but the in-memory data is never re-read. apply_filters then uses the corrupted in-memory data.
  implication: The fix must replace the optimistic update with a SQLite-backed rebuild (build_item_squares_from_vm), same as on_lookup_create_confirmed.

- timestamp: 2026-04-13T00:02:00Z
  checked: Build output after applying fix
  found: Compiled successfully — no errors, only pre-existing unrelated warnings.
  implication: Fix is syntactically correct.

## Resolution

root_cause: on_lookup_item_selected (line ~4178) performed an optimistic update using build_item_squares_from_str_slice, which extracted plain display_name strings from existing item squares and rebuilt them name-only — silently discarding serial_id and has_image on any serial unit squares already on the card.
fix: Removed the optimistic update. After persisting to SQLite, rebuild item squares via build_item_squares_from_vm (reading from the freshly upserted SQLite row), preserving all rich data. Pattern matches on_lookup_create_confirmed.
verification: Build succeeded. Awaiting human UAT.
files_changed: [crates/app/src/main.rs]
