Skip to content
58 changes: 58 additions & 0 deletions docs/doctoring/uv-toolchain-reproducibility.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
# Deterministic uv toolchain selection

## Status and scope

This doctoring record applies to the repository-owned Python dependency, lock, test, and package-build toolchain. It does not change pg-llm-batch runtime semantics, package dependencies, PostgreSQL state, provider behavior, or release authority. The repository root `uv.toml` is the tool-version authority for commands executed through the reviewed `astral-sh/setup-uv` action and for compatible local uv invocations.

## Incident and root cause

The protected-main workflow pinned the `astral-sh/setup-uv` GitHub Action by immutable commit but did not constrain the uv executable selected by that action. With no root `uv.toml` and no `required-version` in `pyproject.toml`, setup-uv logged that it was **falling back to latest** and resolved uv 0.12.3 at execution time. That made package locking, test-environment materialization, and package builds depend on mutable release discovery even when repository source and action commit were unchanged.

The causal boundary is tool selection, not dependency resolution inside `uv.lock`: an immutable action pin fixes the action implementation, while a separate uv version contract fixes the executable the action installs. The package lock remains independently governed by `uv.lock` and the existing `uv lock --check` gate.

## Chosen correction

The root configuration contains exactly:

```toml
required-version = "==0.12.3"
```

The exact PEP 440 equality requirement is deliberate. `setup-uv` discovers this root setting after checkout when no explicit workflow version overrides it, and uv itself rejects execution when its version does not satisfy `required-version`. CI therefore resolves the reviewed uv release from repository source rather than from the moving latest release.

