# Phase 20.1.1.1.1: Round 2 Gap Closure - Context

**Gathered:** 2026-04-15
**Status:** Ready for planning
**Source:** User UAT round 2 on live build (2026-04-15). All decisions below are locked.

<domain>
## Phase Boundary

Close 13 defects discovered in round 2 UAT of Phase 20.1.1/20.1.1.1. Issues span UI (pill styling, sidebar navigation, button flicker), data (note dedup, stale Rx, wrong statuses), and sync (missing card, Shopify connectivity).

**In scope:**
- All 13 items below.
- code_tips entry for the hover-flicker fix (D-08).
- BUGSWEEPER + screenshot validation after fixes.

**Out of scope:**
- Features beyond restoring correct behavior.
- Note edit/delete (still deferred).

</domain>

<decisions>
## Implementation Decisions

### Role/Purpose Pill (Defects from UAT)
- **D-01:** The "Purpose" pill edit mode must be a **dropdown menu** (not a TextInput textbox). The choices are the limited set of roles defined in the GitHub Project — the same values the original summary-popup used. Derive the option list from the GH Project field definition or hardcode the known set.
- **D-02:** The pill position moves to: **right of the avatar, just below the recipient name** in the sidebar header row. Pill border: **2px hollow outline** (no background fill). The ring/border color uses the existing `purpose-color`. Still click-to-edit (opens dropdown).

### Sidebar Navigation (Defects from UAT)
- **D-03:** Clicking a recipient tile in the Recipients tab grid must: (1) show the **filtered card view** for that recipient (pre-20.1.1 behavior — navigate from tile grid to cards), AND (2) show the recipient-detail sidebar alongside those cards. Currently tile-click only shows sidebar on the tile grid without navigating to cards.
- **D-04:** Tab-restore (leaving and returning to Recipients tab) must preserve BOTH the filtered card view AND the sidebar. Currently the card view shows but the sidebar disappears.
- **D-05:** Esc must NOT close the sidebar while it's focused. Remove the FocusScope Esc → close-clicked wiring entirely (sidebar has no X button and no dismiss-on-Esc; it only closes via tab-switch or breadcrumb navigation).
- **D-06:** Breadcrumb-back to the Recipients tab home (tile grid) must dismiss the sidebar. Currently the sidebar stays visible when breadcrumb navigates back.

### Note Dedup (Defects from UAT — D-07 from 20.1.1.1 NOT working)
- **D-07:** Notes are STILL duplicating. The root cause is that `upsert_notes_for_card` keys on `(card_id, note_date, content)` — but the locally-saved note has `author: Some("SaberMage")` and `date: <local ISO timestamp>`, while the GH-fetched version has `author: Some("SaberMage")` and `date: <GH createdAt timestamp>`. The timestamps differ (local time vs GH server time), so the upsert treats them as different notes. **Fix: the upsert/dedup must key on `(card_id, content)` only, or `(card_id, author, content)` — NOT on date.** Alternatively, the locally-saved note can be keyed with a `pending` flag that is cleared when the GH comment is confirmed, and the display layer skips pending rows that have a matching confirmed row.

### Card Face — Product Add Button (Defects from UAT)
- **D-08:** The `+` button **flickers** when the cursor moves over it. This is a known Slint hover-zone issue: the button sits on top of the card-hover-zone TouchArea, and when the cursor enters the button it leaves the card-hover-zone's bounds (or the button's own TouchArea steals the hover), toggling visibility rapidly. **Fix:** identify the established solution from past occurrences in this codebase, record it in `code_tips/SLINT_TIPS.md`, then apply it. The typical fix is a `pointer-event: none` equivalent on the button's TouchArea or expanding the card-hover-zone to encompass the button.
- **D-09:** The `+` button vertical alignment is STILL wrong. The vertical offset doesn't match the product squares in its row. Look at the actual rendered position in the screenshots (Image #7) — the button sits too low relative to the product image squares. Fix the `y` value or the containing layout so the button center-aligns with the product squares.

### Stale Data (Defects from UAT)
- **D-10:** Rx OD/OS values displayed in the sidebar are stale — they show old values instead of what's currently in the GH Project row. Example: Marty's Rx OD should show "Testoooooo" but shows "Test7". This means `populate_recipient_sidebar` reads from a stale source. It should read from the latest SQLite data (which should have been synced from GH Project on the most recent sync cycle).

