This fixes the `test_container_smoke.py` failures that every branch now
hits. #255 shows them, for example.
## What broke
GitHub's runner Python moved to **CPython 3.13.17**. `lc init` writes
the running interpreter's exact patch to `.python-version`, and the
image installs that interpreter with its pinned uv. uv 0.12.23 has no
download for 3.13.17, so every image build stops at:
```
RUN UV_PYTHON_INSTALL_DIR=/opt/python uv python install 3.13.17
error: No download found for request: cpython-3.13.17-linux-x86_64-gnu
```
`main` was last green on Oct 15, before the runner moved.
## The fix
`image.UV_IMAGE` moves to `ghcr.io/astral-sh/uv:0.13.5`, pinned by its
manifest-list digest. This is the same change as #248.
- **Why 0.13.5:** it is the first uv release with a 3.13.17 download. I
checked `uv python list 3.13.17 --all-versions` against 0.12.24 and
0.13.4, and neither has one.
- **Digest:** fetched from ghcr's manifest endpoint for the tag. The
same query for `0.12.23` returns the digest currently in `image.py`.
- **0.13.0 breaking changes:** none of them touch what the image uses uv
for. The image installs a pinned interpreter and runs `uv sync`. The
default-Python change does not apply, because the version is always
explicit.
## Consequence
The uv pin is part of the image identity document, so containerized
outputs read `behind` after this, as with any uv bump. Direct-mode
projects do not move.
## Tests
- `test_image.py`, `test_container.py` and `test_identity.py` pass (96).
- `ruff` and `mypy` are clean.
- The container smoke suite can't build images in my sandbox, which has
no network access for builds, so this PR's CI is its real run.
---
_Generated by [Claude
Code](https://claude.ai/code/session_01Y9FLtiiMxVhhkvZjd9SQp4)_
Co-authored-by: Claude <noreply@anthropic.com>
The container smoke tests have failed on every branch since 2026-10-02 (#245,
fix/sandbox-elf-interpreter,smoke-findings) with:Why
lc initpins.python-versionto the exact interpreter running lc. The image build then installs that pin with the uv copied fromUV_IMAGE. setup-uv gives CI the newest patch of each Python line, which since 2026-10-02 means 3.11.17 and 3.13.16. The pinned uv 0.12.5 predates both. The 3.12 job only passed because 3.12.14 is still known; 3.12.15 is out and would have broken it next.Change
UV_IMAGE→ghcr.io/astral-sh/uv:0.12.23@sha256:61d393e44e249f2e4b526b6c7ddcecce245946826e608e11c93ad4f5bba55b21.linux/amd64andlinux/arm64. I read it from ghcr's registry API and confirmed it by hashing the index body. The same method reproduces the old 0.12.5 pin (e85be844…) exactly.Effect on projects
This is an engine constant whose bump is meant to be visible (see the layer-6 invariants in CLAUDE.md):
lc buildrebuilds, and a newenv_version. Existing outputs readbehind; nothing is remade.Verification
tests/test_container_smoke.pyunder Python 3.13.16 (podman): the old pin fails with CI's exact error and image tag (lc-env-712be96723c9091c). The new pin passes all 6, including materialize in the image anddatalad rerunon a clone that starts with no file contents.test_image.py,test_container.py,test_identity.py: 96 passed.Note
This will happen again whenever a Python patch release is newer than the pinned uv: CI moves to the newest patch, and the image's uv does not. This PR only restores CI; a structural fix is a separate decision.
🤖 Generated with Claude Code