121 lines
5 KiB
Markdown
121 lines
5 KiB
Markdown
---
|
|
phase: 25-file-system
|
|
plan: "06"
|
|
subsystem: api
|
|
tags: [git, versioning, file-history, execFile, child_process]
|
|
|
|
# Dependency graph
|
|
requires:
|
|
- phase: 25-01
|
|
provides: chatFileService, chatFileRoutes, StorageService integration
|
|
- phase: 25-00
|
|
provides: chat_files DB schema, ChatFile shared types
|
|
|
|
provides:
|
|
- gitFileService with ensureRepo, commitFile, getLog using safe execFile
|
|
- GET /files/:fileId/history endpoint returning git log entries
|
|
- Git commit on every file upload (non-blocking fire-and-forget)
|
|
- ChatFileHistoryEntry shared type
|
|
|
|
affects: [25-file-system, any plan using file version history]
|
|
|
|
# Tech tracking
|
|
tech-stack:
|
|
added: [node:child_process execFile, node:util promisify]
|
|
patterns: [fire-and-forget git commit after upload, execFile array args for shell-injection safety]
|
|
|
|
key-files:
|
|
created:
|
|
- server/src/services/git-file-service.ts
|
|
modified:
|
|
- server/src/routes/chat-files.ts
|
|
- packages/shared/src/types/chat.ts
|
|
- packages/shared/src/index.ts
|
|
- .planning/REQUIREMENTS.md
|
|
|
|
key-decisions:
|
|
- "Used execFile (not exec) for all git commands — array-based args prevent shell injection"
|
|
- "Git commit is fire-and-forget (.catch(() => {})) so upload response is not blocked"
|
|
- "History route placed before /content route to prevent Express /files/:fileId/* ambiguity"
|
|
- "resolveDefaultStorageDir() used to find storage root — same path LocalDiskProvider uses"
|
|
|
|
patterns-established:
|
|
- "gitFileService: factory function returning interface with ensureRepo/commitFile/getLog"
|
|
- "ensureRepo lazily initializes git repo on first use — idempotent via .git dir check"
|
|
|
|
requirements-completed: [FILE-09, FILE-10]
|
|
|
|
# Metrics
|
|
duration: 5min
|
|
completed: 2026-04-01
|
|
---
|
|
|
|
# Phase 25 Plan 06: Git File Versioning Summary
|
|
|
|
**Git versioning layer added to file uploads: gitFileService wraps git CLI with safe execFile, every upload creates a commit, GET /files/:fileId/history exposes git log**
|
|
|
|
## Performance
|
|
|
|
- **Duration:** 5 min
|
|
- **Started:** 2026-04-01T21:59:30Z
|
|
- **Completed:** 2026-04-01T22:04:30Z
|
|
- **Tasks:** 2
|
|
- **Files modified:** 5
|
|
|
|
## Accomplishments
|
|
- Created gitFileService with ensureRepo (lazy git init), commitFile (add+commit via execFile), getLog (parse git log output)
|
|
- Wired git commit into POST /conversations/:id/files upload flow as non-blocking fire-and-forget
|
|
- Added GET /files/:fileId/history endpoint returning paginated git log entries (max 100)
|
|
- Added ChatFileHistoryEntry interface to shared types and exported from shared package index
|
|
|
|
## Task Commits
|
|
|
|
Each task was committed atomically:
|
|
|
|
1. **Task 1: Create gitFileService and ChatFileHistoryEntry type** - `eb954635` (feat)
|
|
2. **Task 2: Wire git commits into upload flow and add history endpoint** - `6ba745b9` (feat)
|
|
|
|
**Requirements metadata:** `637ecc74` (chore: mark FILE-09 and FILE-10 complete)
|
|
|
|
## Files Created/Modified
|
|
- `server/src/services/git-file-service.ts` - Git service with ensureRepo/commitFile/getLog methods
|
|
- `server/src/routes/chat-files.ts` - Added git import, storageDir, gitSvc, fire-and-forget commit, /history route
|
|
- `packages/shared/src/types/chat.ts` - Added ChatFileHistoryEntry interface
|
|
- `packages/shared/src/index.ts` - Exported ChatFileHistoryEntry
|
|
- `.planning/REQUIREMENTS.md` - Marked FILE-09 and FILE-10 as Complete
|
|
|
|
## Decisions Made
|
|
- Used `execFile` (promisified from `node:util`) instead of `exec` — array args cannot be shell-injected even if objectKey contained special chars
|
|
- Git commit is fire-and-forget (`gitSvc.commitFile(...).catch(() => {})`) — upload response sent immediately, git tracking happens asynchronously
|
|
- History endpoint placed before `/files/:fileId/content` so Express route matching finds `/history` before falling through to `:fileId` catch-all patterns
|
|
- Used `resolveDefaultStorageDir()` from `home-paths.ts` — the exact same path construction that `LocalDiskProvider` uses for its `root` directory
|
|
|
|
## Deviations from Plan
|
|
|
|
None - plan executed exactly as written.
|
|
|
|
## Issues Encountered
|
|
- Worktree was on a different branch (`worktree-agent-afd26110`) without phase-25 prerequisite files. Resolved by checking out required files from `gsd/phase-25-file-system` branch before applying plan 06 changes.
|
|
|
|
## User Setup Required
|
|
None - no external service configuration required.
|
|
|
|
## Next Phase Readiness
|
|
- Git versioning infrastructure complete for FILE-09 and FILE-10 requirements
|
|
- gitFileService ready for reuse by any future plan needing git tracking on storage files
|
|
- History endpoint available at GET /files/:fileId/history?limit=N
|
|
|
|
## Self-Check: PASSED
|
|
|
|
- FOUND: `server/src/services/git-file-service.ts`
|
|
- FOUND: `packages/shared/src/types/chat.ts` with ChatFileHistoryEntry
|
|
- FOUND: `packages/shared/src/index.ts` exporting ChatFileHistoryEntry
|
|
- FOUND: `server/src/routes/chat-files.ts` with gitSvc.commitFile and /history route
|
|
- FOUND: commit `eb954635` (Task 1)
|
|
- FOUND: commit `6ba745b9` (Task 2)
|
|
- FOUND: commit `637ecc74` (REQUIREMENTS.md)
|
|
- FOUND: commit `f79b79c9` (docs metadata)
|
|
|
|
---
|
|
*Phase: 25-file-system*
|
|
*Completed: 2026-04-01*
|