--- phase: 25-file-system plan: 05 subsystem: api, ui tags: [chat-files, file-promotion, drizzle, express, react] # Dependency graph requires: - phase: 25-01 provides: chatFileService and chatFileRoutes with existing DB schema (projectId nullable FK on chatFiles) provides: - PATCH /files/:fileId/promote endpoint that sets projectId on a chat file - promoteToProject service method on chatFileService - ChatFileCard promote button with FolderUp icon and onPromoted callback - promoteFile API client method in chatApi affects: [ui-chat, chat-file-display, project-integration] # Tech tracking tech-stack: added: [] patterns: - "Route ordering: specific sub-routes (/files/:fileId/promote) registered before catch-all (/files/:fileId) in Express" - "Optional promote callback pattern: file.projectId === null && projectId && onPromoted guards UI button render" key-files: created: [] modified: - server/src/services/chat-files.ts - server/src/routes/chat-files.ts - ui/src/api/chat.ts - ui/src/components/ChatFileCard.tsx - .planning/REQUIREMENTS.md key-decisions: - "PATCH /files/:fileId/promote registered before PATCH /files/:fileId in Express router to prevent route shadowing" - "promoteToProject returns null (not throws) when row missing — route handles 404 explicitly" - "Promote button gated on file.projectId === null AND projectId AND onPromoted — all three required for intent" patterns-established: - "Sub-route ordering: register /files/:fileId/promote before /files/:fileId" - "Optional promote UX: ChatFileCard accepts optional projectId + onPromoted; renders button only when both provided and file not yet promoted" requirements-completed: [FILE-12] # Metrics duration: 8min completed: 2026-04-01 --- # Phase 25 Plan 05: File Scope Promotion Summary **PATCH /files/:fileId/promote endpoint and ChatFileCard promote button with FolderUp icon wired to chatApi.promoteFile** ## Performance - **Duration:** ~8 min - **Started:** 2026-04-01T23:56:00Z - **Completed:** 2026-04-01T23:59:00Z - **Tasks:** 2 - **Files modified:** 4 + REQUIREMENTS.md ## Accomplishments - Added `promoteToProject(fileId, projectId)` method to `chatFileService` — updates `projectId` column via Drizzle ORM - Added `PATCH /files/:fileId/promote` Express route before generic `PATCH /files/:fileId` to prevent shadowing - Added `promoteFile(fileId, projectId)` to `chatApi` in `ui/src/api/chat.ts` with `ChatFile` return type - Added promote button to `ChatFileCard` with `FolderUp` icon, optional `projectId` and `onPromoted` props, and loading state - Marked FILE-12 Complete in REQUIREMENTS.md (checkbox + traceability table) ## Task Commits Each task was committed atomically: 1. **Task 1: Add promoteToProject service method and API endpoint** - `c435655f` (feat) 2. **Task 2: Add promote button to ChatFileCard and API client method** - `4c5deb4c` (feat) 3. **REQUIREMENTS.md update** - `9a911040` (feat, in main repo) ## Files Created/Modified - `server/src/services/chat-files.ts` - Added `promoteToProject(fileId, projectId)` method - `server/src/routes/chat-files.ts` - Added `PATCH /files/:fileId/promote` route before generic PATCH route - `ui/src/api/chat.ts` - Added `ChatFile` import and `promoteFile(fileId, projectId)` method to `chatApi` - `ui/src/components/ChatFileCard.tsx` - Added `projectId?`, `onPromoted?` props, `promoting` state, `FolderUp` button - `.planning/REQUIREMENTS.md` - FILE-12 marked `[x]` and `Complete` in traceability table ## Decisions Made - Registered `/files/:fileId/promote` BEFORE `/files/:fileId` in Express router to prevent the generic catch-all from capturing promote requests - `promoteToProject` returns `null` (not throws) when no rows returned, so the route can send an explicit 404 - Promote button only renders when all three conditions are true: `file.projectId === null`, `projectId` prop provided, `onPromoted` prop provided — clean opt-in pattern for callers ## Deviations from Plan None - plan executed exactly as written. ## Issues Encountered None. ## Known Stubs None — `promoteFile` calls the real API endpoint. `ChatFileCard` renders the button conditionally based on real `file.projectId` field. ## User Setup Required None - no external service configuration required. ## Next Phase Readiness - FILE-12 is complete: chat-scoped files can be promoted to project scope via UI - Callers of `ChatFileCard` (e.g. `ChatFilePreview`, `ChatMessage`) can now pass `projectId` and `onPromoted` to enable the promote affordance contextually - No blockers for subsequent plans ## Self-Check: PASSED All files verified present. Commits c435655f and 4c5deb4c confirmed in git log. --- *Phase: 25-file-system* *Completed: 2026-04-01*