# Phase 20.1.1.1: Phase 20.1.1 Gap Closure - Context

**Gathered:** 2026-04-15
**Status:** Ready for planning
**Source:** User UAT on Phase 20.1.1 live build (2026-04-15). All decisions below are locked by the user.

<domain>
## Phase Boundary

Close 9 defects + 2 redesign gaps surfaced during UAT of Phase 20.1.1:

**Defects:**
1. Recipient tab layout regression — sidebar replaced the recipient card grid instead of rendering alongside it.
2. Sidebar disappears when user leaves and returns to the Recipients tab.
3. Clicking a card's recipient name no longer shows the sidebar on the destination tab.
4. Notes popover row heights are static — multi-line notes bleed out the row box.
5. Posting a note from the composer instantly closes the popover (should stay open).
6. Notes popover loads notes with visible delay (should render from SQLite instantly).
7. Posting a note renders two entries for one real note: one with the author's GH handle and one labeled "You" (dedup bug in optimistic union).
8. Recipient-detail sidebar has a visible X close button (should not exist — dismissal is via tab-switch or breadcrumb).
9. Product-add (`+`) button has a lopsided vertical offset and is permanently visible (should only appear on card hover, same as pre-20.1.1 behavior).

**Redesigns:**
10. Move the sidebar's `Purpose` field to a colored pill beneath the recipient name. Pill color matches the recipient-ring color used on cards and recipient squares. No label. Pill is click-to-inline-edit.
11. Relocate `Discord username`, `Shopify email`, and `Shopify customer` to the TOP of the recipient-detail sidebar (above everything else except the header + new Purpose pill).

**In scope:**
- All 11 items above.
- Validation via BUGSWEEPER (endpoint-driven state checks) AND the screen-timelapse MCP tools (visual confirmation).

**Out of scope:**
- Any new features beyond restoring intended Phase 20.1.1 behavior + the two redesigns.
- Edit/delete of notes (still deferred from Phase 20.1.1).

</domain>

<decisions>
## Implementation Decisions

### Recipient Tab — Sidebar Coexistence (Defects 1-3)
- **D-01:** The Recipients tab shows the recipient card grid (previous pre-20.1.1 behavior). Selecting a recipient tile opens the recipient-detail sidebar AS AN OVERLAY ALONGSIDE the grid, not in place of it. The grid stays visible (shrinks width to accommodate the sidebar, same pattern as the product-detail sidebar in the Products tab).
- **D-02:** Sidebar visibility state persists across tab switches as long as a recipient is selected. Leaving and returning to the Recipients tab while `selected_recipient_*` is populated must re-show the sidebar automatically.
- **D-03:** Dashboard card name-click continues to navigate to the Recipients tab AND auto-open the sidebar — the landing state must also include the grid behind the sidebar (same coexistence model as D-01).

### Notes Popover Stability (Defects 4-7)
- **D-04:** Each note row height is content-driven, not fixed. Rows grow to fit their wrapped text; the scrollable list remains clipped to the popover's outer height. No content bleed.
- **D-05:** Posting a note keeps the popover open. The composer collapses back to the "Add note" button, the new note appears at the top of the list, and the popover itself stays visible until the user dismisses it (Esc / click-outside / explicit close).
- **D-06:** Notes render from SQLite IMMEDIATELY on popover open — no visible loading gap. The targeted GH comment fetch runs in the background and reconciles via `upsert_notes_for_card`; the Slint model patches in place when the fetch returns. Follows the rest of the app: SQLite is source of truth, background sync updates, UI never blocks.
- **D-07:** Optimistic UI must dedup. The author label for a user-posted note is the user's GH handle (same string the GH comment comes back with), not "You". Remove the "You" fallback — or key the optimistic entry on something reconcilable with the fetched comment so the two rows collapse into one. Correct end-state: exactly one row per real ww-note comment.

