From fafd6d0594775252075dcac35c58822c4b45dc04 Mon Sep 17 00:00:00 2001 From: docushell-admin Date: Wed, 24 Jun 2026 10:49:45 +0530 Subject: [PATCH] Record patch 0.1.1 Python wheel reproducibility blocker Signed-off-by: docushell-admin --- ..._1_python_wheel_reproducibility_blocker.py | 122 ++++++++++++++++++ .../scripts/test_release_candidate_prep.py | 1 + CHANGELOG.md | 1 + Makefile | 1 + docs/validation/README.md | 4 + ...ucibility-blocker-validation-2026-06-24.md | 110 ++++++++++++++++ 6 files changed, 239 insertions(+) create mode 100644 .github/scripts/test_patch_0_1_1_python_wheel_reproducibility_blocker.py create mode 100644 docs/validation/patch-0-1-1-python-wheel-reproducibility-blocker-validation-2026-06-24.md diff --git a/.github/scripts/test_patch_0_1_1_python_wheel_reproducibility_blocker.py b/.github/scripts/test_patch_0_1_1_python_wheel_reproducibility_blocker.py new file mode 100644 index 0000000..b34b4cd --- /dev/null +++ b/.github/scripts/test_patch_0_1_1_python_wheel_reproducibility_blocker.py @@ -0,0 +1,122 @@ +#!/usr/bin/env python3 +# +# Copyright 2026 The Ethos maintainers +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# + +from __future__ import annotations + +import re +import subprocess +import unittest +from pathlib import Path + + +ROOT = Path(__file__).resolve().parents[2] +RECORD = ROOT / "docs/validation/patch-0-1-1-python-wheel-reproducibility-blocker-validation-2026-06-24.md" +VALIDATION_README = ROOT / "docs/validation/README.md" +MAKEFILE = ROOT / "Makefile" + +SOURCE_SHORT = "5be31dd" +SOURCE_COMMIT = "5be31dd292a0551caea457fd23db98045e110c00" +SOURCE_TREE = "590dc80d27beaa9950a21f7f188650d2f24dd036" +APPROVED_SHA256 = "faa6c4751341b603b986ad3cf65d3c0c2f574e5df1d7232f76c3afd0221dac14" +FRESH_SHA256 = "52cc738637a84aa084b776db8be866e7af7438d580f3d564801a2ce94492a950" +TIMESTAMP_PIN_SHA256 = "ab84782cd7b7e7db2628f36e5d34e636afcddb9798196305203380a49b36b964" +DETERMINISTIC_SHA256 = "e0292276e711e75d4f7e1bb8c2c6137c6e89d4c343dd308943eb9b22094ea451" +FORBIDDEN = ( + "pypi upload may proceed", + "pypi upload approved", + "wheel is published", + "python package is published", + "production-ready", + "hosted surfaces approved", + "bundled pdfium approved", + "ethos-doc approved", + "ethos-rag approved", +) + + +def read(path: Path) -> str: + return path.read_text(encoding="utf-8") + + +def normalized(path: Path) -> str: + return re.sub(r"\s+", " ", read(path)) + + +def git(*args: str) -> str: + return subprocess.check_output( + ["git", *args], + cwd=ROOT, + encoding="utf-8", + stderr=subprocess.DEVNULL, + ).strip() + + +class Patch011PythonWheelReproducibilityBlockerTests(unittest.TestCase): + def test_blocker_record_is_source_bound_and_indexed(self) -> None: + record = normalized(RECORD) + readme = normalized(VALIDATION_README) + + self.assertIn(RECORD.name, readme) + self.assertIn("patch 0.1.1 Python wheel reproducibility blocker", readme) + self.assertIn(f"Validated source HEAD before this record: `{SOURCE_SHORT}`", read(RECORD)) + self.assertIn(f"Patch 0.1.1 Python wheel reproducibility blocker source commit: `{SOURCE_COMMIT}`", record) + self.assertIn(f"Patch 0.1.1 Python wheel reproducibility blocker source tree: `{SOURCE_TREE}`", record) + self.assertEqual(SOURCE_COMMIT, git("rev-parse", SOURCE_SHORT)) + self.assertEqual(SOURCE_TREE, git("rev-parse", f"{SOURCE_SHORT}^{{tree}}")) + + def test_blocker_records_hash_mismatch_and_root_cause(self) -> None: + record = normalized(RECORD) + + for expected in ( + "ethos_pdf-0.1.1-py3-none-any.whl", + APPROVED_SHA256, + FRESH_SHA256, + TIMESTAMP_PIN_SHA256, + DETERMINISTIC_SHA256, + "wheel member byte content was identical", + "generated `dist-info` ZIP member timestamps differed", + "Setting `SOURCE_DATE_EPOCH=0` produced the same wheel SHA256 twice", + ): + self.assertIn(expected, record) + + def test_blocker_keeps_upload_blocked_until_new_deterministic_decision(self) -> None: + raw = read(RECORD) + lower = normalized(RECORD).lower() + record = normalized(RECORD) + + for expected in ( + "PyPI upload remains blocked.", + "The prior merged approval decision remains useful as historical evidence but is not sufficient for upload because the required pre-upload rebuild produced a different wheel SHA256.", + "A new deterministic wheel approval request and approval decision are required before any PyPI upload.", + "The next candidate should be built with `SOURCE_DATE_EPOCH=0`", + "This record does not approve PyPI upload.", + "This record does not approve the deterministic wheel hash.", + ): + self.assertIn(expected, record) + for forbidden in FORBIDDEN: + self.assertNotIn(forbidden, lower) + self.assertNotIn("/Users/", raw) + self.assertNotIn("/tmp", raw) + self.assertNotIn("/private/tmp", raw) + self.assertNotIn("/private/var", raw) + self.assertNotIn("/var/folders", raw) + self.assertNotIn("saumildiwaker", raw) + + def test_release_candidate_prep_runs_blocker_after_decision_guard(self) -> None: + makefile = read(MAKEFILE) + decision_guard = "$(PYTHON) .github/scripts/test_patch_0_1_1_python_publication_approval_decision.py" + blocker_guard = "$(PYTHON) .github/scripts/test_patch_0_1_1_python_wheel_reproducibility_blocker.py" + npm_guard = "$(PYTHON) .github/scripts/test_npm_binary_package_scaffold.py" + + self.assertIn(blocker_guard, makefile) + self.assertEqual(1, makefile.count(blocker_guard)) + self.assertLess(makefile.index(decision_guard), makefile.index(blocker_guard)) + self.assertLess(makefile.index(blocker_guard), makefile.index(npm_guard)) + + +if __name__ == "__main__": + unittest.main() diff --git a/.github/scripts/test_release_candidate_prep.py b/.github/scripts/test_release_candidate_prep.py index 619ec17..47c8a91 100644 --- a/.github/scripts/test_release_candidate_prep.py +++ b/.github/scripts/test_release_candidate_prep.py @@ -32,6 +32,7 @@ "$(MAKE) python-surface-test PYTHON=$(PYTHON)", "$(PYTHON) .github/scripts/test_patch_0_1_1_python_publication_approval_request.py", "$(PYTHON) .github/scripts/test_patch_0_1_1_python_publication_approval_decision.py", + "$(PYTHON) .github/scripts/test_patch_0_1_1_python_wheel_reproducibility_blocker.py", "$(PYTHON) .github/scripts/test_npm_binary_package_scaffold.py", "npm test --prefix packages/npm/ethos-pdf", "$(PYTHON) .github/scripts/test_npm_vendor_binary_payload_strategy.py", diff --git a/CHANGELOG.md b/CHANGELOG.md index f4f93b3..4feec82 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,7 @@ ## Unreleased +- boundary-exception: record patch `0.1.1` Python wheel reproducibility blocker after pre-upload hash mismatch; no PyPI upload or support-boundary change. - boundary-exception: approve exact patch `0.1.1` Python PyPI wheel publication decision for later operator upload; no PyPI upload or support-boundary change. - boundary-exception: request exact patch `0.1.1` Python PyPI wheel publication approval for decider review; no PyPI upload or support-boundary change. - boundary-exception: close patch `0.1.1` Rust crates.io publication with exact registry evidence; no public installation wording or support-boundary change. diff --git a/Makefile b/Makefile index 11394c3..2b24f1b 100644 --- a/Makefile +++ b/Makefile @@ -274,6 +274,7 @@ release-candidate-prep: $(MAKE) python-surface-test PYTHON=$(PYTHON) $(PYTHON) .github/scripts/test_patch_0_1_1_python_publication_approval_request.py $(PYTHON) .github/scripts/test_patch_0_1_1_python_publication_approval_decision.py + $(PYTHON) .github/scripts/test_patch_0_1_1_python_wheel_reproducibility_blocker.py $(PYTHON) .github/scripts/test_npm_binary_package_scaffold.py npm test --prefix packages/npm/ethos-pdf $(PYTHON) .github/scripts/test_npm_vendor_binary_payload_strategy.py diff --git a/docs/validation/README.md b/docs/validation/README.md index f1c3124..1dcdffd 100644 --- a/docs/validation/README.md +++ b/docs/validation/README.md @@ -623,6 +623,10 @@ recording the exact current-main source candidate and required follow-up evidenc Python PyPI publication approval decision validation accepts the exact `ethos-pdf==0.1.1` wheel candidate, source binding, wheel metadata, SHA256, and retained blockers; operator upload remains pending. +- `patch-0-1-1-python-wheel-reproducibility-blocker-validation-2026-06-24.md` - patch 0.1.1 + Python wheel reproducibility blocker validation records that a fresh standard pre-upload rebuild + did not match the approved wheel SHA256 because generated ZIP timestamps drifted; PyPI upload + remains blocked pending a deterministic wheel approval request and decision. - `milestone-e-validation-command-index-validation-2026-06-20.md` - internal Milestone E validation-command index validation passed through command-alignment checks, schema enum checks, row-record checks, public-surface posture checks, `make milestone-e-prep`, and diff hygiene; the diff --git a/docs/validation/patch-0-1-1-python-wheel-reproducibility-blocker-validation-2026-06-24.md b/docs/validation/patch-0-1-1-python-wheel-reproducibility-blocker-validation-2026-06-24.md new file mode 100644 index 0000000..bacb486 --- /dev/null +++ b/docs/validation/patch-0-1-1-python-wheel-reproducibility-blocker-validation-2026-06-24.md @@ -0,0 +1,110 @@ +# Patch 0.1.1 Python Wheel Reproducibility Blocker Validation - 2026-06-24 + +Validated source HEAD before this record: `5be31dd`. + +Patch 0.1.1 Python wheel reproducibility blocker source commit: +`5be31dd292a0551caea457fd23db98045e110c00`. + +Patch 0.1.1 Python wheel reproducibility blocker source tree: +`590dc80d27beaa9950a21f7f188650d2f24dd036`. + +Status: **patch 0.1.1 Python PyPI upload remains blocked by wheel reproducibility evidence** + +This record captures a pre-upload blocker discovered after the patch `0.1.1` Python PyPI +publication approval decision merged. The exact approved wheel filename was rebuilt before upload, +but the fresh wheel SHA256 did not match the approved candidate SHA256. PyPI upload remains blocked. + +## Subject + +- Repository: `docushell/ethos` +- Lane: Python PyPI wheel publication +- Approved decision record: + `docs/validation/patch-0-1-1-python-publication-approval-decision-validation-2026-06-24.md` +- Approved request record: + `docs/validation/patch-0-1-1-python-publication-approval-request-validation-2026-06-24.md` +- Candidate wheel: `ethos_pdf-0.1.1-py3-none-any.whl` + +## Observed Hashes + +- Approved wheel SHA256: + `faa6c4751341b603b986ad3cf65d3c0c2f574e5df1d7232f76c3afd0221dac14` +- Fresh standard pre-upload rebuild SHA256: + `52cc738637a84aa084b776db8be866e7af7438d580f3d564801a2ce94492a950` +- Rebuild with the original generated timestamp pinned SHA256: + `ab84782cd7b7e7db2628f36e5d34e636afcddb9798196305203380a49b36b964` +- Deterministic rebuild SHA256 using `SOURCE_DATE_EPOCH=0`, first run: + `e0292276e711e75d4f7e1bb8c2c6137c6e89d4c343dd308943eb9b22094ea451` +- Deterministic rebuild SHA256 using `SOURCE_DATE_EPOCH=0`, second run: + `e0292276e711e75d4f7e1bb8c2c6137c6e89d4c343dd308943eb9b22094ea451` + +## Root Cause Classification + +- The approved wheel and the fresh standard pre-upload rebuild had the same filename and file size. +- The wheel member byte content was identical for every member. +- The generated `dist-info` ZIP member timestamps differed. +- The timestamp difference alone changed the whole-wheel SHA256. +- Setting `SOURCE_DATE_EPOCH=0` produced the same wheel SHA256 twice. + +This is a packaging reproducibility blocker, not a Python API or wrapper behavior change. + +## Required Follow-Up + +- A new deterministic wheel approval request and approval decision are required before any PyPI + upload. +- The next candidate should be built with `SOURCE_DATE_EPOCH=0`. +- The next approval request should bind the deterministic wheel SHA256: + `e0292276e711e75d4f7e1bb8c2c6137c6e89d4c343dd308943eb9b22094ea451`. +- The prior merged approval decision remains useful as historical evidence but is not sufficient for + upload because the required pre-upload rebuild produced a different wheel SHA256. + +## Non-Actions + +- This record does not approve PyPI upload. +- This record does not upload any Python distribution. +- This record does not approve the deterministic wheel hash. +- This record does not approve an sdist. +- This record does not approve another wheel. +- This record does not approve public installation wording. +- This record does not approve hosted surfaces. +- This record does not approve production positioning. +- This record does not approve Windows packaged artifacts. +- This record does not approve bundled project-maintained PDFium builds. +- This record does not approve public benchmark reports. +- This record does not approve public benchmark claims. +- This record does not approve `ethos-doc`. +- This record does not approve `ethos-rag`. + +## Retained Blockers + +- Actual PyPI upload remains blocked. +- Public installation wording remains blocked until PyPI availability is closed out. +- Hosted surfaces remain blocked. +- Production positioning remains blocked. +- Public benchmark reports remain blocked. +- Public benchmark claims remain blocked. +- Windows packaged artifacts remain blocked. +- Bundled project-maintained PDFium builds remain blocked. +- `ethos-doc` remains blocked. +- `ethos-rag` remains blocked. +- PDFium remains caller-provided through `ETHOS_PDFIUM_LIBRARY_PATH`. + +## Commands + +```sh +python3 -m build --wheel --outdir +shasum -a 256 /ethos_pdf-0.1.1-py3-none-any.whl +env SOURCE_DATE_EPOCH=0 python3 -m build --wheel --outdir +python3 .github/scripts/test_patch_0_1_1_python_wheel_reproducibility_blocker.py +python3 .github/scripts/test_patch_0_1_1_python_publication_approval_decision.py +python3 .github/scripts/test_patch_0_1_1_python_publication_approval_request.py +make release-candidate-prep PYTHON=python3 +git diff --check +``` + +## Result + +```text +patch 0.1.1 Python wheel reproducibility blocker recorded +Pre-upload PyPI upload is stopped because the fresh standard wheel rebuild did not match the approved SHA256 +Deterministic SOURCE_DATE_EPOCH=0 rebuild evidence is available for a new approval request +```