# Phase 17: GH Issues Client and Product Catalog - Context

**Gathered:** 2026-03-24
**Status:** Ready for planning

<domain>
## Phase Boundary

Build a GH Issues REST client for `ww-product` CRUD (create, read, update) via `gh` CLI, create Product as a standalone entity in SQLite and GH Issues, deliver a browsable product catalog view within the existing "By Product Shipped" discovery tab, and enable product creation with Shopify URL auto-suggest. Cards will reference structured products instead of freeform text by the end of this phase.

Requirements: CLOUD-03, CLOUD-04, PROD-01, PROD-02, PROD-03, PROD-04, PROD-05

</domain>

<decisions>
## Implementation Decisions

### GH Issue Body Format
- JSON code block in the issue body, parsed with serde_json
- `schema_version: 1` field for forward compatibility
- Target repo: `BigscreenVR/beyond-outgoing` (hardcoded, matching DATA-FLOW.md)
- This format will also be used for `ww-card` issues in Phase 18

**Two issue types (with native subissue hierarchy):**

1. **`ww-product`** (parent product type)
   - Issue title = product name
   - Issue label = `ww-product`
   - Body JSON: `product_id`, `image_url`, `shopify_product_url`
   - **No `unit_refs[]` in body** — child units are tracked via GitHub's native subissues feature
   - Issue comments/timeline used for product-level notes
   - Product units appear as subissues in the GitHub UI automatically

2. **`ww-product-unit`** (individual serialized unit — **subissue of parent `ww-product`**)
   - Issue title = serial ID (e.g., "HMD-001")
   - Issue label = `ww-product-unit`
   - Body JSON: `serial_id`, `product_id`, `state`, `assigned_card_id`
   - **No `parent_ref` in body** — the parent relationship is expressed via GitHub's native subissue hierarchy
   - **Each unit gets its own GH Issue** — the issue timeline is used outside WITwhat for comments, documentation, and history of what happens to that specific unit
   - **Created as a subissue** of the parent `ww-product` issue, so the parent↔child relationship is visible in GitHub's UI natively (not just as cross-links)

3. **`ww-card`** (Phase 18 — not this phase, but same format)

**Subissues rule:** Every `ww-product-unit` issue MUST be created as a subissue of its parent `ww-product` issue. This replaces the previous `unit_refs[]` / `parent_ref` cross-linking approach. The subissue hierarchy provides native GitHub UI grouping, auto-linking, and progress tracking. The GhIssuesClient must support the subissues API for creating and listing subissues.

### GH Issues Transport
- Use `gh` CLI (consistent with existing GhCliProjectClient)
- Commands: `gh issue create`, `gh issue edit`, `gh issue list`
- **Subissues:** Research needed — determine whether `gh` CLI supports subissues natively or if the GitHub REST/GraphQL API is required for the subissue relationship
- Auth handled by gh's own login — no new GitHub auth path
- JSON output mode (`--json`) for structured parsing

### Product Catalog UI
- Product catalog lives within the existing "By Product Shipped" discovery tab — no new tab
- Option grid shows product tiles: image + name + recipient count
- Tiles have ellipsis menu (same pattern as card ellipsis menus) with:
  - **Set Image** — native Windows file picker, uploads to `beyond-outgoing/product-images` orphan branch for cloud storage
  - **View on Shopify** — opens Shopify product admin page in default browser (linked products only)
  - **Product Detail** — opens detail panel
  - **Archive** — archive product (similar to card archive behavior)
- Clicking a product tile filters cards by that product (existing "By Product Shipped" behavior)
- No serial/non-serial distinction on the grid — one square per product type
- Products without a custom image auto-fetch the first Shopify product image when linked to a Shopify product

### Product Entity Model
- All products are assumed serializable — no `is_serializable` boolean toggle
- **Product (parent)** fields: `product_id`, `name`, `image_url`, `shopify_product_url`, `github_issue_number`
- **Product Unit (child)** fields: `serial_id`, `product_id`, `state`, `assigned_card_id`, `assigned_at`, `github_issue_number`
- Each product unit has its own dedicated `ww-product-unit` GH Issue — this is critical because the issue timeline serves as a documentation trail for each unit's life outside of WITwhat
- **Parent↔child linking:** Uses GitHub's native subissues feature. Parent product issue lists child unit issues as subissues in the GH UI. No manual cross-link arrays needed in body JSON.
- **Sync:** When syncing, list subissues of a `ww-product` issue to discover its units (instead of parsing `unit_refs` from body JSON)