### Sidebar Structure (Redesigns 10-11, Defect 8)
- **D-08:** Remove the X close button from the recipient-detail sidebar. Dismissal is implicit via tab-switch or breadcrumb-back. The `close-clicked` callback and wiring go with it.
- **D-09:** Purpose moves from a labeled field to a colored pill directly under the recipient name in the sidebar header. Pill background color matches the recipient's ring color (same color source used on cards and recipient tiles). No "Purpose:" label. Clicking the pill enters inline edit (reuse existing inline-edit pattern; TextInput replaces the pill body while editing).
- **D-10:** Field order at the top of the sidebar (below header + Purpose pill, above existing Rx/notes area): (1) Discord username (inline-edit, as today), (2) Shopify email (display only), (3) Shopify customer link. These three fields must be the FIRST content block after the header.

### Card Face — Product Add Button (Defect 9)
- **D-11:** The product-add (`+`) button (on the card's product grid) is hidden by default and shown only while the cursor is hovering the card. Restore the hover-only visibility behavior that existed before Phase 20.1.1's Row 5 removal.
- **D-12:** The `+` button's vertical alignment must match the product squares in its row (no lopsided offset — currently it sits higher/lower than its neighbors because Row 4's grown geometry broke an implicit alignment).

### Validation (Mandatory)
- **D-13:** All fixes must be validated via BUGSWEEPER (`crates/bugsweeper/GUIDE.md`) endpoints AND via the `screen-timelapse` MCP tools (visual confirmation). A validation task at the end of the phase MUST exercise each of D-01..D-12 and record evidence (screenshots + endpoint snapshots) in a VALIDATION.md or similar artifact.
- **D-14:** Any mutations made during the BUGSWEEPER session MUST be reverted before the session ends (per `CLAUDE.md` BUGSWEEPER session rule).

