diff --git a/.github/scripts/test_patch_0_1_1_crates_publication_closeout.py b/.github/scripts/test_patch_0_1_1_crates_publication_closeout.py index 650c235..679a66f 100644 --- a/.github/scripts/test_patch_0_1_1_crates_publication_closeout.py +++ b/.github/scripts/test_patch_0_1_1_crates_publication_closeout.py @@ -7,9 +7,11 @@ from __future__ import annotations +import json import re import subprocess import unittest +import urllib.request from pathlib import Path @@ -49,13 +51,14 @@ def git(*args: str) -> str: ).strip() -def cargo_search(crate: str) -> str: - return subprocess.check_output( - ["cargo", "search", crate, "--limit", "1"], - cwd=ROOT, - encoding="utf-8", - stderr=subprocess.DEVNULL, - ).strip() +def crates_io_version(crate: str, version: str) -> str: + request = urllib.request.Request( + f"https://crates.io/api/v1/crates/{crate}/{version}", + headers={"User-Agent": "ethos-release-validation"}, + ) + with urllib.request.urlopen(request, timeout=20) as response: + payload = json.load(response) + return payload["version"]["num"] class Patch011CratesPublicationCloseoutTests(unittest.TestCase): @@ -84,7 +87,7 @@ def test_closeout_records_all_published_crates_and_commands(self) -> None: def test_live_crates_io_reports_patch_versions(self) -> None: for crate in CRATES: - self.assertIn(f'{crate} = "0.1.1"', cargo_search(crate)) + self.assertEqual("0.1.1", crates_io_version(crate, "0.1.1")) def test_closeout_keeps_other_surfaces_blocked(self) -> None: raw = read(RECORD) diff --git a/.github/scripts/test_patch_0_1_2_crates_publication_closeout.py b/.github/scripts/test_patch_0_1_2_crates_publication_closeout.py new file mode 100644 index 0000000..3aaeda4 --- /dev/null +++ b/.github/scripts/test_patch_0_1_2_crates_publication_closeout.py @@ -0,0 +1,145 @@ +#!/usr/bin/env python3 +# +# Copyright 2026 The Ethos maintainers +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# + +from __future__ import annotations + +import json +import re +import subprocess +import unittest +import urllib.request +from pathlib import Path + +from makefile_guard import target_block + + +ROOT = Path(__file__).resolve().parents[2] +RECORD = ROOT / "docs/validation/patch-0-1-2-crates-publication-closeout-validation-2026-06-25.md" +VALIDATION_README = ROOT / "docs/validation/README.md" +EXECUTION_STATUS = ROOT / "docs/execution-status.md" +PUBLIC_RELEASE_CHECKLIST = ROOT / "docs/public-release-checklist.md" +MAKEFILE = ROOT / "Makefile" + +SOURCE_SHORT = "35d0cca" +SOURCE_COMMIT = "35d0cca87669217f079793ce0553c9ac1121884b" +SOURCE_TREE = "3fcad87f2fc67c59c2f102f4f2c73d9e5c382724" +VERSION = "0.1.2" +CRATES = ("ethos-doc-core", "ethos-verify", "ethos-pdf") +FORBIDDEN = ( + "hosted surfaces approved", + "production-ready", + "windows packaged artifacts 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() + + +def crates_io_version(crate: str, version: str) -> str: + request = urllib.request.Request( + f"https://crates.io/api/v1/crates/{crate}/{version}", + headers={"User-Agent": "ethos-release-validation"}, + ) + with urllib.request.urlopen(request, timeout=20) as response: + payload = json.load(response) + return payload["version"]["num"] + + +class Patch012CratesPublicationCloseoutTests(unittest.TestCase): + def test_closeout_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.2 crates.io publication closeout", readme.lower()) + self.assertIn(f"Validated source HEAD before this record: `{SOURCE_SHORT}`", read(RECORD)) + self.assertIn(f"Patch 0.1.2 crates publication closeout source commit: `{SOURCE_COMMIT}`", record) + self.assertIn(f"Patch 0.1.2 crates publication closeout 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_closeout_records_all_published_crates_and_commands(self) -> None: + record = normalized(RECORD) + + for crate in CRATES: + self.assertIn(f"{crate} = {VERSION}", record) + self.assertIn(f"cargo publish --locked -p {crate}", record) + self.assertIn(f"Published {crate} v{VERSION} at registry `crates-io`", record) + self.assertIn(f'{crate} = "{VERSION}"', record) + self.assertIn("`ethos-doc-core` was published before dependent crates", record) + self.assertIn("`ethos-verify` was published after ethos-doc-core was visible", record) + self.assertIn("`ethos-pdf` was published after ethos-verify was visible", record) + + def test_live_crates_io_reports_patch_versions(self) -> None: + for crate in CRATES: + self.assertEqual(VERSION, crates_io_version(crate, VERSION)) + + def test_status_docs_reference_closeout_and_keep_remaining_boundaries(self) -> None: + for path in (EXECUTION_STATUS, PUBLIC_RELEASE_CHECKLIST): + text = normalized(path) + self.assertIn(RECORD.name, text, str(path)) + self.assertIn("Rust crate public installation wording remains blocked", text, str(path)) + self.assertIn("Python installation remains at `ethos-pdf==0.1.1`", text, str(path)) + self.assertIn("hosted", text.lower(), str(path)) + self.assertIn("production", text.lower(), str(path)) + + def test_closeout_keeps_other_surfaces_blocked(self) -> None: + raw = read(RECORD) + record = normalized(RECORD) + lower = record.lower() + + for expected in ( + "Rust crate public installation wording remains blocked until a separate wording and availability record.", + "Python installation remains at `ethos-pdf==0.1.1` until separate PyPI `0.1.2` publication records pass.", + "Hosted surfaces remain blocked.", + "Production positioning remains 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`.", + ): + self.assertIn(expected, record) + for forbidden in FORBIDDEN: + self.assertNotIn(forbidden, lower) + self.assertNotIn("/Users/", raw) + self.assertNotIn("/private/tmp", raw) + self.assertNotIn("/var/folders", raw) + self.assertNotIn("saumildiwaker", raw) + + def test_release_candidate_prep_runs_closeout_after_decision_guard(self) -> None: + makefile = read(MAKEFILE) + decision_guard = "$(PYTHON) .github/scripts/test_patch_0_1_2_crates_publication_approval_decision.py" + closeout_guard = "$(PYTHON) .github/scripts/test_patch_0_1_2_crates_publication_closeout.py" + first_public_guard = "$(PYTHON) .github/scripts/test_first_public_release_artifact_evidence.py" + block = target_block("release-candidate-prep") + + self.assertIn(closeout_guard, block) + self.assertEqual(1, makefile.count(closeout_guard)) + self.assertLess(block.index(decision_guard), block.index(closeout_guard)) + self.assertLess(block.index(closeout_guard), block.index(first_public_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 d12641b..06e5d7f 100644 --- a/.github/scripts/test_release_candidate_prep.py +++ b/.github/scripts/test_release_candidate_prep.py @@ -71,6 +71,7 @@ "$(PYTHON) .github/scripts/test_patch_0_1_2_public_install_wording_closeout.py", "$(PYTHON) .github/scripts/test_patch_0_1_2_crates_publication_approval_request.py", "$(PYTHON) .github/scripts/test_patch_0_1_2_crates_publication_approval_decision.py", + "$(PYTHON) .github/scripts/test_patch_0_1_2_crates_publication_closeout.py", "$(PYTHON) .github/scripts/test_first_public_release_artifact_evidence.py", "$(PYTHON) .github/scripts/test_first_public_release_final_decider.py", "$(PYTHON) .github/scripts/test_first_public_release_linux_x64_artifact_evidence.py", diff --git a/CHANGELOG.md b/CHANGELOG.md index 7c67636..5bffdb5 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,7 @@ ## Unreleased +- boundary-exception: close patch `0.1.2` crates.io publication for Rust crates `ethos-doc-core`, `ethos-verify`, and `ethos-pdf` while keeping Rust public install wording, PyPI publication, hosted, production, Windows, bundled PDFium, benchmark, `ethos-doc`, and `ethos-rag` surfaces blocked. - boundary-exception: record decider approval for bounded later crates.io publication of patch `0.1.2` Rust crates `ethos-doc-core`, `ethos-verify`, and `ethos-pdf` while keeping actual operator publication, package tag creation, Rust public install wording, PyPI publication, hosted, production, Windows, bundled PDFium, benchmark, `ethos-doc`, and `ethos-rag` surfaces blocked. - boundary-exception: request decider review for exact patch `0.1.2` Rust crates.io publication of `ethos-doc-core`, `ethos-verify`, and `ethos-pdf` while keeping `cargo publish`, package tag creation, Rust public install wording, PyPI publication, hosted, production, Windows, bundled PDFium, benchmark, `ethos-doc`, and `ethos-rag` surfaces blocked. - boundary-exception: close patch `0.1.2` public install wording for the published npm package and GitHub Release CLI artifacts while keeping Rust crates and Python wheel install wording on `0.1.1`, and retaining hosted, production, Windows, bundled PDFium, benchmark, `ethos-doc`, and `ethos-rag` blockers. diff --git a/Makefile b/Makefile index 9251a5c..e2da0d0 100644 --- a/Makefile +++ b/Makefile @@ -322,6 +322,7 @@ release-candidate-prep: $(PYTHON) .github/scripts/test_patch_0_1_2_public_install_wording_closeout.py $(PYTHON) .github/scripts/test_patch_0_1_2_crates_publication_approval_request.py $(PYTHON) .github/scripts/test_patch_0_1_2_crates_publication_approval_decision.py + $(PYTHON) .github/scripts/test_patch_0_1_2_crates_publication_closeout.py $(PYTHON) .github/scripts/test_first_public_release_artifact_evidence.py $(PYTHON) .github/scripts/test_first_public_release_final_decider.py $(PYTHON) .github/scripts/test_first_public_release_linux_x64_artifact_evidence.py diff --git a/docs/execution-status.md b/docs/execution-status.md index 0f570a0..54e87e7 100644 --- a/docs/execution-status.md +++ b/docs/execution-status.md @@ -59,6 +59,13 @@ wording remains blocked until registry availability closeout, and Python install `ethos-pdf==0.1.1` until separate PyPI `0.1.2` approval, operator publication, and closeout records pass. +Patch `0.1.2` crates.io publication closeout is recorded in +`docs/validation/patch-0-1-2-crates-publication-closeout-validation-2026-06-25.md`. crates.io now +reports `ethos-doc-core`, `ethos-verify`, and `ethos-pdf` at `0.1.2`. Rust crate public +installation wording remains blocked until a separate wording and availability closeout, and Python +installation remains at `ethos-pdf==0.1.1` until separate PyPI `0.1.2` approval, operator +publication, and closeout records pass. + Public approval lane blocker prep is recorded in `docs/milestone-e-public-approval-lane-blockers.json` and schema-bound by `schemas/ethos-milestone-e-public-approval-lane-blockers.schema.json`. This public approval lane diff --git a/docs/public-release-checklist.md b/docs/public-release-checklist.md index 8212b66..5c72718 100644 --- a/docs/public-release-checklist.md +++ b/docs/public-release-checklist.md @@ -134,6 +134,15 @@ surfaces remain blocked, production positioning remains blocked, Windows package blocked, bundled project-maintained PDFium builds remain blocked, `ethos-doc` remains blocked, `ethos-rag` remains blocked, and public benchmark claims remain blocked. +Patch `0.1.2` crates.io publication closeout is recorded in +`docs/validation/patch-0-1-2-crates-publication-closeout-validation-2026-06-25.md`. crates.io +reports `ethos-doc-core`, `ethos-verify`, and `ethos-pdf` at `0.1.2`. Rust crate public +installation wording remains blocked until a separate wording and availability closeout, PyPI +publication remains blocked, hosted surfaces remain blocked, production positioning remains +blocked, Windows packaged artifacts remain blocked, bundled project-maintained PDFium builds remain +blocked, `ethos-doc` remains blocked, `ethos-rag` remains blocked, and public benchmark claims +remain blocked. + ## Required Before Public Push - Package-name and trademark decision is closed by accepted ADR-0006 in diff --git a/docs/validation/README.md b/docs/validation/README.md index 18a52d6..1cf910d 100644 --- a/docs/validation/README.md +++ b/docs/validation/README.md @@ -728,6 +728,11 @@ recording the exact current-main source candidate and required follow-up evidenc creation, Rust crate public installation wording, PyPI publication, hosted, production, Windows, bundled PDFium, benchmark, `ethos-doc`, and `ethos-rag` surfaces remain blocked until separate closeout or approval records pass. +- `patch-0-1-2-crates-publication-closeout-validation-2026-06-25.md` - patch 0.1.2 crates.io + publication closeout validation records operator evidence and live crates.io visibility for + `ethos-doc-core`, `ethos-verify`, and `ethos-pdf` `0.1.2`; Rust crate public installation + wording, PyPI publication, hosted, production, Windows, bundled PDFium, benchmark, `ethos-doc`, + and `ethos-rag` surfaces remain blocked until separate closeout or approval records pass. - `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-2-crates-publication-closeout-validation-2026-06-25.md b/docs/validation/patch-0-1-2-crates-publication-closeout-validation-2026-06-25.md new file mode 100644 index 0000000..f5b93c7 --- /dev/null +++ b/docs/validation/patch-0-1-2-crates-publication-closeout-validation-2026-06-25.md @@ -0,0 +1,123 @@ +# Patch 0.1.2 crates.io Publication Closeout Validation - 2026-06-25 + +Validated source HEAD before this record: `35d0cca`. + +Patch 0.1.2 crates publication closeout source commit: `35d0cca87669217f079793ce0553c9ac1121884b`. + +Patch 0.1.2 crates publication closeout source tree: `3fcad87f2fc67c59c2f102f4f2c73d9e5c382724`. + +Status: **patch 0.1.2 Rust crates published to crates.io** + +This record closes the bounded patch `0.1.2` crates.io publication lane for `ethos-doc-core`, +`ethos-verify`, and `ethos-pdf`. It records operator publish evidence and live crates.io +verification. It does not approve Rust crate public installation wording, PyPI publication, hosted +surfaces, production positioning, Windows packaged artifacts, bundled project-maintained PDFium +builds, `ethos-doc`, `ethos-rag`, public benchmark reports, public benchmark claims, or broader +public wording. + +## Published Crates + +- `ethos-doc-core = 0.1.2` +- `ethos-verify = 0.1.2` +- `ethos-pdf = 0.1.2` + +## Operator Publish Evidence + +`ethos-doc-core` command: + +```text +cargo publish --locked -p ethos-doc-core +``` + +Observed result: + +```text +Uploaded ethos-doc-core v0.1.2 to registry `crates-io` +Published ethos-doc-core v0.1.2 at registry `crates-io` +``` + +Registry visibility check: + +```text +ethos-doc-core = "0.1.2" +``` + +`ethos-verify` command: + +```text +cargo publish --locked -p ethos-verify +``` + +Observed result: + +```text +Uploaded ethos-verify v0.1.2 to registry `crates-io` +Published ethos-verify v0.1.2 at registry `crates-io` +``` + +Registry visibility check: + +```text +ethos-verify = "0.1.2" +``` + +`ethos-pdf` command: + +```text +cargo publish --locked -p ethos-pdf +``` + +Observed result: + +```text +Uploaded ethos-pdf v0.1.2 to registry `crates-io` +Published ethos-pdf v0.1.2 at registry `crates-io` +``` + +Registry visibility check: + +```text +ethos-pdf = "0.1.2" +``` + +## Dependency-Order Evidence + +- `ethos-doc-core` was published before dependent crates. +- `ethos-verify` was published after ethos-doc-core was visible on crates.io. +- `ethos-pdf` was published after ethos-verify was visible on crates.io. + +## Retained Blockers + +- Rust crate public installation wording remains blocked until a separate wording and availability record. +- Python installation remains at `ethos-pdf==0.1.1` until separate PyPI `0.1.2` publication + records pass. +- 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 .github/scripts/test_patch_0_1_2_crates_publication_closeout.py +python3 .github/scripts/test_patch_0_1_2_crates_publication_approval_decision.py +python3 .github/scripts/test_patch_0_1_2_crates_publication_approval_request.py +cargo search ethos-doc-core --limit 1 +cargo search ethos-verify --limit 1 +cargo search ethos-pdf --limit 1 +make release-candidate-prep PYTHON=python3 +git diff --check +``` + +## Result + +```text +patch 0.1.2 Rust crates.io publication closeout recorded +ethos-doc-core, ethos-verify, and ethos-pdf 0.1.2 are live on crates.io +Rust crate public installation wording, PyPI, and unrelated public/support surfaces remain blocked +```