feat: backup restore UI (#3) #14

Merged
the_og merged 2 commits from feat/3-backup-restore-ui into main 2026-07-02 01:44:44 -04:00
Owner

Summary

  • Adds list_backups, backup_label, backup_diff, restore_backup to bcc_core.py
  • Adds RestoreDialog to bcc.py: two-panel dialog with backup list (newest-first) + diff preview
  • Adds "Restore…" button in the MainWindow profile top bar

Key design decisions

Selective restore — only mcpServers and _disabledMcpServers are replaced. All other keys in the current config (conversation history, project state, etc.) are preserved verbatim and in their original order. This upholds the cardinal rule and is especially important for ~/.claude.json.

Pre-restore backuprestore_backup goes through write_config, so a backup of the pre-restore state is created automatically before anything is overwritten.

Accurate diffbackup_diff accepts an optional current_cfg so the dialog can diff against the already-loaded in-memory config (e.g. post-repair) instead of re-reading from disk. The diff shows before→after for servers only, matching what will actually be written.

backup_label uses regex — extracts the YYYYMMDD-HHMMSS timestamp with re.compile(r\"(\\d{8}-\\d{6})\") to handle filenames with leading dots (.claude.20260702-004124.json).

Tests added (9 new, 47 total)

  • test_list_backups_returns_newest_first
  • test_list_backups_empty_when_no_bdir
  • test_backup_label_parses_timestamp
  • test_backup_diff_shows_server_change
  • test_backup_diff_no_diff_when_identical
  • test_restore_backup_replaces_servers
  • test_restore_preserves_non_server_keys
  • test_restore_creates_pre_restore_backup
  • test_restore_with_current_cfg_passed

Closes #3

🤖 Generated with Claude Code

## Summary - Adds `list_backups`, `backup_label`, `backup_diff`, `restore_backup` to `bcc_core.py` - Adds `RestoreDialog` to `bcc.py`: two-panel dialog with backup list (newest-first) + diff preview - Adds "Restore…" button in the MainWindow profile top bar ## Key design decisions **Selective restore** — only `mcpServers` and `_disabledMcpServers` are replaced. All other keys in the current config (conversation history, project state, etc.) are preserved verbatim and in their original order. This upholds the cardinal rule and is especially important for `~/.claude.json`. **Pre-restore backup** — `restore_backup` goes through `write_config`, so a backup of the pre-restore state is created automatically before anything is overwritten. **Accurate diff** — `backup_diff` accepts an optional `current_cfg` so the dialog can diff against the already-loaded in-memory config (e.g. post-repair) instead of re-reading from disk. The diff shows before→after for servers only, matching what will actually be written. **`backup_label` uses regex** — extracts the `YYYYMMDD-HHMMSS` timestamp with `re.compile(r\"(\\d{8}-\\d{6})\")` to handle filenames with leading dots (`.claude.20260702-004124.json`). ## Tests added (9 new, 47 total) - `test_list_backups_returns_newest_first` - `test_list_backups_empty_when_no_bdir` - `test_backup_label_parses_timestamp` - `test_backup_diff_shows_server_change` - `test_backup_diff_no_diff_when_identical` - `test_restore_backup_replaces_servers` - `test_restore_preserves_non_server_keys` - `test_restore_creates_pre_restore_backup` - `test_restore_with_current_cfg_passed` Closes #3 🤖 Generated with [Claude Code](https://claude.com/claude-code)
the_og added 1 commit 2026-07-02 01:23:11 -04:00
feat: backup restore UI (#3)
CI / Lint (ruff) (pull_request) Successful in 10s
CI / Tests (py3.10) (pull_request) Successful in 10s
CI / Tests (py3.12) (pull_request) Successful in 10s
df332a06ba
Add list_backups / backup_label / backup_diff / restore_backup to bcc_core,
RestoreDialog to bcc.py, and a "Restore…" button in the profile top bar.

Restore is selective: only mcpServers and _disabledMcpServers are replaced;
all other keys in the config (history, project state) are preserved verbatim.
Goes through write_config() so a pre-restore backup is always created first.

Closes #3

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
the_og added 1 commit 2026-07-02 01:40:30 -04:00
fix: scope backup_diff to server sections and mask secrets
CI / Lint (ruff) (pull_request) Successful in 16s
CI / Tests (py3.10) (pull_request) Successful in 17s
CI / Tests (py3.12) (pull_request) Successful in 14s
6ab4789a70
The diff preview in RestoreDialog was serializing the full config dict,
exposing secret env values and token args in cleartext on a pasteable surface.

- Add _redact_server_data / _redact_servers_block helpers that apply
  redact_args to args and mask env values for is_secret_key() keys
- Rewrite backup_diff to compare only {mcpServers, _disabledMcpServers}
  sections (sanitized), not the whole file — also avoids double-serializing
  multi-MB ~/.claude.json for a servers-only diff
- Add clarifying comment in _restore_from_backup about why full_config
  is the right base after confirm-discard
- Add test: backup_diff with secret args/env → MASK in output, raw values absent
- Add test: restore_backup restores _disabledMcpServers correctly

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Author
Owner

Supervisor review — fix 1 (diff leaks secrets): backup_diff now scopes to only mcpServers/_disabledMcpServers sections and runs redact_args + is_secret_key masking on all server data before diffing. Raw secret values no longer appear in the preview. Added test_backup_diff_masks_secrets.

Supervisor follow-up 2 (_disabledMcpServers round-trip): Added test_restore_backup_restores_disabled_servers — creates a config with a disabled server, overwrites it, restores the backup, and asserts the disabled server is back under _disabledMcpServers.

Supervisor follow-up 3 (dirty-state baseline comment): Added one-line comment in _restore_from_backup explaining why full_config is the right restore base after confirm-discard.

**Supervisor review — fix 1 (diff leaks secrets):** `backup_diff` now scopes to only `mcpServers`/`_disabledMcpServers` sections and runs `redact_args` + `is_secret_key` masking on all server data before diffing. Raw secret values no longer appear in the preview. Added `test_backup_diff_masks_secrets`. **Supervisor follow-up 2 (_disabledMcpServers round-trip):** Added `test_restore_backup_restores_disabled_servers` — creates a config with a disabled server, overwrites it, restores the backup, and asserts the disabled server is back under `_disabledMcpServers`. **Supervisor follow-up 3 (dirty-state baseline comment):** Added one-line comment in `_restore_from_backup` explaining why `full_config` is the right restore base after confirm-discard.
the_og merged commit be45be9eab into main 2026-07-02 01:44:44 -04:00
Sign in to join this conversation.