feat: osa stop --wipe-data to clear all local state - #15
Conversation
By default `osa stop` runs `docker compose down`, keeping the named Postgres volume so the DB survives stop/start. `--wipe-data` additionally passes `--volumes` (drops the DB volume) and removes the `./.data` host bind mount (deposited files + hook artifacts), so local state is fully cleared. A failed `down` aborts before any deletion, and a missing `.data` dir is tolerated, so the flag never destroys data on an unsuccessful stop.
Greptile SummaryAdds an optional destructive reset path to
Confidence Score: 5/5The PR appears safe to merge because no new blocking failure eligible for this follow-up review remains. No blocking failure remains beyond the behavior already covered by the existing review threads.
What T-Rex did
|
| Filename | Overview |
|---|---|
| osa/cli/instance.py | Adds the wipe orchestration, ordered failure handling, and user-facing recovery guidance. |
| osa/cli/main.py | Exposes --wipe-data on the stop command and forwards it to the instance layer. |
| tests/test_instance.py | Covers preservation defaults, successful wipes, missing directories, and failure ordering. |
| uv.lock | Updates only the editable osa-py package version from 0.6.0 to 0.7.0. |
Flowchart
%%{init: {'theme': 'neutral'}}%%
flowchart TD
A[osa stop] --> B{--wipe-data?}
B -- No --> C[docker compose down]
B -- Yes --> D[docker compose down]
D --> E{Stop succeeded?}
E -- No --> F[Exit without deleting local state]
E -- Yes --> G[Remove .data]
G --> H{Removal succeeded or directory absent?}
H -- No --> I[Exit with database volume intact]
H -- Yes --> J[docker compose down --volumes]
J --> K{Volume removal succeeded?}
K -- Yes --> L[Local state wiped]
K -- No --> M[Exit and advise retry]
Reviews (4): Last reviewed commit: "docs: document the wipe ordering trade-o..." | Re-trigger Greptile
| # `down` alone keeps named volumes (the DB persists across stop/start). | ||
| # `--volumes` additionally removes them — here, the postgres_data DB volume. |
There was a problem hiding this comment.
Wipe leaves partially deleted state
When .data contains container-owned files that the host user cannot remove, down --volumes permanently deletes the Postgres database before shutil.rmtree raises, causing the command to fail with deposited files and hook artifacts left orphaned on disk.
Artifacts
Repro: focused pytest harness exercising the real stop_instance path
- Evidence file captured while the check ran.
- The full command output behind this check.
There was a problem hiding this comment.
Fixed in f0a54bc. Reordered so --wipe-data destroys nothing until it can destroy everything: stop containers keeping volumes → remove ./.data → only then down --volumes. If .data removal fails (container-owned files), we abort with the DB volume intact rather than half-wiping, and the error says so + hints to remove with elevated permissions and retry. Added a regression test asserting the volume drop never runs when data removal fails.
Previously `down --volumes` dropped the DB volume before removing ./.data, so if the bind-mount held container-owned files the host couldn't delete, the DB was already gone when shutil.rmtree raised — leaving deposited files orphaned and the command failed (Greptile P1). Reorder so nothing is destroyed until everything can be: stop containers keeping volumes, remove ./.data (abort here with the DB intact on failure), then drop the volumes. Adds a regression test asserting the volume drop never runs when data removal fails.
| "container-owned), then re-run `osa stop --wipe-data`.", | ||
| ) from e | ||
|
|
||
| wiped = run_streamed([*cmd, "down", "--volumes"], task=task, cwd=project_dir) |
There was a problem hiding this comment.
Volume failure leaves partial wipe
When .data removal succeeds but the subsequent docker compose down --volumes command fails, the deposited files and hook artifacts are already irreversibly deleted while the Postgres volume remains, causing the command to exit with a partially wiped instance.
Artifacts
Repro: focused pytest harness executing the partial-wipe failure path
- Evidence file captured while the check ran.
Repro: verbose pytest output showing deleted files, failed volume removal, and InstanceError
- The full command output behind this check.
There was a problem hiding this comment.
This one is inherent: a host bind mount (./.data) and a Docker volume can't be deleted atomically, so some single-step failure can always leave a partial state. What we can control is which failure and how recoverable it is:
- The common failure is
.dataremoval (container-owned files on Linux). That step runs first, so its failure aborts before anything is destroyed — DB volume intact. - The volume drop runs last and rarely fails (containers are already down; it's essentially
docker volume rm). If it does, the state is files gone, volume remaining — and re-runningosa stop --wipe-datacompletes it idempotently (the missing.datais tolerated). The error message directs exactly that.
Documented the trade-off in a comment and added test_wipe_volume_drop_failure_is_recoverable proving the re-run finishes the wipe (96595e2). I don't think it's worth trading this rare, recoverable case for the common-and-unrecoverable one the other ordering produces.
There was a problem hiding this comment.
That's a well-reasoned argument, and the added comment + test make the trade-off explicit and verifiable. The "common failure is safe, rare failure is recoverable" ordering is the right call here — the original concern is addressed.
Tip: You can customize Greptile's behavior for this repo with .greptile/rules.md and .greptile/config.json.
Follow-up to the reorder (Greptile): deleting a host bind mount and a Docker volume can't be atomic. We order the likely-to-fail step (./.data removal) first, so the common failure aborts before any destruction; the volume drop rarely fails and, if it does, the wipe is merely incomplete and re-running finishes it idempotently. Documents this in a comment and adds a test proving the volume-drop failure is recoverable by re-running.
Adds a
--wipe-dataflag toosa stopfor a clean local reset.osa stop) runsdocker compose down, keeping the named Postgres volume so the DB survives stop/start (unchanged behaviour).osa stop --wipe-dataadditionally passes--volumes(drops the Postgres volume) and removes the./.datahost bind mount — deposited files and hook artifacts thatdown --volumesdoesn't touch — so all local state is cleared.Safety:
downraises before any deletion, so a broken stop never destroys data..datadir is tolerated (no error).Tests cover: default keeps volume + data,
--wipe-datadrops both, missing.datatolerated, and a failed stop preserves data.