Skip to content

feat(cleanup): delete-run / archive+purge-project endpoints + file cleanup - #43

Merged
martinemnoble1 merged 2 commits into
mainfrom
feat/delete-cleanup-endpoints
Jun 11, 2026
Merged

feat(cleanup): delete-run / archive+purge-project endpoints + file cleanup#43
martinemnoble1 merged 2 commits into
mainfrom
feat/delete-cleanup-endpoints

Conversation

@martinemnoble1

Copy link
Copy Markdown
Owner

Implements the deletion & cleanup design (docs/DELETION_AND_CLEANUP.md) and the Materia delete-endpoint brief, with the five corrections found in review. Two reviewable chunks (commits).

Why

Materia needs to drive teardown of Reinspect Projects and Runs (re-run a campaign and clear the prior run; throwaway dev projects; decommissioning). Until now the ViewSets were read-only — teardown meant hand-typed ORM in a shell.

The core idea

The DB cascades were already wired; the hard part is which bytes are ours to delete. The discriminators:

  • Run.runner_handle — non-empty iff we dispatched the run (so we wrote its out_dir). The synthetic in-place-ingest Run leaves it empty and sets out_dir == source_root (the user's own tree), so we never rm data we don't own.
  • Artifact.origin — BUILT/REFINED bytes are ours to sweep; IMPORTED in-place trees are the user's.
  • Project.source_managed — gates the zip-import copy under PANDDA_DATA_ROOT/<name>.

Endpoints

Endpoint Effect
DELETE /runs/<id>?delete_outdir=false|true|force Hard-delete run + (optionally) its tree
DELETE /projects/<id> Archive (soft, reversible) + loss summary
POST /projects/<id>/unarchive/ Restore
POST /projects/<id>/purge/?delete_outdirs=false|true|force Irreversible cascade + file sweep; requires archived first

<mode>: false (DB-only, returns path), true (safe — ownership/orphan/shared guarded), force (rm regardless).

Chunk 1 — run-delete core + paired zombie guard (f5b75da)

  • DataStore.delete(relpath) on the seam (Local unlinks the leaf, symlink-safe; Azure deletes the blob).
  • Project.source_managed (migration 0019); set True by the zip importer, False for in-place ingest.
  • cleanup.delete_run: ownership predicate, corrected orphan check (dataset__project, skips embedded ligand CIFs), shared-out_dir guard, audit summary. DB cascade commits first; tree rm best-effort after. Findings/Crystals kept.
  • Submit-side zombie guard in runservice.submit_run — refuse a populated out_dir no Run owns (the desync the false default would otherwise create). Ships with the delete endpoint.

Chunk 2 — project archive + purge (0a74e86)

  • Project.archived tombstone (migration 0020); list hides archived (showable via ?include_archived).
  • cleanup.archive_project / purge_project: whole-project purge skips the per-run orphan check and current_model guard (everything's going), still ownership-gates out_dir rm + source_managed source-tree rm + BUILT/REFINED byte sweep; force nukes regardless.
  • project_loss_summary: the confirm summary (decisions / built / refined that purge would destroy).

The five Materia-brief corrections (all encoded + tested)

  1. Orphan query keyed on dataset__project (dataset-scoped artifacts carry project=NULL).
  2. Embedded ligand CIFs excluded from disk sweep (they're in contents, not files).
  3. Shared-out_dir guard on run-delete; orphan check skipped on project-purge.
  4. source_managed gate for the zip-import copy.
  5. Submit-side zombie guard ships alongside the delete endpoints.

Tests

inspect_api/tests/test_delete_cleanup.py — 33 cases (seam delete incl. symlink-safety, ownership, orphan/shared/force paths, endpoint modes, zombie guard, archive/unarchive/list-filtering, purge modes incl. in-place safety + BUILT sweep). Full suite green: 152.

Heads-up for Materia

The project DELETE→archive / purge split means delete_outdirs moved off the project DELETE onto /purge/ — their CLI needs a separate purge command. (Noted in §4.)

Not in scope (follow-ups)

  • Disk sweeps run synchronously in-request, not yet as a Job (design §1/§4 Q3) — fine now, worth doing before big-campaign teardown.
  • Azure whole-out_dir teardown deferred (per-blob delete() works; tree-rm is local-fs only).

🤖 Generated with Claude Code

martinemnoble1 and others added 2 commits June 11, 2026 11:04
Chunk 1 of the deletion/cleanup work (docs/DELETION_AND_CLEANUP.md §3–§4),
implementing the Materia delete-run brief with the five corrections from review.

- storage: DataStore.delete(relpath) on the seam (Local unlinks the leaf —
  symlink-safe, never the target; Azure deletes the blob).
- models: Project.source_managed (+ migration 0019); set True by the zip
  importer (we own the copied tree), left False for in-place ingest_path.
- cleanup.py: delete_run() with the runner_handle ownership predicate +
  terminal-status guard, the corrected orphan check (dataset__project, skips
  embedded ligand CIFs), the shared-out_dir guard, and an audit summary. DB
  cascade commits first; the out_dir rm is best-effort after. Findings/Crystals
  are kept (durable human layer).
- views: DELETE /runs/<id>?delete_outdir=false|true|force.
- runservice: submit-side zombie guard — refuse a populated out_dir that no
  Run row owns (the desync the delete default would otherwise create).
- tests: 23 cases across the seam delete, ownership, orphan/shared/force paths,
  endpoint modes, and the zombie guard. Full suite green (142).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Chunk 2 of the deletion/cleanup work (docs/DELETION_AND_CLEANUP.md §2).

- models: Project.archived tombstone (+ migration 0020).
- cleanup.py: project_loss_summary (the confirm summary — decisions/built/
  refined that a purge would destroy), archive_project (reversible soft-delete),
  purge_project (whole-project hard delete: no per-run orphan check, no
  current_model guard, ownership-gated out_dir rm + source_managed source-tree
  rm + BUILT/REFINED byte sweep via the seam; force nukes regardless).
- views: DELETE /projects/<id> archives; POST /projects/<id>/unarchive restores;
  POST /projects/<id>/purge?delete_outdirs=false|true|force does the
  irreversible work and requires the project be archived first. List hides
  archived (showable via ?include_archived); serializer exposes `archived`.
- tests: +10 (archive/unarchive, list filtering, purge requires-archived,
  db-only vs owned-tree vs in-place-safety vs force vs source_managed, and the
  BUILT byte sweep under an unremoved source_root). Full suite green (152).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
@martinemnoble1
martinemnoble1 merged commit 316c45d into main Jun 11, 2026
7 of 8 checks passed
@martinemnoble1
martinemnoble1 deleted the feat/delete-cleanup-endpoints branch June 11, 2026 10:29
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant