- Rewrite tests/helpers/db.ts to use drizzle-orm/pglite with async createTestDb()
- Generate initial migration with 13 CREATE TABLE statements in drizzle-pg/
- Add drizzle-pg to biome ignore list (generated files)
- PGlite smoke test confirms migrations apply and seed works
- Replace all 13 sqliteTable definitions with pgTable (pg-core)
- Convert integer timestamps to native timestamp type with defaultNow()
- Convert real columns to doublePrecision, integer used to boolean
- Rewrite db connection to use postgres.js driver with DATABASE_URL
- Rewrite migrate.ts to use postgres-js migrator targeting drizzle-pg/
- Convert seed.ts to async
- Update drizzle.config.ts to postgresql dialect
- Install postgres and @electric-sql/pglite, remove better-sqlite3
The MCP auth spec (2025-06-18+) requires /.well-known/oauth-protected-resource
in addition to /.well-known/oauth-authorization-server. Claude fetches
the protected resource metadata first after receiving a 401, then discovers
the authorization server from it. Also fixes WWW-Authenticate header to
use absolute URL pointing to the protected resource endpoint.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
verifyAccessToken is async and returns a Promise. Without await,
the Promise object is always truthy, so any Bearer token (even
invalid ones) was accepted. This fixes MCP OAuth authentication.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Required for claude.ai browser-based OAuth flows that make
cross-origin requests to discovery, token, and MCP endpoints.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Add well-known metadata, dynamic client registration, authorization
flow with PKCE, and token exchange/refresh endpoints with route-level
integration tests.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Document the Gitea Actions release pipeline and how to trigger it
via API with patch/minor/major bump types.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
The MCP SDK v1.29.0 changed server.tool() to require Zod schemas
(raw shapes) instead of plain JSON Schema objects. The old format
triggered "expected a Zod schema or ToolAnnotations" errors.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Replace the plain "Sign out" button in the header with a user icon
that opens a dropdown menu containing Settings and Sign out options.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Quantity was missing from three places in item.service.ts:
- getAllItems didn't select it (API returned undefined)
- createItem didn't pass it to insert (always used DB default of 1)
- updateItem type didn't include it (silently stripped from updates)
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Two fixes:
- Remove onSettled clearing tempItems before refetch completes,
let useEffect clear it when fresh server data arrives
- Track isDragging ref to suppress edit panel click after drag
- Remove layout="position" which interfered with reorder detection
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Remove dragControls/dragListener pattern which prevented onReorder
from firing. The whole row is now the drag target with visual feedback
(scale + shadow). Grip icon remains as a visual indicator.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
- Remove transition-all from list items (fights framer-motion layout)
- Add layout="position" to Reorder.Item for proper sibling tracking
- Replace CSS gap with marginBottom (gap confuses layout engine)
- Add explicit short transition duration for snappy reorder
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
The previous approach used onPointerUp on the Reorder.Group which
fired unreliably — triggering on non-drag clicks and sometimes not
at all after a drag. Moving to onDragEnd on each Reorder.Item gives
clean, predictable drag-to-reorder behavior.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Adds Gear/Planning/Setups pill tabs to the collection page so users
can switch tabs without going back to the dashboard. Also skips
React Query retries on 404 responses for immediate error display.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Prevents 10-second loading skeleton when navigating to non-existent
threads, setups, or items. Shows error/not-found state immediately.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
The Drizzle migration journal and snapshot for 0008 (quantity column)
were not committed, causing test failures in CI. Also updates CI to
trigger on all branches, not just Develop.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Adds export (GET /api/items/export) and import (POST /api/items/import) routes
backed by a pure csv.service with no external deps, plus useExportItems/useImportItems
hooks and an Import/Export section in the Settings page.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Adds SetupImpactSelector dropdown and ImpactDeltaBadge inline badge, wired into the thread detail page. Delta badges appear on CandidateListItem, CandidateCard, and ComparisonTable (Weight Impact / Price Impact rows) whenever a setup is selected for comparison.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Adds POST /api/items/:id/duplicate endpoint, useDuplicateItem hook, and a
Duplicate button on ItemCard (collection view only) that opens the new item
for editing immediately after creation.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Implements computeImpactDeltas pure function with 8 TDD tests covering replace/add/none modes and null weight/price handling. Adds useImpactDeltas hook, categoryId to ThreadWithCandidates, and selectedSetupId state to uiStore.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- totals.service: multiply weight/cost sums by quantity in category and global totals
- setup.service: multiply by quantity in getAllSetups SQL subqueries; expose quantity in getSetupWithItems item list
- thread.service: explicitly pass quantity: 1 when inserting resolved item
- ItemForm: add Quantity number input (min=1, default=1) after price field
- ItemCard: show ×N badge next to item name when quantity > 1
- CollectionView: pass quantity prop to ItemCard in both filtered and grouped views
- $setupId.tsx: pass quantity to ItemCard; multiply by quantity in client-side per-setup totals
- WeightSummaryCard: multiply by quantity in all chart and legend weight calculations
- useItems / useSetups: add quantity to ItemWithCategory / SetupItemWithCategory interfaces
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Add integer quantity column (default 1) to the items table, generate
the corresponding Drizzle migration, and extend createItemSchema /
updateItemSchema with an optional positive-integer quantity field.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Runs as a separate job after unit tests pass, using the official
Playwright Docker image with Chromium pre-installed.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Prevents bare `bun test` from picking up Playwright .spec.ts files
in the e2e/ directory.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Also fix CandidateListItem to not use Reorder.Item when isActive=false,
which caused a framer-motion crash on resolved thread detail pages.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Covers dashboard card rendering (item count, nav links, active thread/setup counts)
and collection page (gear display, search, category filter, tab switching).
Updates playwright config to serve production build with pre-seeded test DB.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>