Inspired by Copilot CLI: /rollback keeps user hand-edits by default

Copilot CLI 1.0.78 reworked /rewind to restore only the files the agent
changed, 'skipping any file whose contents no longer match what Copilot
last wrote'. This ports that protection to Hermes checkpoints:

- tools/checkpoint_manager.py: per-project agent-write ledger
  (sha256 of every landed write_file/patch), safe_restore_plan()
  classifier, and restore(safe=True) that reverts only agent-authored
  changes, deletes agent-created files, and preserves user hand-edits.
  Empty ledger (pre-existing stores) falls back to the classic full
  restore.
- run_agent.py: feed the ledger from _record_file_mutation_result on
  every landed mutation (zero new hooks; rides the existing verifier).
- CLI + gateway /rollback: safe mode is the default; --all/--force
  restores everything; skipped files are reported with a hint.
- 17 locales: new gateway.rollback.kept_user_edits key.
- Docs: checkpoints-and-rollback.md updated.
- Tests: 7 new cases incl. user-edit preservation, post-agent user
  tweaks, agent-created file removal, empty-ledger fallback.
This commit is contained in:
Teknium
2026-08-06 20:35:06 -07:00
parent 9d139320d4
commit bd4b709258
24 changed files with 429 additions and 19 deletions
+1
View File
@@ -279,6 +279,7 @@ Future messages in this room will use that transcript until `/reset` or another
none_found: "Geen kontrolepunte vir {cwd} gevind nie"
invalid_number: "Ongeldige kontrolepunt-nommer. Gebruik 1-{max}."
restored: "✅ Herstel na kontrolepunt {hash}: {reason}\n'n Voor-terugrol-momentopname is outomaties gestoor."
kept_user_edits: "↷ Jou handwysigings is behou: {files}\nGebruik /rollback <N> --all om dié ook te herstel."
restore_failed: "❌ {error}"
diff:
+1
View File
@@ -299,6 +299,7 @@ gateway:
none_found: "لم يُعثر على نقاط تحقّق لـ {cwd}"
invalid_number: "رقم نقطة تحقّق غير صالح. استخدم 1-{max}."
restored: "✅ استُعيد إلى نقطة التحقّق {hash}: {reason}\nحُفظت لقطة ما قبل التراجع تلقائيًا."
kept_user_edits: "↷ احتُفظ بتعديلاتك اليدوية: {files}\nاستخدم ‎/rollback <N> --all لاستعادتها أيضًا."
restore_failed: "❌ {error}"
diff:
+1
View File
@@ -279,6 +279,7 @@ Future messages in this room will use that transcript until `/reset` or another
none_found: "Keine Checkpoints für {cwd} gefunden"
invalid_number: "Ungültige Checkpoint-Nummer. Verwenden Sie 1-{max}."
restored: "✅ Auf Checkpoint {hash} wiederhergestellt: {reason}\nEin Pre-Rollback-Snapshot wurde automatisch gespeichert."
kept_user_edits: "↷ Deine manuellen Änderungen wurden behalten: {files}\nNutze /rollback <N> --all, um auch diese wiederherzustellen."
restore_failed: "❌ {error}"
diff:
+1
View File
@@ -291,6 +291,7 @@ gateway:
none_found: "No checkpoints found for {cwd}"
invalid_number: "Invalid checkpoint number. Use 1-{max}."
restored: "✅ Restored to checkpoint {hash}: {reason}\nA pre-rollback snapshot was saved automatically."
kept_user_edits: "↷ Kept your hand-edits: {files}\nUse /rollback <N> --all to restore those too."
restore_failed: "❌ {error}"
diff:
+1
View File
@@ -276,6 +276,7 @@ gateway:
none_found: "No se encontraron checkpoints para {cwd}"
invalid_number: "Número de checkpoint inválido. Usa 1-{max}."
restored: "✅ Restaurado al checkpoint {hash}: {reason}\nSe guardó automáticamente un snapshot previo al rollback."
kept_user_edits: "↷ Se conservaron tus ediciones manuales: {files}\nUsa /rollback <N> --all para restaurarlas también."
restore_failed: "❌ {error}"
diff:
+1
View File
@@ -279,6 +279,7 @@ Future messages in this room will use that transcript until `/reset` or another
none_found: "Aucun point de contrôle trouvé pour {cwd}"
invalid_number: "Numéro de point de contrôle invalide. Utilisez 1-{max}."
restored: "✅ Restauré au point de contrôle {hash} : {reason}\nUn instantané pré-rollback a été enregistré automatiquement."
kept_user_edits: "↷ Vos modifications manuelles ont été conservées : {files}\nUtilisez /rollback <N> --all pour les restaurer aussi."
restore_failed: "❌ {error}"
diff:
+1
View File
@@ -283,6 +283,7 @@ Future messages in this room will use that transcript until `/reset` or another
none_found: "Níor aimsíodh aon seicphointe do {cwd}"
invalid_number: "Uimhir seicphointe neamhbhailí. Úsáid 1-{max}."
restored: "✅ Aischurtha go seicphointe {hash}: {reason}\nSábháladh roghchóip réamh-rollback go huathoibríoch."
kept_user_edits: "↷ Coinníodh do chuid athruithe láimhe: {files}\nÚsáid /rollback <N> --all chun iad sin a aischur freisin."
restore_failed: "❌ {error}"
diff:
+1
View File
@@ -279,6 +279,7 @@ Future messages in this room will use that transcript until `/reset` or another
none_found: "Nem található ellenőrzőpont ehhez: {cwd}"
invalid_number: "Érvénytelen ellenőrzőpont-szám. Használj 1-{max} közötti értéket."
restored: "✅ Visszaállítva a(z) {hash} ellenőrzőpontra: {reason}\nA visszaállítás előtti pillanatkép automatikusan elmentve."
kept_user_edits: "↷ A kézi szerkesztéseid megmaradtak: {files}\nHasználd a /rollback <N> --all parancsot, hogy azokat is visszaállítsd."
restore_failed: "❌ {error}"
diff:
+1
View File
@@ -279,6 +279,7 @@ Future messages in this room will use that transcript until `/reset` or another
none_found: "Nessun checkpoint trovato per {cwd}"
invalid_number: "Numero di checkpoint non valido. Usa 1-{max}."
restored: "✅ Ripristinato al checkpoint {hash}: {reason}\nUno snapshot pre-rollback è stato salvato automaticamente."
kept_user_edits: "↷ Le tue modifiche manuali sono state conservate: {files}\nUsa /rollback <N> --all per ripristinare anche quelle."
restore_failed: "❌ {error}"
diff:
+1
View File
@@ -279,6 +279,7 @@ Future messages in this room will use that transcript until `/reset` or another
none_found: "{cwd} のチェックポイントが見つかりません"
invalid_number: "無効なチェックポイント番号です。1-{max} を使用してください。"
restored: "✅ チェックポイント {hash} に復元しました: {reason}\nロールバック前のスナップショットが自動的に保存されました。"
kept_user_edits: "↷ 手動編集は保持されました: {files}\nそれらも復元するには /rollback <N> --all を使用してください。"
restore_failed: "❌ {error}"
diff:
+1
View File
@@ -279,6 +279,7 @@ Future messages in this room will use that transcript until `/reset` or another
none_found: "{cwd}에 체크포인트를 찾을 수 없습니다"
invalid_number: "잘못된 체크포인트 번호입니다. 1-{max}을 사용하세요."
restored: "✅ 체크포인트 {hash}(으)로 복원됨: {reason}\n롤백 전 스냅샷이 자동으로 저장되었습니다."
kept_user_edits: "↷ 직접 수정한 내용은 유지되었습니다: {files}\n해당 파일도 복원하려면 /rollback <N> --all 을 사용하세요."
restore_failed: "❌ {error}"
diff:
+1
View File
@@ -279,6 +279,7 @@ Future messages in this room will use that transcript until `/reset` or another
none_found: "Não foram encontrados checkpoints para {cwd}"
invalid_number: "Número de checkpoint inválido. Usa 1-{max}."
restored: "✅ Restaurado para o checkpoint {hash}: {reason}\nFoi guardado automaticamente um snapshot anterior ao rollback."
kept_user_edits: "↷ As suas edições manuais foram mantidas: {files}\nUse /rollback <N> --all para restaurar essas também."
restore_failed: "❌ {error}"
diff:
+1
View File
@@ -279,6 +279,7 @@ Future messages in this room will use that transcript until `/reset` or another
none_found: "Контрольных точек для {cwd} не найдено"
invalid_number: "Недействительный номер контрольной точки. Используйте 1-{max}."
restored: "✅ Восстановлено до контрольной точки {hash}: {reason}\nСнимок перед откатом сохранён автоматически."
kept_user_edits: "↷ Ваши ручные правки сохранены: {files}\nИспользуйте /rollback <N> --all, чтобы восстановить и их."
restore_failed: "❌ {error}"
diff:
+1
View File
@@ -279,6 +279,7 @@ Future messages in this room will use that transcript until `/reset` or another
none_found: "{cwd} için kontrol noktası bulunamadı"
invalid_number: "Geçersiz kontrol noktası numarası. 1-{max} aralığını kullanın."
restored: "✅ {hash} kontrol noktasına geri yüklendi: {reason}\nGeri alma öncesi anlık görüntü otomatik olarak kaydedildi."
kept_user_edits: "↷ Elle yaptığınız düzenlemeler korundu: {files}\nOnları da geri yüklemek için /rollback <N> --all kullanın."
restore_failed: "❌ {error}"
diff:
+1
View File
@@ -279,6 +279,7 @@ Future messages in this room will use that transcript until `/reset` or another
none_found: "Контрольних точок для {cwd} не знайдено"
invalid_number: "Недійсний номер контрольної точки. Використовуйте 1-{max}."
restored: "✅ Відновлено до контрольної точки {hash}: {reason}\nЗнімок перед відкатом збережено автоматично."
kept_user_edits: "↷ Ваші ручні правки збережено: {files}\nВикористайте /rollback <N> --all, щоб відновити і їх."
restore_failed: "❌ {error}"
diff:
+1
View File
@@ -279,6 +279,7 @@ Future messages in this room will use that transcript until `/reset` or another
none_found: "找不到 {cwd} 的檢查點"
invalid_number: "無效的檢查點編號。請使用 1-{max}。"
restored: "✅ 已還原至檢查點 {hash}:{reason}\n已自動儲存回復前的快照。"
kept_user_edits: "↷ 已保留您的手動編輯:{files}\n若也要還原這些檔案,請使用 /rollback <N> --all。"
restore_failed: "❌ {error}"
diff:
+1
View File
@@ -279,6 +279,7 @@ Future messages in this room will use that transcript until `/reset` or another
none_found: "未找到 {cwd} 的检查点"
invalid_number: "无效的检查点编号。请使用 1-{max}。"
restored: "✅ 已恢复到检查点 {hash}:{reason}\n已自动保存回滚前的快照。"
kept_user_edits: "↷ 已保留您的手动编辑:{files}\n如需一并恢复这些文件,请使用 /rollback <N> --all。"
restore_failed: "❌ {error}"
diff: