Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -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()
1 change: 1 addition & 0 deletions .github/scripts/test_release_candidate_prep.py
Original file line number Diff line number Diff line change
Expand Up @@ -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",
Expand Down
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand Down
1 change: 1 addition & 0 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
4 changes: 4 additions & 0 deletions docs/validation/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
Original file line number Diff line number Diff line change
@@ -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 <candidate-dir>
shasum -a 256 <candidate-dir>/ethos_pdf-0.1.1-py3-none-any.whl
env SOURCE_DATE_EPOCH=0 python3 -m build --wheel --outdir <candidate-dir>
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
```
Loading