### Product Creation Flow
- Products are created manually via "Add Product" in the catalog
- Create form: Name (required), Shopify URL (optional), image (optional via Set Image)
- Shopify URL field auto-suggests unlinked Shopify order products (products from orders that don't already have a linked catalog entry)
- GH Project parallel-array columns (`product_names`, `product_shopify_urls`) are WITwhat-owned columns — not yet created on the GH Project; user will create them when needed
- Existing products loaded from GH Issues (`ww-product` label) and paired to catalog

### Product Add-to-Card Flow
- When adding a product to a card, user can:
  1. Add any product generically (product type without a specific serial)
  2. Select an existing serialized instance that is in stock (shown as nested list under parent product)
  3. Click `+` beside the product name to create a new serialized child instance inline
- Product assignment replaces freeform `item_summary` with structured `product_refs`

### Claude's Discretion
- Image resize/compression strategy before upload
- Product-images branch structure (flat vs folder-per-product)
- How to handle product name edits (rename GH Issue title + update references)
- Exact Shopify URL auto-suggest implementation (when to fetch, caching)

---

## Gap Closure Decisions (from 2026-03-26 UAT)

### Product Detail Sidecar — Location and Features
- **No ellipsis menu on product tiles.** Remove entirely.
- **Product detail sidecar appears as a right sidebar** in the filtered card view (when user clicks a product tile). Always visible while in product-filtered view. No close button needed — user navigates away by clicking the back breadcrumb.
- **All former ellipsis actions move into the sidecar:**
  - **Set Image:** Hovering the image frame in the sidecar shows a tooltip. Clicking the image frame opens native file picker for upload.
  - **Archive:** Full-width button docked to the bottom of the sidecar.
  - **View on Shopify:** Clickable link text (not a button) replacing the plain Shopify URL display.
  - **GH Issue:** Clickable `beyond-outgoing#123` text that opens in browser.

### Serial Unit Creation UX
- **Inline text input** for serial number. Click '+' spawns a text input inline in the units list. **Auto-focus** the input on spawn. Press Enter to confirm. If blank, auto-generate a sequential SN.
- **Debounce:** Disable the '+' button while a creation is in progress to prevent duplicates.
- **Issue title format:** `{Product Name} ({Unit Serial})` — e.g., "HMD (HMD-003)"
- **Unit selection model (radio-button behavior):**
  - Units in the sidecar act as radio buttons — clicking one highlights it, clicking again deselects
  - When a unit is selected:
    - Subtext appears under the sidecar header showing the unit's SN
    - A circular "UNIT" badge appears over the bottom corner of the product image
    - The top banner breadcrumb changes from `{Product Name}` to `{Product Name} <- {SN}` with the back arrow being clickable to deselect
    - The "Issue" link changes to point to the unit's subissue
    - The card view filters to only cards that have the selected serial unit assigned (regardless of current possession)
- **Multiple units layout:** Each unit on its own line, top-aligned, properly separated (not sharing a single line)

### Add Product Modal Styling
- **Keep the current form structure** (AddProductForm with Name, Shopify URL, Discard, Add Product).
- **Fix styling to match LookupModal:** colors, border-radius, padding, font sizes, backdrop.
- Do NOT restructure or reuse LookupModal — just align visual appearance.

### Product Sync Behavior
- **Tab-switch persistence:** Product grid must remain populated when switching away from and back to the Products tab. Do not clear on tab switch.
- **Stale data cleanup:** On sync, check for products in SQLite that no longer have a corresponding open GH Issue:
  - If the GH Issue exists in a non-open state (closed/archived): archive the product locally
  - If the GH Issue doesn't exist at all: remove the ProductRow from SQLite
- **Shopify image auto-fetch:** When a product has a `shopify_product_url` but no `image_url`, fetch the first product image from Shopify and store it on the product-images orphan branch. This should happen during sync, not just on product creation.

### Product Lookup Modal Wiring
- The "Shipment Product Lookup" modal (for adding products to cards) must be wired to show products from the SQLite catalog (`read_all_products`), not just freeform text.

### Bug Fixes (no discussion needed — just fix)
- Product tile hover/ellipsis flicker (z-order or TouchArea conflict)
- Sidecar "x" button pointer cursor (if sidecar still has x button in non-filtered contexts)
- Periodic sync must NOT close the product detail sidecar

</decisions>

<canonical_refs>
## Canonical References

**Downstream agents MUST read these before planning or implementing.**

### Data architecture
- `.planning/DATA-FLOW.md` — Layer 3 (Products) and Layer 4 (Serial Instances) define the product schema, GH Issue storage model, and serial lifecycle state machine. Also RULE-03 (SQLite single read source), RULE-05 (GH Issues are cloud of record), RULE-07 (item_summary deprecated)

### Requirements
- `.planning/REQUIREMENTS.md` — CLOUD-03, CLOUD-04, PROD-01 through PROD-05 define acceptance criteria

### Existing patterns
- `crates/integrations/src/github/gh_cli_client.rs` — Existing `gh` CLI integration pattern (GhCliProjectClient). New GH Issues client should follow similar structure.
- `crates/integrations/src/github/project_client.rs` — GithubProjectClient trait pattern for abstraction
- `crates/integrations/src/github/avatar_branch_client.rs` — Orphan branch upload pattern (used for Discord avatars). Product-images branch should follow this same pattern.
- `crates/service/src/db/sqlite.rs` — SqliteStore pattern for product table CRUD
- `crates/app/src/dashboard/discovery.rs` — ByProductShipped mode and option grid logic

</canonical_refs>

<code_context>
## Existing Code Insights

### Reusable Assets
- `GhCliProjectClient` (`gh_cli_client.rs`): Pattern for shelling out to `gh` CLI — reuse for Issues client
- `avatar_branch_client.rs`: Orphan branch upload pattern — reuse for product-images branch
- `SqliteStore` (`sqlite.rs`): Single `Arc<Mutex<Connection>>` + WAL mode pattern — extend with products table
- `ByProductShipped` discovery mode (`discovery.rs`): Already has option grid and card filtering — extend with richer tiles
- Card ellipsis menu pattern (`main.rs`): Existing ellipsis/menu component — reuse for product tile menus
- `product_names` / `product_shopify_urls` already flow through the pipeline (21 files reference them)

### Established Patterns
- `gh` CLI for all GitHub communication (no direct HTTP for GitHub)
- `refinery` embedded SQL migrations for schema changes
- Slint UI with `tokens.slint` color/typography globals
- View model pipeline: domain -> DashboardCardViewModel -> Slint CardData

### Integration Points
- `SqliteStore`: Add `products` table, `product_units` table, product CRUD methods
- `live_client.rs`: Add product sync (fetch `ww-product` + `ww-product-unit` issues, upsert to SQLite)
- `discovery.rs`: Enhance ByProductShipped option building to use product catalog data
- `main.rs`: Wire product creation callbacks, ellipsis menu actions, Set Image file picker
- `projection.rs`: Map product_refs to card item squares (replacing freeform product_names)

</code_context>

<specifics>
## Specific Ideas

- Product tile ellipsis menus should mirror card ellipsis menu UX (same visual pattern, interaction model)
- "Set Image" uses native Windows file dialog — same platform-native feel as the rest of the app
- Shopify URL auto-suggest on "Create New Product" form should list unlinked products from existing Shopify orders, formatted as "Product Name (order #XXXX)"
- Product-images branch on beyond-outgoing follows the same orphan-branch pattern as recipient-avatars branch (Phase 16.1)
- When a product is linked to Shopify but has no custom image, auto-fetch the first product image from Shopify — no manual step needed
- Subissues should be used to express the parent product → child unit hierarchy natively in GitHub

</specifics>

<deferred>
## Deferred Ideas

None — discussion stayed within phase scope

</deferred>

---

*Phase: 17-gh-issues-client-and-product-catalog*
*Context gathered: 2026-03-24 (updated with subissues decision)*
