- Reads all image files from uploads/ directory
- Uploads each to S3 bucket preserving original filenames as object keys
- Handles errors per-file without aborting entire migration
- Preserves original files (manual deletion after verification)
- Replace all /uploads/ path construction with imageUrl presigned URLs
- Add imageUrl prop to ItemCard, CandidateCard, CandidateListItem, ComparisonTable
- Update ImageUpload to use presigned URLs + local preview for new uploads
- Pass imageUrl through from parent components (CollectionView, forms, routes)
- Replace Bun.write/mkdir with uploadImage() from storage.service
- Remove uploadsDir parameter from fetchImageFromUrl
- Update tests to mock storage service instead of checking filesystem
- Add MinIO + mc init container to docker-compose.dev.yml (fixed creds, console on :9001)
- Add MinIO + mc init container to docker-compose.yml (env var creds, no console)
- Add S3 env vars to app service in production compose
- Remove uploads volume from production compose (replaced by MinIO)
- Add S3 configuration section to .env.example
Route and MCP test files were calling createTestDb() without await,
causing db to be a Promise object instead of a Drizzle instance.
Also rewrote auth route tests for OIDC-based auth (merge picked old version).
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Merge conflict resolution picked the old password-based oauth tests.
Restored the OIDC session mock version with proper userId destructuring.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
- All 8 route test files destructure { db, userId } from createTestDb()
- All route test middleware sets c.set("userId", userId)
- MCP tools.test.ts passes userId to all registerXTools(db, userId) calls
- MCP tools.test.ts passes userId to getCollectionSummary(db, userId)
- Added 4 cross-user isolation tests for MCP tools (items, item by ID, threads, collection summary)
- OAuth test db type annotation updated for new createTestDb return shape
- Images test now uses createTestDb with userId context
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
- Destructure { db, userId } from createTestDb() in all 8 service test files
- Pass userId to every service function call
- Add cross-user isolation tests for items, categories, threads, setups
- Add composite unique constraint test for categories
- Update verifyApiKey assertions to check { userId } return
- Update verifyAccessToken assertions to check { userId } return
- Pass userId to exchangeCode and refreshAccessToken calls
- Update createMcpServer signature to accept (db, userId)
- MCP auth middleware resolves userId from API key and Bearer token
- Store userId alongside transport in session map
- All 4 tool registration functions accept and pass userId
- Collection summary resource passes userId to all service calls
- Extract userId via c.get('userId') in every route handler
- Pass userId to all service function calls as second argument
- Update settings routes to use composite key [userId, key]
- Update Env type to include userId in Variables
- Auth routes pass userId to API key management functions
- SUMMARY.md documents 7 service files updated with userId parameter
- STATE.md advanced to plan 2 of 4 in phase 16
- ROADMAP.md updated with plan progress
- Requirements MULTI-01, MULTI-02, MULTI-03, MULTI-06 marked complete
- All functions accept userId, no more prodDb defaults
- Thread operations verify ownership via and(eq(id), eq(userId))
- Candidate operations verify parent thread ownership before proceeding
- resolveThread includes userId in new item insert and verifies category ownership
- Setup operations use and() for composite id+userId conditions
- syncSetupItems validates both setup and item ownership via inArray
- updateItemClassification and removeSetupItem verify setup ownership
- Auth service: reordered createApiKey params to (db, userId, name)
- verifyApiKey unchanged (already returns { userId } from Plan 01)
- All functions accept userId as second parameter, no more prodDb defaults
- All queries filter by eq(table.userId, userId) for data isolation
- Get-by-id, update, delete use and() for composite id+userId conditions
- deleteCategory uses dynamic getOrCreateUncategorized(db, userId) not hardcoded ID
- CSV import scopes category lookup/creation and item creation to userId
- CSV export filters items by userId
- Category service converted from sync SQLite to async Postgres patterns
- Add 16-01-SUMMARY.md with schema, middleware, and test changes
- Update STATE.md with phase 16 progress and decisions
- Update ROADMAP.md with plan progress (1/4 complete)
- Mark MULTI-01, MULTI-04, MULTI-06 complete in REQUIREMENTS.md
- createTestDb uses PGlite with drizzle-pg migrations
- Seeds test user with logtoSub and per-user Uncategorized category
- Returns { db, userId } instead of just db
- Add createSecondTestUser helper for cross-user isolation tests
- verifyApiKey returns { userId } | null instead of boolean
- verifyAccessToken returns { userId } | null instead of boolean
- Add getOrCreateUser upsert function in auth.service
- Add getOrCreateUncategorized helper in category.service
- requireAuth sets userId on Hono context for all 3 auth methods
- Remove GET bypass: all API routes require auth for userId resolution
- Keep bypass for /api/auth and /api/health paths
- Add await to all oauth service calls in routes (registerClient, getClient, etc.)
- Rewrite oauth tests to use mocked OIDC session instead of createUser/password
- Test consent-based authorize flow instead of credential-based flow
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
The executor agents wrote sync SQLite-style calls (.get(), .all(), .run())
instead of the async Postgres pattern established in Phase 14. Fixed:
- auth.service.ts: use await + destructuring for all DB operations
- auth routes: await listApiKeys
- All auth test files: async createTestDb(), await service calls
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
- E2E seed creates API key instead of user for authentication
- Auth service tests cover only API key CRUD (removed user/session tests)
- Auth middleware tests validate three-way auth: API key, Bearer token, OIDC session
- Auth route tests mock getAuth for OIDC session, test /me and /keys endpoints
- Remove all references to createUser, verifyPassword, createSession in auth tests
- Login page redirects to Logto instead of showing credential form
- AuthState uses string id (Logto sub claim) instead of number
- Remove useLogin, useSetup, useChangePassword hooks
- useLogout redirects to /logout (server-side OIDC logout)
- Remove ChangePasswordSection from settings page
- Update UserMenu to use new useLogout API
- Settings page shows API keys section when authenticated
- Add 15-02-SUMMARY.md with execution results
- Update STATE.md with position, decisions, session info
- Update ROADMAP.md with plan progress
- Mark AUTH-01, AUTH-02, AUTH-03 requirements complete