This change does not edit `.github/workflows/ci.yml`, which is independently owned by the exact-source-governance work. It preserves the immutable setup-uv action pin, lock freshness, Python 3.10/3.12/**Python 3.14** tests, package build, coverage/docstring, security, SAST, and container gates.

## Test-first evidence

The first regression head added `tests/test_uv_toolchain_pin.py` before the configuration file existed. CI failed exactly because `uv.toml` was absent; the same run visibly logged the setup-uv latest-release fallback and installed uv 0.12.3. Adding the exact root requirement then made CI, Security Scan, and SAST succeed, and setup-uv logged that it found version 0.12.3 in `uv.toml` before installation.

A second RED documentation contract, `tests/test_uv_toolchain_documentation.py`, required this rationale, version authority, update procedure, rollback path, Python 3.14 preservation, and primary-source references before this doctoring file existed. That failure is intentional development evidence and does not count as final passing evidence for the later head.

## Update procedure

A uv upgrade is a reviewed toolchain change, not ordinary latest-version drift.

1. Review the candidate uv release and relevant setup-uv/uv release notes and security information from authoritative Astral sources.
2. Change the exact `required-version` value in `uv.toml` in a bounded pull request.
3. Do not change `uv.lock` merely because the tool version changed; regenerate it only when dependency or lock-format semantics actually require a reviewed lock change.
4. Run the full permanent CI matrix, including Python 3.10, 3.12, and Python 3.14, 100% owned production statement/branch coverage, public docstrings, Ruff, lock freshness, clean package build, Compose/container builds, Security Scan, and SAST.
5. Inspect setup-uv evidence to confirm it resolved the exact reviewed version from root `uv.toml`, not from a latest-version fallback or an unrelated workflow override.
6. Revalidate any release reproducibility or provenance evidence whose build-tool identity depends on uv before publication.

## Rollback and recovery

If a reviewed uv upgrade causes lock, test, build, packaging, or platform regressions, rollback means restoring the last known accepted exact `required-version` and rerunning the same gates on the resulting source. Do not recover by deleting `uv.toml`, broadening the specifier, or selecting `latest`; those actions recreate the mutable tool-selection boundary.

If Astral distribution of the pinned executable is temporarily unavailable, treat that as tool-distribution infrastructure failure. Do not silently select another uv version to make CI pass. A deliberate version replacement requires the update procedure above and fresh evidence.

## Security and supply-chain implications

Pinning the executable reduces unreviewed toolchain drift but is not an artifact-authentication or provenance claim. The immutable action commit, setup-uv download behavior, runner controls, uv executable version, `uv.lock`, source revision, built artifacts, SBOM, and provenance are separate evidence classes. A green uv-version contract does not replace dependency vulnerability scanning, package reproducibility, or publication attestation.

## APA 7 references

Astral Software. (2026). *setup-uv: Advanced version configuration*. GitHub. https://github.com/astral-sh/setup-uv/blob/main/docs/advanced-version-configuration.md

Astral Software. (2026). *Using uv in GitHub Actions*. uv documentation. https://docs.astral.sh/uv/guides/integration/github/

Astral Software. (2026). *uv settings: required-version*. uv documentation. https://docs.astral.sh/uv/reference/settings/#required-version
34 changes: 34 additions & 0 deletions tests/test_uv_toolchain_documentation.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
# SPDX-License-Identifier: Apache-2.0
"""Documentation contract for deterministic uv toolchain selection."""

from pathlib import Path


ROOT = Path(__file__).resolve().parents[1]
DOCTORING = ROOT / "docs/doctoring/uv-toolchain-reproducibility.md"


def test_uv_toolchain_doctoring_records_reproducibility_and_recovery() -> None:
"""The exact uv pin must retain rationale, update, rollback, and standards."""
assert DOCTORING.exists(), "missing uv toolchain reproducibility doctoring"

raw = DOCTORING.read_text(encoding="utf-8").lower()
normalized = " ".join(raw.split())

assert 'required-version = "==0.12.3"' in raw
assert "## update procedure" in raw
assert "## rollback and recovery" in raw
assert (
"do not recover by deleting `uv.toml`, broadening the specifier, "
"or selecting `latest`"
in normalized
)

for phrase in (
"falling back to latest",
"setup-uv",
"python 3.14",
"astral software",
"apa 7",
):
assert phrase in normalized, phrase
83 changes: 83 additions & 0 deletions tests/test_uv_toolchain_pin.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,83 @@
# SPDX-License-Identifier: Apache-2.0
"""Regression contract for the repository uv toolchain version."""

from pathlib import Path
import re


ROOT = Path(__file__).resolve().parents[1]
UV_CONFIG = ROOT / "uv.toml"
CI_WORKFLOW = ROOT / ".github/workflows/ci.yml"
EXPECTED_REQUIRED_VERSION = 'required-version = "==0.12.3"'
_SETUP_UV_USES = "uses: astral-sh/setup-uv@"
_SETUP_UV_VERSION_INPUT = re.compile(r"(?m)^\s+(?:version|version-file):\s*")
_SETUP_UV_WORKING_DIRECTORY = re.compile(
r"(?m)^\s+working-directory:\s*(?P<value>.+?)\s*$"
)
_ALLOWED_ROOT_WORKING_DIRECTORIES = {".", "${{ github.workspace }}"}


def _setup_uv_step_blocks(workflow: str) -> list[str]:
"""Return each complete setup-uv step without inspecting unrelated steps."""
lines = workflow.splitlines()
blocks: list[str] = []

for uses_index, line in enumerate(lines):
if _SETUP_UV_USES not in line:
continue

uses_indent = len(line) - len(line.lstrip())
step_start = uses_index
while step_start > 0:
candidate = lines[step_start - 1]
stripped = candidate.lstrip()
indent = len(candidate) - len(stripped)
if stripped.startswith("- ") and indent < uses_indent:
step_start -= 1
break
step_start -= 1
else:
raise AssertionError("setup-uv use is not contained in a workflow step")

step_indent = len(lines[step_start]) - len(lines[step_start].lstrip())
step_end = len(lines)
for index in range(uses_index + 1, len(lines)):
candidate = lines[index]
stripped = candidate.lstrip()
indent = len(candidate) - len(stripped)
if stripped.startswith("- ") and indent == step_indent:
step_end = index
break

blocks.append("\n".join(lines[step_start:step_end]))

return blocks


def test_uv_toolchain_version_is_exactly_pinned() -> None:
"""CI must not resolve a different uv release merely because time passed."""
assert UV_CONFIG.exists(), "missing root uv.toml toolchain authority"

substantive_lines = [
line.strip()
for line in UV_CONFIG.read_text(encoding="utf-8").splitlines()
if line.strip() and not line.lstrip().startswith("#")
]
assert substantive_lines == [EXPECTED_REQUIRED_VERSION]


def test_ci_uses_setup_uv_without_an_explicit_latest_override() -> None:
"""Every setup-uv step must defer version authority to the repository root."""
workflow = CI_WORKFLOW.read_text(encoding="utf-8")
setup_uv_steps = _setup_uv_step_blocks(workflow)

assert len(setup_uv_steps) == 2, "unexpected setup-uv step count"
for step in setup_uv_steps:
assert _SETUP_UV_VERSION_INPUT.search(step) is None, step

working_directory = _SETUP_UV_WORKING_DIRECTORY.search(step)
if working_directory is not None:
assert (
working_directory.group("value").strip().strip('"\'')
in _ALLOWED_ROOT_WORKING_DIRECTORIES
), step
1 change: 1 addition & 0 deletions uv.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
required-version = "==0.12.3"
Loading