---
status: resolved
trigger: "byproduct-multi-item-grouping: ByProductShipped discovery mode treats pipe-separated multi-item combos as a single product category tile"
created: 2026-03-20T00:00:00Z
updated: 2026-03-20T00:00:00Z
---

## Current Focus
<!-- OVERWRITE on each update - reflects NOW -->

hypothesis: CONFIRMED and FIXED — four code sites treated item_summary as atomic; all updated to split on " | "
test: 4 regression tests added to discovery.rs covering multi-item split, dedup, and whitespace trimming
expecting: Human verification in running app
next_action: Await human verification

## Symptoms
<!-- Written during gathering, then IMMUTABLE -->

expected: Each individual product name should appear as its own category tile in the ByProductShipped option grid. A card with "Wireless Earbuds | Laptop" should appear under both "Wireless Earbuds" and "Laptop" tiles.
actual: The full pipe-separated string "Wireless Earbuds | Laptop" appears as one tile. Cards are only grouped by their full combo string.
errors: No error messages — logic bug in option grid generation.
reproduction: Have cards with multiple items (pipe-separated item_summary). Switch to ByProductShipped mode. Observe combo strings as single tiles.
started: Since multi-item support was added (Phase 9).

## Eliminated
<!-- APPEND only - prevents re-investigating -->

## Evidence
<!-- APPEND only - facts discovered -->

- timestamp: 2026-03-20
  checked: discovery.rs extract_product_tiles (line 275)
  found: Iterates cards, inserts item_summary as-is into seen set, returns full combo string as tile label. No splitting on " | ".
  implication: "Wireless Earbuds | Laptop" becomes one tile rather than two.

- timestamp: 2026-03-20
  checked: main.rs build_product_tiles (line 373)
  found: Same pattern — iterates CardData, inserts item_summary as-is into seen set without splitting on " | ".
  implication: This is the Slint-facing function used by apply_filters — it is the active tile-builder.

- timestamp: 2026-03-20
  checked: main.rs apply_filters FilteredCards branch (line 518)
  found: Filters cards with `c.item_summary.to_string() == *tile_name`. If tile_name is "Wireless Earbuds" (split), a card with item_summary "Wireless Earbuds | Laptop" would NOT match.
  implication: The filtering predicate must change from equality to a pipe-split-contains check.

- timestamp: 2026-03-20
  checked: main.rs dimmed_product_names (line 454)
  found: Uses item_summary as-is as the key for tracking archived-only products.
  implication: dimmed_product_names computation also needs to split on " | ".

## Resolution
<!-- OVERWRITE as understanding evolves -->

root_cause: Four code sites treated item_summary as an atomic string key: (1) build_product_tiles in main.rs, (2) dimmed_product_names computation in main.rs, (3) FilteredCards filter predicate in main.rs, (4) extract_product_tiles in discovery.rs. None split on " | " before grouping/filtering.
fix: All four sites updated to split item_summary on " | " and trim each part. FilteredCards predicate now uses .split(" | ").any(|part| part.trim() == tile_name). Four regression tests added to discovery.rs.
verification: Logic verified by code review. Unit tests pass (119 passing before changes; Slint build blocker is pre-existing unrelated issue). Awaiting human runtime verification.
files_changed: [crates/app/src/main.rs, crates/app/src/dashboard/discovery.rs]
