fix(worktrees): protect user changes across ambiguous move failures
Post-merge hardening of the session-to-worktree move (#2998), driven by review findings on the follow-up pass: - an ambiguous transport failure (relay abort, timeout) on the change-carrying move no longer force-deletes the fresh worktree that may hold the user's only copy of their changes; both intent kinds surface honest guidance and refresh both directories - assertSdkSuccess re-tags ambiguous transport errors when wrapping SDK failures, so ambiguity classification survives the wrapper on every path, matching the prompt-send precedent - session liveness checks scan all child stores plus the global status index, and report unknown (not idle) when no store covers the session — an evicted background directory can no longer make a busy session look movable - incomplete-rollback errors carry the changes-may-be-in-destination guidance instead of swallowing it - move-message assembly shared across the three call sites; tests now exercise the real ambiguity classifier (extracted to send-failure-classification.ts) instead of a hand-mirrored mock - i18n fallout from the merge train: Turkish gains the 21 worktree-move keys, all 12 locales get the hedged ambiguous-failure toast; owning DOCUMENTATION.md files record the new contracts
This commit is contained in:
@@ -3015,6 +3015,7 @@ export const dict = {
|
||||
'sessions.sidebar.session.moveToWorktree.confirm.cancel': 'Abbrechen',
|
||||
'sessions.sidebar.session.moveToWorktree.sourceVerificationFailed': 'Die Änderungen in der Quelle konnten nicht geprüft werden. Es wurde kein Worktree und keine Sitzung geändert.',
|
||||
'sessions.sidebar.session.moveToWorktree.applyChangesFailed': 'Das Ziel konnte die Änderungen der Quelle nicht übernehmen. Sitzung und Änderungen wurden nicht verschoben. Versuche es erneut und wähle Nur Sitzung verschieben.',
|
||||
'sessions.sidebar.session.moveToWorktree.changesMayBeInDestination': 'Die Verbindung brach ab, bevor das Ziel den Wechsel bestätigt hat. Die Sitzung wurde möglicherweise nicht verschoben, und deine nicht committeten Änderungen liegen eventuell schon im Ziel-Worktree. Sieh dort nach, bevor du es erneut versuchst.',
|
||||
'sessions.sidebar.session.export.failedLoadHistory': 'Die vollständige Sitzungshistorie konnte nicht geladen werden',
|
||||
'sessions.sidebar.session.status.movingToWorktree': 'Sitzung wird in einen neuen Worktree verschoben',
|
||||
'gitView.header.updateBranch': 'Branch aktualisieren',
|
||||
|
||||
@@ -563,6 +563,7 @@ export const dict = {
|
||||
'sessions.sidebar.session.moveToWorktree.confirm.cancel': 'Cancel',
|
||||
'sessions.sidebar.session.moveToWorktree.sourceVerificationFailed': 'Source changes could not be verified. No worktree or session was changed.',
|
||||
'sessions.sidebar.session.moveToWorktree.applyChangesFailed': 'The destination could not accept the source changes. The session and source changes were not moved. Retry and choose Move session only.',
|
||||
'sessions.sidebar.session.moveToWorktree.changesMayBeInDestination': 'The connection dropped before the destination confirmed the move. The session may not have moved, and your uncommitted changes may already be in the destination worktree. Check there before retrying.',
|
||||
'sessions.sidebar.session.menu.runFusion': 'Run fusion',
|
||||
'sessions.sidebar.session.menu.openInSidePanel': 'Open in Side Panel',
|
||||
'sessions.sidebar.session.actions.openInEditor': 'Open in Editor',
|
||||
|
||||
@@ -564,6 +564,7 @@ export const dict: Record<I18nKey, string> = {
|
||||
"sessions.sidebar.session.moveToWorktree.confirm.cancel": "Cancelar",
|
||||
"sessions.sidebar.session.moveToWorktree.sourceVerificationFailed": "No se pudieron verificar los cambios de la fuente. No se modificó ningún worktree ni sesión.",
|
||||
"sessions.sidebar.session.moveToWorktree.applyChangesFailed": "El destino no pudo aceptar los cambios de la fuente. No se movieron la sesión ni los cambios. Reintenta y elige Mover solo la sesión.",
|
||||
"sessions.sidebar.session.moveToWorktree.changesMayBeInDestination": "La conexión se cortó antes de que el destino confirmara el movimiento. Puede que la sesión no se haya movido y que tus cambios sin confirmar ya estén en el worktree de destino. Compruébalo antes de volver a intentarlo.",
|
||||
"sessions.sidebar.session.menu.runFusion": "Ejecutar fusion",
|
||||
"sessions.sidebar.session.menu.openInSidePanel": "Abrir en panel lateral",
|
||||
"sessions.sidebar.session.actions.openInEditor": "Abrir en el editor",
|
||||
|
||||
@@ -394,6 +394,7 @@ export const dict = {
|
||||
'sessions.sidebar.session.moveToWorktree.confirm.cancel': 'Annuler',
|
||||
'sessions.sidebar.session.moveToWorktree.sourceVerificationFailed': 'Les modifications de la source n’ont pas pu être vérifiées. Aucun worktree ni session n’a été modifié.',
|
||||
'sessions.sidebar.session.moveToWorktree.applyChangesFailed': 'La destination n’a pas pu accepter les modifications de la source. La session et les modifications n’ont pas été déplacées. Réessayez et choisissez Déplacer la session uniquement.',
|
||||
'sessions.sidebar.session.moveToWorktree.changesMayBeInDestination': 'La connexion a été perdue avant que la destination ne confirme le déplacement. La session n’a peut-être pas été déplacée, et vos modifications non validées se trouvent peut-être déjà dans le worktree de destination. Vérifiez-le avant de réessayer.',
|
||||
'sessions.sidebar.session.menu.runFusion': 'Exécuter la fusion',
|
||||
'sessions.sidebar.session.menu.openInSidePanel': 'Ouvrir dans le panneau latéral',
|
||||
'sessions.sidebar.session.actions.openInEditor': 'Ouvrir dans l\'éditeur',
|
||||
|
||||
@@ -564,6 +564,7 @@ export const dict: Record<I18nKey, string> = {
|
||||
'sessions.sidebar.session.moveToWorktree.confirm.cancel': 'キャンセル',
|
||||
'sessions.sidebar.session.moveToWorktree.sourceVerificationFailed': 'ソースの変更を検証できませんでした。worktreeもセッションも変更されませんでした。',
|
||||
'sessions.sidebar.session.moveToWorktree.applyChangesFailed': '宛先がソースの変更を受け付けられませんでした。セッションもソースの変更も移動されていません。再試行して「セッションのみ移動」を選んでください。',
|
||||
'sessions.sidebar.session.moveToWorktree.changesMayBeInDestination': '移動が宛先で確定する前に接続が切れました。セッションは移動していない可能性があり、コミットしていない変更はすでに移動先のワークツリーにあるかもしれません。再試行する前に確認してください。',
|
||||
'sessions.sidebar.session.menu.runFusion': 'フュージョンを実行',
|
||||
'sessions.sidebar.session.menu.openInSidePanel': 'サイドパネルで開く',
|
||||
'sessions.sidebar.session.actions.openInEditor': 'エディターで開く',
|
||||
|
||||
@@ -564,6 +564,7 @@ export const dict: Record<I18nKey, string> = {
|
||||
'sessions.sidebar.session.moveToWorktree.confirm.cancel': '취소',
|
||||
'sessions.sidebar.session.moveToWorktree.sourceVerificationFailed': '원본 변경 사항을 확인하지 못했습니다. worktree와 세션 모두 변경되지 않았습니다.',
|
||||
'sessions.sidebar.session.moveToWorktree.applyChangesFailed': '목적지가 원본 변경 사항을 받아들이지 못했습니다. 세션과 원본 변경 사항이 이동되지 않았습니다. 다시 시도해 세션만 이동을 선택하세요.',
|
||||
'sessions.sidebar.session.moveToWorktree.changesMayBeInDestination': '목적지가 이동을 확인하기 전에 연결이 끊겼습니다. 세션이 이동하지 않았을 수 있고, 커밋하지 않은 변경 사항이 이미 대상 워크트리에 있을 수 있습니다. 다시 시도하기 전에 확인하세요.',
|
||||
'sessions.sidebar.session.menu.runFusion': 'fusion 실행',
|
||||
'sessions.sidebar.session.menu.openInSidePanel': '사이드 패널에서 열기',
|
||||
'sessions.sidebar.session.actions.openInEditor': '편집기에서 열기',
|
||||
|
||||
@@ -357,6 +357,7 @@ export const dict: Record<I18nKey, string> = {
|
||||
'sessions.sidebar.session.moveToWorktree.confirm.cancel': 'Anuluj',
|
||||
'sessions.sidebar.session.moveToWorktree.sourceVerificationFailed': 'Nie udało się zweryfikować zmian w źródle. Żaden worktree ani sesja nie został zmieniony.',
|
||||
'sessions.sidebar.session.moveToWorktree.applyChangesFailed': 'Cel nie mógł przyjąć zmian ze źródła. Sesja i zmiany w źródle nie zostały przeniesione. Spróbuj ponownie i wybierz Przenieś tylko sesję.',
|
||||
'sessions.sidebar.session.moveToWorktree.changesMayBeInDestination': 'Połączenie zostało zerwane, zanim cel potwierdził przeniesienie. Sesja mogła nie zostać przeniesiona, a niezatwierdzone zmiany mogą już być w docelowym worktree. Sprawdź go przed ponowną próbą.',
|
||||
'sessions.sidebar.session.menu.runFusion': 'Uruchom fusion',
|
||||
'sessions.sidebar.session.menu.openInSidePanel': 'Otwórz w panelu bocznym',
|
||||
'sessions.sidebar.session.actions.openInEditor': 'Otwórz w edytorze',
|
||||
|
||||
@@ -564,6 +564,7 @@ export const dict: Record<I18nKey, string> = {
|
||||
"sessions.sidebar.session.moveToWorktree.confirm.cancel": "Cancelar",
|
||||
"sessions.sidebar.session.moveToWorktree.sourceVerificationFailed": "As alterações da fonte não puderam ser verificadas. Nenhum worktree ou sessão foi alterado.",
|
||||
"sessions.sidebar.session.moveToWorktree.applyChangesFailed": "O destino não pôde aceitar as alterações da fonte. A sessão e as alterações da fonte não foram movidas. Tente novamente e escolha Mover apenas a sessão.",
|
||||
"sessions.sidebar.session.moveToWorktree.changesMayBeInDestination": "A conexão caiu antes de o destino confirmar a movimentação. A sessão pode não ter sido movida, e suas alterações não commitadas podem já estar no worktree de destino. Confira lá antes de tentar de novo.",
|
||||
"sessions.sidebar.session.menu.runFusion": "Executar fusion",
|
||||
"sessions.sidebar.session.menu.openInSidePanel": "Abrir no painel lateral",
|
||||
"sessions.sidebar.session.actions.openInEditor": "Abrir no editor",
|
||||
|
||||
@@ -524,6 +524,28 @@ export const dict = {
|
||||
'sessions.sidebar.session.moveToWorktree.tooltip': 'Geçerli branch\'ten yeni bir worktree oluşturur, commit edilmemiş değişiklikleri aktarır ve bu session\'ı alt session\'larıyla birlikte oraya taşır.',
|
||||
'sessions.sidebar.session.moveToWorktree.tooltipBusy': 'Session boşta olduğunda kullanılabilir. Geçerli etkinliği durdur veya bitmesini bekle.',
|
||||
'sessions.sidebar.session.moveToWorktree.tooltipMoving': 'Bu session zaten yeni bir worktree\'ye taşınıyor.',
|
||||
'sessions.sidebar.session.menu.moveToWorktreeTargets': 'Worktree\'ye taşı',
|
||||
'sessions.sidebar.session.menu.newWorktree': 'Yeni worktree...',
|
||||
'sessions.sidebar.session.moveToWorktree.main': 'Ana worktree',
|
||||
'sessions.sidebar.session.moveToWorktree.refreshing': 'Worktree\'ler yenileniyor...',
|
||||
'sessions.sidebar.session.moveToWorktree.loadFailed': 'Worktree\'ler yüklenemedi',
|
||||
'sessions.sidebar.session.moveToWorktree.current': 'Geçerli worktree',
|
||||
'sessions.sidebar.session.moveToWorktree.existingSuccess': 'Session worktree\'ye taşındı',
|
||||
'sessions.sidebar.session.moveToWorktree.existingFailed': 'Session worktree\'ye taşınamadı',
|
||||
'sessions.sidebar.session.moveToWorktree.tooltipTargets': 'Mevcut worktree\'leri ve bu session için yeni bir tane oluşturma seçeneğini gösterir.',
|
||||
'sessions.sidebar.session.moveToWorktree.confirm.title': 'Kaynakta commit edilmemiş değişiklikler var',
|
||||
'sessions.sidebar.session.moveToWorktree.confirm.changedFiles': 'Bu worktree\'de değişen dosya sayısı: {count}.',
|
||||
'sessions.sidebar.session.moveToWorktree.confirm.ownership': 'OpenCode bu değişiklikleri session\'a göre değil, dizine göre izler.',
|
||||
'sessions.sidebar.session.moveToWorktree.confirm.sessionOnlyHelp': 'Bu session\'ı ve alt session\'larını taşır, kaynaktaki dosyalara hiç dokunmaz.',
|
||||
'sessions.sidebar.session.moveToWorktree.confirm.allChangesHelp': 'Session dizinindeki değişiklikleri aktarır. Stage\'lenmemiş ve takip edilmeyen dosyalar başarıdan sonra kaynaktan çıkar.',
|
||||
'sessions.sidebar.session.moveToWorktree.confirm.stagedWarning': 'Stage\'lenmiş değişiklikler kaynakta kalır ve hedefe kopyalanır.',
|
||||
'sessions.sidebar.session.moveToWorktree.confirm.baseWarning': 'Hedef farklı bir Git tabanı kullanıyorsa aktarım başarısız olabilir.',
|
||||
'sessions.sidebar.session.moveToWorktree.confirm.sessionOnly': 'Yalnızca session\'ı taşı',
|
||||
'sessions.sidebar.session.moveToWorktree.confirm.allChanges': 'Kaynaktaki tüm değişiklikleri taşı',
|
||||
'sessions.sidebar.session.moveToWorktree.confirm.cancel': 'İptal',
|
||||
'sessions.sidebar.session.moveToWorktree.sourceVerificationFailed': 'Kaynaktaki değişiklikler doğrulanamadı. Hiçbir worktree veya session değiştirilmedi.',
|
||||
'sessions.sidebar.session.moveToWorktree.applyChangesFailed': 'Hedef, kaynaktaki değişiklikleri kabul edemedi. Session da kaynaktaki değişiklikler de taşınmadı. Tekrar deneyip Yalnızca session\'ı taşı seçeneğini kullan.',
|
||||
'sessions.sidebar.session.moveToWorktree.changesMayBeInDestination': 'Hedef taşımayı onaylamadan önce bağlantı koptu. Session taşınmamış olabilir ve commit edilmemiş değişikliklerin hedef worktree içinde olabilir. Tekrar denemeden önce oraya bak.',
|
||||
'sessions.sidebar.session.menu.runFusion': 'Fusion\'ı çalıştır',
|
||||
'sessions.sidebar.session.menu.openInSidePanel': 'Yan panelde aç',
|
||||
'sessions.sidebar.session.actions.openInEditor': 'Editörde aç',
|
||||
|
||||
@@ -564,6 +564,7 @@ export const dict: Record<I18nKey, string> = {
|
||||
"sessions.sidebar.session.moveToWorktree.confirm.cancel": "Скасувати",
|
||||
"sessions.sidebar.session.moveToWorktree.sourceVerificationFailed": "Не вдалося перевірити зміни в джерелі. Жоден worktree чи сесію не змінено.",
|
||||
"sessions.sidebar.session.moveToWorktree.applyChangesFailed": "Призначення не змогло прийняти зміни з джерела. Сесію та зміни в джерелі не перенесено. Спробуйте знову й оберіть Перенести лише сесію.",
|
||||
"sessions.sidebar.session.moveToWorktree.changesMayBeInDestination": "З’єднання обірвалося, перш ніж призначення підтвердило перенесення. Сесія могла не переїхати, а незакомічені зміни можуть уже бути в цільовому worktree. Перевірте його, перш ніж повторювати.",
|
||||
"sessions.sidebar.session.menu.runFusion": "Запустити fusion",
|
||||
"sessions.sidebar.session.menu.openInSidePanel": "Відкрити на бічній панелі",
|
||||
"sessions.sidebar.session.actions.openInEditor": "Відкрити в редакторі",
|
||||
|
||||
@@ -564,6 +564,7 @@ export const dict: Record<I18nKey, string> = {
|
||||
'sessions.sidebar.session.moveToWorktree.confirm.cancel': '取消',
|
||||
'sessions.sidebar.session.moveToWorktree.sourceVerificationFailed': '无法验证源的更改。未更改任何工作树或会话。',
|
||||
'sessions.sidebar.session.moveToWorktree.applyChangesFailed': '目的地无法接受源的更改。会话和源更改均未移动。请重试并选择“仅移动会话”。',
|
||||
'sessions.sidebar.session.moveToWorktree.changesMayBeInDestination': '在目的地确认移动之前连接中断。会话可能没有移动,未提交的更改可能已经在目标工作树中。重试前请先检查。',
|
||||
'sessions.sidebar.session.menu.runFusion': '运行融合',
|
||||
'sessions.sidebar.session.menu.openInSidePanel': '在侧边面板中打开',
|
||||
'sessions.sidebar.session.actions.openInEditor': '在编辑器中打开',
|
||||
|
||||
@@ -577,6 +577,7 @@ export const dict: Record<I18nKey, string> = {
|
||||
'sessions.sidebar.session.moveToWorktree.confirm.cancel': '取消',
|
||||
'sessions.sidebar.session.moveToWorktree.sourceVerificationFailed': '無法驗證來源的變更。未變更任何工作樹或工作階段。',
|
||||
'sessions.sidebar.session.moveToWorktree.applyChangesFailed': '目的地無法接受來源的變更。工作階段與來源變更均未移動。請重試並選擇「僅移動工作階段」。',
|
||||
'sessions.sidebar.session.moveToWorktree.changesMayBeInDestination': '在目的地確認移動之前連線中斷。工作階段可能沒有移動,未提交的變更可能已經在目標工作樹中。重試前請先檢查。',
|
||||
'sessions.sidebar.session.menu.runFusion': '執行 fusion',
|
||||
'sessions.sidebar.session.menu.openInSidePanel': '在側邊面板中開啟',
|
||||
'sessions.sidebar.session.actions.openInEditor': '在編輯器中開啟',
|
||||
|
||||
Reference in New Issue
Block a user