---
phase: 15-sqlite-foundation-deprecated-field-cleanup-and-ui-polish-gap-closure
verified: 2026-03-22T00:00:00Z
status: passed
score: 11/11 must-haves verified
re_verification: false
---

# Phase 15: SQLite Foundation, Deprecated Field Cleanup, and UI Polish Gap Closure — Verification Report

**Phase Goal:** SQLite foundation, deprecated field cleanup, and UI polish gap closure
**Verified:** 2026-03-22
**Status:** PASSED
**Re-verification:** No — initial verification

---

## Goal Achievement

### Observable Truths

| #  | Truth                                                                              | Status     | Evidence                                                                         |
|----|------------------------------------------------------------------------------------|------------|----------------------------------------------------------------------------------|
| 1  | `github_profile_url` does not appear in any Rust struct or construction site       | VERIFIED   | No `pub github_profile_url` or `github_profile_url:` in any .rs file; remaining occurrences are string key lookups in project_mapping.rs (legitimate — reading GH column) and comments in sqlite.rs |
| 2  | `shipment_status` and `tracking_state` do not appear on the Recipient struct       | VERIFIED   | `grep -n "shipment_status\|tracking_state" crates/core/src/domain/recipient.rs` returns zero results |
| 3  | All existing tests compile and pass after field removal (Plan 01)                  | VERIFIED   | `cargo test --workspace` all test suites show 0 failed |
| 4  | `item_summary` string field does not exist on any struct (replaced by product_names Vec<String>) | VERIFIED   | Not present in RecipientCardSnapshot, RecipientSnapshot, or DashboardCardViewModel as a struct field; only occurrence is a test function name `filter_by_search_matches_item_summary` (not a field) |
| 5  | `latest_note Option<String>` does not exist on any struct (replaced by notes Vec<NoteEntry>) | VERIFIED   | Not present in RecipientCardSnapshot, RecipientSnapshot, or DashboardCardViewModel; Package.latest_note still exists (correct — Package is a legacy in-memory struct, not a cleaned target) |
| 6  | `first_item_image_hint` does not exist on any struct                               | VERIFIED   | Zero results for `first_item_image_hint` in struct positions across all .rs files |
| 7  | `item-summary` and `image-hint` properties do not exist in any .slint file        | VERIFIED   | `grep -n "item-summary\|image-hint" crates/app/ui/card.slint crates/app/ui/dashboard.slint` returns zero results |
| 8  | Card body uses VerticalLayout for content-driven height                            | VERIFIED   | card.slint line 202: `VerticalLayout` with `spacing: 4px` for card body |
| 9  | Toast background is conditional on toast-is-warning property                       | VERIFIED   | dashboard.slint line 658: `background: root.toast-is-warning ? Colors.toast-warning-bg : Colors.toast-info-bg;` |
| 10 | App data survives a restart without fetching from GitHub or Shopify                | VERIFIED   | LiveClient reads from SQLite via `read_all_cards()` on startup; sync writes to SQLite via `upsert_card()`; db at `%APPDATA%/WITwhat/witwhat.db` |
| 11 | SQLite schema contains tables for all DATA-FLOW.md entities                        | VERIFIED   | V001__initial_schema.sql has exactly 8 CREATE TABLE statements: recipients, cards, card_product_names, notes, products, serial_instances, archive_records, pending_edits |

**Score:** 11/11 truths verified

---

### Required Artifacts

