grind(): os.path.join(work, abs_path) discards worktree in bwrap sandbox #2

Closed
opened 2026-07-25 08:12:39 +00:00 by marfrit · 0 comments
Owner

Analysis

In grind() (the --once mode of bullpen-grinder), when test_rel is an absolute path, os.path.join(work, test_rel) discards the worktree path and returns the absolute path instead. This path doesn't exist inside the bwrap sandbox because /tmp is a fresh tmpfs.

The bug

In grind() on boltzmann at /usr/local/bin/bullpen-grinder, line 301:

test_src = open(os.path.join(work, test_rel)).read()

When test_rel is an absolute path like /tmp/bullpen-build/mneme-namespace-ranking/tests/test_namespace_query.py, Python's os.path.join discards the first argument if the second is absolute:

os.path.join("/tmp/grind-12345", "/tmp/bullpen-build/.../test_namespace_query.py")
# → "/tmp/bullpen-build/.../test_namespace_query.py"

This means it looks for the file at the original repo path, NOT inside the worktree.

Then inside run_tests(), bwrap runs the test inside a sandbox with --tmpfs /tmp (line 170 of bullpen-grinder):

cmd = [BWRAP, "--unshare-all", "--ro-bind", "/", "/", "--dev", "/dev", "--proc", "/proc",
       "--tmpfs", "/home", "--tmpfs", "/root", "--tmpfs", "/tmp",
       "--bind", work, work, "--chdir", work, ...]

The original repo at /tmp/bullpen-build/... is under /tmp which is shadowed by the fresh tmpfs. Only the worktree at work is bound.

Impact

When the coordinator resolves test_rel to an absolute path (e.g. via the full-absolute SPEC format SPEC: /tmp/.../tests/test_file.py), the grinder can't find the test file inside the sandbox and fails immediately with:

no tests ran in 0.00s
ERROR: file or directory not found: /tmp/bullpen-build/.../tests/test_file.py

Workaround

Ensure test_rel is always a relative path (e.g. tests/test_file.py). The named-file fallback in _resolve_test_rel returns relative paths from git ls-files, which work correctly.

Room reference

  • Message 1567: INVALID TICKET ❌ — / ...does not exist in /tmp/bullpen-build/...
  • Grind results /tmp/grind-result-1784965351.txt, /tmp/grind-result-1784964238.txt, /tmp/grind-result-1784963754.txt all show this error pattern

Hosts

Host Role
boltzmann Grind host — grind() runs here; code in /usr/local/bin/bullpen-grinder
noether Coordinator — dispatches via sic to boltzmann

Suggested fix

In grind(), before using test_rel, strip the REPO prefix if test_rel is absolute:

if os.path.isabs(test_rel) and test_rel.startswith(REPO):
    test_rel = os.path.relpath(test_rel, REPO)

This converts /tmp/bullpen-build/mneme-namespace-ranking/tests/test_file.py to tests/test_file.py, which then works correctly with os.path.join(work, ...).

## Analysis In `grind()` (the `--once` mode of `bullpen-grinder`), when `test_rel` is an absolute path, `os.path.join(work, test_rel)` discards the worktree path and returns the absolute path instead. This path doesn't exist inside the bwrap sandbox because `/tmp` is a fresh tmpfs. ### The bug In `grind()` on boltzmann at `/usr/local/bin/bullpen-grinder`, line 301: ```python test_src = open(os.path.join(work, test_rel)).read() ``` When `test_rel` is an absolute path like `/tmp/bullpen-build/mneme-namespace-ranking/tests/test_namespace_query.py`, Python's `os.path.join` discards the first argument if the second is absolute: ```python os.path.join("/tmp/grind-12345", "/tmp/bullpen-build/.../test_namespace_query.py") # → "/tmp/bullpen-build/.../test_namespace_query.py" ``` This means it looks for the file at the original repo path, NOT inside the worktree. Then inside `run_tests()`, bwrap runs the test inside a sandbox with `--tmpfs /tmp` (line 170 of bullpen-grinder): ```python cmd = [BWRAP, "--unshare-all", "--ro-bind", "/", "/", "--dev", "/dev", "--proc", "/proc", "--tmpfs", "/home", "--tmpfs", "/root", "--tmpfs", "/tmp", "--bind", work, work, "--chdir", work, ...] ``` The original repo at `/tmp/bullpen-build/...` is under `/tmp` which is shadowed by the fresh tmpfs. Only the worktree at `work` is bound. ### Impact When the coordinator resolves `test_rel` to an absolute path (e.g. via the full-absolute SPEC format `SPEC: /tmp/.../tests/test_file.py`), the grinder can't find the test file inside the sandbox and fails immediately with: ``` no tests ran in 0.00s ERROR: file or directory not found: /tmp/bullpen-build/.../tests/test_file.py ``` ### Workaround Ensure `test_rel` is always a relative path (e.g. `tests/test_file.py`). The named-file fallback in `_resolve_test_rel` returns relative paths from `git ls-files`, which work correctly. ### Room reference - Message 1567: `INVALID TICKET ❌ — / ...does not exist in /tmp/bullpen-build/...` - Grind results `/tmp/grind-result-1784965351.txt`, `/tmp/grind-result-1784964238.txt`, `/tmp/grind-result-1784963754.txt` all show this error pattern ### Hosts | Host | Role | |---|---| | **boltzmann** | Grind host — `grind()` runs here; code in `/usr/local/bin/bullpen-grinder` | | **noether** | Coordinator — dispatches via sic to boltzmann | ### Suggested fix In `grind()`, before using `test_rel`, strip the REPO prefix if `test_rel` is absolute: ```python if os.path.isabs(test_rel) and test_rel.startswith(REPO): test_rel = os.path.relpath(test_rel, REPO) ``` This converts `/tmp/bullpen-build/mneme-namespace-ranking/tests/test_file.py` to `tests/test_file.py`, which then works correctly with `os.path.join(work, ...)`.
Sign in to join this conversation.
No Label
1 Participants
Notifications
Due Date
No due date set.
Dependencies

No dependencies set.

Reference: marfrit/bullpen#2