docs(25-06): complete git file versioning plan — SUMMARY, STATE, ROADMAP updated

- gitFileService with ensureRepo/commitFile/getLog using safe execFile
- GET /files/:fileId/history endpoint for git version history
- FILE-09 and FILE-10 marked Complete
This commit is contained in:
Nexus Dev 2026-04-02 00:06:18 +00:00
parent 0b9af89ef7
commit 59991367dd
3 changed files with 123 additions and 9 deletions

View file

@ -116,7 +116,7 @@ Plans:
5. When an agent generates a placeholder asset, `PLACEHOLDERS.md` is updated in the project directory; when the placeholder is replaced, the DB records the replacement chain and the manifest reflects the change
6. A file uploaded in a conversation linked to a project lives in `files/projects/<slug>/`; a file from an unlinked conversation lives in `files/chat/<conversation-id>/`; the user can promote a chat file to project scope
7. Voice input is available when local AI is enabled: user can hold the record button, speak, see a transcription preview, and confirm to send
**Plans:** 7/9 plans executed
**Plans:** 8/9 plans executed
Plans:
- [x] 25-00-PLAN.md — DB schema (chat_files + chat_file_references), shared types/validators, test stubs
@ -125,7 +125,7 @@ Plans:
- [x] 25-03-PLAN.md — UI: ChatFilePreview/ChatFileCard components, ChatMessage/ChatPanel wiring
- [x] 25-04-PLAN.md — Gap: Code syntax-highlighted preview (FILE-06) + admin claims (FILE-07, FILE-13)
- [x] 25-05-PLAN.md — Gap: File scope promotion API + UI (FILE-12)
- [ ] 25-06-PLAN.md — Gap: Git integration for file operations + version history (FILE-09, FILE-10)
- [x] 25-06-PLAN.md — Gap: Git integration for file operations + version history (FILE-09, FILE-10)
- [ ] 25-07-PLAN.md — Gap: Agent-generated files + placeholder tracking (FILE-08, FILE-11)
- [x] 25-08-PLAN.md — Gap: Voice input via Whisper (INPUT-04) + admin claims (INPUT-02, INPUT-03)
@ -227,5 +227,5 @@ All 65 v1 requirements are mapped to exactly one phase. No orphans.
| 22. Agent Streaming | v1.3 | 6/6 | Complete | 2026-04-01 |
| 23. Brainstormer Flow | v1.3 | 4/4 | Complete | 2026-04-01 |
| 24. Search, History & Branching | v1.3 | 4/4 | Complete | 2026-04-01 |
| 25. File System | v1.3 | 7/9 | In Progress| |
| 25. File System | v1.3 | 8/9 | In Progress| |
| 26. PWA & Performance | v1.3 | 0/? | Not started | - |

View file

@ -3,14 +3,14 @@ gsd_state_version: 1.0
milestone: v1.3
milestone_name: milestone
status: executing
stopped_at: Completed 25-file-system-25-04-PLAN.md
last_updated: "2026-04-02T00:04:47.523Z"
stopped_at: Completed 25-file-system-25-06-PLAN.md
last_updated: "2026-04-02T00:06:07.957Z"
last_activity: 2026-04-02
progress:
total_phases: 6
completed_phases: 4
total_plans: 30
completed_plans: 28
completed_plans: 29
percent: 100
---
@ -26,7 +26,7 @@ See: .planning/PROJECT.md (updated 2026-03-30)
## Current Position
Phase: 25 (file-system) — EXECUTING
Plan: 3 of 9
Plan: 4 of 9
Status: Ready to execute
Last activity: 2026-04-02
@ -84,6 +84,7 @@ Progress: [██████████] 100%
| Phase 25-file-system P03 | 3 | 2 tasks | 7 files |
| Phase 25-file-system P08 | 8 | 2 tasks | 5 files |
| Phase 25-file-system P04 | 5min | 2 tasks | 5 files |
| Phase 25-file-system P06 | 5 | 2 tasks | 5 files |
## Accumulated Context
@ -153,6 +154,9 @@ Recent decisions affecting current work:
- [Phase 25-file-system]: execFileAsync over exec for whisper CLI invocation — no shell injection risk with system-generated tmpPath
- [Phase 25-file-system]: Used DOMParser + replaceChildren to safely render hljs output — avoids raw HTML injection pattern while preserving same visual output as rehype-highlight
- [Phase 25-file-system]: highlight.js added as explicit ui/package.json dependency (was transitive via rehype-highlight only)
- [Phase 25-file-system]: Used execFile (not exec) for git commands in gitFileService — array-based args prevent shell injection
- [Phase 25-file-system]: Git commit is fire-and-forget after upload — response not blocked by git operation
- [Phase 25-file-system]: History route placed before /content route to avoid Express path ambiguity on /files/:fileId/*
### Pending Todos
@ -165,6 +169,6 @@ None yet.
## Session Continuity
Last session: 2026-04-02T00:04:47.520Z
Stopped at: Completed 25-file-system-25-04-PLAN.md
Last session: 2026-04-02T00:06:07.954Z
Stopped at: Completed 25-file-system-25-06-PLAN.md
Resume file: None

View file

@ -0,0 +1,110 @@
---
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
---
*Phase: 25-file-system*
*Completed: 2026-04-01*