fix(project): relocate session history when a worktree is renamed or moved - #45133
fix(project): relocate session history when a worktree is renamed or moved#45133neohiro wants to merge 5 commits into
Conversation
…moved Renaming or moving a project folder outside of opencode (file explorer, mv, IDE refactor) orphaned every stored reference to the old absolute path: sessions disappeared from the UI and Desktop kept a ghost project whose delete call failed server-side. - add relocation module with an encoding-preserving path splicer (plain / JSON-escaped at two depths / drive-less forms; safe against sibling folders, other users, and recorded content such as diffs) - migrate session.directory/path plus message, part, todo and event payload text in one transaction when a missing worktree re-resolves to the same project identity during fromDirectory() - document the failure model and migration design (docs/) Field evidence: recovered a real affected install where ~6.9k payload fields across four encodings had to move together; naive equality only matched a handful (anomalyco#23248, anomalyco#34737, anomalyco#44256, anomalyco#44538).
|
Thanks for your contribution! This PR doesn't have a linked issue. All PRs must reference an existing issue. Please:
See CONTRIBUTING.md for details. |
- drop dead leadingGroupOffset / unused rewriteCandidates helper
- unicode-safe boundaries (\p{L}\p{N} guards, u flag): glued unicode
neighbors can no longer bypass the bare-form guard; ReDoS-safety noted
- throw on identical locations (case/separator-insensitive) - nothing to
relocate; callers degrade instead of no-op scanning
- relocation scoping tightened to sessions actually keyed to the old
location (was: whole project); sessions metric now reflects migrated rows
- call site degrades gracefully: a failed migration logs and continues,
it can no longer fail instance startup
- docs: failure semantics + reference implementation pointers
tests: 18/18 (adds triple-depth payload, g-flag statefulness regression,
unicode guard, trailing/mixed separators, identical-location throws,
1MB perf smoke ~14ms)
|
Audit/hardening round pushed (38c817f):
Tests grew 12 → 18 (all green via |
- project.update accepts worktree: when it points at a new location the stored history (sessions, messages, parts, todos, events) migrates via the relocation splicer, so Right-click > Edit > folder change in the Desktop keeps every session visible (anomalyco#23248, anomalyco#34737) - new DELETE /project/:id with mode=cascade|detach removes registration, directories and (cascade) all sessions + event aggregates; global project and unknown ids fail with ProjectNotFoundError - httpapi group + handler wiring for both operations GUI follow-up lands after `bun run codegen` regenerates sdk clients: Edit dialog gains a Folder field bound to update({worktree}), project context menu gains Delete... with a history-loss confirmation dialog.
|
Added rename-via-update (worktree field triggers full history relocation) and DELETE /project/:id?mode=cascade|detach (bf6401d). Desktop wiring: Edit dialog Folder field + context-menu Delete with confirmation once \�un run codegen\ regenerates sdk clients - happy to push that immediately if maintainers want it in this same PR. |
|
Thanks for updating your PR! It now meets our contributing guidelines. 👍 |
- Edit dialog gains a Folder field: changing it sends worktree through project.update, which relocates all stored history server-side - Danger zone with two-step Delete confirmation (cascade removes the project plus every session and its history) calling project.remove - sdk client: worktree param on update + hand-added remove method (bun run codegen will normalize these gen files) - en locale strings for folder/delete flows
|
GUI wiring pushed (3bca359): Edit Project dialog now has a Folder field (rename-with-relocation via |
…nstead of a 500 defect
Issue for this PR
Closes #23248, closes #34737. Towards #44256, #44538, #29703, #27822.
Type of change
What does this PR do?
Renaming or moving a project folder outside opencode orphaned every session (empty UI history, ghost Desktop entry whose Delete failed). This PR:
fromDirectory()resolves the same project identity at a new path, all references migrate:session.directory/path, plus message/part/todo/event payloads, rewritten by an encoding-preserving splicer that handles plain, forward-slashed, drive-less and JSON-escaped (1x/2x) forms while leaving recorded content (diffs, source quotes, sibling folders, other users' paths) untouched.project.updateacceptsworktree; changing it relocates history the same way (this is what the Desktop Edit-project dialog will call).DELETE /project/:id?mode=cascade|detach: cascade removes registration, directories, sessions, messages/parts/todos/event aggregates; global project is protected.project-rename-move-reliability.mddocuments the failure model measured on a real affected install (~6.9k payload fields across 4 encodings had to move together).Follow-up (small, after codegen): Edit dialog gains a Folder field bound to
update({worktree}), project context menu gains Delete… with a history-loss confirmation callingproject.remove. Server surface for both is complete here.How did you verify your code works?
bun test test/project/relocation-paths.test.ts: 18 tests green (escape depths incl. triple-JSON, moves shallower/deeper, case handling, unicode boundaries, g-flag statefulness regression, sibling/other-user guards, 1MB perf smoke ~14ms).Screenshots / recordings
No UI pixels changed in this PR; the follow-up GUI wiring PR will include recordings of Rename and the Delete confirmation.
Checklist