Files
boocode/docs/features/git-diff-panel/artifacts/.discovery-notes.md
indifferentketchup ca028a4024 docs: add git-diff-panel implementation planning artifacts
Implementation decision log, iteration history, synthesis input, the
implementation plan, and discovery notes for the git-diff-panel feature.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-03 02:26:04 +00:00

7.4 KiB
Raw Blame History

Discovery notes — git-diff-panel implementation

Single source of truth for project context. Specialists: read this first; do NOT re-grep what is here. Source spec: ../feature-specification.md (+ decision-log.md D1D18, team-findings.md F1F21). No feature-technical-notes.md (no load-bearing mechanic qualified at spec time).

Tech stack

  • pnpm monorepo. apps/web (React 18 + Vite SPA), apps/server (BooChat — Fastify + postgres, native inference, read-only file/git tools), apps/coder (BooCoder — host systemd service, port 9502, write-capable, runs git writes today), apps/booterm. TS strict, NodeNext (.js import suffixes) on server + coder. @boocode/contracts package single-sources WS frames + provider/message types.
  • Tests: vitest ^3. server pnpm -C apps/server test; coder pnpm -C apps/coder test (globals:false — import describe/it/expect). Include glob src/**/__tests__/**/*.test.ts. No web test harness. DB-integration tests opt-in via DATABASE_URL + describe.runIf.
  • Deploy by surface: apps/coder change → sudo systemctl restart boocoder; apps/web|server change → docker compose up --build -d boocode (rebuilds web+server from working tree; web HMR live on dev only).
  • Shiki ^1.29.2 already in apps/web (CodeBlock.tsx, FileViewerOverlay.tsx); lang:'diff' is valid — the path of least resistance for rendering a unified diff. No react-diff-viewer / diff2html installed.

ADRs / coding standards

  • No docs/adr/. Decisions live in boocode_roadmap.md (Decisions log) + per-app CLAUDE.md (auto-loaded when editing that subtree) + openspec/changes/archived/.
  • Coding standards: docs/coding-standards/ (canonical), surfaced via .claude/rules/coding-standards/ path-scoped indexes. Cross-cutting conventions in root CLAUDE.md.

Code touch points

Git data sources today (read)

  • apps/server/src/services/git_meta.ts:44 getGitMeta(rootPath) — runs runGit([...argv], rootPath) (SAFE: discrete argv, no shell), returns {branch,is_dirty,ahead,behind} only (NO diff text), 30s cache. This is the read-side precedent for the new read route and the F2 argv-safety bar. Uses rev-list --left-right --count HEAD...@{upstream} (the upstream-resolution precedent for D2/D13's base).
  • apps/server/src/routes/projects.ts:426 GET /api/projects/:id/git — returns the GitMeta shape (no diff). api.projects.git(id) (apps/web/src/api/client.ts:154), polled 30s by useProjectGit. The new read route slots beside this.
  • apps/coder/src/services/worktrees.ts:46 diffWorktree(worktreePath, projectPath, {baseRef}) — produces a real unified git diff <base>...<head> but via hostExec(shell string) + shellEscape, and commits with per-invocation -c user.email=boocoder@local -c user.name=BooCoder. Caution: this is the SHELL pattern F2 warns against; the new git-write ops should follow git_meta.ts's argv runGit, not this. But it IS the precedent that the coder/host can run git writes and inject identity per-invocation.
  • pending_changes.diff is unified only for external-agent edits; native BooCode edits store {old,new} JSON. The new Git panel is project-repo git state, complementary to the pending-changes panel (spec Coordinations).

The "file browser" host (apps/web)

  • apps/web/src/components/RightRail.tsx — the right-side file panel (NOT a workspace pane; the legacy file_browser pane kind is dead). Header at ~:209 renders a static "Files" label + 2 icon buttons; desktop w-64, mobile drawer w-[85vw] max-w-sm via useRightRailDrawer. Already applies max-md:min-h-[44px] to header buttons (the 44px convention for D18). Fetches tree via api.projects.files/listDir/viewFile. The Files / Git tab (D1, D16) is added to THIS header — and must fit one line (toolbar-fit rule, D18). open_file_in_browser sessionEvent already opens the panel programmatically.
  • Rendered in App.tsx:~89 for every /session/:id (so the panel — and D8's Git tab — appears in all session types). Session.tsx:~397 mobile FolderTree toggle button (the dirty-indicator host for D17).

Refresh-trigger plumbing (F20, D10)

  • message_complete WS frame = "agent turn complete" trigger. Coder pending-changes refresh precedent: usePendingChanges in CoderPane.tsx:~786 refetches on message-complete. Pending-apply/discard has no named frame — driven by the refresh() callback. Adding a new event/frame requires the CLAUDE.md parity steps: a new WS frame → BOTH server/contracts WsFrameSchema AND web WsFrame (apps/web/src/api/types.ts); a new sessionEvent → a case in useSidebar.ts applyEvent.

Security surfaces

  • apps/server/src/services/path_guard.ts resolveProjectRoot(project.path) — derives + scopes project paths from the DB project row, never from the request (the F2 "server-derived root + relative-arg validation" precedent). secret_guard.ts deny-list applies to the assistant's read tools (not the user's git panel — spec D8). HTML artifacts run in a sandboxed iframe with connect-src 'none' (BOOCHAT.md) — the evidence behind F1 (an artifact cannot POST to the new write endpoints). No app-layer auth (Authelia at the proxy; 'default' user key).
  • BooCoder proxy: apps/server forwards /api/coder/* to apps/coder (coder-proxy.ts) — the route by which a web client reaches coder (host) endpoints.

Recent activity / precedent

  • HEAD ~v2.7.11. Pure-helper + TDD precedent: backends/turn-guard.ts, lifecycle-decisions.ts, mistake-tracker.ts (pure module + unit test, then wire). The diff-parse / base-resolution / mode-decision logic should follow it (testable pure helpers).
  • Sibling backlog plan at docs/plans/post-review-backlog/ is the format precedent for the plan files.

Enumerated gaps / open implementation questions for the team

  1. THE architecture decision (F18 / JD-005): which service owns the new git operations? Options: (a) all in apps/server (read + write) — but apps/server is "read-only" by posture (the git-write would be a new write surface there); (b) read in apps/server (consistent with git_meta), write in apps/coder (the write-capable host service, already runs git writes) via the /api/coder/* proxy; (c) all in apps/coder. Note apps/coder runs on the host (can git-write to the project path); apps/server runs in Docker. The diff panel appears in ALL sessions incl. plain BooChat — does that constrain which service answers? software-architect to settle.
  2. No HTTP route returns a full working-tree git diff today — a new read route is needed regardless.
  3. The diff-parse + per-file expand/collapse + staged/unstaged grouping + Shiki lang:'diff' rendering is net-new UI in RightRail.tsx; no unified-diff renderer exists yet.
  4. F2 argv-safety: the new write path must use discrete-argv git (like git_meta.runGit), NOT the hostExec(shell) pattern worktrees.ts uses — concrete bar for the security/structural recommendation.
  5. Committer identity (D6/D12/F3): server-derived. Precedents differ — worktrees.ts injects -c user.email=boocoder@local; project_bootstrap.ts hardcodes samkintop@gmail.com. The plan must pick the source for a USER commit (host git config vs a configured value) — not request-supplied.
  6. Base resolution (D2/D13): @{upstream} precedent exists in git_meta; default-branch fallback unspecified in code (needs git symbolic-ref refs/remotes/origin/HEAD or rev-parse --abbrev-ref origin/HEAD).