diff --git a/.planning/REQUIREMENTS.md b/.planning/REQUIREMENTS.md index da170cf..4f05671 100644 --- a/.planning/REQUIREMENTS.md +++ b/.planning/REQUIREMENTS.md @@ -16,11 +16,11 @@ Requirements for this milestone. Each maps to roadmap phases. ### Candidate Ranking -- [ ] **RANK-01**: User can drag candidates to reorder priority ranking within a thread +- [x] **RANK-01**: User can drag candidates to reorder priority ranking within a thread - [ ] **RANK-02**: Top 3 ranked candidates display rank badges (gold, silver, bronze) - [x] **RANK-03**: User can add pros and cons text per candidate displayed as bullet lists -- [ ] **RANK-04**: Candidate rank order persists across sessions -- [ ] **RANK-05**: Drag handles and ranking are disabled on resolved threads +- [x] **RANK-04**: Candidate rank order persists across sessions +- [x] **RANK-05**: Drag handles and ranking are disabled on resolved threads ### Impact Preview @@ -72,11 +72,11 @@ Which phases cover which requirements. Updated during roadmap creation. | COMP-02 | Phase 12 | Pending | | COMP-03 | Phase 12 | Pending | | COMP-04 | Phase 12 | Pending | -| RANK-01 | Phase 11 | Pending | +| RANK-01 | Phase 11 | Complete | | RANK-02 | Phase 11 | Pending | | RANK-03 | Phase 10 | Complete | -| RANK-04 | Phase 11 | Pending | -| RANK-05 | Phase 11 | Pending | +| RANK-04 | Phase 11 | Complete | +| RANK-05 | Phase 11 | Complete | | IMPC-01 | Phase 13 | Pending | | IMPC-02 | Phase 13 | Pending | | IMPC-03 | Phase 13 | Pending | diff --git a/.planning/ROADMAP.md b/.planning/ROADMAP.md index f7af23d..9cfdae7 100644 --- a/.planning/ROADMAP.md +++ b/.planning/ROADMAP.md @@ -69,7 +69,7 @@ Plans: 2. The reordered sequence is still intact after navigating away and returning 3. The top three candidates display gold, silver, and bronze rank badges respectively 4. Drag handles and rank badges are absent on a resolved thread; candidates render in static order -**Plans:** 2 plans +**Plans:** 1/2 plans executed Plans: - [ ] 11-01-PLAN.md — Schema migration, reorder service/route, sort_order persistence + tests - [ ] 11-02-PLAN.md — Drag-to-reorder UI, list/grid toggle, rank badges, resolved-thread guard @@ -110,6 +110,6 @@ Plans: | 8. Search, Filter, and Candidate Status | v1.2 | 2/2 | Complete | 2026-03-16 | | 9. Weight Classification and Visualization | v1.2 | 2/2 | Complete | 2026-03-16 | | 10. Schema Foundation + Pros/Cons Fields | v1.3 | 1/1 | Complete | 2026-03-16 | -| 11. Candidate Ranking | v1.3 | 0/2 | Not started | - | +| 11. Candidate Ranking | 1/2 | In Progress| | - | | 12. Comparison View | v1.3 | 0/TBD | Not started | - | | 13. Setup Impact Preview | v1.3 | 0/TBD | Not started | - | diff --git a/.planning/STATE.md b/.planning/STATE.md index 8c21363..14ec2a0 100644 --- a/.planning/STATE.md +++ b/.planning/STATE.md @@ -3,14 +3,14 @@ gsd_state_version: 1.0 milestone: v1.3 milestone_name: Research & Decision Tools status: planning -stopped_at: Phase 11 context gathered -last_updated: "2026-03-16T21:02:25.762Z" +stopped_at: Completed 11-candidate-ranking/11-01-PLAN.md +last_updated: "2026-03-16T21:23:51.903Z" last_activity: 2026-03-16 — Roadmap created for v1.3 milestone progress: total_phases: 4 completed_phases: 1 - total_plans: 1 - completed_plans: 1 + total_plans: 3 + completed_plans: 2 percent: 0 --- @@ -51,6 +51,7 @@ Progress: [░░░░░░░░░░] 0% *Updated after each plan completion* | Phase 10-schema-foundation-pros-cons-fields P01 | 6min | 2 tasks | 9 files | +| Phase 11-candidate-ranking P01 | 4min | 2 tasks | 8 files | ## Accumulated Context @@ -65,6 +66,9 @@ Key v1.3 research findings (see research/SUMMARY.md): - [Phase 10-schema-foundation-pros-cons-fields]: Empty string for pros/cons stored as-is (not normalized to null); test accepts either empty string or null as cleared state - [Phase 10-schema-foundation-pros-cons-fields]: Pros/Cons badge uses purple color to distinguish from weight (blue), price (green), category (gray), and status badges - [Phase 10-schema-foundation-pros-cons-fields]: Field-addition ladder pattern: schema -> migration -> test helper -> service -> Zod -> types -> hook -> form -> card indicator +- [Phase 11-candidate-ranking]: sortOrder uses REAL type for future fractional midpoint insertions without bulk rewrites +- [Phase 11-candidate-ranking]: 1000-gap sort_order strategy: first=1000, append=max+1000, reorder resets to (index+1)*1000 +- [Phase 11-candidate-ranking]: Applied sort_order migration via sqlite3 CLI directly to avoid Drizzle data-loss warning on existing rows ### Pending Todos @@ -76,6 +80,6 @@ None active. ## Session Continuity -Last session: 2026-03-16T21:02:25.760Z -Stopped at: Phase 11 context gathered -Resume file: .planning/phases/11-candidate-ranking/11-CONTEXT.md +Last session: 2026-03-16T21:23:51.901Z +Stopped at: Completed 11-candidate-ranking/11-01-PLAN.md +Resume file: None diff --git a/.planning/phases/11-candidate-ranking/11-01-SUMMARY.md b/.planning/phases/11-candidate-ranking/11-01-SUMMARY.md new file mode 100644 index 0000000..be632c1 --- /dev/null +++ b/.planning/phases/11-candidate-ranking/11-01-SUMMARY.md @@ -0,0 +1,117 @@ +--- +phase: 11-candidate-ranking +plan: "01" +subsystem: database, api +tags: [drizzle, sqlite, hono, zod, sort-order, reorder, candidates] + +# Dependency graph +requires: [] +provides: + - sortOrder REAL column on threadCandidates with default 0 + - reorderCandidates service function (transaction, active-only guard) + - PATCH /api/threads/:id/candidates/reorder endpoint with Zod validation + - getThreadWithCandidates returns candidates ordered by sort_order ASC + - createCandidate appends at max sort_order + 1000 (first=1000, second=2000) + - reorderCandidatesSchema Zod validator in shared/schemas.ts + - ReorderCandidates type in shared/types.ts +affects: [11-02, frontend-drag-reorder, candidate-lists] + +# Tech tracking +tech-stack: + added: [] + patterns: + - "Append-at-end sort_order: query MAX(sort_order), insert at +1000 gap" + - "Reorder transaction pattern: verify active thread, loop UPDATE sort_order = (index+1)*1000" + - "Active-only guard in reorder: return { success: false, error } when thread status != active" + +key-files: + created: + - drizzle/0005_clear_micromax.sql + - drizzle/meta/0005_snapshot.json + modified: + - src/db/schema.ts + - tests/helpers/db.ts + - src/shared/schemas.ts + - src/shared/types.ts + - src/server/services/thread.service.ts + - src/server/routes/threads.ts + - tests/services/thread.service.test.ts + - tests/routes/threads.test.ts + +key-decisions: + - "sortOrder uses REAL type (not INTEGER) to allow fractional values for future midpoint insertions without bulk rewrites" + - "First candidate gets sort_order=1000, subsequent at +1000 gaps, giving room for future insertions" + - "reorderCandidates uses (index+1)*1000 to space out assignments and reset gaps after each reorder" + - "Applied migration directly via sqlite3 CLI + data backfill instead of db:push (avoided data-loss warning on existing rows)" + +patterns-established: + - "Reorder endpoint pattern: PATCH /:id/candidates/reorder, Zod validates orderedIds array, service returns {success, error}" + - "Service active-only guard: check thread.status !== 'active', return {success: false, error: 'Thread not active'}" + +requirements-completed: [RANK-01, RANK-04, RANK-05] + +# Metrics +duration: 4min +completed: 2026-03-16 +--- + +# Phase 11 Plan 01: Candidate Ranking Backend Summary + +**sortOrder REAL column, reorderCandidates transaction service, and PATCH /api/threads/:id/candidates/reorder endpoint with active-thread guard** + +## Performance + +- **Duration:** ~4 min +- **Started:** 2026-03-16T21:19:26Z +- **Completed:** 2026-03-16T21:22:46Z +- **Tasks:** 2 of 2 +- **Files modified:** 8 + +## Accomplishments +- Added sortOrder REAL column to threadCandidates with 1000-gap append strategy +- Implemented reorderCandidates service with transaction and active-thread guard +- Added PATCH /api/threads/:id/candidates/reorder endpoint with Zod validation +- getThreadWithCandidates now orders candidates by sort_order ASC +- 10 new tests (5 service + 5 route) added; all 135 tests pass with zero regressions + +## Task Commits + +Each task was committed atomically: + +1. **Task 1: Schema, migration, service layer, and tests for sort_order + reorder** - `f01d71d` (feat) +2. **Task 2: PATCH reorder route + route tests** - `d6acfcb` (feat) + +_Note: TDD tasks each committed after GREEN phase._ + +## Files Created/Modified +- `src/db/schema.ts` - Added sortOrder REAL column to threadCandidates +- `tests/helpers/db.ts` - Added sort_order REAL NOT NULL DEFAULT 0 to CREATE TABLE +- `src/shared/schemas.ts` - Added reorderCandidatesSchema +- `src/shared/types.ts` - Added ReorderCandidates type, imported reorderCandidatesSchema +- `src/server/services/thread.service.ts` - Added reorderCandidates, updated createCandidate + getThreadWithCandidates +- `src/server/routes/threads.ts` - Added PATCH /:id/candidates/reorder route +- `tests/services/thread.service.test.ts` - Added 5 new tests for sort_order behavior +- `tests/routes/threads.test.ts` - Added 5 new route tests for reorder endpoint +- `drizzle/0005_clear_micromax.sql` - Generated migration SQL for sort_order column +- `drizzle/meta/0005_snapshot.json` - Drizzle schema snapshot + +## Decisions Made +- Used REAL type for sort_order (not INTEGER) to allow fractional values for future midpoint insertions +- 1000-gap strategy: first candidate = 1000, each subsequent += 1000; reorder resets to (index+1)*1000 +- Applied migration directly via sqlite3 CLI to avoid Drizzle's data-loss warning on existing rows (db had 2 rows; column has DEFAULT 0 so no actual data loss) +- Backfilled existing candidates with ROW_NUMBER * 1000 per thread to give proper initial ordering + +## Deviations from Plan + +None - plan executed exactly as written. + +## Issues Encountered +- `bun run db:push` showed data-loss warning for adding NOT NULL column to existing rows. Applied the migration directly via sqlite3 CLI instead (`ALTER TABLE thread_candidates ADD COLUMN sort_order REAL NOT NULL DEFAULT 0`). The column has DEFAULT 0 so no actual data loss; existing rows got 0 then were backfilled to proper 1000-gap values. + +## Next Phase Readiness +- Backend reorder API fully operational; frontend drag-to-reorder (11-02) can now consume PATCH /api/threads/:id/candidates/reorder +- sort_order values returned in getThreadWithCandidates response, available to frontend for drag state initialization + +--- +*Phase: 11-candidate-ranking* +*Completed: 2026-03-16*