/** * Session-delete work-loss guard (coder side). * * Session delete itself lives in apps/server (Docker), which CANNOT see the * host worktree dirs (/tmp/booworktrees) or run git on them. Only BooCoder * (host systemd) can. So the server's DELETE route calls these endpoints * pre-delete to learn whether a session's worktree holds work at risk, and to * stash it. The server owns the gate; coder owns the git truth. */ import type { FastifyInstance } from 'fastify'; import type { Sql } from '../db.js'; import { checkWorktreeWorkAtRisk, stashWorktree } from '../services/worktrees.js'; export function registerWorktreeSafetyRoutes(app: FastifyInstance, sql: Sql): void { // GET risk for a session's worktree(s). One row per session today (PK on // session_id); the loop already handles the Phase-1.5 multi-worktree case. app.get<{ Params: { sessionId: string } }>( '/api/sessions/:sessionId/worktree-risk', async (req) => { const rows = await sql<{ worktree_path: string }[]>` SELECT path AS worktree_path FROM worktrees WHERE session_id = ${req.params.sessionId} `; const reports = []; for (const row of rows) { reports.push(await checkWorktreeWorkAtRisk(row.worktree_path)); } return { reports }; }, ); // Stash a session's worktree(s) — clears the dirty risk; recoverable. app.post<{ Params: { sessionId: string } }>( '/api/sessions/:sessionId/worktree-stash', async (req) => { const rows = await sql<{ worktree_path: string }[]>` SELECT path AS worktree_path FROM worktrees WHERE session_id = ${req.params.sessionId} `; const results = []; for (const row of rows) { results.push({ worktreePath: row.worktree_path, ...(await stashWorktree(row.worktree_path)) }); } return { results }; }, ); }