| Artifact                                                                    | Expected                                                    | Status     | Details                                                                               |
|-----------------------------------------------------------------------------|-------------------------------------------------------------|------------|---------------------------------------------------------------------------------------|
| `crates/core/src/domain/recipient.rs`                                       | Recipient struct without deprecated fields                  | VERIFIED   | `pub struct Recipient` present; no github_profile_url, shipment_status, tracking_state |
| `crates/app/src/service_client.rs`                                          | RecipientCardSnapshot with product_names and notes          | VERIFIED   | Contains `pub product_names: Vec<String>` (line 27) and `pub notes: Vec<NoteEntry>` (line 28) |
| `crates/service/src/api/recipients.rs`                                      | RecipientSnapshot without github_profile_url                | VERIFIED   | Struct has product_names, notes, no deprecated fields |
| `crates/core/src/domain/note.rs`                                            | NoteEntry struct in core crate                              | VERIFIED   | File exists; `pub struct NoteEntry` at line 5 |
| `crates/app/ui/card.slint`                                                  | RecipientCard without item-summary or image-hint            | VERIFIED   | Zero grep results for those properties |
| `crates/app/ui/dashboard.slint`                                             | CardData without item-summary or image-hint                 | VERIFIED   | Zero grep results for those properties in the struct |
| `crates/service/src/db/sqlite.rs`                                           | SqliteStore with open(), open_in_memory(), upsert/read      | VERIFIED   | 289 lines (exceeds min 100); all required methods present |
| `crates/service/src/db/migrations/V001__initial_schema.sql`                 | Full schema for all entities                                | VERIFIED   | 8 CREATE TABLE statements confirmed |
| `crates/service/tests/sqlite_tests.rs`                                      | Integration tests for SQLite persistence                    | VERIFIED   | 9 test functions using open_in_memory() |

---

### Key Link Verification

| From                                        | To                                           | Via                                                          | Status     | Details                                                             |
|---------------------------------------------|----------------------------------------------|--------------------------------------------------------------|------------|---------------------------------------------------------------------|
| `crates/integrations/src/github/project_mapping.rs` | `GithubMappedRecipient`                | `recipient_key` derivation preserved after github_profile_url removal | VERIFIED   | `pub recipient_key: String` still on struct (line 8); URL parsing logic intact in map_rows() |
| `crates/app/src/live_client.rs`             | `RecipientCardSnapshot`                      | `product_names` populated from sync pipeline                 | VERIFIED   | `product_names` appears in card_row_to_snapshot, snapshot_to_card_row, and all sync helpers |
| `crates/app/src/main.rs`                    | `crates/app/ui/dashboard.slint`              | Rust-to-Slint CardData mapping no longer sets item-summary or image-hint | VERIFIED   | Zero results for `item_summary` or `image_hint` in main.rs |
| `crates/app/src/live_client.rs`             | `crates/service/src/db/sqlite.rs`            | LiveClient holds `Arc<SqliteStore>` instead of `Arc<Mutex<Repository>>` (primary) | VERIFIED   | `store: Arc<service::db::sqlite::SqliteStore>` at line 22 |
| `crates/app/src/main.rs`                    | `crates/service/src/db/sqlite.rs`            | `SqliteStore::open()` called with db_path in LiveClient::new()  | VERIFIED   | `SqliteStore::open(&db)` at live_client.rs line 60 (called from new()) |
| `crates/service/src/db/sqlite.rs`           | `crates/service/src/db/migrations/V001__initial_schema.sql` | `embed_migrations!` runs on open() | VERIFIED   | `embed_migrations!("src/db/migrations")` at sqlite.rs lines 15-16 |

---

### Requirements Coverage

| Requirement | Source Plan | Description                                              | Status     | Evidence                                                                       |
|-------------|-------------|----------------------------------------------------------|------------|--------------------------------------------------------------------------------|
| CLEAN-01    | 15-01       | `github_profile_url` removed from all structs            | SATISFIED  | Zero `pub github_profile_url` occurrences in .rs files; string key lookup preserved |
| CLEAN-02    | 15-01       | Shipment status/tracking moved off Recipient to card-level | SATISFIED  | Recipient struct has no shipment_status or tracking_state; RecipientCardSnapshot retains shipment_status |
| CLEAN-03    | 15-02       | `item_summary` replaced by `Vec<ProductRef>` on cards    | SATISFIED  | `product_names: Vec<String>` on RecipientCardSnapshot, RecipientSnapshot, DashboardCardViewModel |
| CLEAN-04    | 15-02       | `latest_note` replaced by `Vec<NoteEntry>` on cards      | SATISFIED  | `notes: Vec<NoteEntry>` on RecipientCardSnapshot and RecipientSnapshot |
| POLISH-01   | 15-02       | RecipientCard vertical layout + toast-is-warning wiring  | SATISFIED  | VerticalLayout confirmed in card.slint line 202; toast conditional confirmed in dashboard.slint line 658 |
| PERSIST-01  | 15-03       | Card data survives app restart without re-fetching        | SATISFIED  | run_sync_cycle writes via upsert_card(); fetch_card_snapshots reads via read_all_cards() |
| PERSIST-02  | 15-03       | App reads all dashboard data from SQLite, not in-memory Repository | SATISFIED  | fetch_card_snapshots() calls `self.store.read_all_cards()` (live_client.rs line 205); Repository retained only for legacy item ops (add/remove/rename) with Phase 17 TODO |
| PERSIST-03  | 15-03       | SQLite schema mirrors all DATA-FLOW.md entities           | SATISFIED  | V001 creates 8 tables: recipients, cards, card_product_names, notes, products, serial_instances, archive_records, pending_edits |

