diff --git a/.planning/REQUIREMENTS.md b/.planning/REQUIREMENTS.md index 27b8176..f183d00 100644 --- a/.planning/REQUIREMENTS.md +++ b/.planning/REQUIREMENTS.md @@ -16,10 +16,10 @@ Requirements for initial release. Each maps to roadmap phases. ### Planning Threads -- [ ] **THRD-01**: User can create a planning thread with a name (e.g. "Helmet") -- [ ] **THRD-02**: User can add candidate products to a thread with weight, price, notes, and product link -- [ ] **THRD-03**: User can edit and remove candidates from a thread -- [ ] **THRD-04**: User can resolve a thread by picking a winner, which moves to their collection +- [x] **THRD-01**: User can create a planning thread with a name (e.g. "Helmet") +- [x] **THRD-02**: User can add candidate products to a thread with weight, price, notes, and product link +- [x] **THRD-03**: User can edit and remove candidates from a thread +- [x] **THRD-04**: User can resolve a thread by picking a winner, which moves to their collection ### Setups @@ -78,10 +78,10 @@ Which phases cover which requirements. Updated during roadmap creation. | COLL-02 | Phase 1 | Complete | | COLL-03 | Phase 1 | Complete | | COLL-04 | Phase 1 | Complete | -| THRD-01 | Phase 2 | Pending | -| THRD-02 | Phase 2 | Pending | -| THRD-03 | Phase 2 | Pending | -| THRD-04 | Phase 2 | Pending | +| THRD-01 | Phase 2 | Complete | +| THRD-02 | Phase 2 | Complete | +| THRD-03 | Phase 2 | Complete | +| THRD-04 | Phase 2 | Complete | | SETP-01 | Phase 3 | Pending | | SETP-02 | Phase 3 | Pending | | SETP-03 | Phase 3 | Pending | diff --git a/.planning/STATE.md b/.planning/STATE.md index 55d0ebe..ba4b12c 100644 --- a/.planning/STATE.md +++ b/.planning/STATE.md @@ -2,16 +2,16 @@ gsd_state_version: 1.0 milestone: v1.0 milestone_name: milestone -status: completed -stopped_at: Phase 2 context gathered -last_updated: "2026-03-15T10:18:27.410Z" -last_activity: 2026-03-14 — Completed 01-04 onboarding wizard and visual verification +status: in-progress +stopped_at: Completed 02-01-PLAN.md +last_updated: "2026-03-15T10:39:24Z" +last_activity: 2026-03-15 — Completed 02-01 thread backend API progress: total_phases: 3 completed_phases: 1 - total_plans: 4 - completed_plans: 4 - percent: 100 + total_plans: 7 + completed_plans: 5 + percent: 71 --- # Project State @@ -21,16 +21,16 @@ progress: See: .planning/PROJECT.md (updated 2026-03-14) **Core value:** Make it effortless to manage gear and plan new purchases — see how a potential buy affects your total setup weight and cost before committing. -**Current focus:** Phase 1: Foundation and Collection +**Current focus:** Phase 2: Planning Threads ## Current Position -Phase: 1 of 3 (Foundation and Collection) -Plan: 4 of 4 in current phase (complete) -Status: Phase 1 complete -Last activity: 2026-03-14 — Completed 01-04 onboarding wizard and visual verification +Phase: 2 of 3 (Planning Threads) +Plan: 1 of 3 in current phase +Status: In progress +Last activity: 2026-03-15 — Completed 02-01 thread backend API -Progress: [██████████] 100% +Progress: [███████---] 71% ## Performance Metrics @@ -53,6 +53,7 @@ Progress: [██████████] 100% | Phase 01 P02 | 3min | 2 tasks | 13 files | | Phase 01 P03 | 3min | 2 tasks | 16 files | | Phase 01 P04 | 3min | 2 tasks | 5 files | +| Phase 02 P01 | 5min | 2 tasks | 9 files | ## Accumulated Context @@ -71,6 +72,9 @@ Recent decisions affecting current work: - [Phase 01-03]: CategoryPicker uses native ARIA combobox pattern with keyboard navigation - [Phase 01-04]: Onboarding state persisted in SQLite settings table, not Zustand (source of truth in DB) - [Phase 01-04]: Settings API is generic key-value store usable beyond onboarding +- [Phase 02-01]: Drizzle sql template literals use raw table.column refs in correlated subqueries (not interpolated) +- [Phase 02-01]: Thread deletion collects candidate image filenames before cascade for filesystem cleanup +- [Phase 02-01]: Resolution validates categoryId existence, falls back to Uncategorized (id=1) ### Pending Todos @@ -83,6 +87,6 @@ None yet. ## Session Continuity -Last session: 2026-03-15T10:18:27.408Z -Stopped at: Phase 2 context gathered -Resume file: .planning/phases/02-planning-threads/02-CONTEXT.md +Last session: 2026-03-15T10:39:24Z +Stopped at: Completed 02-01-PLAN.md +Resume file: .planning/phases/02-planning-threads/02-01-SUMMARY.md diff --git a/.planning/phases/02-planning-threads/02-01-SUMMARY.md b/.planning/phases/02-planning-threads/02-01-SUMMARY.md new file mode 100644 index 0000000..74a9797 --- /dev/null +++ b/.planning/phases/02-planning-threads/02-01-SUMMARY.md @@ -0,0 +1,131 @@ +--- +phase: 02-planning-threads +plan: 01 +subsystem: api +tags: [drizzle, hono, sqlite, tdd, threads, candidates, transactions] + +requires: + - phase: 01-foundation-and-collection + provides: items table, item.service.ts DI pattern, test helper, Hono route pattern +provides: + - threads and threadCandidates database tables + - Thread service with full CRUD and transactional resolution + - Thread API routes at /api/threads with nested candidate endpoints + - Zod validation schemas for threads, candidates, and resolution + - Shared TypeScript types for Thread and ThreadCandidate +affects: [02-planning-threads, 03-setups-and-dashboard] + +tech-stack: + added: [] + patterns: [correlated SQL subqueries for aggregate metadata, transactional resolution pattern] + +key-files: + created: + - src/server/services/thread.service.ts + - src/server/routes/threads.ts + - tests/services/thread.service.test.ts + - tests/routes/threads.test.ts + modified: + - src/db/schema.ts + - src/shared/schemas.ts + - src/shared/types.ts + - src/server/index.ts + - tests/helpers/db.ts + +key-decisions: + - "Drizzle sql template literals use raw table.column references in correlated subqueries (not interpolated column objects)" + - "Thread deletion collects candidate image filenames before cascade delete for filesystem cleanup" + - "Resolution validates categoryId existence and falls back to Uncategorized (id=1)" + +patterns-established: + - "Correlated subquery pattern: raw SQL references in Drizzle sql`` for aggregate columns (candidateCount, minPrice, maxPrice)" + - "Transaction pattern: resolveThread atomically creates item + archives thread in single db.transaction()" + - "Nested route pattern: candidates CRUD mounted under /api/threads/:id/candidates" + +requirements-completed: [THRD-01, THRD-02, THRD-03, THRD-04] + +duration: 5min +completed: 2026-03-15 +--- + +# Phase 2 Plan 01: Thread Backend API Summary + +**Thread and candidate CRUD API with transactional resolution that atomically creates collection items from winning candidates using Drizzle transactions** + +## Performance + +- **Duration:** 5 min +- **Started:** 2026-03-15T10:34:32Z +- **Completed:** 2026-03-15T10:39:24Z +- **Tasks:** 2 +- **Files modified:** 9 + +## Accomplishments +- Full thread CRUD (create, read, update, delete) with cascading candidate cleanup +- Full candidate CRUD with all item-compatible fields (name, weight, price, category, notes, productUrl, image) +- Thread list returns aggregate metadata (candidateCount, minPriceCents, maxPriceCents) via correlated subqueries +- Transactional thread resolution: atomically creates collection item from candidate data and archives thread +- 33 tests (19 unit + 14 integration) all passing with zero regressions on existing 30 Phase 1 tests + +## Task Commits + +Each task was committed atomically (TDD: RED then GREEN): + +1. **Task 1: Schema, shared schemas, test helper, and service layer** + - `e146eea` (test) - RED: failing tests for thread service + - `1a8b91e` (feat) - GREEN: implement thread service +2. **Task 2: Thread API routes with integration tests** + - `37c9999` (test) - RED: failing integration tests for thread routes + - `add3e33` (feat) - GREEN: implement thread routes and mount + +## Files Created/Modified +- `src/db/schema.ts` - Added threads and threadCandidates table definitions +- `src/shared/schemas.ts` - Added Zod schemas for thread/candidate/resolve validation +- `src/shared/types.ts` - Added Thread, ThreadCandidate, and related input types +- `src/server/services/thread.service.ts` - Thread and candidate business logic with resolution transaction +- `src/server/routes/threads.ts` - Hono API routes for threads and candidates +- `src/server/index.ts` - Mounted threadRoutes at /api/threads +- `tests/helpers/db.ts` - Added threads and thread_candidates table creation +- `tests/services/thread.service.test.ts` - 19 unit tests for thread service +- `tests/routes/threads.test.ts` - 14 integration tests for thread API + +## Decisions Made +- Used raw SQL table.column references in Drizzle `sql` template literals for correlated subqueries (interpolated column objects bind as parameters, not column references) +- Thread deletion collects candidate image filenames before cascade delete to enable filesystem cleanup +- Resolution validates categoryId existence and falls back to Uncategorized (id=1) to handle deleted categories + +## Deviations from Plan + +### Auto-fixed Issues + +**1. [Rule 1 - Bug] Fixed correlated subquery column reference in getAllThreads** +- **Found during:** Task 1 (GREEN phase) +- **Issue:** Drizzle `sql` template literal with `${threads.id}` binds as a parameter value, not a SQL column reference, causing COUNT to return 1 instead of correct count +- **Fix:** Changed to raw SQL reference `threads.id` instead of interpolated `${threads.id}` in correlated subqueries +- **Files modified:** src/server/services/thread.service.ts +- **Verification:** candidateCount returns correct values in tests +- **Committed in:** 1a8b91e (Task 1 GREEN commit) + +--- + +**Total deviations:** 1 auto-fixed (1 bug) +**Impact on plan:** Essential fix for correct aggregate metadata. No scope creep. + +## Issues Encountered +None beyond the subquery fix documented above. + +## User Setup Required +None - no external service configuration required. + +## Next Phase Readiness +- Thread API fully operational, ready for frontend consumption in Plan 02 +- All endpoints follow established Phase 1 patterns (DI, Hono context, Zod validation) +- Test infrastructure updated to support threads in all future tests + +--- +*Phase: 02-planning-threads* +*Completed: 2026-03-15* + +## Self-Check: PASSED + +All 8 files verified present. All 4 commit hashes verified in git log.