### New Issues (discovered during UAT, not from 20.1.1 scope)
- **D-11:** Product text on cards appears enlarged (from Row 4 growth in Plan 06), but product icons and unit serial number labels did NOT get proportionally enlarged. The icons and SN text should scale up to match the larger row.
- **D-12:** All return-status cards show "Return Created" but they have tracking numbers, which means they should show the next lifecycle status ("Return Underway"). The status derivation logic in `shopify_projection` or `compute_last_activity` may be incorrect.
- **D-13:** Most cards' product text line reads "No items added" even when the card has products assigned. The `item_display_label` field is empty/wrong for cards that clearly have `item_squares` populated.
- **D-14:** A card is MISSING entirely. There should be an unlinked card visible. Connection status is stuck at "Connecting..." even though the Shopify admin shows recent API activity. Shopify sync may be broken — investigate `run_sync_cycle` and the Shopify client connection. This is potentially the most critical issue.

### Claude's Discretion
- Dropdown implementation for the role pill: PopupWindow with a list of options, or a Slint `ComboBox` if available and fits the styling.
- Exact dedup strategy for notes — prefer the simplest approach that eliminates the timestamp-based false-negative match.
- Whether D-12/D-13/D-14 are Shopify sync bugs (same root cause) or independent issues.
- Whether D-14 warrants a separate investigation phase or can be diagnosed + fixed inline.

</decisions>

<canonical_refs>
## Canonical References

### Phase 20.1.1 and 20.1.1.1 Artifacts
- `.planning/phases/20.1.1-change-the-i-button-on-cards-to-show-a-scrollable-history-of/20.1.1-CONTEXT.md` — D-01..D-20 original decisions
- `.planning/phases/20.1.1.1-phase-20-1-1-gap-closure-recipient-tab-sidebar-coexistence-n/20.1.1.1-CONTEXT.md` — D-01..D-14 first gap-closure decisions
- `.planning/phases/20.1.1.1-phase-20-1-1-gap-closure-recipient-tab-sidebar-coexistence-n/20.1.1.1-05-SUMMARY.md` — main.rs wiring (tab-restore, targeted post-patch)

### Data Architecture
- `.planning/DATA-FLOW.md`
- `code_tips/SQLITE_TIPS.md`
- `code_tips/SLINT_TIPS.md` — MUST add hover-flicker fix here (D-08)

### UI Files
- `crates/app/ui/recipient-detail.slint` — sidebar (pill, close-button, Esc, field layout)
- `crates/app/ui/dashboard.slint` — sidebar mount, tab layout, grid coexistence
- `crates/app/ui/card.slint` — notes popover, product add button, item squares
- `crates/app/ui/option-grid.slint` — tile-click handler

### Data + Sync
- `crates/app/src/main.rs` — callbacks, populate_recipient_sidebar, tab-restore, breadcrumb-back
- `crates/app/src/live_client.rs` — fetch_notes_for_card, gh_user_login, run_sync_cycle
- `crates/app/src/dashboard/view_model.rs` — NoteDisplayEntry, item_display_label
- `crates/service/src/db/sqlite.rs` — upsert_notes_for_card (dedup key), read_notes
- `crates/integrations/src/github/issues_client.rs` — list_issue_comments
- `crates/app/src/dashboard/projection.rs` — Shopify status derivation
- `crates/service/src/sync/shopify_projection.rs` — status mapping, tracking state

</canonical_refs>

<specifics>
## Specific Ideas

- Note dedup: the simplest fix is to change the upsert key from `(card_id, date, content)` to `(card_id, author, content)`. This works because notes are append-only — same author + same content = same note. The date of the winning row can be updated to the GH-authoritative timestamp.
- Hover flicker: the fix in most Slint codebases is to ensure the hover zone and the conditional element don't create a feedback loop. Either (a) make the card-hover-zone cover the full card including the button area so hovering the button doesn't leave the zone, or (b) add a small `has-hover` delay/hysteresis, or (c) use a separate non-interactive overlay for the hover detection.
- D-14 (missing card + connecting): could be a Shopify API token expiry, rate limit, or the sync cycle encountering an error that silently fails. Check stderr output from the running app.

</specifics>

<deferred>
## Deferred Ideas

(None — this phase focuses on closing all remaining UAT gaps.)

</deferred>

---

*Phase: 20.1.1.1.1-phase-20-1-1-round-2-gap-closure-note-dedup-sidebar-nav-role*
*Context captured: 2026-04-15 from user UAT round 2*
