diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index e54b5f9..cc28cf7 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -14,9 +14,11 @@ jobs: strategy: fail-fast: false matrix: - python-version: ["3.10", "3.12"] + python-version: ["3.10", "3.11", "3.12"] steps: - uses: actions/checkout@v7.0.1 + with: + ref: ${{ github.event.pull_request.head.sha || github.sha }} - name: Checkout pinned skill-lib authority uses: actions/checkout@v7.0.1 with: @@ -71,9 +73,43 @@ jobs: from tools._boundary_pytest import run_suite raise SystemExit(run_suite(["tests", "-c", "pyproject.toml", "--noconftest", "--strict-config"], Path.cwd())) PY - - name: Build package - run: .venv/bin/python -m build + - name: Build package from clean exact Git source + env: + EXPECTED_SOURCE_COMMIT: ${{ github.event.pull_request.head.sha || github.sha }} + run: | + test -z "$(git status --porcelain --untracked-files=all)" + test "$(git rev-parse HEAD)" = "$EXPECTED_SOURCE_COMMIT" + mkdir /tmp/ucns-build-source + git archive "$EXPECTED_SOURCE_COMMIT" | tar -x -C /tmp/ucns-build-source + .venv/bin/python -m build --outdir "$PWD/dist" /tmp/ucns-build-source - name: Check distributions run: | .venv/bin/python -m twine check dist/* .venv/bin/python tools/verify_distributions.py . dist + - name: Replay exact wheel and source distribution in clean environments + run: bash tools/replay_distributions.sh . dist /tmp/ucns-replay python + - name: Collect exact artifact replay evidence + env: + EXPECTED_SOURCE_COMMIT: ${{ github.event.pull_request.head.sha || github.sha }} + run: | + mkdir -p /tmp/ucns-evidence/artifacts /tmp/ucns-evidence/replay + cp dist/*.whl dist/*.tar.gz /tmp/ucns-evidence/artifacts/ + (cd /tmp/ucns-evidence/artifacts; sha256sum -c /tmp/ucns-replay/archives.sha256) + cp /tmp/ucns-replay/*.json /tmp/ucns-replay/*.xml /tmp/ucns-replay/archives.sha256 /tmp/ucns-replay/dependencies.txt /tmp/ucns-evidence/replay/ + python - <<'PY' + import json, os, pathlib, subprocess, sys + def git(*args): + return subprocess.check_output(["git", *args], text=True).strip() + if git("status", "--porcelain", "--untracked-files=all"): + raise SystemExit("checkout changed before provenance capture") + if git("rev-parse", "HEAD") != os.environ["EXPECTED_SOURCE_COMMIT"]: + raise SystemExit("checkout no longer matches the selected source commit") + record = {"source_commit": git("rev-parse", "HEAD"), "source_tree": git("rev-parse", "HEAD^{tree}"), "python": sys.version, "node": subprocess.check_output(["node", "--version"], text=True).strip(), "authority": "execution evidence; independent receipt and source verification required"} + pathlib.Path("/tmp/ucns-evidence/source.json").write_text(json.dumps(record, indent=2) + "\n") + PY + - name: Retain artifacts and replay receipts + uses: actions/upload-artifact@v7.0.1 + with: + name: ucns-evidence-${{ matrix.python-version }} + path: /tmp/ucns-evidence/ + if-no-files-found: error diff --git a/README.md b/README.md index dca6b4b..a7ac28b 100644 --- a/README.md +++ b/README.md @@ -177,6 +177,26 @@ Exact modular and trace records require immutable tuples with non-Boolean intege residues; prefer the public builders. MPFR rational constructors accept only integers or `Fraction`, and NaN cannot participate in interval ordering. +To replay both exact distribution files after the archive gate, with no editable +installation or source-path dependency: + +```bash +bash tools/replay_distributions.sh . dist /tmp/ucns-replay python3.12 +``` + +The output directory must be new and outside this checkout. The script exports +hash-locked dependencies, installs the wheel and sdist into separate clean +venvs, and runs all geometry tests against each installed package. Its receipt +records artifact hashes, Python versions, loaded package paths, test counts, +and zero skipped checks. It also binds a selected exact-input receipt executed +from the archived source, whose UCNS hashes must match both installed artifacts. +CI repeats this on Python 3.10, 3.11, and 3.12 against the exact PR head. Each +job retains its wheel, source archive, dependency export, test reports, selected +receipt, and replay receipt in `ucns-evidence-`. The accompanying +`source.json` binds the Git commit/tree and runtimes. Download those bundles +from the workflow run and verify their artifact/receipt hashes and source maps +before accepting the chain; a successful Actions job alone is not acceptance. +The source archive includes this replay script and the exact build-tool pins. The no-exec graph also reconciles both exact vendored reference parsers, with local Python and TypeScript numeric-field/no-execution witnesses. The complete repository evidence suite requires Node 24.15.0 for that TypeScript check; UCNS @@ -260,3 +280,7 @@ checks; it is not a sandbox for hostile test code. A `passed` receipt covers onl checks; it does not select geometry, ratify candidates, or establish freshness. `hmmm`: ratification of the modular-orbit / continuum-boundary-trace candidates, the complete higher-dimensional UCNS construction, the exact visible-circle wave-trace lift into the native Möbius carrier, any law selecting one continuum covering lift from a finite modular congruence class, and the exact geometric operation of every Public Gonol function position remain unresolved. Unresolved geometry stays unresolved; semantic machinery is not used to fill it. + +Distribution replays hash the complete extracted tree before and after each full suite, reject persistent source changes, and retain the full source map. Each installed distribution inventory covers package files, dist-info payloads, and validated uv installer metadata/RECORD; imports remain separately witnessed. Check downloaded archives from their directory with `sha256sum -c ../replay/archives.sha256`. These checks establish execution provenance, not scientific ratification. + +Standalone replay requires uv 0.11.18. It verifies the bootstrap version, synchronizes the verification environment from the hash-locked export, uses that environment’s uv for later installation, and retains both executable identities. Fixture source builds consume the same locked dependency export. Optimized Python mode is rejected before evidence can be produced. diff --git a/docs/work-graphs/repository-plan-report.json b/docs/work-graphs/repository-plan-report.json index 772552c..5cd4d26 100644 --- a/docs/work-graphs/repository-plan-report.json +++ b/docs/work-graphs/repository-plan-report.json @@ -9,10 +9,10 @@ "blob_sha": "9b347b2dff7692054b571602f30ee6d00c2e7265" }, "source": { - "branch": "repair/ucns-audit-20260911", - "commit": "af02fa61c225b2ada4cb36d1c8016fc81f94f1c0", + "branch": "evidence/clean-install-final-20260912", + "commit": "61d329b851952bbc0fec5eca782c8fd05dcc82d2", "generated_at": "2026-09-12", - "note": "Describes the exact implementation commit preceding this report-only refresh. Maintenance PR #226 is in final exact-head review; merge remains a separate acceptance observation. No scientific or deployment authority is transferred." + "note": "Describes the exact implementation commit preceding this report-only refresh. Artifact acceptance requires inspecting the hash-bound replay bundle for the selected source identity; job success alone is not acceptance. No scientific or deployment authority is transferred." }, "authority": { "owns": [ @@ -38,7 +38,7 @@ } }, "status": { - "state": "active geometry repository; maintenance repair in exact-head review", + "state": "active geometry repository with an exact artifact replay chain", "current_claim": "UCNS retains exact geometric carriers and candidate modular/wave relations. Local repairs harden immutable/exact inputs, MPFR NaN ordering, no-exec contract auditing, selected-check receipts, and replayable distributions; no candidate is ratified by those repairs." }, "delivered": [ @@ -71,10 +71,15 @@ "surface": "Source distribution replay inputs and artifact gate", "status": "implemented exact wheel/sdist metadata and directory validation with pinned build backend and source replay inputs", "boundary": "archive presence and matching bytes are packaging evidence; archived certificates are not thereby recomputed" + }, + { + "surface": "Clean wheel and source-install evidence chain", + "status": "implemented 159-test artifact replay on Python 3.10, 3.11, and 3.12 with hash-locked dependencies, full source snapshots, complete installed-distribution inventories and retained installer metadata, archived-source exact-input receipts, and portable CI bundles", + "boundary": "each receipt binds its own exact artifact and source identity; Python/Node execution and passing packaging evidence do not ratify geometry or grant publication/freshness standing" } ], "active_frontier": [ - "close exact-head maintenance review and then verify the source-distribution/clean-install replay chain across supported Python versions", + "inspect and accept the exact artifact replay bundle for each selected source identity across supported Python versions", "reconcile the intrinsic/extrinsic Structural Null representation before constructing an origin attachment", "derive the complete visible-circle wave trace into the native frame-bearing M\u00f6bius carrier", "ratify or reject modular-orbit / continuum-boundary-trace candidates under explicit falsifiers", @@ -82,9 +87,9 @@ ], "next_actions": [ { - "action": "Complete exact-head review and Python 3.10/3.12 CI for UCNS PR #226; merge after all live findings and gates close", - "owner": "The-Interdependency/ucns maintainers", - "dependency": "Exact canonical skill-lib 5e7239b52dc5403d8d01fbc63341fe397719ec3f binds the declared parser; nine installed skills pass strict drift verification" + "action": "Verify artifact hashes, source maps, dependency exports, and selected receipt identities from each supported runtime bundle before accepting the evidence chain", + "owner": "The-Interdependency/ucns evidence maintainers", + "dependency": "accepted exact source and skill-lib pins; no acceptance from Actions status alone" }, { "action": "Correct stale stack research claims about PR #220 and reconcile its exact research base before consuming results", @@ -113,7 +118,6 @@ } ], "blocked": [ - "Final exact-head CI and merge remain unobserved at this report refresh.", "Origin attachment and recursive traversal cannot be supplied by numerical defaults or downstream interpretations." ], "cross_repository_relations": [ diff --git a/pyproject.toml b/pyproject.toml index d68221d..dab017b 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -23,6 +23,10 @@ classifiers = [ [project.optional-dependencies] test = [ "pytest>=9.1", + "build==1.5.0", + "setuptools==84.0.0", + "wheel==0.48.0", + "uv==0.11.18", "tomli>=2; python_version < '3.11'", "mpmath>=1.3,<2", "sympy>=1.12,<2", @@ -31,7 +35,7 @@ research = [ "mpmath>=1.3,<2", "sympy>=1.12,<2", ] -build = ["build>=1.2", "twine>=5", "tomli>=2; python_version < '3.11'"] +build = ["build==1.5.0", "twine==7.0.0", "setuptools==84.0.0", "wheel==0.48.0", "tomli>=2; python_version < '3.11'"] [tool.setuptools] package-dir = {"" = "src"} diff --git a/tests/test_distribution_replay.py b/tests/test_distribution_replay.py new file mode 100644 index 0000000..aea11ba --- /dev/null +++ b/tests/test_distribution_replay.py @@ -0,0 +1,172 @@ +# ratios: loc_comments=81:81 imports_exports=17:3 calls_definitions=83:3 +# === CHECKS === +# id: check_distribution_replay_source_integrity +# proves: ucns_distributions_replay_installed_code, ucns_distribution_evidence_binds_all_files +# call: self::test_replay_source_changes_fail_closed +# requires: python3, pytest +# timeout: 10 +# mutates: temporary_path +# cleanup: pytest temporary_path +# +# id: check_distribution_replay_installed_inventory +# proves: ucns_distributions_replay_installed_code, ucns_distribution_evidence_binds_all_files +# call: self::test_installed_distribution_inventory +# requires: python3, pytest, uv +# timeout: 30 +# mutates: temporary_path +# cleanup: pytest temporary_path +# +# id: check_distribution_replay_source_install +# proves: ucns_distributions_replay_installed_code, ucns_distribution_evidence_binds_all_files +# call: self::test_sdist_installer_metadata_is_recorded +# requires: python3, pytest, uv, build +# timeout: 60 +# mutates: temporary_path +# cleanup: pytest temporary_path +# === END CHECKS === +"""Usage: pytest tests/test_distribution_replay.py. Exercise real installed files.""" +import base64 +import csv +import hashlib +import io +import json +import os +from pathlib import Path +import subprocess +import sys +import zipfile + +import pytest + +from tools._distribution_evidence import source_snapshot, verify_snapshot +from tools.verify_skill_lib_contracts import audit_repository + + +def test_replay_source_changes_fail_closed(tmp_path): + source = tmp_path / "source" + source.mkdir() + for name in ("test_case.py", "helper.sh", "evidence.json", "extensionless"): + (source / name).write_text("original") + script = Path(__file__).resolve().parents[1] / "tools/replay_distributions.sh" + for optimize in ("1", "2"): + rejected = subprocess.run(["bash", str(script)], env=dict(os.environ, PYTHONOPTIMIZE=optimize), capture_output=True, text=True) + assert rejected.returncode == 2 and "optimized Python mode" in rejected.stderr + binary = tmp_path / "bin" + binary.mkdir() + fake_uv = binary / "uv" + fake_uv.write_text("#!/bin/sh\nprintf '%s\\n' 'uv 0.11.17'\n") + fake_uv.chmod(0o755) + rejected = subprocess.run(["bash", str(script)], env=dict(os.environ, PYTHONOPTIMIZE="0", PATH=str(binary) + os.pathsep + os.environ["PATH"]), capture_output=True, text=True) + assert rejected.returncode == 2 and "unsupported uv" in rejected.stderr + baseline = source_snapshot(source) + verify_snapshot(source, baseline) + for name in baseline: + path = source / name + path.write_text("mutated") + with pytest.raises(ValueError, match="archived source changed"): + verify_snapshot(source, baseline) + path.write_text("original") + (source / "added").write_text("extra") + with pytest.raises(ValueError, match="archived source changed"): + verify_snapshot(source, baseline) + (source / "added").unlink() + (source / "link").symlink_to(source / "evidence.json") + with pytest.raises(ValueError, match="symlink"): + verify_snapshot(source, baseline) + # A behavior-bearing shell contract must enter the no-exec graph. + (tmp_path / "tools").mkdir() + (tmp_path / "tools/replay.sh").write_text("# === CONTRACTS ===\n# id: shell_unwitnessed\n# given: replay inputs\n# then: replay evidence\n# === END CONTRACTS ===\nexit 0\n") + ok, gaps = audit_repository(tmp_path) + assert not ok and any("shell_unwitnessed" in gap for gap in gaps), gaps + + +def test_installed_distribution_inventory(tmp_path): + wheel = tmp_path / "ucns-0-py3-none-any.whl" + info = "ucns-0.dist-info/" + files = {"ucns/__init__.py": b"VALUE = 1\n", info + "METADATA": b"Metadata-Version: 2.1\nName: ucns\nVersion: 0\n", info + "WHEEL": b"Wheel-Version: 1.0\nGenerator: fixture\nRoot-Is-Purelib: true\nTag: py3-none-any\n"} + record = io.StringIO() + writer = csv.writer(record) + for name, payload in files.items(): + writer.writerow((name, "sha256=" + base64.urlsafe_b64encode(hashlib.sha256(payload).digest()).rstrip(b"=").decode(), len(payload))) + writer.writerow((info + "RECORD", "", "")) + files[info + "RECORD"] = record.getvalue().encode() + with zipfile.ZipFile(wheel, "w") as archive: + for name, payload in files.items(): + archive.writestr(name, payload) + environment = tmp_path / "environment" + subprocess.run(["uv", "venv", "--python", sys.executable, str(environment)], check=True, capture_output=True) + python = environment / "bin/python" + uri = wheel.as_uri() + "#sha256=" + hashlib.sha256(wheel.read_bytes()).hexdigest() + subprocess.run(["uv", "pip", "install", "--python", str(python), "--no-deps", "ucns @ " + uri], check=True, capture_output=True) + from tools import _distribution_evidence + helper = str(Path(_distribution_evidence.__file__).resolve()) + script = """ +import importlib.util, json, pathlib, sys +spec = importlib.util.spec_from_file_location('evidence', sys.argv[1]) +module = importlib.util.module_from_spec(spec) +spec.loader.exec_module(module) +wheel = pathlib.Path(sys.argv[2]) +report = module.installed_inventory(wheel, wheel) +print(json.dumps(report)) +""" + clean = {name: value for name, value in os.environ.items() if name not in {"PYTHONPATH", "PYTHONHOME", "UCNS_BOUND_SOURCE_ROOT"}} + command = [str(python), "-c", script, helper, str(wheel)] + initial = subprocess.run(command, env=clean, capture_output=True, text=True) + assert initial.returncode == 0, initial.stderr + report = json.loads(initial.stdout) + assert info + "METADATA" in report["files_sha256"] and info + "direct_url.json" in report["installer_metadata"] + site = next(environment.glob("lib/python*/site-packages")) + for name in (info + "METADATA", info + "RECORD", info + "direct_url.json", "ucns/__init__.py"): + path = site / name + original = path.read_bytes() + path.write_bytes(original + b"altered") + assert subprocess.run(command, env=clean, capture_output=True).returncode != 0, name + path.write_bytes(original) + for name in (info + "unexpected", "ucns/unrecorded.txt"): + path = site / name + path.write_text("extra") + assert subprocess.run(command, env=clean, capture_output=True).returncode != 0, name + path.unlink() + (site / (info + "METADATA")).unlink() + assert subprocess.run(command, env=clean, capture_output=True).returncode != 0 + + +def test_sdist_installer_metadata_is_recorded(tmp_path): + source = tmp_path / "project" + (source / "src/ucns").mkdir(parents=True) + (source / "src/ucns/__init__.py").write_text("VALUE = 1\n") + (source / "pyproject.toml").write_text('[build-system]\nrequires=["setuptools==84.0.0", "wheel==0.48.0"]\nbuild-backend="setuptools.build_meta"\n[project]\nname="ucns"\nversion="0"\n[tool.setuptools.packages.find]\nwhere=["src"]\n') + dist = tmp_path / "dist" + built = subprocess.run([sys.executable, "-m", "build", "--no-isolation", "--outdir", str(dist), str(source)], capture_output=True, text=True) + assert built.returncode == 0, built.stdout + built.stderr + wheel = next(dist.glob("*.whl")) + artifact = next(dist.glob("*.tar.gz")) + environment = tmp_path / "environment" + subprocess.run(["uv", "venv", "--python", sys.executable, str(environment)], check=True, capture_output=True) + python = environment / "bin/python" + dependencies = tmp_path / "dependencies.txt" + project = Path(__file__).resolve().parents[1] + subprocess.run(["uv", "export", "--project", str(project), "--locked", "--extra", "test", "--extra", "build", "--no-emit-project", "--no-dev", "--format", "requirements.txt", "--output-file", str(dependencies)], check=True, capture_output=True) + subprocess.run(["uv", "pip", "sync", "--python", str(python), "--require-hashes", str(dependencies)], check=True, capture_output=True) + uri = artifact.as_uri() + "#sha256=" + hashlib.sha256(artifact.read_bytes()).hexdigest() + result = subprocess.run(["uv", "pip", "install", "--python", str(python), "--no-deps", "--no-build-isolation", "ucns @ " + uri], capture_output=True, text=True) + assert result.returncode == 0, result.stdout + result.stderr + from tools import _distribution_evidence + script = """ +import importlib.util, json, pathlib, sys +spec = importlib.util.spec_from_file_location('evidence', sys.argv[1]) +module = importlib.util.module_from_spec(spec) +spec.loader.exec_module(module) +print(json.dumps(module.installed_inventory(pathlib.Path(sys.argv[2]), pathlib.Path(sys.argv[3])))) +""" + clean = {name: value for name, value in os.environ.items() if name not in {"PYTHONPATH", "PYTHONHOME", "UCNS_BOUND_SOURCE_ROOT"}} + command = [str(python), "-c", script, str(Path(_distribution_evidence.__file__).resolve()), str(wheel), str(artifact)] + result = subprocess.run(command, env=clean, capture_output=True, text=True) + assert result.returncode == 0, result.stderr + inventory = json.loads(result.stdout) + name, = [name for name in inventory["installer_metadata"] if name.endswith("/uv_build.json")] + assert json.loads(inventory["installer_metadata"][name]) == {} + path = next(environment.glob("lib/python*/site-packages")) / name + path.write_text('{"unexpected": true}') + assert subprocess.run(command, env=clean, capture_output=True).returncode != 0 +# ratios: loc_comments=81:81 imports_exports=17:3 calls_definitions=83:3 diff --git a/tools/_distribution_evidence.py b/tools/_distribution_evidence.py new file mode 100644 index 0000000..b35b9a0 --- /dev/null +++ b/tools/_distribution_evidence.py @@ -0,0 +1,136 @@ +# ratios: loc_comments=94:28 imports_exports=10:4 calls_definitions=63:4 +# === MODULE_BUILD === +# id: ucns_distribution_evidence +# module_name: _distribution_evidence +# module_kind: instrument +# summary: binds complete archived inputs and installed distribution files to replay evidence +# owner: Erin Spencer +# public_surface: snapshot and verify-snapshot CLI; installed_inventory +# internal_surface: source_snapshot +# auth_boundary: none +# storage_boundary: write +# storage_notes: caller-selected snapshot receipt +# network_boundary: none +# user_data_boundary: none +# admin_only: false +# tests: tests/test_distribution_replay.py +# rollout: distribution replay gate +# rollback: retain earlier evidence as historical only +# === END MODULE_BUILD === +# === CONTRACTS === +# id: ucns_distribution_evidence_binds_all_files +# given: a source archive and a clean installed distribution +# then: persistent source changes and missing, altered, or unexpected installed files fail replay +# class: evidence +# === END CONTRACTS === +"""Usage: python tools/_distribution_evidence.py snapshot|verify-snapshot SOURCE JSON. + +Snapshots exclude bytecode caches only. Installed RECORD and installer-generated +metadata are retained and validated separately from immutable wheel payloads. +""" +from __future__ import annotations + +import base64 +import csv +import hashlib +from importlib import metadata +import io +import json +from pathlib import Path +import sys +import zipfile + + +def source_snapshot(root: Path) -> dict[str, str]: + entries = tuple(root.rglob("*")) + if root.is_symlink() or any(path.is_symlink() for path in entries): + raise ValueError("source snapshot contains a symlink") + return {path.relative_to(root).as_posix(): hashlib.sha256(path.read_bytes()).hexdigest() + for path in entries if path.is_file() and "__pycache__" not in path.relative_to(root).parts} + + +def verify_snapshot(root: Path, expected: dict[str, str]) -> None: + if source_snapshot(root) != expected: + raise ValueError("archived source changed during distribution replay") + + +def installed_inventory(wheel: Path, artifact: Path) -> dict: + distribution = metadata.distribution("ucns") + base = Path(distribution.locate_file("")).resolve() + if not base.is_relative_to(Path(sys.prefix).resolve()): + raise ValueError("installed distribution escaped verification environment") + with zipfile.ZipFile(wheel) as archive: + expected = {name: archive.read(name) for name in archive.namelist() if not name.endswith("/")} + records = [name for name in expected if name.endswith(".dist-info/RECORD")] + if len(records) != 1: + raise ValueError("wheel RECORD identity is ambiguous") + record = records[0] + info = record.rsplit("/", 1)[0] + generated = {info + "/" + name for name in ("RECORD", "INSTALLER", "REQUESTED", "direct_url.json", "uv_cache.json", "uv_build.json")} + paths = {str(path): Path(distribution.locate_file(path)) for path in distribution.files or ()} + for name, path in paths.items(): + if Path(name).is_absolute() or ".." in Path(name).parts or path.is_symlink() or not path.resolve().is_relative_to(base): + raise ValueError("unsafe installed path: " + name) + actual = {name: path.read_bytes() for name, path in paths.items() if "__pycache__" not in Path(name).parts} + if set(actual) - set(expected) - generated or set(expected) - set(actual): + raise ValueError("unexpected or missing installed distribution file") + for directory in (base / "ucns", base / info): + entries = tuple(directory.rglob("*")) + if directory.is_symlink() or any(path.is_symlink() for path in entries): + raise ValueError("installed distribution contains a symlink") + observed = {path.relative_to(base).as_posix() for path in entries if path.is_file() and "__pycache__" not in path.relative_to(base).parts} + declared = {name for name in actual if Path(name).is_relative_to(directory.relative_to(base))} + if observed != declared: + raise ValueError("installed distribution has unrecorded files") + for name, payload in expected.items(): + if name != record and actual[name] != payload: + raise ValueError("installed wheel payload differs: " + name) + if actual.get(info + "/INSTALLER") != b"uv" or actual.get(info + "/REQUESTED", b"") != b"": + raise ValueError("unexpected installer metadata") + direct = json.loads(actual[info + "/direct_url.json"]) + expected_uri = artifact.resolve().as_uri() + "#sha256=" + hashlib.sha256(artifact.read_bytes()).hexdigest() + if direct != {"url": expected_uri, "archive_info": {}}: + raise ValueError("installed artifact origin differs") + build_metadata = actual.get(info + "/uv_build.json") + if artifact.name.endswith(".tar.gz"): + if build_metadata is None or json.loads(build_metadata) != {}: + raise ValueError("unexpected uv build metadata") + elif build_metadata is not None: + raise ValueError("wheel install contains source-build metadata") + cache = json.loads(actual[info + "/uv_cache.json"]) + if set(cache) != {"timestamp", "commit", "tags", "env", "directories"} or cache["commit"] is not None or cache["tags"] is not None or cache["env"] != {} or cache["directories"] != {}: + raise ValueError("unexpected uv cache metadata") + timestamp = cache["timestamp"] + if set(timestamp) != {"secs_since_epoch", "nanos_since_epoch"} or not all(type(value) is int and value >= 0 for value in timestamp.values()) or timestamp["nanos_since_epoch"] >= 1000000000: + raise ValueError("invalid uv cache timestamp") + rows = list(csv.reader(io.StringIO(actual[record].decode()))) + if any(len(row) != 3 for row in rows) or len({row[0] for row in rows}) != len(rows): + raise ValueError("invalid installed RECORD") + if {row[0] for row in rows} != set(paths): + raise ValueError("installed RECORD coverage differs") + for name, digest, size in rows: + if name == record or "__pycache__" in Path(name).parts: + if digest or size: + raise ValueError("unexpected generated RECORD hash") + continue + wanted = "sha256=" + base64.urlsafe_b64encode(hashlib.sha256(actual[name]).digest()).rstrip(b"=").decode() + if digest != wanted or size != str(len(actual[name])): + raise ValueError("installed RECORD digest differs: " + name) + return {"files_sha256": {name: hashlib.sha256(payload).hexdigest() for name, payload in sorted(actual.items())}, + "installer_metadata": {name: actual[name].decode() for name in sorted(generated & actual.keys())}} + + +def main() -> None: + action, source, receipt = sys.argv[1:] + root, output = Path(source), Path(receipt) + if action == "snapshot": + output.write_text(json.dumps(source_snapshot(root), indent=2, sort_keys=True) + "\n") + elif action == "verify-snapshot": + verify_snapshot(root, json.loads(output.read_text())) + else: + raise ValueError("unknown snapshot action") + + +if __name__ == "__main__": + main() +# ratios: loc_comments=94:28 imports_exports=10:4 calls_definitions=63:4 diff --git a/tools/replay_distributions.sh b/tools/replay_distributions.sh new file mode 100644 index 0000000..2b11e15 --- /dev/null +++ b/tools/replay_distributions.sh @@ -0,0 +1,147 @@ +#!/usr/bin/env bash +# === MODULE_BUILD === +# id: ucns_distribution_replay +# module_name: replay_distributions +# module_kind: instrument +# summary: runs the complete geometry suite against clean wheel and sdist installations +# owner: Erin Spencer +# public_surface: bash tools/replay_distributions.sh ROOT DIST OUTPUT PYTHON +# internal_surface: none +# auth_boundary: none +# storage_boundary: write +# storage_notes: new caller-selected output directory and uv cache +# network_boundary: external +# network_notes: locked Python build/test dependencies +# user_data_boundary: none +# admin_only: false +# tests: full geometry suite under both installed artifacts +# rollout: CI on Python 3.10, 3.11, and 3.12 +# rollback: remove the replay CI step +# === END MODULE_BUILD === +# === CONTRACTS === +# id: ucns_distributions_replay_installed_code +# given: archives match the source inputs and dependencies resolve from the lock +# then: each artifact installs without editable source and every geometry test passes without skips while ucns resolves inside its clean environment +# class: evidence +# === END CONTRACTS === + +# Usage: bash tools/replay_distributions.sh . dist /tmp/ucns-replay python3.12 +# OUTPUT must not exist and must be outside ROOT. Receipts cover packaging and +# executed tests, never candidate ratification or historical expensive replay. +set -euo pipefail +case "${PYTHONOPTIMIZE-}" in ""|0) ;; *) echo 'optimized Python mode cannot produce replay evidence' >&2; exit 2;; esac +bootstrap_uv=$(command -v uv) +test -f "$bootstrap_uv" +bootstrap_version=$("$bootstrap_uv" --version) +uv_pattern='^uv 0[.]11[.]18( \([a-z0-9_-]+\))?$' +if [[ ! "$bootstrap_version" =~ $uv_pattern ]]; then echo "unsupported uv: $bootstrap_version" >&2; exit 2; fi +bootstrap_sha=$(sha256sum "$bootstrap_uv"); bootstrap_sha=${bootstrap_sha%% *} +repo=$(realpath "$1") +dist=$(realpath "$2") +output=$(realpath -m "$3") +runtime=${4:-python3} +case "$output/" in "$repo/"*) echo 'OUTPUT must be outside source' >&2; exit 2;; esac +test ! -e "$output" +mkdir -p "$output" +"$bootstrap_uv" export --project "$repo" --locked --extra test --extra build --no-emit-project --no-dev --format requirements.txt --output-file "$output/dependencies.txt" >/dev/null +"$bootstrap_uv" venv --python "$runtime" "$output/verification-venv" +"$bootstrap_uv" pip sync --python "$output/verification-venv/bin/python" --require-hashes "$output/dependencies.txt" +installer_uv="$output/verification-venv/bin/uv" +installer_version=$("$installer_uv" --version) +if [[ ! "$installer_version" =~ $uv_pattern ]]; then echo 'locked uv version mismatch' >&2; exit 2; fi +installer_sha=$(sha256sum "$installer_uv"); installer_sha=${installer_sha%% *} +"$output/verification-venv/bin/python" "$repo/tools/verify_distributions.py" "$repo" "$dist" +(cd "$dist"; sha256sum ./*.whl ./*.tar.gz) > "$output/archives.sha256" +mkdir "$output/source" +tar -xzf "$dist"/*.tar.gz -C "$output/source" +source_root=$(find "$output/source" -mindepth 1 -maxdepth 1 -type d) +snapshot_tool="$repo/tools/_distribution_evidence.py" +"$output/verification-venv/bin/python" "$snapshot_tool" snapshot "$source_root" "$output/source-snapshot.json" +for kind in wheel sdist; do + "$output/verification-venv/bin/python" "$snapshot_tool" verify-snapshot "$source_root" "$output/source-snapshot.json" + environment="$output/$kind-venv" + "$installer_uv" venv --python "$runtime" "$environment" + "$installer_uv" pip sync --python "$environment/bin/python" --require-hashes "$output/dependencies.txt" + if [ "$kind" = wheel ]; then artifact=("$dist"/*.whl); else artifact=("$dist"/*.tar.gz); fi + artifact_uri=$("$output/verification-venv/bin/python" - "${artifact[0]}" <<'PYTHON' +import hashlib +from pathlib import Path +import sys +if sys.flags.optimize: + raise SystemExit("optimized Python mode cannot produce replay evidence") +artifact = Path(sys.argv[1]) +print(artifact.as_uri() + "#sha256=" + hashlib.sha256(artifact.read_bytes()).hexdigest()) +PYTHON + ) + "$installer_uv" pip install --python "$environment/bin/python" --no-deps --no-build-isolation "ucns @ $artifact_uri" + ( + cd "$source_root" + env -u PYTHONPATH -u PYTHONHOME -u PYTEST_ADDOPTS -u PYTEST_PLUGINS PYTHONDONTWRITEBYTECODE=1 PYTEST_DISABLE_PLUGIN_AUTOLOAD=1 PYTHONPYCACHEPREFIX="$output/$kind-bytecode" PATH="$environment/bin:$PATH" \ + "$environment/bin/python" - "$output/$kind.xml" "$output/$kind-import.json" "$dist"/*.whl "${artifact[0]}" <<'PY' +import hashlib +import json +from pathlib import Path +import sys +if sys.flags.optimize: + raise SystemExit("optimized Python mode cannot produce replay evidence") +import xml.etree.ElementTree as ET +import ucns +from tools._boundary_pytest import run_suite +from tools._distribution_evidence import installed_inventory + +installed = Path(ucns.__file__).resolve() +assert installed.is_relative_to(Path(sys.prefix)), installed +initial = installed.read_bytes() +expected = {p.relative_to(Path("src")).as_posix(): hashlib.sha256(p.read_bytes()).hexdigest() for p in Path("src/ucns").rglob("*") if p.is_file() and "__pycache__" not in p.parts} +def installed_sources(): + entries = tuple(installed.parent.rglob("*")) + assert not any(path.is_symlink() for path in entries), "installed package contains a symlink" + return {"ucns/" + p.relative_to(installed.parent).as_posix(): hashlib.sha256(p.read_bytes()).hexdigest() for p in entries if p.is_file() and "__pycache__" not in p.parts} +assert installed_sources() == expected +distribution_before = installed_inventory(Path(sys.argv[3]), Path(sys.argv[4])) +result = run_suite(["tests", "-p", "no:cacheprovider", "-c", "pyproject.toml", "--noconftest", "--strict-config", "--junitxml=" + sys.argv[1]], Path.cwd()) +assert result == 0, result +assert Path(ucns.__file__).resolve() == installed +assert installed.read_bytes() == initial +assert installed_sources() == expected +assert installed_inventory(Path(sys.argv[3]), Path(sys.argv[4])) == distribution_before +origins = {name: str(Path(module.__file__).resolve()) for name, module in sys.modules.items() if (name == "ucns" or name.startswith("ucns.")) and getattr(module, "__file__", None)} +assert all(Path(path).is_relative_to(installed.parent) for path in origins.values()), origins +cases = list(ET.parse(sys.argv[1]).getroot().iter("testcase")) +assert cases and not any(c.find("skipped") is not None or c.find("failure") is not None or c.find("error") is not None for c in cases) +Path(sys.argv[2]).write_text(json.dumps({"python": sys.version, "ucns_path": str(installed), "ucns_init_sha256": hashlib.sha256(initial).hexdigest(), "installed_source_sha256": expected, "installed_distribution": distribution_before, "imported_origins": origins, "tests": len(cases), "skips": 0, "status": "passed"}, indent=2) + "\n") +PY + ) + "$output/verification-venv/bin/python" "$snapshot_tool" verify-snapshot "$source_root" "$output/source-snapshot.json" +done +# This receipt executes the exact source archived above. Installed wheel/sdist +# execution is separately witnessed by the two complete suites and source maps. +env -u PYTHONPATH -u PYTHONHOME -u PYTEST_ADDOPTS -u PYTEST_PLUGINS PYTHONDONTWRITEBYTECODE=1 PATH="$output/verification-venv/bin:$PATH" \ + "$output/verification-venv/bin/python" "$source_root/tools/run_skill_lib_boundaries.py" "$source_root" \ + --check check_modular_orbit_fails_closed \ + --check check_gonal_boundary_trace_fails_closed_on_incompatible_geometry \ + --check check_mpfr_nan_is_not_ordered_evidence \ + --check check_mpfr_exact_rational_admission \ + --check check_boundary_runner_nonactivation --receipt "$output/exact-input-receipt.json" +(cd "$dist"; sha256sum -c "$output/archives.sha256") +"$output/verification-venv/bin/python" "$snapshot_tool" verify-snapshot "$source_root" "$output/source-snapshot.json" +"$output/verification-venv/bin/python" "$repo/tools/verify_distributions.py" "$repo" "$dist" +test "$(sha256sum "$bootstrap_uv" | cut -d ' ' -f 1)" = "$bootstrap_sha" +test "$(sha256sum "$installer_uv" | cut -d ' ' -f 1)" = "$installer_sha" +"$output/verification-venv/bin/python" - "$dist" "$output" "$bootstrap_version" "$bootstrap_sha" "$installer_version" "$installer_sha" <<'PY' +import hashlib +import json +from pathlib import Path +import sys +if sys.flags.optimize: + raise SystemExit("optimized Python mode cannot produce replay evidence") +dist, out = map(Path, sys.argv[1:3]) +boundary = json.loads((out / "exact-input-receipt.json").read_text()) +assert boundary["status"] == "passed" and boundary["source_unchanged"] and boundary["snapshot_errors"] == [] +for kind in ("wheel", "sdist"): + installed = json.loads((out / (kind + "-import.json")).read_text())["installed_source_sha256"] + assert all(boundary["source_files_sha256"]["src/" + name] == digest for name, digest in installed.items()) +receipt = {"uv_identity": {"bootstrap": {"version": sys.argv[3], "sha256": sys.argv[4]}, "installer": {"version": sys.argv[5], "sha256": sys.argv[6]}}, "schema": "ucns.distribution-replay", "version": "1.0.0", "status": "passed", "artifacts_sha256": {p.name: hashlib.sha256(p.read_bytes()).hexdigest() for p in sorted(dist.iterdir()) if p.suffix == ".whl" or p.name.endswith(".tar.gz")}, "runs": {kind: json.loads((out / (kind + "-import.json")).read_text()) for kind in ("wheel", "sdist")}, "dependency_export_sha256": hashlib.sha256((out / "dependencies.txt").read_bytes()).hexdigest(), "candidate_ratification": "none", "exact_input_receipt_sha256": hashlib.sha256((out / "exact-input-receipt.json").read_bytes()).hexdigest(), "exact_input_receipt_identity": boundary["receipt_sha256"], "full_replay_source_sha256": json.loads((out / "source-snapshot.json").read_text()), "exact_input_source_boundary": "archived source; installed artifact execution witnessed separately above"} +(out / "receipt.json").write_text(json.dumps(receipt, indent=2, sort_keys=True) + "\n") +print(json.dumps(receipt, indent=2)) +PY diff --git a/tools/run_skill_lib_boundaries.py b/tools/run_skill_lib_boundaries.py index 0069ac4..d8d7123 100644 --- a/tools/run_skill_lib_boundaries.py +++ b/tools/run_skill_lib_boundaries.py @@ -230,7 +230,7 @@ def _capability_available(name: str) -> bool: return hasattr(os, "sched_getaffinity") and hasattr(os, "sched_setaffinity") if name in {"libmpfr", "system-libmpfr"}: return ctypes.util.find_library("mpfr") is not None - if name in {"mpmath", "numpy", "sympy", "pytest"}: + if name in {"mpmath", "numpy", "sympy", "pytest", "build"}: return importlib.util.find_spec(name) is not None return shutil.which(name) is not None @@ -372,7 +372,7 @@ def _execute_check(root: Path, check: Entry) -> CheckOutcome: junit_path = Path(temporary) / "outcomes.xml" report_path = Path(temporary) / "outcomes.json" command = (sys.executable, str(BOOTSTRAP), str(root), str(report_path), - "-q", "-c", str(root / "pyproject.toml"), "--noconftest", f"{relative_source}::{function}", f"--junitxml={junit_path}", "-o", "xfail_strict=true") + "-q", "-p", "no:cacheprovider", "-c", str(root / "pyproject.toml"), "--noconftest", f"{relative_source}::{function}", f"--junitxml={junit_path}", "-o", "xfail_strict=true") command = (sys.executable, str(SUPERVISOR), str(timeout), *command[1:]) environment = dict(os.environ) for name in ("PYTHONPATH", "PYTHONHOME", "PYTEST_ADDOPTS", "PYTEST_PLUGINS"): diff --git a/tools/verify_skill_lib_contracts.py b/tools/verify_skill_lib_contracts.py index 9d71e46..1fde2c2 100644 --- a/tools/verify_skill_lib_contracts.py +++ b/tools/verify_skill_lib_contracts.py @@ -114,7 +114,7 @@ def _source_files(root: Path) -> Iterable[Path]: yield parser for base in (root / "src", root / "tools", root / "tests"): if base.exists(): - yield from (path for path in sorted(base.rglob("*.py")) if "__pycache__" not in path.parts) + yield from (path for path in sorted(base.rglob("*")) if path.suffix in {".py", ".sh"} and path.is_file() and "__pycache__" not in path.parts) def parse_blocks(path: Path) -> List[Entry]: diff --git a/uv.lock b/uv.lock index bb30689..b8aaeb9 100644 --- a/uv.lock +++ b/uv.lock @@ -881,6 +881,15 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/b7/46/f5af3402b579fd5e11573ce652019a67074317e18c1935cc0b4ba9b35552/secretstorage-3.5.0-py3-none-any.whl", hash = "sha256:0ce65888c0725fcb2c5bc0fdb8e5438eece02c523557ea40ce0703c266248137", size = 15554, upload-time = "2025-11-23T19:02:51.545Z" }, ] +[[package]] +name = "setuptools" +version = "84.0.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/6d/44/f5da03a8ef95d369145c5bb53050e7877c9f3d312e128605fd9504829143/setuptools-84.0.0.tar.gz", hash = "sha256:f4695c21257f0d9b537ec2692c941d02ee143b7cc1276941349a546573b2ef73", size = 1168449, upload-time = "2026-08-08T18:27:58.365Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/95/9c/c510029fc6ef33a6275cd2c5d3cecd6613dfd6aa401d57c54f1c18852ccf/setuptools-84.0.0-py3-none-any.whl", hash = "sha256:51a52592b3b99e102b609654876bd65f19f999935166d1352678931132b0c670", size = 818216, upload-time = "2026-08-08T18:27:56.719Z" }, +] + [[package]] name = "sympy" version = "1.14.0" @@ -990,33 +999,45 @@ dependencies = [ [package.optional-dependencies] build = [ { name = "build" }, + { name = "setuptools" }, { name = "tomli", marker = "python_full_version < '3.11'" }, { name = "twine" }, + { name = "wheel" }, ] research = [ { name = "mpmath" }, { name = "sympy" }, ] test = [ + { name = "build" }, { name = "mpmath" }, { name = "pytest" }, + { name = "setuptools" }, { name = "sympy" }, { name = "tomli", marker = "python_full_version < '3.11'" }, + { name = "uv" }, + { name = "wheel" }, ] [package.metadata] requires-dist = [ - { name = "build", marker = "extra == 'build'", specifier = ">=1.2" }, + { name = "build", marker = "extra == 'build'", specifier = "==1.5.0" }, + { name = "build", marker = "extra == 'test'", specifier = "==1.5.0" }, { name = "mpmath", specifier = ">=1.3" }, { name = "mpmath", marker = "extra == 'research'", specifier = ">=1.3,<2" }, { name = "mpmath", marker = "extra == 'test'", specifier = ">=1.3,<2" }, { name = "numpy", specifier = ">=1.24" }, { name = "pytest", marker = "extra == 'test'", specifier = ">=9.1" }, + { name = "setuptools", marker = "extra == 'build'", specifier = "==84.0.0" }, + { name = "setuptools", marker = "extra == 'test'", specifier = "==84.0.0" }, { name = "sympy", marker = "extra == 'research'", specifier = ">=1.12,<2" }, { name = "sympy", marker = "extra == 'test'", specifier = ">=1.12,<2" }, { name = "tomli", marker = "python_full_version < '3.11' and extra == 'build'", specifier = ">=2" }, { name = "tomli", marker = "python_full_version < '3.11' and extra == 'test'", specifier = ">=2" }, - { name = "twine", marker = "extra == 'build'", specifier = ">=5" }, + { name = "twine", marker = "extra == 'build'", specifier = "==7.0.0" }, + { name = "uv", marker = "extra == 'test'", specifier = "==0.11.18" }, + { name = "wheel", marker = "extra == 'build'", specifier = "==0.48.0" }, + { name = "wheel", marker = "extra == 'test'", specifier = "==0.48.0" }, ] provides-extras = ["test", "research", "build"] @@ -1029,6 +1050,44 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/7f/3e/5db95bcf282c52709639744ca2a8b149baccf648e39c8cc87553df9eae0c/urllib3-2.7.0-py3-none-any.whl", hash = "sha256:9fb4c81ebbb1ce9531cce37674bbc6f1360472bc18ca9a553ede278ef7276897", size = 131087, upload-time = "2026-05-07T16:13:17.151Z" }, ] +[[package]] +name = "uv" +version = "0.11.18" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/68/ea/c25007a2964e5f5f5300db2cab16fe8c2fc1284f0b9bea6212e06097d05b/uv-0.11.18.tar.gz", hash = "sha256:61f2bc99898383f9bf04e24b984e42e19cde378dd79192935ca21d75563368a8", size = 4209283, upload-time = "2026-06-01T19:43:07.998Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/4f/f1/692f05ecffe73a8bbf62c19ed3ed897556c1a4b00a7b2dd18e5b73f92daa/uv-0.11.18-py3-none-linux_armv6l.whl", hash = "sha256:b35d25a5fda7d058b1684476a47555afbe0e9560318c7a8f72cccc6b77b42e1a", size = 23592196, upload-time = "2026-06-01T19:42:27.089Z" }, + { url = "https://files.pythonhosted.org/packages/38/ad/57a31ea9ffc53a2fb5cd9a60b5edb9e4df7c526ba80be4517c6d73cf4fa7/uv-0.11.18-py3-none-macosx_10_12_x86_64.whl", hash = "sha256:90685bda9e15600ae9a2a10c326008a69f28d8652555a2e760e1493e4b1ae7b5", size = 23193111, upload-time = "2026-06-01T19:42:52.293Z" }, + { url = "https://files.pythonhosted.org/packages/af/3b/130515418bdd4be1aec5941ca2fa53dc0281750434e835ff633e6f8cd944/uv-0.11.18-py3-none-macosx_11_0_arm64.whl", hash = "sha256:0571ec649f25e2cca9eb994637aa1ae27ab3db58f87c5a9b5f9776d5d7cd2298", size = 21621552, upload-time = "2026-06-01T19:42:49.709Z" }, + { url = "https://files.pythonhosted.org/packages/95/de/7dccc8c8449235172e872a9b13b03a67e4f7975ffb875da70a3eada8a4f6/uv-0.11.18-py3-none-manylinux_2_17_aarch64.manylinux2014_aarch64.musllinux_1_1_aarch64.whl", hash = "sha256:5df2b2a59f1ee4392066df24ef9904b3bbdc226e8ab9bdff8e290127fcddc3a3", size = 23456077, upload-time = "2026-06-01T19:43:00.595Z" }, + { url = "https://files.pythonhosted.org/packages/5b/26/7490fc8a7b56847ce4aabcb40b0a938f4daf79fd7bd3a8f3a6d6babe00cc/uv-0.11.18-py3-none-manylinux_2_17_armv7l.manylinux2014_armv7l.musllinux_1_1_armv7l.whl", hash = "sha256:351db8ef413055b7e8e894ff54f30ce3fd001f4f3bbae66c2f519f30008fa5b5", size = 23147860, upload-time = "2026-06-01T19:42:57.748Z" }, + { url = "https://files.pythonhosted.org/packages/8e/56/deddff68533144d7c40a2793914fa2994d0273ef105144cbcd3c08f80eb3/uv-0.11.18-py3-none-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:0adb4553e7962132f4d3a3bcbdc49332008486fc7f3e147636c18fdc49dac17e", size = 23168337, upload-time = "2026-06-01T19:42:38.344Z" }, + { url = "https://files.pythonhosted.org/packages/69/b0/71473c35536b92bc94a0ec317a7bddf800aeb90ad341044f61e17a45c6f4/uv-0.11.18-py3-none-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:f9c2ab380ffafa1d754c92799ce818ce9bb20a5b053538e391fbdf0d10954a6a", size = 24543532, upload-time = "2026-06-01T19:43:03.348Z" }, + { url = "https://files.pythonhosted.org/packages/c4/74/00a422128320ed174c15b0ff7084533280850853592cf802c2f3e3bd25c2/uv-0.11.18-py3-none-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:73c59f8bca927b922b897a2d6123e45146dc97bfc73aab40ae10359ee37897c4", size = 25497616, upload-time = "2026-06-01T19:42:32.991Z" }, + { url = "https://files.pythonhosted.org/packages/57/b9/a74e7a5029f7eebd7f8c45f6e87fbb2855705dd595980328e2403317a588/uv-0.11.18-py3-none-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:6f2d8240e4b8eaa9507d423ede6cca8396057a24d398b89fbcc3117c79e8a42d", size = 24731187, upload-time = "2026-06-01T19:42:35.647Z" }, + { url = "https://files.pythonhosted.org/packages/ca/c4/8112b3c95db60a39c98db0641dd49bd4228f2fedb9d0ef5e4f3f48b52a0d/uv-0.11.18-py3-none-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9a4ee93dd0cc86046eb234ff8f3a5090c6cbb1466a825c5201aaf4ee4b45158d", size = 24868841, upload-time = "2026-06-01T19:42:46.905Z" }, + { url = "https://files.pythonhosted.org/packages/39/5b/1adc8261a132604bac83438f77080b1cea4b87a633890c6b68dac0d68400/uv-0.11.18-py3-none-manylinux_2_28_aarch64.whl", hash = "sha256:57f4e2dea12cac7749628bc87eb66d3aaa5c3ed27420598899d6d82aee4d1b0d", size = 23536198, upload-time = "2026-06-01T19:42:41.196Z" }, + { url = "https://files.pythonhosted.org/packages/f7/16/44b5f182c5fc442ec8866c84cceec7675b7430417dcf119c1d5b656bce89/uv-0.11.18-py3-none-manylinux_2_31_riscv64.musllinux_1_1_riscv64.whl", hash = "sha256:8b7708376d0c79f84403d293c0774e0de76a7546e1cc80e911d0fed83cee65bf", size = 24290310, upload-time = "2026-06-01T19:43:15.415Z" }, + { url = "https://files.pythonhosted.org/packages/1a/43/021c0033918330f681eb3dafcf44db4b746978b0adb9c6851b2177597b56/uv-0.11.18-py3-none-manylinux_2_31_riscv64.whl", hash = "sha256:df158cc578aa4acff250505eab422a19794c9ff732dcab9faa068caa9dbe661d", size = 24380029, upload-time = "2026-06-01T19:43:12.746Z" }, + { url = "https://files.pythonhosted.org/packages/6c/38/2474a09f24ef57cfe99549a8d2d1a887549bab522a193ad50706a4fff021/uv-0.11.18-py3-none-musllinux_1_1_i686.whl", hash = "sha256:24f251b09f6dd7c367520b6b62e22038b92c18ce56ecee8b426a6cd78151466f", size = 23854064, upload-time = "2026-06-01T19:42:54.948Z" }, + { url = "https://files.pythonhosted.org/packages/75/6a/faa140a30e993453400f94ad5675c9d0673a71191ff523d3117c312779a6/uv-0.11.18-py3-none-musllinux_1_1_x86_64.whl", hash = "sha256:6e461d64cdc957da38fec9946758020ee3a66b4508f76cb940a866f704b20355", size = 25055765, upload-time = "2026-06-01T19:43:09.914Z" }, + { url = "https://files.pythonhosted.org/packages/e4/8a/077ded1832e92161fc880a420fbd513495369716531055215399d3056eba/uv-0.11.18-py3-none-win32.whl", hash = "sha256:97d60103eec0c4295c2728f0483def79d62a3f265baa11fdf94912c8f10be019", size = 22415962, upload-time = "2026-06-01T19:43:05.896Z" }, + { url = "https://files.pythonhosted.org/packages/58/bc/68cf345e104a958f0b8971ae60a7d27c69b5892dcddc2e1d9e4a62c69931/uv-0.11.18-py3-none-win_amd64.whl", hash = "sha256:cbbedeb5fefd8c346fe2e1d71af229c04ece00a23ce277e9664db088d35c4f67", size = 25135111, upload-time = "2026-06-01T19:42:30.303Z" }, + { url = "https://files.pythonhosted.org/packages/a7/89/08bf00b50a62639129dd5c65b80a3182e9fddb67899739f9cfdc0ae1f138/uv-0.11.18-py3-none-win_arm64.whl", hash = "sha256:c9f61f96aa88ec1a890a50ca45fc05b51f4f066699e001245199895844708e38", size = 23542490, upload-time = "2026-06-01T19:42:44Z" }, +] + +[[package]] +name = "wheel" +version = "0.48.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "packaging" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/d0/20/50ed6bdf27dec98b568a8ae25dc599f35baa3d9709f9e83fd1edb56b9a90/wheel-0.48.0.tar.gz", hash = "sha256:94800765601e9171bf5d58d066e640662842bcedcbab982b2c90787a2c987322", size = 66471, upload-time = "2026-08-11T22:02:27.327Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/2e/29/69cfbb602cd91690c55d38ba9fe53e6a7e76a6fa647bf38f19c138d25449/wheel-0.48.0-py3-none-any.whl", hash = "sha256:3217dcc807155e45db462d7ef2431f5ddda0d7273b700d05a67b271ceb1287ab", size = 33320, upload-time = "2026-08-11T22:02:26.1Z" }, +] + [[package]] name = "zipp" version = "4.1.0"