Skip to content

Guard RuntimeError in confined worktree cleanup #632

Description

@pbean

Problem

src/bmad_loop/workspace.py::_rmtree_confined resolves both the run worktree root and the candidate worktree before deciding whether shutil.rmtree is confined, but its refusal boundary catches only ValueError and OSError. On Python 3.11/3.12 a resolution loop can raise RuntimeError, so that provider failure escapes the cleanup helper instead of returning False.

This is a same-family follow-up from the #556 audit. The live callers are fallback cleanup paths reached from close_unit_workspace and discard_worktree; an escaping exception can abort worktree teardown even though uncertain containment should refuse deletion.

Reproduction

  1. Monkeypatch either unit_worktrees_dir(run_dir).resolve() or wt.resolve() to raise RuntimeError("symlink loop").
  2. Call _rmtree_confined(wt, run_dir).
  3. Observe the RuntimeError escape because the current except (ValueError, OSError) does not own it.

The regression test should cover both resolution sites and assert that shutil.rmtree is never called when either path is uncertain.

Scope

  • Catch RuntimeError at this destructive confinement boundary alongside ValueError and OSError.
  • Preserve the existing refusal contract: return False without deleting anything.
  • Keep the change limited to _rmtree_confined and its direct tests; provisioning, mount opening, and other path-resolution families have separate ownership.

Metadata

Metadata

Assignees

No one assigned

    Labels

    P3Robustness, enhancement, tests, or docs worth schedulingarea:engineOrchestrator engine and run lifecyclebugSomething isn't working

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions