Repository navigation
Add permanent experiment delete (OR-564) - #759
Conversation
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
… failure Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
# Conflicts: # ui/dist/assets/PdfPreview-Bh6puvd-.js # ui/dist/assets/PdfPreview-CiGD6qiO.js # ui/dist/assets/PdfPreview-DHY1H0AM.js # ui/dist/assets/index-BAQVx49C.js # ui/dist/assets/index-CQIDX7GA.js # ui/dist/assets/index-DNEZ9j_w.js # ui/dist/index.html
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
[High impact] The change reaches local experiment records, files, branches, and remote cleanup. Fix the race with experiment creation and reparenting before merging. Findings
Fix with agent prompt### Issue 1
src/store.rs:2229-2232
Concurrent experiment creation or `orx exp move` can add a child after the first child check. Neither takes the artifacts lock. By the time the final check refuses deletion, `before_rows()` has deleted the branch and `cleanup_deleted_run_artifacts()` has removed the run files. Rolling back the rows cannot restore those files.
A child creation that forks before deletion and inserts after commit can also retain a deleted parent ID. Make creation, reparenting, and deletion share one lock, and recheck the parent while holding it, before deleting anything.
---
For each issue above, determine whether it is valid and should be fixed. If so, fix it directly.SummaryAdds permanent experiment deletion through the CLI, API, and dashboard.
|
| before_rows()?; | ||
| // Keep rows on filesystem errors so deletion can be retried with the same handles. | ||
| self.cleanup_deleted_run_artifacts(&runs, "experiment")?; | ||
| let tx = self.begin()?; |
There was a problem hiding this comment.
Refused deletion still removes files
Concurrent experiment creation or orx exp move can add a child after the first child check. Neither takes the artifacts lock. By the time the final check refuses deletion, before_rows() has deleted the branch and cleanup_deleted_run_artifacts() has removed the run files. Rolling back the rows cannot restore those files.
A child creation that forks before deletion and inserts after commit can also retain a deleted parent ID. Make creation, reparenting, and deletion share one lock, and recheck the parent while holding it, before deleting anything.
Prompt To Fix With AI
This is a comment left during a code review.
Path: src/store.rs
Line: 2229-2232
Comment:
**Refused deletion still removes files**
Concurrent experiment creation or `orx exp move` can add a child after the first child check. Neither takes the artifacts lock. By the time the final check refuses deletion, `before_rows()` has deleted the branch and `cleanup_deleted_run_artifacts()` has removed the run files. Rolling back the rows cannot restore those files.
A child creation that forks before deletion and inserts after commit can also retain a deleted parent ID. Make creation, reparenting, and deletion share one lock, and recheck the parent while holding it, before deleting anything.
---
For each issue above, determine whether it is valid and should be fixed. If so, fix it directly.There was a problem hiding this comment.
Fixed in 1344342 / 837d6e3. Experiment create and orx exp move now hold run-artifacts.lock shared for their whole operation (Store::with_run_artifacts_shared). Experiment and project deletes hold it exclusively from the child check through branch, artifact and row deletion, so a child can no longer appear mid-delete. Under the lock, create re-reads the project and the parent and forks from the fresh rows, so a create that waited behind a delete refuses with "no longer exists" instead of leaving a dangling parent. Delete also refuses if its row is already gone, so an overlapping second delete can't remove a reused branch. With every writer now excluded, the in-transaction child recheck became dead code and was removed. Covered by tree_edits_wait_out_deletes_and_recheck_what_they_build_on and the repeat-delete assertion in the store test.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
# Conflicts: # src/store.rs
# Conflicts: # ui/dist/assets/PdfPreview-CN-ZA2ej.js # ui/dist/assets/PdfPreview-CsvPNokB.js # ui/dist/assets/PdfPreview-DHY1H0AM.js # ui/dist/assets/index-A7FL_j-_.js # ui/dist/assets/index-BsYgd7Kt.js # ui/dist/assets/index-CQIDX7GA.js # ui/dist/index.html # ui/src/components/TreeView.tsx
#754's squash is identical to the myles/remote-storage-root changes already merged here. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
# Conflicts: # src/store.rs # ui/dist/assets/PdfPreview-CKyJoJPc.js # ui/dist/assets/PdfPreview-CqIZ8dlt.js # ui/dist/assets/PdfPreview-CsvPNokB.js # ui/dist/assets/index-Bi1HNN1U.js # ui/dist/assets/index-BsYgd7Kt.js # ui/dist/assets/index-Cqdzdm_Q.js # ui/dist/index.html
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…etions; revisit source caches Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…weeps Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…e experiment Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
# Conflicts: # src/store.rs
# Conflicts: # ui/dist/assets/PdfPreview-Arq-xDe9.js # ui/dist/assets/PdfPreview-CKyJoJPc.js # ui/dist/assets/PdfPreview-OX5NEmJW.js # ui/dist/assets/index-BfOmY9IZ.js # ui/dist/assets/index-Cqdzdm_Q.js # ui/dist/assets/index-wYBdJvxx.js # ui/dist/index.html
# Conflicts: # src/store.rs # ui/dist/assets/PdfPreview-Arq-xDe9.js # ui/dist/assets/PdfPreview-BvWccRfV.js # ui/dist/assets/PdfPreview-OX5NEmJW.js # ui/dist/assets/index-BfOmY9IZ.js # ui/dist/assets/index-DFE76ylj.js # ui/dist/assets/index-wYBdJvxx.js # ui/dist/index.html
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Closes OR-564. This is phase 4 of OR-569. It is stacked on #757, which is stacked on #754; retarget it to
mainas those merge.Summary
CLI:
orx exp delete <expId>permanently deletes an experiment node. It removes:orx/<slug>git branchIts SSH/Slurm runs' remote dirs are queued for full removal through Clean up older remote run dirs and unused source tars (OR-575) #757's cleanup queue. The command prints the deleted branch's commit with a
git branch <b> <sha>restore hint.API and dashboard:
DELETE /api/experiments/{id}backs a new "Delete permanently…" item in the experiment menu. It uses danger styling and a confirm dialog, and only appears for nodes without children, matching the existing archive menu.Refusals (conservative): the delete is refused, with nothing changed, when:
orx exp movefirst)Branches that are never deleted: the project's base branch (legacy roots) and a branch another experiment uses.
Order and retry:
run-artifacts.lock.A failure after the branch is gone still reports its restore commit, and retrying completes the delete.
Store refactor: project delete and experiment delete now share the artifacts-lock wait (
while_deleting) and run-id-scoped artifact cleanup. Project-delete behaviour is unchanged.Docs:
orx-experiment-treeand the root skill table say to use delete only when the user asks, and archive otherwise.Judgment calls
orx create-experimentandorx exp movenow holdrun-artifacts.lockshared, so they wait (up to 60s) while an experiment or project delete or an orphan scan holds it. They then refuse if the project or parent was deleted meanwhile ("… no longer exists"), or fail with "A deletion or artifact cleanup is in progress; retry shortly."experiment.deletedSSE event yet.Test plan
cargo fmt --all --check,cargo clippy --all-targets -- -D warnings,cargo build --locked,cargo test --lockedtsc --noEmit,vite build(ui/dist committed),node --test(405 pass)🤖 Generated with Claude Code