Pipeline step 4 dogfood. `/config-audit rollback` could not see a single one of
the four real backups on this machine, and reported "Backup not found" for one
that was sitting right there.
Four defects, one root: nothing agreed on where a backup lives or what its
manifest looks like.
- M-BUG-22 `lib/backup.mjs` resolved `~/.config-audit/backups` (pre-v2.2.0)
while every command, agent and doc uses `~/.claude/config-audit/backups`.
The auto-backup hook and fix-cli wrote to the first, implement to the second,
rollback read only the first. Canonical root now, with the legacy root kept
readable so older backups stay listable and restorable (`legacy: true`).
- M-BUG-25 `parseManifest` understood only the engine's quoted `original_path:`
spelling, but implement hand-builds its manifest with `- backup:`/`original:`/
`sha256:`. Every implement-made backup parsed to zero files and restoreBackup
returned `{restored: [], failed: []}` — a success-shaped no-op. Both formats
parse now, and a manifest with unparseable entries throws instead of
pretending to succeed.
- M-BUG-23 both session hooks watched `~/.config-audit/sessions`, which does not
exist; sessions live under `~/.claude/`. "Check for active sessions" had never
fired once. It fires now.
- M-BUG-24 the suite called createBackup() against the developer's real home —
it had left nine stray backups there, and cleanupOldBackups() deletes past ten.
Root is overridable via CONFIG_AUDIT_BACKUP_ROOT; both test files use it.
Rollback still cannot delete files implement CREATED — no backup can hold a file
that never existed. It no longer does so silently: manifests carry a `created:`
list, restoreBackup returns `createdNotRemoved`, and rollback.md requires the
report. Automatic deletion is a destructive action and needs its own design.
Verified against backup 20260717_032636 on a throwaway copy: all three files
restore byte-exact (sha256 match), zero writes outside the copy, backup dir
unmodified. Suite 1382 -> 1398/0; frozen v5.0.0 snapshots untouched.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01SejM9RQAa1Hfuq7Ek2WfFr
107 lines
4.1 KiB
Markdown
107 lines
4.1 KiB
Markdown
---
|
|
name: config-audit:rollback
|
|
description: Restore configuration from backup — list available backups or rollback a specific one
|
|
argument-hint: "[backup-id]"
|
|
allowed-tools: Read, Write, Glob, Grep, Bash, AskUserQuestion
|
|
model: sonnet
|
|
---
|
|
|
|
# Config-Audit: Rollback
|
|
|
|
Restore configuration files from a previous backup. Without arguments, lists available backups. With a backup ID, restores files from that backup.
|
|
|
|
## Arguments
|
|
|
|
- `$ARGUMENTS` may contain a backup ID (format: `YYYYMMDD_HHMMSS`)
|
|
- `--raw`: pass-through flag accepted for CLI surface consistency. Rollback is file restoration only (no scanner output, no findings prose), so `--raw` is a no-op here, but the flag is still parsed so users get uniform behaviour across the toolchain.
|
|
|
|
## Behavior
|
|
|
|
### List mode (no argument)
|
|
|
|
Parse flags and list available backups from `~/.claude/config-audit/backups/`:
|
|
|
|
```bash
|
|
RAW_FLAG=""
|
|
if echo "$ARGUMENTS" | grep -q -- "--raw"; then RAW_FLAG="--raw"; fi
|
|
ls -1 ~/.claude/config-audit/backups/
|
|
```
|
|
|
|
```
|
|
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
|
Available Backups
|
|
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
|
|
|
1. 20260403_163045 — 3 files (settings.json, hooks.json, typescript.md)
|
|
2. 20260403_141230 — 1 file (CLAUDE.md)
|
|
3. 20260402_092015 — 5 files (full audit)
|
|
|
|
Usage: /config-audit rollback 20260403_163045
|
|
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
|
```
|
|
|
|
Use the Read tool on each backup's `manifest.yaml` (the list of changes captured at backup time) to extract the file list and timestamps.
|
|
|
|
### Restore mode (with backup ID)
|
|
|
|
1. Read the list of changes from `~/.claude/config-audit/backups/{backup-id}/manifest.yaml` using the Read tool
|
|
2. Show files that will be restored — ask for confirmation:
|
|
```
|
|
AskUserQuestion:
|
|
question: "Restore 3 files from backup 20260403_163045?"
|
|
options:
|
|
- "Yes, restore"
|
|
- "Cancel"
|
|
```
|
|
3. For each file in the list of changes:
|
|
a. Read the backup file from `~/.claude/config-audit/backups/{backup-id}/files/{safeName}`
|
|
b. Write to the original path
|
|
c. Verify the checksum matches the recorded value in the list of changes
|
|
4. Show result:
|
|
```
|
|
Restored 3 files from backup 20260403_163045
|
|
- .claude/settings.json (checksum verified)
|
|
- hooks/hooks.json (checksum verified)
|
|
- .claude/rules/typescript.md (checksum verified)
|
|
```
|
|
5. **Report what rollback cannot undo.** A backup only holds files that already
|
|
existed, so files the implement step CREATED survive the restore. If the
|
|
manifest has a `created:` section (or `restoreBackup()` returns a non-empty
|
|
`createdNotRemoved`), list those paths and say plainly that they remain:
|
|
```
|
|
Left in place — created by implement, no backup exists:
|
|
- .claude/rules/post-quality.md
|
|
- guidelines/posting-rhythm.md
|
|
Remove them manually if you want the pre-implement state exactly.
|
|
```
|
|
Never finish a restore without this section when the list is non-empty; a
|
|
silently half-restored target reads as a clean rollback.
|
|
|
|
### Delete mode
|
|
|
|
If user says "delete" after listing, confirm and remove the backup directory.
|
|
|
|
## Implementation
|
|
|
|
Use the backup and rollback libraries directly:
|
|
```javascript
|
|
import { listBackups, restoreBackup, deleteBackup } from '../scanners/rollback-engine.mjs';
|
|
import { parseManifest, getBackupDir } from '../scanners/lib/backup.mjs';
|
|
```
|
|
|
|
Both read `~/.claude/config-audit/backups` and fall back to the pre-v2.2.0
|
|
`~/.config-audit/backups`, so a backup made before the move still resolves;
|
|
`listBackups()` flags those with `legacy: true`. Prefer this API over ad-hoc
|
|
`cp` — it verifies the checksum before and after each write.
|
|
|
|
Or via Bash:
|
|
```bash
|
|
# List backups
|
|
ls -1 ~/.claude/config-audit/backups/
|
|
|
|
# Read manifest
|
|
cat ~/.claude/config-audit/backups/{id}/manifest.yaml
|
|
|
|
# Restore (copy back)
|
|
cp ~/.claude/config-audit/backups/{id}/files/{safeName} {originalPath}
|
|
```
|