**Orphaned requirements:** None. All 8 IDs claimed in plan frontmatter appear in REQUIREMENTS.md and are mapped to Phase 15.

---

### Anti-Patterns Found

| File | Line | Pattern | Severity | Impact |
|------|------|---------|----------|--------|
| `crates/app/src/live_client.rs` | 24-25 | `TODO Phase 17: Replace with SQLite product-based item management` — Repository still held for add/remove/rename item ops | INFO | Intentional and documented in SUMMARY. add_item/remove_item/rename_item/search_item_catalog still use in-memory Repository. Not a blocker — PERSIST-02 applies to card read path (which is fully SQLite). Phase 17 will close. |
| `crates/app/src/live_client.rs` | 317-318 | `discord_username: None, // TODO Phase 17: join recipients table` in card_row_to_snapshot | INFO | Discord username not populated from SQLite join yet. Not a blocker for Phase 15 goals. |
| `crates/app/src/dashboard/discovery.rs` | 589 | `filter_by_search_matches_item_summary` test function name | INFO | Stale test name — function body uses product_names correctly. No behavioral impact. |

No BLOCKER or WARNING severity anti-patterns found.

---

### Human Verification Required

#### 1. Data Survival Across Restart

**Test:** Launch the app, trigger a sync, quit. Relaunch without network access (or with GitHub/Shopify blocked).
**Expected:** Dashboard shows cards loaded from `%APPDATA%/WITwhat/witwhat.db` without requiring a sync.
**Why human:** Cannot run the Slint GUI app in automated verification. The SQLite wiring is verified programmatically but end-to-end restart behavior requires a live test.

#### 2. Card Body Height Driven by Content

**Test:** View cards with varying numbers of items and notes. A card with 3 items should be taller than a card with 1 item.
**Expected:** Card body height grows naturally with content; no clipping.
**Why human:** Visual layout behavior requires a running GUI to observe.

#### 3. Toast Color Distinction

**Test:** Trigger a warning toast (e.g., a sync error) and an info toast.
**Expected:** Warning toast has a visually distinct (e.g., orange/yellow) background vs. the info toast (e.g., blue/green).
**Why human:** Color rendering requires a live UI.

---

### Summary

Phase 15 achieved its goal. All three plans delivered:

**Plan 01 (CLEAN-01, CLEAN-02):** `github_profile_url` is fully absent from all Rust struct definitions and construction sites. The `recipient_key` derivation in `project_mapping.rs` is preserved (URL string still parsed from GH column, just not stored on the struct). `shipment_status` and `tracking_state` removed from the `Recipient` domain struct; they remain correctly on `Package` and `RecipientCardSnapshot`. 247 tests passed.

**Plan 02 (CLEAN-03, CLEAN-04, POLISH-01):** `item_summary`, `latest_note`, and `first_item_image_hint` replaced by typed equivalents across all Rust structs and Slint property declarations. `NoteEntry` placed in the core crate and shared cross-crate without circular dependencies. `item-summary` and `image-hint` are gone from both `card.slint` and `dashboard.slint`. POLISH-01 was already implemented — documented as verified rather than re-implemented.

**Plan 03 (PERSIST-01, PERSIST-02, PERSIST-03):** `SqliteStore` with refinery-managed V001 schema (8 tables) is wired into `LiveClient`. Production reads go through `read_all_cards()`. Sync writes go through `upsert_card()`. `open_in_memory()` used for tests. 9 SQLite integration tests pass. Repository is retained as a secondary field only for legacy item add/remove/rename operations, with explicit Phase 17 TODO markers — this is an intentional documented deviation, not a stub.

---

_Verified: 2026-03-22_
_Verifier: Claude (gsd-verifier)_
