---
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*
