---
phase: 17-gh-issues-client-and-product-catalog
plan: 01
subsystem: api, database
tags: [github-issues, gh-cli, subissues-api, sqlite, product-catalog, serde]

# Dependency graph
requires:
  - phase: 15-sqlite-foundation
    provides: SqliteStore with WAL mode, refinery migrations, RecipientRow/CardRow CRUD
  - phase: 16.1-discord-username-inline-editing
    provides: AvatarBranchClient pattern (gh CLI subprocess + temp file)
provides:
  - GhIssuesClient with CRUD for ww-product and ww-product-unit GH Issues
  - GitHub Subissues REST API integration (add_subissue, list_subissues)
  - Body parsing helpers (parse_product_body, parse_unit_body, format_issue_body_json)
  - V004 migration adding github_issue_number to products and serial_instances
  - ProductRow and ProductUnitRow structs with SqliteStore CRUD methods
affects: [17-02, 17-03, 17-04, 17-05, 18-gh-issue-card-sync]

# Tech tracking
tech-stack:
  added: []
  patterns:
    - "GhIssuesClient: gh CLI subprocess with --body-file temp pattern for large payloads"
    - "Subissues REST API: POST/GET repos/{owner}/{repo}/issues/{number}/sub_issues"
    - "Issue body JSON: ```json fenced code block with schema_version field"
    - "ProductRow/ProductUnitRow: INSERT OR REPLACE with is_serializable always 1"

key-files:
  created:
    - crates/integrations/src/github/issues_client.rs
    - crates/service/src/db/migrations/V004__products_schema_fix.sql
  modified:
    - crates/integrations/src/github/mod.rs
    - crates/service/src/db/sqlite.rs

key-decisions:
  - "No uuid dependency added — not used by issues_client methods"
  - "All products assumed serializable (is_serializable = 1 hardcoded in upsert)"
  - "No unit_refs or parent_ref fields anywhere — GitHub Subissues handle parent/child natively"
  - "get_issue_database_id uses gh api --jq .id (numeric REST id), not gh issue view --json id (GraphQL node_id)"

patterns-established:
  - "GhIssuesClient::new() reuses find_gh() from gh_cli_client — single binary discovery function"
  - "Issue body format: parse_issue_body_json strips ```json fences, format_issue_body_json wraps with fences"
  - "V004 migration: ALTER TABLE ADD COLUMN pattern for incremental schema evolution"

requirements-completed: [CLOUD-03, CLOUD-04, PROD-02]

# Metrics
duration: 6min
completed: 2026-03-25
---

# Phase 17 Plan 01: GH Issues Client and Product Catalog Summary

**GhIssuesClient with 7 CRUD methods (including GitHub Subissues REST API), V004 migration adding github_issue_number, and SqliteStore ProductRow/ProductUnitRow with upsert/read CRUD**

## Performance

- **Duration:** 6 min
- **Started:** 2026-03-25T08:25:49Z
- **Completed:** 2026-03-25T08:31:51Z
- **Tasks:** 2
- **Files modified:** 4

## Accomplishments
- GhIssuesClient with create_issue, get_issue_database_id, add_subissue, create_unit_as_subissue, list_subissues, edit_issue_body, list_issues_by_label
- Pure body parsing functions with unit tests (parse_product_body, parse_unit_body, parse_issue_body_json round-trip)
- V004 migration adding github_issue_number INTEGER to products and serial_instances tables
- ProductRow and ProductUnitRow structs with full CRUD: upsert_product, read_all_products, read_product_by_id, upsert_product_unit, read_units_by_product, read_all_units

## Task Commits

Each task was committed atomically:

1. **Task 1: Create GhIssuesClient with CRUD, subissue operations, and body parsing helpers** - `05af760` (feat)
2. **Task 2: V004 migration and SqliteStore ProductRow/ProductUnitRow CRUD** - `09fc90c` (feat)

## Files Created/Modified
- `crates/integrations/src/github/issues_client.rs` - GhIssuesClient with all CRUD methods, subissue operations, body parsing helpers, and unit tests
- `crates/integrations/src/github/mod.rs` - Added `pub mod issues_client;`
- `crates/service/src/db/migrations/V004__products_schema_fix.sql` - ALTER TABLE adding github_issue_number to products and serial_instances
- `crates/service/src/db/sqlite.rs` - ProductRow, ProductUnitRow structs and 6 CRUD methods with 4 unit tests

## Decisions Made
- Skipped uuid dependency addition since issues_client.rs does not use it (plan mentioned adding it but no code path requires it)
- Fixed product_unit_roundtrip test to use None for assigned_card_id since FK constraint references cards table (no card inserted in test)

## Deviations from Plan

### Auto-fixed Issues

**1. [Rule 1 - Bug] Fixed FK constraint violation in product_unit_roundtrip test**
- **Found during:** Task 2
- **Issue:** Test used assigned_card_id = Some("CARD-XYZ") but no card row existed, causing FK violation
- **Fix:** Changed test to use assigned_card_id = None (valid for unassigned units)
- **Files modified:** crates/service/src/db/sqlite.rs
- **Verification:** All 4 product tests pass
- **Committed in:** 09fc90c (Task 2 commit)

---

**Total deviations:** 1 auto-fixed (1 bug)
**Impact on plan:** Test fix necessary for correctness. No scope creep.

## Issues Encountered
None

## User Setup Required
None - no external service configuration required.

## Next Phase Readiness
- GhIssuesClient ready for use by Plans 02-05 (product catalog UI, sync orchestrator)
- V004 migration will run automatically on next app startup
- ProductRow/ProductUnitRow CRUD ready for product catalog features

---
*Phase: 17-gh-issues-client-and-product-catalog*
*Completed: 2026-03-25*
