Skip to content

fix(dogfood): uncontain Validate, and record why the Test binding is not a template for it - #420

Merged
tpouyer merged 1 commit into
mainfrom
fix/uncontain-validate
Sep 10, 2026
Merged

fix(dogfood): uncontain Validate, and record why the Test binding is not a template for it#420
tpouyer merged 1 commit into
mainfrom
fix/uncontain-validate

Conversation

@tpouyer

@tpouyer tpouyer commented Sep 9, 2026

Copy link
Copy Markdown
Contributor

Restores main, which is red. main went red on run 34415733664,
Restores main, which is red. container job, uv run in-lockstep run selfcheck. My change in #418 containerised this
repository's own Validate binding, and the reverting PR restores green while this issue holds
what it cost to learn.

What happened

validate  failed
          validate.command_failed: make lint typecheck exited 2
warning: Ignoring existing virtual environment linked to non-existent Python interpreter:
         .venv/bin/python3 -> python
Using CPython 3.11.14 interpreter at: /usr/local/bin/python3
Removed virtual environment at: .venv
  × Failed to download `httpx==0.28.1`
  ╰─▶ failed to lookup address information: Try again
make: *** [Makefile:36: lint] Error 1
test      errored  (pytest is not installed in python (resolved inside the container image))

Read that in order, because two separate things go wrong and the second is worse:

  1. make lint shells out to uv run. uv run found /work/.venv — the working tree's venv,
    writable because a working tree has to be, where mounts are :ro — and its interpreter
    symlink dangled, because the image's Python was 3.11.14 and the runner had built that venv with
    3.11.16. ghcr.io/astral-sh/uv:python3.11-bookworm is a mutable tag, so which patch you get
    is whatever was pushed last.
  2. uv's remedy for a broken venv is to delete and rebuild it. Rebuilding needs the network the
    sandbox correctly does not have, so lint failed — and the deletion took the suite down with it
    in the same job: test errored (pytest is not installed).

The identical code was green on the pull request thirty minutes earlier
(34415227793): validate succeeded, test succeeded. The only difference in the logs is Status: Downloaded newer image
and a second interpreter version. So this was not a flake that will pass on a retry; it is
deterministic whenever the image's patch differs from the runner's, which is now.

The part worth keeping

A contained verb deleted state the rest of the job depended on. Not through a bind mount —
those are :ro and did their job — but through the working tree, which cannot be read-only. Any
contained command that repairs its environment in place can do this. It is not specific to uv and
it is not specific to Validate.

Why Test works and Validate cannot, on the same sandbox. PytestTest runs
python -m pytest with the mounted .venv's site-packages on PYTHONPATH, and site-packages are
path-independent within one minor version. make typecheck runs uv run mypy, which executes a
console script whose shebang carries the host's absolute venv path. Nothing mounted makes that
path exist inside the image. The Test binding is not a template Validate can copy; it is a trick
that works for module invocation and not for console scripts.

Measured rather than reasoned, in ghcr.io/astral-sh/uv:python3.11-bookworm with --network=none:

attempt result
UV_NO_SYNC=1 alone still Removed virtual environment at: /venv, then rebuilds
UV_PROJECT_ENVIRONMENT at a path inside the image the mount is untouched; uv run falls through to PATH
…then uv run ruff works — ruff is a real binary on PATH
…then uv run mypy fails — console script, host shebang

Full analysis and the options are in #419. This PR is the revert only.

Verification

…not a template for it

`main` went red on run 34415733664: `run selfcheck`'s container job, `make lint typecheck exited
2`. #418's binding change is the trigger and this restores green.

What it did, in order. `make lint` shells out to `uv run`; `uv run` found `/work/.venv` — the
working tree's venv, writable because a working tree has to be, where `mounts` are `:ro` — and its
interpreter symlink dangled, the image carrying CPython 3.11.14 where the runner had built that
venv with 3.11.16 on a mutable image tag. uv's remedy for a broken venv is to delete and rebuild
it, rebuilding wants a network the sandbox correctly does not have, and the deletion took the suite
down with it in the same job: `test errored (pytest is not installed)`. The identical code was
green on the pull request half an hour earlier; the only difference in the logs is `Downloaded
newer image`.

The general shape, which #419 holds: a contained command that repairs its environment in place can
destroy state the rest of the run depends on, through the working tree rather than through a bind
mount.

And the specific one, recorded beside both bindings because it is the trap that produced this:
`PytestTest` runs `python -m pytest` and site-packages are path-independent, so mounting the host's
`.venv` works. `uv run mypy` executes a console script whose shebang carries the host's absolute
venv path, which nothing mounted makes exist inside the image. Measured under `--network=none`:
`UV_NO_SYNC` does not prevent the delete, and `UV_PROJECT_ENVIRONMENT` inside the image does but
then falls through to `PATH`, which serves `ruff` and not `mypy`.

`test_gate_sandbox_2_every_binding_that_executes_what_a_model_wrote_is_contained` goes with the
binding. It is the right test and this repository does not pass it: `staged_refusal` now refuses
this Validate for a model-staged run, so `/implement` and `/fix` here do not check what they wrote.
A test asserting something the repository does not do is worse than the gap it hides, so the gap is
stated in the module and in #419 and the test returns with the fix.

Refs #419

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
@github-actions

github-actions Bot commented Sep 9, 2026

Copy link
Copy Markdown
Contributor

in-lockstep review — intent

succeeded · $0.0226 · 3 in / 530 out tokens

location finding
⚠️ tests/in_lockstep/test_own_ceilings.py:240 GATE-SANDBOX-2 test deleted with no replacement or skip, leaving the containment property untested.
⚠️ .lockstep/lockstep.py:174 Validate is now uncontained; the comment documents this but the diff does more than the description implies.

@github-actions

github-actions Bot commented Sep 9, 2026

Copy link
Copy Markdown
Contributor

in-lockstep review — performance

succeeded · $0.0156 · 3 in / 50 out tokens

No findings.

@github-actions

github-actions Bot commented Sep 9, 2026

Copy link
Copy Markdown
Contributor

in-lockstep review — security

succeeded · $0.0239 · 3 in / 592 out tokens

location finding
⚠️ .lockstep/lockstep.py:174 Validate now runs on the host with no container isolation, executing model-authored files.
⚠️ tests/in_lockstep/test_own_ceilings.py GATE-SANDBOX-2 enforcement test removed, leaving no automated check that code-executing bindings are contained.
⚠️ 1 finding(s) kept without a line: tests/in_lockstep/test_own_ceilings.py:243 points outside every hunk this change produced in that file. The claim may still be right, so it is reported; the coordinate is not, so it is not.

@github-actions

github-actions Bot commented Sep 9, 2026

Copy link
Copy Markdown
Contributor

in-lockstep review — tests

succeeded · $0.0208 · 3 in / 407 out tokens

location finding
⚠️ tests/in_lockstep/test_own_ceilings.py:240 GATE-SANDBOX-2 test deleted with no replacement, leaving the uncontained Validate binding untested.

@tpouyer
tpouyer merged commit 6ee5f8b into main Sep 10, 2026
3 of 4 checks passed
@tpouyer
tpouyer deleted the fix/uncontain-validate branch September 10, 2026 00:21
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