Flagged by the automated push security review on v2.7.1.
- GET /checkpoints?chat_id= : the chat_id branch filtered by chat_id alone
(any session's chat_id read its checkpoints). Now joins chats and gates on
chats.session_id.
- restoreCheckpoint scope guard was fail-open: `cp.session_id && cp.session_id
!== sessionId` fell through on a null denormalized session_id, allowing a
cross-session restore (worktree reset + transcript trim). Now resolves the
owning session via the checkpoint's chat and denies on missing/mismatch.
- Adds a DB-integration regression for the null-session_id cross-session case.
Both scope authoritatively through chats.session_id (checkpoints.session_id is
a nullable hint). Coder suite 234 passing; 7/7 checkpoint tests (incl. the
regression) against live postgres+git; typecheck clean. Hotfix on v2.7.1.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
#3 Fuzzy patch applier: new pure fuzzy-match.ts (locateMatch, exact→trim→
unicode-canon→Levenshtein≥0.66, refuse-on-ambiguous) wired into pending_changes
applyOne/rewindOne so local-model whitespace/unicode drift in old_string no
longer loses the edit.
#4 Worktree checkpoint + conversation-trim: checkpoints table + checkpoints.ts
(shadow-commit of tracked+untracked into refs/boocode/checkpoints, hooked into
the 3 external-agent dispatcher paths) + POST restore route (reset --hard +
clean -fd -> transcript trim -> backend-session reset) + "Restore to here" UI.
Built by 3 parallel agents; DB-integration testing caught a created_at
self-deletion bug. Coder suite 234 passing; server+coder build + web tsc clean.
Builds on v2.7.0-mit. openspec write-edit-robustness.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>