### Claude's Discretion
- Exact width of the recipient sidebar overlay (match product-detail sidebar's width unless visual balance requires otherwise).
- Whether to keep the `close-clicked` callback plumbing in place (stubbed) or delete it entirely.
- The precise rendering of the colored pill (corner radius, padding, font size) — keep it consistent with existing tag/chip styling in the project if any exists; otherwise keep it tight and small.
- Whether the optimistic-dedup strategy is (a) replace the "You" row with the real row when the fetch returns, (b) post with the user's known GH handle from config and key on (author, content, near-timestamp), or (c) something equivalent. Pick whichever is cleanest given existing auth/config wiring.
- Exact row-growth mechanism in the Slint popover (content-height measurement + list re-layout, or a simpler per-row `preferred-height` + `min-height` approach).

</decisions>

<canonical_refs>
## Canonical References

**Downstream agents MUST read these before planning or implementing.**

### Phase 20.1.1 Artifacts (PREDECESSOR — required reading)
- `.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 from the parent phase (defines the intended behavior).
- `.planning/phases/20.1.1-change-the-i-button-on-cards-to-show-a-scrollable-history-of/20.1.1-RESEARCH.md` — Slint popover + SQLite read path research.
- `.planning/phases/20.1.1-change-the-i-button-on-cards-to-show-a-scrollable-history-of/20.1.1-PATTERNS.md` — analog-file patterns (product-detail sidebar, tile-click wiring).
- `.planning/phases/20.1.1-change-the-i-button-on-cards-to-show-a-scrollable-history-of/20.1.1-VERIFICATION.md` — the 7 UAT-gated items that this phase now closes plus the ones discovered during UAT.
- `.planning/phases/20.1.1-change-the-i-button-on-cards-to-show-a-scrollable-history-of/20.1.1-08-SUMMARY.md` — Plan 08 details: where sidebar is mounted today (the `if root.show-option-grid && root.show-recipient-grid && root.recipient-detail-visible` guard that is too restrictive — causes defects 1 and 2).

### Data Architecture (MANDATORY per CLAUDE.md)
- `.planning/DATA-FLOW.md`
- `code_tips/SQLITE_TIPS.md`
- `code_tips/SLINT_TIPS.md` (row-height and list layout gotchas)

### UI (primary modification targets)
- `crates/app/ui/recipient-detail.slint` — sidebar component. Purpose field, header layout, close-button, field order.
- `crates/app/ui/dashboard.slint` — sidebar mount condition, Recipients tab layout, grid-width coexistence binding.
- `crates/app/ui/option-grid.slint` — recipient tile click, ring-color source (used by D-09 pill).
- `crates/app/ui/card.slint` — notes popover row height (D-04), composer-close-on-submit logic (D-05), product-add button visibility + alignment (D-11, D-12).
- `crates/app/ui/tokens.slint` — color tokens for the colored pill (reuse ring color path).

### Data + Sync
- `crates/app/src/live_client.rs` — `fetch_notes_for_card` + callback that refreshes the model. D-06 (immediate SQLite render + background fetch) lives here.
- `crates/app/src/main.rs` — `on_card_notes_popover_opened`, `on_card_post_note`, `on_card_name_navigate`, `populate_recipient_sidebar`, `on_sidebar_close`. Defects 3, 5, 6, 7, and 8's cleanup are in this file.
- `crates/app/src/dashboard/view_model.rs` — `NoteDisplayEntry.from_note_entry` with the `"You"` fallback (D-07 dedup source).

### Validation Tooling (MANDATORY per D-13)
- `crates/bugsweeper/GUIDE.md` — endpoint reference for state queries, property reads, callback invocations.
- `screen-timelapse` MCP (already available in this session) — visual capture.

</canonical_refs>

<code_context>
## Existing Code Insights

- Sidebar mount condition in `dashboard.slint` currently gates visibility on `show-option-grid` being true — too narrow. When a recipient is selected AFTER the grid has scrolled or when the tab is re-entered, the visibility binding reports false.
- `populate_recipient_sidebar` is the reusable helper from Phase 20.1.1-08; call it from both tile-click (existing) and tab-restore / name-click paths (new / re-fix).
- `apply_filters` is the current refresh hook after `save_note` — it re-renders the full card list, which collapses the popover because the `notes-popup` is bound to a single `RecipientCard` instance. Keep the note-model patch but scope it so the popover doesn't lose its open state.
- `format_relative_time` returns `"just now"` for t < 60s; the `"You"` author label is added in `from_note_entry` when `author: None`. Both are the origin of the twin-row bug.
- Product-add (`+`) button visibility rule existed pre-Phase 20.1.1 — check git history of `card.slint` pre-commit `3fe30c1` (Plan 06 Row 5 removal) for the exact `visible: parent.has-hover` binding to restore.

</code_context>

<specifics>
## Specific Ideas

- Coexistence width pattern: the Products tab already does this for `ProductDetailPanel` (`RecipientGrid.width: parent.width - 330px` when sidebar visible). Clone that exact approach for Recipients.
- Colored pill should read from the same source as the card's colored ring — one code path, one color token per recipient.
- Dedup strategy recommendation (Claude's discretion, but this is the cleanest): on post, construct the optimistic NoteDisplayEntry with `author_display = <user's GH handle from config>` (same string the GH fetch will return). When the targeted fetch reconciles via `upsert_notes_for_card`, the SQLite row's `author` matches and `read_notes` returns exactly one row.
- D-05 fix: after `save_note` + `apply_optimistic_note_update`-equivalent, do NOT call `apply_filters` (which re-renders the full card list, collapsing the popup). Instead, patch just the `notes` Slint model on the affected card — mirror what `on_card_notes_popover_opened` already does for the GH-fetch callback.
- Validation evidence goes in a new `20.1.1.1-VALIDATION.md` (BUGSWEEPER endpoint output + timelapse screenshot paths referenced by filename).

</specifics>

<deferred>
## Deferred Ideas

(None — this phase is focused strictly on closing the 20.1.1 UAT gaps.)

</deferred>

---

*Phase: 20.1.1.1-phase-20-1-1-gap-closure-recipient-tab-sidebar-coexistence-n*
*Context captured: 2026-04-15 (from user UAT; no further discuss-phase needed — decisions locked)*
