Stage 3's `pip install /tmp/wheels/*.whl` re-resolved and re-downloaded
~50 transitive deps on every build, because the wheel filenames (and
thus the layer hash) changed on every version bump.
Two changes:
1. Emit /wheels/deps.txt in the build stage — external deps only,
notify-bridge-* siblings filtered out. The runtime stage installs
from this file first, so the cache key is the pyproject.toml deps
(stable across releases) instead of the local wheel filenames
(changes every release). Registry buildcache now serves the whole
install layer on version-only bumps.
2. Swap pip for uv (ghcr.io/astral-sh/uv:0.11.7). uv resolves and
downloads ~10x faster than pip; combined with a BuildKit cache
mount on /root/.cache/uv and UV_COMPILE_BYTECODE=1, a cold install
drops from ~60-90s to a few seconds.
Local wheels are now installed with --no-deps since externals are
already satisfied.