diff --git a/.github/scripts/cargo_manifest_guard.py b/.github/scripts/cargo_manifest_guard.py new file mode 100644 index 0000000..bf74b2b --- /dev/null +++ b/.github/scripts/cargo_manifest_guard.py @@ -0,0 +1,47 @@ +# +# Copyright 2026 The Ethos maintainers +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# + +from __future__ import annotations + +import re +from typing import Protocol + + +class _Asserts(Protocol): + def assertIn(self, member: object, container: object, msg: object = ...) -> None: ... + def assertRegex(self, text: str, expected_regex: str, msg: object = ...) -> None: ... + + +def workspace_package_version(workspace_manifest: str) -> str: + match = re.search( + r"(?ms)^\[workspace\.package\]\s+.*?^version = \"([^\"]+)\"", + workspace_manifest, + ) + if match is None: + raise AssertionError("workspace manifest is missing [workspace.package] version") + return match.group(1) + + +def assert_workspace_version_is_semver(case: _Asserts, workspace_manifest: str) -> str: + version = workspace_package_version(workspace_manifest) + case.assertRegex(version, r"^\d+\.\d+\.\d+$") + return version + + +def assert_workspace_dependency_uses_workspace_version( + case: _Asserts, + workspace_manifest: str, + *, + dependency: str, + package: str, + path: str, + default_features_false: bool = False, +) -> None: + version = assert_workspace_version_is_semver(case, workspace_manifest) + expected = f'{dependency} = {{ package = "{package}", path = "{path}", version = "{version}"' + case.assertIn(expected, workspace_manifest) + if default_features_false: + case.assertIn("default-features = false", workspace_manifest) diff --git a/.github/scripts/test_milestone_e_package_publication_activation_applied.py b/.github/scripts/test_milestone_e_package_publication_activation_applied.py index 83edc63..16f597d 100644 --- a/.github/scripts/test_milestone_e_package_publication_activation_applied.py +++ b/.github/scripts/test_milestone_e_package_publication_activation_applied.py @@ -22,6 +22,7 @@ import unittest from pathlib import Path +from cargo_manifest_guard import assert_workspace_dependency_uses_workspace_version from makefile_guard import target_block @@ -126,10 +127,13 @@ def test_candidate_manifests_are_activated_and_non_candidates_stay_blocked(self) lockfile = read(ROOT / "Cargo.lock") verify = read(ROOT / "crates/ethos-verify/Cargo.toml") pdf = read(ROOT / "crates/ethos-pdf/Cargo.toml") - self.assertIn( - 'ethos-core = { package = "ethos-doc-core", path = "crates/ethos-core", ' - 'version = "0.1.2", default-features = false }', + assert_workspace_dependency_uses_workspace_version( + self, workspace, + dependency="ethos-core", + package="ethos-doc-core", + path="crates/ethos-core", + default_features_false=True, ) self.assertIn('name = "ethos-doc-core"', lockfile) self.assertNotIn('name = "ethos-core"', lockfile) diff --git a/.github/scripts/test_milestone_e_package_publication_approval_prep.py b/.github/scripts/test_milestone_e_package_publication_approval_prep.py index 45fc943..6fc3a3f 100644 --- a/.github/scripts/test_milestone_e_package_publication_approval_prep.py +++ b/.github/scripts/test_milestone_e_package_publication_approval_prep.py @@ -22,6 +22,7 @@ from pathlib import Path from typing import Any +from cargo_manifest_guard import assert_workspace_version_is_semver from makefile_guard import target_block @@ -493,7 +494,7 @@ def test_candidate_crates_remain_publish_false_until_later_approval(self) -> Non self.assertNotIn("publish = false", verify) self.assertIn('name = "ethos-pdf"', pdf) self.assertNotIn("publish = false", pdf) - self.assertIn('version = "0.1.2"', read(ROOT / "Cargo.toml")) + assert_workspace_version_is_semver(self, read(ROOT / "Cargo.toml")) def test_evidence_status_matches_decider_input(self) -> None: status = load_json(PREP)["evidence_review_status"] @@ -611,7 +612,7 @@ def test_semver_package_version_decision_prep_keeps_version_unselected(self) -> "semver_decision_inputs_recorded_version_unselected_publication_blocked", review["review_state"], ) - self.assertIn('version = "0.1.2"', cargo) + assert_workspace_version_is_semver(self, cargo) self.assertIn('reserved_crates_io_version = "0.0.0-reserved.0"', core_manifest) self.assertIn('reserved_crates_io_version = "0.0.0-reserved.0"', verify_manifest) self.assertIn('reserved_crates_io_version = "0.0.0-reserved.0"', pdf_manifest) @@ -667,7 +668,7 @@ def test_combined_package_publication_decision_prep_bundle_blocks_all_actions(se self.assertIn('name = "ethos-doc-core"', core_manifest) self.assertIn('name = "ethos-verify"', verify_manifest) self.assertIn('name = "ethos-pdf"', pdf_manifest) - self.assertIn('version = "0.1.2"', cargo) + assert_workspace_version_is_semver(self, cargo) def test_package_publication_approval_request_packet_keeps_all_actions_blocked(self) -> None: packet = load_json(PREP)["package_publication_approval_request_packet"] @@ -741,7 +742,7 @@ def test_package_publication_pre_approval_gap_ledger_keeps_resolution_inputs_exp self.assertIn('name = "ethos-doc-core"', core_manifest) self.assertIn('name = "ethos-verify"', verify_manifest) self.assertIn('name = "ethos-pdf"', pdf_manifest) - self.assertIn('version = "0.1.2"', cargo) + assert_workspace_version_is_semver(self, cargo) def test_pdfium_boundary_keeps_ethos_pdf_held_until_confirmed(self) -> None: approved = load_json(PREP)["approved_package_publication_prep"] diff --git a/.github/scripts/test_milestone_e_package_publication_candidate_activation_evidence.py b/.github/scripts/test_milestone_e_package_publication_candidate_activation_evidence.py index 1fff090..fb21684 100644 --- a/.github/scripts/test_milestone_e_package_publication_candidate_activation_evidence.py +++ b/.github/scripts/test_milestone_e_package_publication_candidate_activation_evidence.py @@ -23,6 +23,7 @@ import unittest from pathlib import Path +from cargo_manifest_guard import workspace_package_version from makefile_guard import target_block @@ -121,7 +122,7 @@ def test_candidate_activation_script_passes_with_registry_equivalent_consumer(se commands = [entry["command"] for entry in result["commands"]] self.assertEqual("pass", result["status"]) - self.assertEqual("0.1.2", result["candidate_version"]) + self.assertEqual(workspace_package_version(read(ROOT / "Cargo.toml")), result["candidate_version"]) self.assertEqual(["ethos-doc-core", "ethos-verify", "ethos-pdf"], result["candidate_packages"]) self.assertEqual("pass", result["registry_equivalent_consumer_check"]) self.assertTrue(result["source_candidate_manifests_activated"]) @@ -149,9 +150,10 @@ def test_candidate_activation_preserves_import_and_dependency_shape(self) -> Non self.assertEqual(["full"], activation["pdf_core_features"]) self.assertTrue(all(checks.values())) self.assertEqual({"ethos-doc-core", "ethos-verify", "ethos-pdf"}, set(artifacts)) + candidate_version = self.result["candidate_version"] for artifact in artifacts.values(): self.assertRegex(artifact["sha256"], r"^[0-9a-f]{64}$") - self.assertTrue(artifact["crate_file"].endswith("-0.1.2.crate")) + self.assertTrue(artifact["crate_file"].endswith(f"-{candidate_version}.crate")) def test_source_candidate_manifests_are_activated_and_profile_copy_is_in_sync(self) -> None: core_manifest = read(ROOT / "crates/ethos-core/Cargo.toml") diff --git a/.github/scripts/test_milestone_e_package_publication_current_registry_assembly.py b/.github/scripts/test_milestone_e_package_publication_current_registry_assembly.py index 2b93fd9..36ef465 100644 --- a/.github/scripts/test_milestone_e_package_publication_current_registry_assembly.py +++ b/.github/scripts/test_milestone_e_package_publication_current_registry_assembly.py @@ -23,6 +23,7 @@ import unittest from pathlib import Path +from cargo_manifest_guard import workspace_package_version from makefile_guard import target_block @@ -115,7 +116,7 @@ def test_current_registry_equivalent_assembly_passes_after_manifest_activation(s commands = [entry["command"] for entry in result["commands"]] self.assertEqual("pass", result["status"]) - self.assertEqual("0.1.2", result["candidate_version"]) + self.assertEqual(workspace_package_version(read(ROOT / "Cargo.toml")), result["candidate_version"]) self.assertEqual(["ethos-doc-core", "ethos-verify", "ethos-pdf"], result["candidate_packages"]) self.assertEqual("pass", result["registry_equivalent_consumer_check"]) self.assertTrue(result["source_manifest_activation_applied"]) @@ -139,9 +140,10 @@ def test_artifacts_and_manifest_shape_are_current(self) -> None: self.assertEqual(["grounding", "verify-types"], activation["verify_core_features"]) self.assertEqual(["full"], activation["pdf_core_features"]) self.assertEqual({"ethos-doc-core", "ethos-verify", "ethos-pdf"}, set(artifacts)) + candidate_version = self.result["candidate_version"] for artifact in artifacts.values(): self.assertRegex(artifact["sha256"], r"^[0-9a-f]{64}$") - self.assertTrue(artifact["crate_file"].endswith("-0.1.2.crate")) + self.assertTrue(artifact["crate_file"].endswith(f"-{candidate_version}.crate")) def test_source_candidate_manifests_are_activated_while_tags_and_registry_stay_absent(self) -> None: for manifest in ( diff --git a/.github/scripts/test_milestone_e_package_publication_evidence_records.py b/.github/scripts/test_milestone_e_package_publication_evidence_records.py index ff8fa64..269a5fa 100644 --- a/.github/scripts/test_milestone_e_package_publication_evidence_records.py +++ b/.github/scripts/test_milestone_e_package_publication_evidence_records.py @@ -23,6 +23,7 @@ from pathlib import Path from typing import Any +from cargo_manifest_guard import assert_workspace_version_is_semver from makefile_guard import target_block @@ -176,7 +177,7 @@ def test_version_tag_policy_record_keeps_placeholder_and_workspace_versions_sepa record = normalized(record_path(RECORDS["version_tag_policy"])) root_manifest = read(ROOT / "Cargo.toml") - self.assertIn('version = "0.1.2"', root_manifest) + assert_workspace_version_is_semver(self, root_manifest) self.assertIn("Workspace package version is `0.1.0`", record) self.assertIn("`0.0.0-reserved.0` placeholders", record) self.assertIn("`ethos-source-snapshot-660f268`", record) diff --git a/.github/scripts/test_milestone_e_package_publication_manifest_activation_applied.py b/.github/scripts/test_milestone_e_package_publication_manifest_activation_applied.py index 50101ca..74d9beb 100644 --- a/.github/scripts/test_milestone_e_package_publication_manifest_activation_applied.py +++ b/.github/scripts/test_milestone_e_package_publication_manifest_activation_applied.py @@ -23,6 +23,7 @@ import unittest from pathlib import Path +from cargo_manifest_guard import assert_workspace_dependency_uses_workspace_version from makefile_guard import target_block @@ -125,10 +126,13 @@ def test_source_manifests_have_activated_but_blocked_shape(self) -> None: verify = read(ROOT / "crates/ethos-verify/Cargo.toml") pdf = read(ROOT / "crates/ethos-pdf/Cargo.toml") - self.assertIn( - 'ethos-core = { package = "ethos-doc-core", path = "crates/ethos-core", ' - 'version = "0.1.2", default-features = false }', + assert_workspace_dependency_uses_workspace_version( + self, workspace, + dependency="ethos-core", + package="ethos-doc-core", + path="crates/ethos-core", + default_features_false=True, ) self.assertIn('name = "ethos-doc-core"', core) self.assertIn('[lib]\nname = "ethos_core"', core) diff --git a/.github/scripts/test_milestone_e_package_publication_manifest_migration_prep.py b/.github/scripts/test_milestone_e_package_publication_manifest_migration_prep.py index 4f5273c..08f9ba1 100644 --- a/.github/scripts/test_milestone_e_package_publication_manifest_migration_prep.py +++ b/.github/scripts/test_milestone_e_package_publication_manifest_migration_prep.py @@ -22,6 +22,7 @@ import unittest from pathlib import Path +from cargo_manifest_guard import assert_workspace_dependency_uses_workspace_version from makefile_guard import target_block @@ -113,9 +114,13 @@ def test_current_manifests_remain_unmigrated_source_tree_manifests(self) -> None self.assertNotIn("publish = false", core) self.assertNotIn("publish = false", verify) self.assertNotIn("publish = false", pdf) - self.assertIn( - 'ethos-core = { package = "ethos-doc-core", path = "crates/ethos-core", version = "0.1.2", default-features = false }', + assert_workspace_dependency_uses_workspace_version( + self, workspace, + dependency="ethos-core", + package="ethos-doc-core", + path="crates/ethos-core", + default_features_false=True, ) self.assertIn('ethos-core = { workspace = true, features = ["grounding", "verify-types"] }', verify) self.assertIn('ethos-core = { workspace = true, features = ["full"] }', pdf) diff --git a/.github/scripts/test_milestone_e_package_publication_real_version_selection_prep.py b/.github/scripts/test_milestone_e_package_publication_real_version_selection_prep.py index e092141..41f8b21 100644 --- a/.github/scripts/test_milestone_e_package_publication_real_version_selection_prep.py +++ b/.github/scripts/test_milestone_e_package_publication_real_version_selection_prep.py @@ -22,6 +22,7 @@ import unittest from pathlib import Path +from cargo_manifest_guard import assert_workspace_version_is_semver from makefile_guard import target_block @@ -106,7 +107,7 @@ def test_current_versions_and_manifests_stay_source_tree_only(self) -> None: verify = read(ROOT / "crates/ethos-verify/Cargo.toml") pdf = read(ROOT / "crates/ethos-pdf/Cargo.toml") - self.assertIn('version = "0.1.2"', workspace) + assert_workspace_version_is_semver(self, workspace) self.assertIn('reserved_crates_io_version = "0.0.0-reserved.0"', core) self.assertIn('reserved_crates_io_version = "0.0.0-reserved.0"', verify) self.assertIn('reserved_crates_io_version = "0.0.0-reserved.0"', pdf) diff --git a/.github/scripts/test_milestone_e_package_publication_registry_assembly_prep.py b/.github/scripts/test_milestone_e_package_publication_registry_assembly_prep.py index 9b21bf1..50e179e 100644 --- a/.github/scripts/test_milestone_e_package_publication_registry_assembly_prep.py +++ b/.github/scripts/test_milestone_e_package_publication_registry_assembly_prep.py @@ -22,6 +22,7 @@ import unittest from pathlib import Path +from cargo_manifest_guard import assert_workspace_dependency_uses_workspace_version from makefile_guard import target_block @@ -110,9 +111,13 @@ def test_current_manifests_stay_source_tree_only(self) -> None: self.assertNotIn("publish = false", core) self.assertNotIn("publish = false", verify) self.assertNotIn("publish = false", pdf) - self.assertIn( - 'ethos-core = { package = "ethos-doc-core", path = "crates/ethos-core", version = "0.1.2", default-features = false }', + assert_workspace_dependency_uses_workspace_version( + self, workspace, + dependency="ethos-core", + package="ethos-doc-core", + path="crates/ethos-core", + default_features_false=True, ) self.assertIn('ethos-core = { workspace = true, features = ["grounding", "verify-types"] }', verify) self.assertIn('ethos-core = { workspace = true, features = ["full"] }', pdf) diff --git a/.github/scripts/test_milestone_e_package_publication_tag_binding_refresh.py b/.github/scripts/test_milestone_e_package_publication_tag_binding_refresh.py index 58f5c03..ee0cb0e 100644 --- a/.github/scripts/test_milestone_e_package_publication_tag_binding_refresh.py +++ b/.github/scripts/test_milestone_e_package_publication_tag_binding_refresh.py @@ -22,6 +22,7 @@ import unittest from pathlib import Path +from cargo_manifest_guard import assert_workspace_version_is_semver from makefile_guard import target_block @@ -138,7 +139,7 @@ def test_binding_points_at_activated_candidate_manifest_state(self) -> None: ) self.assertIn('reserved_crates_io_version = "0.0.0-reserved.0"', text, str(manifest)) - self.assertIn('version = "0.1.2"', read(ROOT / "Cargo.toml")) + assert_workspace_version_is_semver(self, read(ROOT / "Cargo.toml")) for manifest in ( ROOT / "crates/ethos-cli/Cargo.toml", diff --git a/.github/scripts/test_milestone_e_package_publication_tag_creation_prep.py b/.github/scripts/test_milestone_e_package_publication_tag_creation_prep.py index 973da13..2fb7a62 100644 --- a/.github/scripts/test_milestone_e_package_publication_tag_creation_prep.py +++ b/.github/scripts/test_milestone_e_package_publication_tag_creation_prep.py @@ -22,6 +22,7 @@ import unittest from pathlib import Path +from cargo_manifest_guard import assert_workspace_version_is_semver from makefile_guard import target_block @@ -105,7 +106,7 @@ def test_current_manifests_and_versions_stay_unchanged(self) -> None: verify = read(ROOT / "crates/ethos-verify/Cargo.toml") pdf = read(ROOT / "crates/ethos-pdf/Cargo.toml") - self.assertIn('version = "0.1.2"', workspace) + assert_workspace_version_is_semver(self, workspace) self.assertIn('reserved_crates_io_version = "0.0.0-reserved.0"', core) self.assertIn('reserved_crates_io_version = "0.0.0-reserved.0"', verify) self.assertIn('reserved_crates_io_version = "0.0.0-reserved.0"', pdf) diff --git a/.github/scripts/test_milestone_e_package_publication_version_tag_policy.py b/.github/scripts/test_milestone_e_package_publication_version_tag_policy.py index 29536de..c655139 100644 --- a/.github/scripts/test_milestone_e_package_publication_version_tag_policy.py +++ b/.github/scripts/test_milestone_e_package_publication_version_tag_policy.py @@ -22,6 +22,7 @@ import unittest from pathlib import Path +from cargo_manifest_guard import assert_workspace_version_is_semver from makefile_guard import target_block @@ -101,7 +102,7 @@ def test_workspace_and_reserved_versions_stay_separate(self) -> None: adr = normalized(ROOT / "docs/decisions/ADR-0006-package-identifiers.md") record = normalized(RECORD) - self.assertIn('version = "0.1.2"', root_manifest) + assert_workspace_version_is_semver(self, root_manifest) self.assertIn("0.0.0-reserved.0", adr) self.assertIn("Workspace package version `0.1.0` remains a source-tree version", record) self.assertIn("ADR-0006 crates.io reservations remain `0.0.0-reserved.0` placeholders", record) diff --git a/.github/scripts/test_npm_binary_package_scaffold.py b/.github/scripts/test_npm_binary_package_scaffold.py index a355e3a..ccd949f 100644 --- a/.github/scripts/test_npm_binary_package_scaffold.py +++ b/.github/scripts/test_npm_binary_package_scaffold.py @@ -60,7 +60,7 @@ def test_package_metadata_is_bounded_to_supported_release_targets(self) -> None: package = json.loads(read(PACKAGE_JSON)) self.assertEqual("@docushell/ethos-pdf", package["name"]) - self.assertEqual("0.1.2", package["version"]) + self.assertRegex(package["version"], r"^\d+\.\d+\.\d+$") self.assertEqual("Apache-2.0", package["license"]) self.assertEqual({"ethos": "./bin/ethos-pdf.js"}, package["bin"]) self.assertIn("vendor/", package["files"]) diff --git a/.github/scripts/test_python_public_api_policy.py b/.github/scripts/test_python_public_api_policy.py index 05dcd53..3d3e833 100644 --- a/.github/scripts/test_python_public_api_policy.py +++ b/.github/scripts/test_python_public_api_policy.py @@ -56,6 +56,13 @@ def pyproject_text() -> str: return read(PYPROJECT) +def pyproject_version() -> str: + match = re.search(r'^version = "([^"]+)"$', pyproject_text(), re.MULTILINE) + if match is None: + raise AssertionError("pyproject.toml is missing a project version") + return match.group(1) + + def parse_init_assignments() -> dict[str, object]: module = ast.parse(read(INIT)) values: dict[str, object] = {} @@ -70,9 +77,10 @@ def parse_init_assignments() -> dict[str, object]: class PythonPublicApiPolicyTests(unittest.TestCase): def test_package_metadata_declares_public_release_policy(self) -> None: text = pyproject_text() + version = pyproject_version() self.assertIn('name = "ethos-pdf"', text) - self.assertIn('version = "0.1.2"', text) + self.assertRegex(version, r"^\d+\.\d+\.\d+$") self.assertIn('requires-python = ">=3.8"', text) self.assertIn('license = "Apache-2.0"', text) self.assertNotIn('license = { text = "Apache-2.0" }', text) @@ -83,7 +91,7 @@ def test_package_metadata_declares_public_release_policy(self) -> None: def test_public_module_version_and_all_match_metadata(self) -> None: assignments = parse_init_assignments() - self.assertEqual("0.1.2", assignments["__version__"]) + self.assertEqual(pyproject_version(), assignments["__version__"]) self.assertEqual(list(PUBLIC_API), assignments["__all__"]) def test_readme_documents_semver_and_exact_public_api_boundary(self) -> None: diff --git a/.github/scripts/test_release_artifact_workflow_prep.py b/.github/scripts/test_release_artifact_workflow_prep.py index ae62fdf..8d8d2d2 100644 --- a/.github/scripts/test_release_artifact_workflow_prep.py +++ b/.github/scripts/test_release_artifact_workflow_prep.py @@ -48,7 +48,7 @@ def test_workflow_generates_draft_artifacts_without_publication(self) -> None: self.assertIn("cargo build --locked --release -p ethos-cli", text) self.assertIn("write_release_artifact_inventory.py", text) self.assertIn("smoke_release_cli_artifact.py", text) - self.assertIn('--expected-version "ethos 0.1.2"', text) + self.assertIn('--expected-version "ethos 0.2.0"', text) self.assertIn("--target \"${{ matrix.artifact_target }}\"", text) self.assertIn("*.smoke.json", text) self.assertIn("validate_release_artifact_inventory.py", text) diff --git a/.github/scripts/test_v0_2_0_draft_artifact_evidence.py b/.github/scripts/test_v0_2_0_draft_artifact_evidence.py new file mode 100644 index 0000000..6156d7d --- /dev/null +++ b/.github/scripts/test_v0_2_0_draft_artifact_evidence.py @@ -0,0 +1,181 @@ +#!/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 unittest +from pathlib import Path + +from makefile_guard import target_block +from validation_record_source import assert_record_source_binding + + +ROOT = Path(__file__).resolve().parents[2] +RECORD = ROOT / "docs/validation/v0-2-0-draft-artifact-evidence-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" + +SOURCE_SHORT = "36955ca" +SOURCE_COMMIT = "36955cac69dc4eed624feb22b1a8c5e8a811d3bd" +SOURCE_TREE = "7ca47e76dfa2d23d40768dbcb88e2b97fba619b2" +RUN_URL = "https://github.com/docushell/ethos/actions/runs/28175143857" +RUN_ID = "28175143857" +MACOS_SHA256 = "c588ee77bbaf99a7d933673e6cd9db190f5992e47d40955def803435a9f9fc5a" +LINUX_SHA256 = "00137b20ca2c2a2d2089df1d135920b021b0905d779b1347d134e8a2fb7bfa23" +EXPECTED_ARTIFACTS = ( + "ethos-cli-draft-macos-arm64/ethos-macos-arm64.tar.gz", + "ethos-cli-draft-macos-arm64/ethos-macos-arm64.tar.gz.sha256", + "ethos-cli-draft-macos-arm64/ethos-macos-arm64.inventory.json", + "ethos-cli-draft-macos-arm64/ethos-macos-arm64.smoke.json", + "ethos-cli-draft-linux-x64/ethos-linux-x64.tar.gz", + "ethos-cli-draft-linux-x64/ethos-linux-x64.tar.gz.sha256", + "ethos-cli-draft-linux-x64/ethos-linux-x64.inventory.json", + "ethos-cli-draft-linux-x64/ethos-linux-x64.smoke.json", +) +RETAINED_BLOCKERS = ( + "GitHub Release artifact publication remains blocked", + "Registry publication remains blocked", + "PyPI upload remains blocked", + "npm vendor refresh remains blocked pending a separate refresh record", + "npm publication remains blocked", + "Release tag creation remains blocked", + "Package tag creation remains blocked", + "Public installation wording remains blocked", + "Hosted surfaces remain blocked", + "Production positioning remains blocked", + "Windows packaged artifacts remain blocked", + "Bundled project-maintained PDFium builds remain blocked", + "Public benchmark reports remain blocked", + "Public benchmark claims remain blocked", + "`ethos-doc` remains blocked", + "`ethos-rag` remains blocked", +) +FORBIDDEN_APPROVALS = ( + "github release artifact publication approved", + "github release publication approved", + "registry publication approved", + "pypi upload approved", + "npm vendor refresh approved", + "npm publication approved", + "release tag creation approved", + "package tag creation approved", + "public installation wording approved", + "installable 0.2.0 wording approved", + "production-ready", + "hosted surfaces approved", + "windows packaged artifacts approved", + "bundled pdfium approved", + "public benchmark claims approved", +) +PRIVATE_PATH_MARKERS = ( + "/" + "Users/", + "/" + "private/tmp", + "/" + "private/var", + "/" + "var/folders", + "saumil" + "diwaker", + "Desktop/" + "Stuff", + "project/repo/" + "ethos", +) + + +def read(path: Path) -> str: + return path.read_text(encoding="utf-8") + + +def normalized(path: Path) -> str: + return re.sub(r"\s+", " ", read(path)) + + +class V020DraftArtifactEvidenceTests(unittest.TestCase): + def test_record_is_source_and_workflow_bound(self) -> None: + raw = read(RECORD) + record = normalized(RECORD) + + assert_record_source_binding( + self, + root=ROOT, + raw_record=raw, + normalized_record=record, + validated_head=SOURCE_SHORT, + source_label="v0.2.0 draft artifact evidence", + source_commit=SOURCE_COMMIT, + source_tree=SOURCE_TREE, + ) + self.assertIn(RUN_URL, record) + self.assertIn(f"run watch {RUN_ID}", record) + self.assertIn("event: `workflow_dispatch`", record) + self.assertIn("branch: `dev/v0-2-approval-packet`", record) + self.assertIn(f"head SHA: `{SOURCE_COMMIT}`", record) + self.assertIn("status: `completed`", record) + self.assertIn("conclusion: `success`", record) + self.assertIn("created at: `2026-06-25T13:52:38Z`", record) + self.assertIn("updated at: `2026-06-25T13:53:52Z`", record) + + def test_record_captures_both_platform_artifacts_inventory_smoke_and_archives(self) -> None: + raw = read(RECORD) + record = normalized(RECORD) + + for artifact in EXPECTED_ARTIFACTS: + self.assertIn(artifact, record) + for expected in ( + "cli-draft-artifacts (macos-arm64, macos-14, tar.gz)", + "cli-draft-artifacts (linux-x64, ubuntu-latest, tar.gz)", + MACOS_SHA256, + LINUX_SHA256, + "ethos-macos-arm64/", + "ethos-linux-x64/", + "pdfium-manual-setup.md", + '"version_stdout": "ethos 0.2.0"', + '"missing_pdfium_exit_code": 12', + "ETHOS_PDFIUM_LIBRARY_PATH", + ): + self.assertIn(expected, record) + self.assertEqual(2, raw.count('"schema": "ethos.release_artifact_inventory.v1"')) + self.assertEqual(2, raw.count('"schema": "ethos.release_artifact_smoke.v1"')) + self.assertEqual(2, raw.count('"publication": "blocked"')) + self.assertEqual(2, raw.count('"status": "draft_not_release_ready"')) + self.assertEqual(2, raw.count('"pdfium_policy": "caller-provided"')) + + def test_record_keeps_publication_vendor_tags_and_install_wording_blocked(self) -> None: + raw = read(RECORD) + record = normalized(RECORD) + lower = record.lower() + + self.assertIn("public install baseline remains `0.1.2`", record) + self.assertIn("This record does not approve GitHub Release artifact publication.", record) + self.assertIn("This record does not approve npm vendor refresh.", record) + self.assertIn("This record does not create or approve release tags or package tags.", record) + for blocker in RETAINED_BLOCKERS: + self.assertIn(blocker, record) + for forbidden in FORBIDDEN_APPROVALS: + self.assertNotIn(forbidden, lower) + for marker in PRIVATE_PATH_MARKERS: + self.assertNotIn(marker, raw) + + def test_record_is_indexed_and_wired_after_package_build_evidence_guard(self) -> None: + readme = normalized(VALIDATION_README) + execution = normalized(EXECUTION_STATUS) + checklist = normalized(PUBLIC_RELEASE_CHECKLIST) + block = target_block("v0-2-release-prep") + package_guard = "$(PYTHON) .github/scripts/test_v0_2_0_package_build_evidence.py" + draft_guard = "$(PYTHON) .github/scripts/test_v0_2_0_draft_artifact_evidence.py" + claims = "$(PYTHON) .github/scripts/claims_gate.py" + + for text in (readme, execution, checklist): + self.assertIn(RECORD.name, text) + self.assertIn("draft artifact evidence", text.lower()) + self.assertIn("ethos 0.2.0", text) + self.assertIn(draft_guard, block) + self.assertEqual(1, block.count(draft_guard)) + self.assertLess(block.index(package_guard), block.index(draft_guard)) + self.assertLess(block.index(draft_guard), block.index(claims)) + + +if __name__ == "__main__": + unittest.main() diff --git a/.github/scripts/test_v0_2_0_ethos_doc_core_cargo_publish_dry_run_evidence.py b/.github/scripts/test_v0_2_0_ethos_doc_core_cargo_publish_dry_run_evidence.py new file mode 100644 index 0000000..0231e14 --- /dev/null +++ b/.github/scripts/test_v0_2_0_ethos_doc_core_cargo_publish_dry_run_evidence.py @@ -0,0 +1,170 @@ +#!/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 unittest +from pathlib import Path + +from makefile_guard import target_block +from validation_record_source import assert_record_source_binding + + +ROOT = Path(__file__).resolve().parents[2] +RECORD = ROOT / ( + "docs/validation/" + "v0-2-0-ethos-doc-core-cargo-publish-dry-run-evidence-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" +GUARD_NAME = "test_v0_2_0_ethos_doc_core_cargo_publish_dry_run_evidence.py" +OLD_GUARD_NAME = "test_v0_2_0_ethos_doc_core_dry_run.py" + +SOURCE_SHORT = "9ca0147" +SOURCE_COMMIT = "9ca01477a14b9addd542e7aa9c5217a1b1df6831" +SOURCE_TREE = "095ce634fa0a90e81fbc4805574d75fa4d06bb71" +CRATE_SHA256 = "de86ce74dd791b50d0722cddc878756cceabae2162f747e9e24902b88e5c7de1" +PACKAGE_FILES = ( + ".cargo_vcs_info.json", + "Cargo.lock", + "Cargo.toml", + "Cargo.toml.orig", + "NOTICE.md", + "README.md", + "src/c14n.rs", + "src/codes.rs", + "src/config.rs", + "src/crop_element.rs", + "src/error.rs", + "src/evidence_anchor.rs", + "src/fingerprint.rs", + "src/geom.rs", + "src/grounding.rs", + "src/ids.rs", + "src/lib.rs", + "src/model.rs", + "src/traits.rs", + "src/verify_types.rs", +) +PRIVATE_PATH_MARKERS = ( + "/" + "Users/", + "/" + "private/tmp", + "/" + "private/var", + "/" + "var/folders", + "saumil" + "diwaker", + "Desktop/" + "Stuff", + "project/repo/" + "ethos", +) +FORBIDDEN_APPROVALS = ( + "cargo publish approved", + "published crates", + "crates are published", + "installable 0.2.0 wording approved", + "pypi upload approved", + "npm publish approved", + "github release approved", + "tag creation approved", + "hosted surfaces approved", + "production-ready", +) + + +def read(path: Path) -> str: + return path.read_text(encoding="utf-8") + + +def normalized(path: Path) -> str: + return re.sub(r"\s+", " ", read(path)) + + +class V020EthosDocCoreDryRunTests(unittest.TestCase): + def test_record_is_source_bound_and_indexed(self) -> None: + raw = read(RECORD) + record = normalized(RECORD) + + assert_record_source_binding( + self, + root=ROOT, + raw_record=raw, + normalized_record=record, + validated_head=SOURCE_SHORT, + source_label="v0.2.0 ethos-doc-core dry-run", + source_commit=SOURCE_COMMIT, + source_tree=SOURCE_TREE, + ) + + for path in (VALIDATION_README, EXECUTION_STATUS, PUBLIC_RELEASE_CHECKLIST): + text = normalized(path) + self.assertIn(RECORD.name, text, str(path)) + self.assertIn("ethos-doc-core", text, str(path)) + self.assertIn("dry-run", text.lower(), str(path)) + + def test_record_captures_exact_dry_run_evidence(self) -> None: + record = normalized(RECORD) + + self.assertIn("Status: **ethos-doc-core 0.2.0 cargo publish dry-run evidence recorded; cargo publish remains blocked**", record) + self.assertIn("cargo publish --dry-run --locked -p ethos-doc-core", record) + self.assertIn("Packaging ethos-doc-core v0.2.0", record) + self.assertIn("Packaged 20 files, 162.1KiB (37.6KiB compressed)", record) + self.assertIn("Verifying ethos-doc-core v0.2.0", record) + self.assertIn("Compiling ethos-doc-core v0.2.0", record) + self.assertIn("Uploading ethos-doc-core v0.2.0", record) + self.assertIn("warning: aborting upload due to dry run", record) + self.assertIn("RESULT: PASS (dry-run)", record) + self.assertIn("The dry run exited `0`.", record) + self.assertIn(CRATE_SHA256, record) + self.assertIn(GUARD_NAME, record) + self.assertNotIn(OLD_GUARD_NAME, record) + + def test_record_captures_package_file_list(self) -> None: + record = read(RECORD) + + for file_name in PACKAGE_FILES: + self.assertIn(file_name, record) + self.assertEqual(20, len(PACKAGE_FILES)) + + def test_boundaries_remain_closed_and_private_paths_absent(self) -> None: + raw = read(RECORD) + record = normalized(RECORD) + lower = record.lower() + + for expected in ( + "`cargo publish` remains blocked until an explicit operator publication decision is recorded.", + "`ethos-verify` and `ethos-pdf` dry-runs remain blocked", + "PyPI upload remains blocked.", + "`npm publish` remains blocked.", + "GitHub Release `v0.2.0` artifact upload remains blocked.", + "Release tag creation remains blocked.", + "Package tag creation remains blocked.", + "Installable `0.2.0` public wording remains blocked.", + "`ethos-doc` remains blocked.", + "`ethos-rag` remains blocked.", + ): + self.assertIn(expected, record) + for forbidden in FORBIDDEN_APPROVALS: + self.assertNotIn(forbidden, lower) + for private in PRIVATE_PATH_MARKERS: + self.assertNotIn(private, raw) + + def test_v0_2_release_prep_runs_dry_run_guard_after_activation(self) -> None: + makefile = read(MAKEFILE) + activation_guard = "$(PYTHON) .github/scripts/test_v0_2_0_version_activation.py" + dry_run_guard = f"$(PYTHON) .github/scripts/{GUARD_NAME}" + claims = "$(PYTHON) .github/scripts/claims_gate.py" + block = target_block("v0-2-release-prep") + + self.assertIn(dry_run_guard, block) + self.assertEqual(1, makefile.count(dry_run_guard)) + self.assertLess(block.index(activation_guard), block.index(dry_run_guard)) + self.assertLess(block.index(dry_run_guard), block.index(claims)) + + +if __name__ == "__main__": + unittest.main() diff --git a/.github/scripts/test_v0_2_0_package_build_evidence.py b/.github/scripts/test_v0_2_0_package_build_evidence.py new file mode 100644 index 0000000..bcb9bd7 --- /dev/null +++ b/.github/scripts/test_v0_2_0_package_build_evidence.py @@ -0,0 +1,153 @@ +#!/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 unittest +from pathlib import Path + +from makefile_guard import target_block +from validation_record_source import assert_record_source_binding + + +ROOT = Path(__file__).resolve().parents[2] +RECORD = ROOT / "docs/validation/v0-2-0-package-build-evidence-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" +RELEASE_WORKFLOW = ROOT / ".github/workflows/release.yml" + +SOURCE_SHORT = "977306e" +SOURCE_COMMIT = "977306eb19dbd4070a600bff36e6f52a1e26f776" +SOURCE_TREE = "6ad9746f54c75985ad1069b73926e1877bf7d848" +PYTHON_WHEEL_SHA256 = "5eb6eabb1d3e8a4d6d5f0280741a89103701c13706182e9004d5bf6ec2402ef4" +MACOS_ARTIFACT_SHA256 = "2a41e3457cc074394ad0e347c967d8e90e353a1179d8beaa2dda82c2725ad84a" +NPM_SHASUM = "42a0d591e6dd55ee0a9b6ed9976e455786b643c0" +NPM_INTEGRITY = ( + "sha512-98NfgYjTl49v+gahz+lrnOk3BDEvnDGYwHEIAWknksJIiwfZ7thzP0Q2WMZFJpwfVW1C/9oeccG5b5lbwmlFiA==" +) +GUARD_NAME = "test_v0_2_0_package_build_evidence.py" +PRIVATE_PATH_MARKERS = ( + "/" + "Users/", + "/" + "private/tmp", + "/" + "private/var", + "/" + "var/folders", + "saumil" + "diwaker", + "Desktop/" + "Stuff", + "project/repo/" + "ethos", +) +FORBIDDEN_APPROVALS = ( + "pypi upload approved", + "npm publish approved", + "github release approved", + "installable 0.2.0 wording approved", + "release tag creation approved", + "package tag creation approved", + "hosted surfaces approved", + "production-ready", +) + + +def read(path: Path) -> str: + return path.read_text(encoding="utf-8") + + +def normalized(path: Path) -> str: + return re.sub(r"\s+", " ", read(path)) + + +class V020PackageBuildEvidenceTests(unittest.TestCase): + def test_record_is_source_bound_and_indexed(self) -> None: + raw = read(RECORD) + record = normalized(RECORD) + + assert_record_source_binding( + self, + root=ROOT, + raw_record=raw, + normalized_record=record, + validated_head=SOURCE_SHORT, + source_label="v0.2.0 package/build", + source_commit=SOURCE_COMMIT, + source_tree=SOURCE_TREE, + ) + + for path in (VALIDATION_README, EXECUTION_STATUS, PUBLIC_RELEASE_CHECKLIST): + text = normalized(path) + self.assertIn(RECORD.name, text, str(path)) + self.assertIn("package/build", text.lower(), str(path)) + + def test_record_captures_python_cli_and_npm_evidence(self) -> None: + record = normalized(RECORD) + + for expected in ( + "python3 .github/scripts/test_release_artifact_workflow_prep.py", + "cargo build --locked --release -p ethos-cli", + "make python-surface-test PYTHON=python3", + "npm test --prefix packages/npm/ethos-pdf", + "Successfully built ethos_pdf-0.2.0-py3-none-any.whl", + "Successfully installed ethos-pdf-0.2.0", + "Name: ethos-pdf", + "Version: 0.2.0", + PYTHON_WHEEL_SHA256, + MACOS_ARTIFACT_SHA256, + "version_stdout: ethos 0.2.0", + "missing_pdfium_exit_code: 12", + "name: @docushell/ethos-pdf", + "filename: docushell-ethos-pdf-0.2.0.tgz", + NPM_SHASUM, + NPM_INTEGRITY, + "node packages/npm/ethos-pdf/bin/ethos-pdf.js --version: ethos 0.1.2", + ): + self.assertIn(expected, record) + + def test_npm_and_cross_platform_artifact_blockers_remain_explicit(self) -> None: + record = normalized(RECORD) + + for expected in ( + "npm v0.2.0 artifact candidacy: BLOCKED by vendored ethos 0.1.2 payload", + "Linux x64 CLI artifact evidence remains required before any two-platform GitHub Release artifact approval or npm vendor refresh decision.", + "npm vendor refresh remains blocked until both v0.2.0 CLI artifact payloads exist.", + "`npm publish` remains blocked.", + "PyPI upload remains blocked.", + "GitHub Release `v0.2.0` artifact upload remains blocked.", + "Installable `0.2.0` public wording remains blocked.", + "`ethos-doc` remains blocked.", + "`ethos-rag` remains blocked.", + ): + self.assertIn(expected, record) + + def test_release_workflow_expects_v0_2_smoke(self) -> None: + workflow = read(RELEASE_WORKFLOW) + + self.assertIn('--expected-version "ethos 0.2.0"', workflow) + self.assertNotIn('--expected-version "ethos 0.1.2"', workflow) + + def test_boundaries_private_paths_and_v0_2_release_prep_guard(self) -> None: + raw = read(RECORD) + lower = raw.lower() + block = target_block("v0-2-release-prep") + guard = f"$(PYTHON) .github/scripts/{GUARD_NAME}" + dry_run_guard = ( + "$(PYTHON) .github/scripts/" + "test_v0_2_0_ethos_doc_core_cargo_publish_dry_run_evidence.py" + ) + claims = "$(PYTHON) .github/scripts/claims_gate.py" + + for forbidden in FORBIDDEN_APPROVALS: + self.assertNotIn(forbidden, lower) + for private in PRIVATE_PATH_MARKERS: + self.assertNotIn(private, raw) + self.assertIn(guard, block) + self.assertLess(block.index(dry_run_guard), block.index(guard)) + self.assertLess(block.index(guard), block.index(claims)) + + +if __name__ == "__main__": + unittest.main() diff --git a/.github/scripts/test_v0_2_0_release_approval_decision.py b/.github/scripts/test_v0_2_0_release_approval_decision.py new file mode 100644 index 0000000..d3e6f7d --- /dev/null +++ b/.github/scripts/test_v0_2_0_release_approval_decision.py @@ -0,0 +1,173 @@ +#!/usr/bin/env python3 +# +# Copyright 2026 The Ethos maintainers +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# + +from __future__ import annotations + +import re +import unittest +from pathlib import Path + +from makefile_guard import target_block +from validation_record_source import assert_record_source_binding + + +ROOT = Path(__file__).resolve().parents[2] +RECORD = ROOT / "docs/validation/v0-2-0-release-approval-decision-validation-2026-06-25.md" +REQUEST = ROOT / "docs/validation/v0-2-0-release-approval-request-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 = "bebc3b0" +SOURCE_COMMIT = "bebc3b0a2a20fd762ad70351291222c162631eb6" +SOURCE_TREE = "90b19b657df2df50a957a991dcde7b9474e1f758" +REQUEST_SOURCE_COMMIT = "fa15fa6f60993e30aa90540526903e4eb72c8252" +REQUEST_SOURCE_TREE = "983f484ff1249ee3ea88da79ebafa9d2cd2410f5" +VERSION = "0.2.0" +CRATES = ("ethos-doc-core", "ethos-verify", "ethos-pdf") +PACKAGE_TAGS = ( + "ethos-package-ethos-doc-core-0.2.0", + "ethos-package-ethos-verify-0.2.0", + "ethos-package-ethos-pdf-0.2.0", +) +FORBIDDEN_APPROVALS = ( + "cargo publish approved", + "crates are published", + "published crates", + "pypi upload approved", + "npm publish approved", + "github release approved", + "tag creation approved", + "installable 0.2.0 wording approved", + "hosted surfaces approved", + "production-ready", + "windows packaged artifacts approved", + "bundled pdfium approved", + "public benchmark claims approved", + "ethos-doc approved", + "ethos-rag approved", +) +PRIVATE_PATH_MARKERS = ( + "/" + "Users/", + "/" + "private/tmp", + "/" + "private/var", + "/" + "var/folders", + "saumil" + "diwaker", + "Desktop/" + "Stuff", + "project/repo/" + "ethos", +) + + +def read(path: Path) -> str: + return path.read_text(encoding="utf-8") + + +def normalized(path: Path) -> str: + return re.sub(r"\s+", " ", read(path)) + + +class V020ReleaseApprovalDecisionTests(unittest.TestCase): + def test_decision_record_is_source_bound_and_indexed(self) -> None: + raw = read(RECORD) + record = normalized(RECORD) + + assert_record_source_binding( + self, + root=ROOT, + raw_record=raw, + normalized_record=record, + validated_head=SOURCE_SHORT, + source_label="v0.2.0 release approval decision", + source_commit=SOURCE_COMMIT, + source_tree=SOURCE_TREE, + ) + + for path in (VALIDATION_README, EXECUTION_STATUS, PUBLIC_RELEASE_CHECKLIST): + text = normalized(path) + self.assertIn(RECORD.name, text, str(path)) + self.assertIn("v0.2.0 release approval decision", text.lower(), str(path)) + self.assertIn("remain blocked", text, str(path)) + + def test_decision_accepts_exact_request_packet_and_branch_instruction(self) -> None: + record = normalized(RECORD) + + self.assertIn(REQUEST.name, record) + self.assertIn("Decision: accept the exact `v0.2.0` release approval request packet.", record) + self.assertIn( + f"Approval request source commit accepted by this decision: `{REQUEST_SOURCE_COMMIT}`", + record, + ) + self.assertIn( + f"Approval request source tree accepted by this decision: `{REQUEST_SOURCE_TREE}`", + record, + ) + self.assertIn("continue on `dev/v0-2-approval-packet` as the release-candidate working branch", record) + self.assertIn("do not create a separate branch for this lane", record) + + def test_decision_accepts_exact_scope_without_publication(self) -> None: + raw = read(RECORD) + record = normalized(RECORD) + lower = record.lower() + + self.assertIn(f"Exact target version accepted by this decision: `{VERSION}`", record) + for crate in CRATES: + self.assertIn(f"`{crate} = {VERSION}`", record) + for tag in PACKAGE_TAGS: + self.assertIn(tag, record) + self.assertIn("Python is public in `v0.2.0` only as `ethos-pdf==0.2.0`", record) + self.assertIn("PyPI wheel", record) + self.assertIn("caller-provided `ethos` CLI binary", record) + self.assertIn("`@docushell/ethos-pdf@0.2.0` may remain in scope only as a CLI binary distribution package", record) + self.assertIn("does not approve a Node API, Node SDK, N-API binding, or WASM package", record) + self.assertIn('`reserved_crates_io_version = "0.0.0-reserved.0"`', record) + self.assertIn("A bad publish can only be yanked", record) + + for forbidden in FORBIDDEN_APPROVALS: + self.assertNotIn(forbidden, lower) + for private in PRIVATE_PATH_MARKERS: + self.assertNotIn(private, raw) + + def test_approved_candidate_work_is_version_activation_only(self) -> None: + record = normalized(RECORD) + + for expected in ( + "bump Rust workspace/package dependency versions from `0.1.2` to `0.2.0`", + "bump Python metadata and `ethos_pdf.__version__` from `0.1.2` to `0.2.0`", + "bump npm `@docushell/ethos-pdf` from `0.1.2` to `0.2.0`", + "finalize `CHANGELOG.md`", + "update version-pinned docs to release-candidate wording, not installable wording", + ): + self.assertIn(expected, record) + self.assertIn("This decision record does not run `cargo publish`.", record) + self.assertIn("Installable `0.2.0` public wording remains blocked", record) + + def test_docushell_pilot_boundary_is_internal_only(self) -> None: + record = normalized(RECORD) + + self.assertIn("Evidence-Checked Answers", record) + self.assertIn("internal/design-partner", record) + self.assertIn("claim extraction quality", record) + self.assertIn("non-PDF ingestion", record) + self.assertIn("does not approve hosted DocuShell surfaces", record) + + def test_v0_2_release_prep_runs_decision_guard_after_request_guard(self) -> None: + makefile = read(MAKEFILE) + request_guard = "$(PYTHON) .github/scripts/test_v0_2_0_release_approval_request.py" + decision_guard = "$(PYTHON) .github/scripts/test_v0_2_0_release_approval_decision.py" + claims = "$(PYTHON) .github/scripts/claims_gate.py" + block = target_block("v0-2-release-prep") + + self.assertIn(decision_guard, block) + self.assertEqual(1, makefile.count(decision_guard)) + self.assertLess(block.index(request_guard), block.index(decision_guard)) + self.assertLess(block.index(decision_guard), block.index(claims)) + + +if __name__ == "__main__": + unittest.main() diff --git a/.github/scripts/test_v0_2_0_release_approval_request.py b/.github/scripts/test_v0_2_0_release_approval_request.py new file mode 100644 index 0000000..4334a86 --- /dev/null +++ b/.github/scripts/test_v0_2_0_release_approval_request.py @@ -0,0 +1,207 @@ +#!/usr/bin/env python3 +# +# Copyright 2026 The Ethos maintainers +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +from __future__ import annotations + +import re +import unittest +from pathlib import Path + +from makefile_guard import target_block +from validation_record_source import assert_record_source_binding + + +ROOT = Path(__file__).resolve().parents[2] +RECORD = ROOT / "docs/validation/v0-2-0-release-approval-request-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" +RELEASE_PREP = ROOT / "docs/v0-2-0-release-prep.md" +MAKEFILE = ROOT / "Makefile" + +SOURCE_SHORT = "fa15fa6" +SOURCE_COMMIT = "fa15fa6f60993e30aa90540526903e4eb72c8252" +SOURCE_TREE = "983f484ff1249ee3ea88da79ebafa9d2cd2410f5" +CURRENT_VERSION = "0.1.2" +TARGET_VERSION = "0.2.0" +CRATES = ("ethos-doc-core", "ethos-verify", "ethos-pdf") +PACKAGE_TAGS = ( + "ethos-package-ethos-doc-core-0.2.0", + "ethos-package-ethos-verify-0.2.0", + "ethos-package-ethos-pdf-0.2.0", +) +REQUIRED_REQUEST_FIELDS = ( + "exact source commit requested", + "version-bump plan requested", + "explicit `ethos-pdf` continuity decision requested", + "Python decision requested", + "npm `@docushell/ethos-pdf` fate requested", + "CLI artifact decision requested", + "Tag and package-tag approval requested", + "ADR-0006/name ownership confirmation requested", + "`reserved_crates_io_version` handling requested", + "crates.io append-only risk accepted for review", + "Operator requested", + "Closeout owner requested", + "Retained Blockers", +) +FORBIDDEN_APPROVALS = ( + "version bump approved", + "release-candidate branch approved", + "cargo publish approved", + "pypi upload approved", + "npm publish approved", + "github release approved", + "tag creation approved", + "installable 0.2.0 wording approved", + "production-ready", + "hosted surfaces approved", + "windows packaged artifacts approved", + "bundled pdfium approved", + "public benchmark claims approved", +) +PRIVATE_PATH_MARKERS = ( + "/" + "Users/", + "/" + "private/tmp", + "/" + "private/var", + "/" + "var/folders", + "saumil" + "diwaker", + "Desktop/" + "Stuff", + "project/repo/" + "ethos", +) + + +def read(path: Path) -> str: + return path.read_text(encoding="utf-8") + + +def normalized(path: Path) -> str: + return re.sub(r"\s+", " ", read(path)) + + +class V020ReleaseApprovalRequestTests(unittest.TestCase): + def test_request_record_is_source_bound_and_indexed(self) -> None: + raw = read(RECORD) + record = normalized(RECORD) + + assert_record_source_binding( + self, + root=ROOT, + raw_record=raw, + normalized_record=record, + validated_head=SOURCE_SHORT, + source_label="v0.2.0 release approval request", + source_commit=SOURCE_COMMIT, + source_tree=SOURCE_TREE, + ) + + for path in (VALIDATION_README, EXECUTION_STATUS, PUBLIC_RELEASE_CHECKLIST): + text = normalized(path) + self.assertIn(RECORD.name, text, str(path)) + self.assertIn("v0.2.0 release approval request", text.lower(), str(path)) + self.assertIn("remain blocked", text, str(path)) + + def test_request_names_every_required_decision_field(self) -> None: + record = normalized(RECORD) + record_lower = record.lower() + + self.assertIn( + "Status: **v0.2.0 release approval request recorded; version bump, package publication, " + "tag creation, artifact publication, and installable wording remain blocked**", + record, + ) + self.assertIn(f"Current source version baseline before approval: `{CURRENT_VERSION}`", record) + self.assertIn(f"Requested target version after approval: `{TARGET_VERSION}`", record) + for required in REQUIRED_REQUEST_FIELDS: + self.assertIn(required.lower(), record_lower) + for crate in CRATES: + self.assertIn(f"`{crate} = {TARGET_VERSION}`", record) + for tag in PACKAGE_TAGS: + self.assertIn(tag, record) + self.assertIn("release tag candidate: `v0.2.0`", record) + + def test_python_npm_cli_and_name_decisions_are_bounded(self) -> None: + record = normalized(RECORD) + + self.assertIn("include Python `ethos-pdf==0.2.0` in public `v0.2.0` only if", record) + self.assertIn("PyPI wheel for `ethos-pdf==0.2.0`", record) + self.assertIn("caller-provided `ethos` CLI binary", record) + self.assertIn("include `@docushell/ethos-pdf@0.2.0` only as a CLI binary distribution package", record) + self.assertIn("does not approve a Node API, Node SDK, N-API binding, or WASM package", record) + self.assertIn("prepare macOS arm64 and Linux x64 GitHub Release CLI artifacts", record) + self.assertIn("Windows packaged artifacts remain blocked", record) + self.assertIn("retain `docushell/ethos` as the canonical source repository", record) + self.assertIn("`ethos-pdf` as the PyPI package/import surface `ethos_pdf`", record) + + def test_reserved_version_append_only_risk_operator_and_blockers_are_explicit(self) -> None: + raw = read(RECORD) + record = normalized(RECORD) + lower = record.lower() + + self.assertIn('`reserved_crates_io_version = "0.0.0-reserved.0"`', record) + self.assertIn("keep `reserved_crates_io_version = \"0.0.0-reserved.0\"` as historical reservation metadata", record) + self.assertIn("The real package version comes from the workspace/package version", record) + self.assertIn("once `0.2.0` is published to crates.io, the exact version cannot be deleted or overwritten", record) + self.assertIn("A bad publish can only be yanked", record) + self.assertIn("Operator requested: `docushell-admin`", record) + self.assertIn("Closeout owner requested: `docushell-admin`", record) + self.assertIn("Explicit decider approval remains required before a release-candidate branch", record) + self.assertIn("Installable `0.2.0` public wording remains blocked", record) + + for forbidden in FORBIDDEN_APPROVALS: + self.assertNotIn(forbidden, lower) + for private in PRIVATE_PATH_MARKERS: + self.assertNotIn(private, raw) + + def test_doc_pilot_boundary_and_release_sequence_match_request(self) -> None: + record = normalized(RECORD) + prep = normalized(RELEASE_PREP) + + for text in (record, prep): + self.assertIn("Evidence-Checked Answers", text) + self.assertIn("internal/design-partner", text) + self.assertIn("claim extraction quality", text) + self.assertIn("non-PDF ingestion", text) + self.assertIn("Only after registry/artifact availability and smoke evidence", prep) + self.assertIn("Only after publication and smoke evidence", record) + + def test_request_packet_does_not_itself_perform_release_actions(self) -> None: + record = normalized(RECORD) + + self.assertIn("This request record does not approve a version bump.", record) + self.assertIn("This request record does not create a release-candidate branch.", record) + self.assertIn("This request record does not approve `cargo publish`.", record) + self.assertIn("This request record does not approve PyPI upload.", record) + self.assertIn("This request record does not approve `npm publish`.", record) + self.assertIn("This request record does not upload CLI artifacts.", record) + self.assertIn("This request record does not approve installable `0.2.0` public wording.", record) + + def test_v0_2_release_prep_runs_request_guard_once_before_claims(self) -> None: + makefile = read(MAKEFILE) + guard = "$(PYTHON) .github/scripts/test_v0_2_0_release_approval_request.py" + claims = "$(PYTHON) .github/scripts/claims_gate.py" + public_claims = "$(PYTHON) .github/scripts/public_boundary_claims_gate.py" + block = target_block("v0-2-release-prep") + + self.assertIn(guard, block) + self.assertEqual(1, makefile.count(guard)) + self.assertLess(block.index(guard), block.index(claims)) + self.assertLess(block.index(claims), block.index(public_claims)) + + +if __name__ == "__main__": + unittest.main() diff --git a/.github/scripts/test_v0_2_0_version_activation.py b/.github/scripts/test_v0_2_0_version_activation.py new file mode 100644 index 0000000..7b53c13 --- /dev/null +++ b/.github/scripts/test_v0_2_0_version_activation.py @@ -0,0 +1,197 @@ +#!/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 unittest +from pathlib import Path + +from makefile_guard import target_block +from validation_record_source import assert_record_source_binding + + +ROOT = Path(__file__).resolve().parents[2] +RECORD = ROOT / "docs/validation/v0-2-0-version-activation-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" +README = ROOT / "README.md" +CLAIMS = ROOT / "docs/public-boundary-claims.json" +INSTALL_WORDING_SURFACES = ( + ROOT / "README.md", + ROOT / "python/README.md", + ROOT / "python/QUICKSTART.md", + ROOT / "packages/npm/ethos-pdf/README.md", + ROOT / "packages/npm/ethos-pdf/QUICKSTART.md", + ROOT / "crates/ethos-core/README.md", + ROOT / "crates/ethos-verify/README.md", + ROOT / "crates/ethos-pdf/README.md", + ROOT / "adapters/grounding/opendataloader-json/README.md", +) +CARGO = ROOT / "Cargo.toml" +CARGO_LOCK = ROOT / "Cargo.lock" +CLI_CARGO = ROOT / "crates/ethos-cli/Cargo.toml" +PYPROJECT = ROOT / "pyproject.toml" +PYTHON_INIT = ROOT / "python/ethos_pdf/__init__.py" +NPM_PACKAGE = ROOT / "packages/npm/ethos-pdf/package.json" + +SOURCE_SHORT = "523e114" +SOURCE_COMMIT = "523e1143bec52e16e596593f5dd649df741b4971" +SOURCE_TREE = "8f13de3588a36927635a967cf120fba8f73a39f6" +VERSION = "0.2.0" +PUBLIC_BASELINE = "0.1.2" +RELEASE_CANDIDATE_SENTENCE = ( + "v0.2.0 release-candidate source versions are activated for JSON verification and evidence " + "anchoring." +) +FORBIDDEN_INSTALL_WORDING = ( + "cargo add ethos-doc-core@0.2.0", + "cargo add ethos-verify@0.2.0", + "cargo add ethos-pdf@0.2.0", + "python3 -m pip install ethos-pdf==0.2.0", + "npm install -g @docushell/ethos-pdf@0.2.0", + "0.2.0 is released", + "v0.2.0 is released", + "0.2.0 is published", + "v0.2.0 is published", +) +PRIVATE_PATH_MARKERS = ( + "/" + "Users/", + "/" + "private/tmp", + "/" + "private/var", + "/" + "var/folders", + "saumil" + "diwaker", + "Desktop/" + "Stuff", + "project/repo/" + "ethos", +) + + +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 normalized_markdown(path: Path) -> str: + return re.sub( + r"\s+", + " ", + " ".join(line.removeprefix("> ").strip() for line in read(path).splitlines()), + ) + + +class V020VersionActivationTests(unittest.TestCase): + def test_record_is_source_bound_and_indexed(self) -> None: + raw = read(RECORD) + record = normalized(RECORD) + + assert_record_source_binding( + self, + root=ROOT, + raw_record=raw, + normalized_record=record, + validated_head=SOURCE_SHORT, + source_label="v0.2.0 version activation", + source_commit=SOURCE_COMMIT, + source_tree=SOURCE_TREE, + ) + + for path in (VALIDATION_README, EXECUTION_STATUS, PUBLIC_RELEASE_CHECKLIST): + text = normalized(path) + self.assertIn(RECORD.name, text, str(path)) + self.assertIn("v0.2.0 version activation", text.lower(), str(path)) + self.assertIn("remain blocked", text, str(path)) + + def test_rust_python_and_npm_versions_are_activated(self) -> None: + cargo = read(CARGO) + cli = read(CLI_CARGO) + lock = read(CARGO_LOCK) + npm = json.loads(read(NPM_PACKAGE)) + + self.assertIn(f'version = "{VERSION}"', cargo) + self.assertIn(f'ethos-core = {{ package = "ethos-doc-core", path = "crates/ethos-core", version = "{VERSION}"', cargo) + self.assertIn(f'ethos-layout = {{ path = "crates/ethos-layout", version = "{VERSION}" }}', cargo) + self.assertIn(f'ethos-tables = {{ path = "crates/ethos-tables", version = "{VERSION}" }}', cargo) + self.assertIn(f'ethos-pdf = {{ path = "../ethos-pdf", version = "{VERSION}" }}', cli) + self.assertIn(f'ethos-verify = {{ path = "../ethos-verify", version = "{VERSION}" }}', cli) + self.assertIn( + f'ethos-grounding-opendataloader-json = {{ path = "../../adapters/grounding/opendataloader-json", version = "{VERSION}" }}', + cli, + ) + self.assertGreaterEqual(lock.count(f'version = "{VERSION}"'), 7) + self.assertIn(f'version = "{VERSION}"', read(PYPROJECT)) + self.assertIn(f'__version__ = "{VERSION}"', read(PYTHON_INIT)) + self.assertEqual(VERSION, npm["version"]) + + def test_public_install_commands_remain_on_approved_baseline(self) -> None: + readme = read(README) + claims = json.loads(read(CLAIMS))["surfaces"]["readme"]["claims"] + joined_claims = "\n".join(claims) + + for expected in ( + f"cargo add ethos-doc-core@{PUBLIC_BASELINE}", + f"cargo add ethos-verify@{PUBLIC_BASELINE}", + f"cargo add ethos-pdf@{PUBLIC_BASELINE}", + f"python3 -m pip install ethos-pdf=={PUBLIC_BASELINE}", + f"npm install -g @docushell/ethos-pdf@{PUBLIC_BASELINE}", + ): + self.assertIn(expected, readme) + self.assertIn(expected, joined_claims) + + self.assertIn(RELEASE_CANDIDATE_SENTENCE, normalized_markdown(README)) + self.assertIn(RELEASE_CANDIDATE_SENTENCE, joined_claims) + for forbidden in FORBIDDEN_INSTALL_WORDING: + self.assertNotIn(forbidden, readme) + self.assertNotIn(forbidden, joined_claims) + for path in INSTALL_WORDING_SURFACES: + self.assertNotIn(forbidden, read(path), str(path)) + + def test_boundaries_remain_closed(self) -> None: + raw = read(RECORD) + record = normalized(RECORD) + + for phrase in ( + "does not approve a release", + "does not approve a tag", + "does not approve package publish", + "does not approve npm publish", + "does not approve PyPI publish", + "does not approve crates.io publish", + "does not approve a GitHub Release artifact", + "does not approve public installation wording for `0.2.0`", + "does not approve hosted surfaces", + "does not approve production positioning", + "does not approve Windows packaged artifacts", + "does not approve bundled project-maintained PDFium builds", + "does not approve public benchmark claims", + "does not approve `ethos-doc`", + "does not approve `ethos-rag`", + ): + self.assertIn(phrase, record) + for private in PRIVATE_PATH_MARKERS: + self.assertNotIn(private, raw) + + def test_v0_2_release_prep_runs_activation_guard_after_decision_guard(self) -> None: + makefile = read(MAKEFILE) + decision_guard = "$(PYTHON) .github/scripts/test_v0_2_0_release_approval_decision.py" + activation_guard = "$(PYTHON) .github/scripts/test_v0_2_0_version_activation.py" + claims = "$(PYTHON) .github/scripts/claims_gate.py" + block = target_block("v0-2-release-prep") + + self.assertIn(activation_guard, block) + self.assertEqual(1, makefile.count(activation_guard)) + self.assertLess(block.index(decision_guard), block.index(activation_guard)) + self.assertLess(block.index(activation_guard), block.index(claims)) + + +if __name__ == "__main__": + unittest.main() diff --git a/.github/scripts/test_validation_record_source.py b/.github/scripts/test_validation_record_source.py new file mode 100644 index 0000000..079ae2e --- /dev/null +++ b/.github/scripts/test_validation_record_source.py @@ -0,0 +1,93 @@ +#!/usr/bin/env python3 +# +# Copyright 2026 The Ethos maintainers +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# + +from __future__ import annotations + +import subprocess +import tempfile +import unittest +from pathlib import Path + +from validation_record_source import assert_record_source_binding + + +ROOT = Path(__file__).resolve().parents[2] + + +def git(*args: str) -> str: + return subprocess.check_output( + ["git", *args], + cwd=ROOT, + encoding="utf-8", + stderr=subprocess.DEVNULL, + ).strip() + + +class ValidationRecordSourceTests(unittest.TestCase): + def test_unreachable_commit_binding_is_self_contained(self) -> None: + commit = "a" * 40 + tree = "b" * 40 + raw = f"Validated source HEAD before this record: `{commit[:7]}`" + normalized = ( + f"example validation source commit: `{commit}` " + f"example validation source tree: `{tree}`" + ) + with tempfile.TemporaryDirectory() as temp: + assert_record_source_binding( + self, + root=Path(temp), + raw_record=raw, + normalized_record=normalized, + validated_head=commit[:7], + source_label="example validation", + source_commit=commit, + source_tree=tree, + ) + + def test_reachable_commit_tree_is_verified(self) -> None: + commit = git("rev-parse", "HEAD") + tree = git("rev-parse", "HEAD^{tree}") + raw = f"Validated source HEAD before this record: `{commit[:7]}`" + normalized = ( + f"reachable validation source commit: `{commit}` " + f"reachable validation source tree: `{tree}`" + ) + + assert_record_source_binding( + self, + root=ROOT, + raw_record=raw, + normalized_record=normalized, + validated_head=commit[:7], + source_label="reachable validation", + source_commit=commit, + source_tree=tree, + ) + + def test_reachable_commit_tree_mismatch_fails(self) -> None: + commit = git("rev-parse", "HEAD") + raw = f"Validated source HEAD before this record: `{commit[:7]}`" + normalized = ( + f"reachable validation source commit: `{commit}` " + f"reachable validation source tree: `{'c' * 40}`" + ) + + with self.assertRaises(AssertionError): + assert_record_source_binding( + self, + root=ROOT, + raw_record=raw, + normalized_record=normalized, + validated_head=commit[:7], + source_label="reachable validation", + source_commit=commit, + source_tree="c" * 40, + ) + + +if __name__ == "__main__": + unittest.main() diff --git a/.github/scripts/validation_record_source.py b/.github/scripts/validation_record_source.py new file mode 100644 index 0000000..c0bd62f --- /dev/null +++ b/.github/scripts/validation_record_source.py @@ -0,0 +1,72 @@ +# +# Copyright 2026 The Ethos maintainers +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# + +from __future__ import annotations + +import re +import subprocess +from pathlib import Path +from typing import Protocol + + +class _Asserts(Protocol): + def assertEqual(self, first: object, second: object, msg: object = ...) -> None: ... + def assertIn(self, member: object, container: object, msg: object = ...) -> None: ... + def assertRegex(self, text: str, expected_regex: str, msg: object = ...) -> None: ... + def assertTrue(self, expr: object, msg: object = ...) -> None: ... + + +_SHA1 = r"[0-9a-f]{40}" + + +def _git(root: Path, *args: str) -> subprocess.CompletedProcess[str]: + return subprocess.run( + ["git", *args], + cwd=root, + encoding="utf-8", + stderr=subprocess.DEVNULL, + stdout=subprocess.PIPE, + check=False, + ) + + +def _commit_exists(root: Path, commit: str) -> bool: + return _git(root, "cat-file", "-e", f"{commit}^{{commit}}").returncode == 0 + + +def assert_record_source_binding( + case: _Asserts, + *, + root: Path, + raw_record: str, + normalized_record: str, + validated_head: str, + source_label: str, + source_commit: str, + source_tree: str, +) -> None: + """Validate a release record's source binding without requiring commit reachability. + + Validation records need to survive squash merges and fresh main checkouts. The record itself + carries the immutable commit and tree values; when that commit object is available locally, this + helper also verifies the tree. If the object is not available, the self-contained record binding + remains enforceable instead of making CI depend on branch-history preservation. + """ + + case.assertRegex(source_commit, f"^{_SHA1}$") + case.assertRegex(source_tree, f"^{_SHA1}$") + case.assertTrue( + source_commit.startswith(validated_head), + f"{validated_head!r} must be a prefix of {source_commit!r}", + ) + case.assertIn(f"Validated source HEAD before this record: `{validated_head}`", raw_record) + case.assertIn(f"{source_label} source commit: `{source_commit}`", normalized_record) + case.assertIn(f"{source_label} source tree: `{source_tree}`", normalized_record) + + if _commit_exists(root, source_commit): + tree = _git(root, "rev-parse", f"{source_commit}^{{tree}}") + case.assertEqual(0, tree.returncode) + case.assertEqual(source_tree, tree.stdout.strip()) diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 5d69b58..1f8e7a0 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -65,7 +65,7 @@ jobs: run: | python3 .github/scripts/smoke_release_cli_artifact.py \ --artifact-dir "target/release-artifacts/ethos-${{ matrix.artifact_target }}" \ - --expected-version "ethos 0.1.2" \ + --expected-version "ethos 0.2.0" \ --target "${{ matrix.artifact_target }}" \ --out "target/release-artifacts/ethos-${{ matrix.artifact_target }}.smoke.json" - name: validate draft artifact inventory diff --git a/CHANGELOG.md b/CHANGELOG.md index a9135af..b269e56 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,23 @@ ## Unreleased +- boundary-exception: record passing `ethos-doc-core 0.2.0` locked cargo publish dry-run evidence + while keeping actual `cargo publish`, dependent-crate dry-runs, PyPI upload, npm publish, GitHub + Release artifact publication, release/package tags, installable `0.2.0` wording, hosted, + production, Windows, bundled PDFium, benchmark, `ethos-doc`, and `ethos-rag` surfaces blocked. +- boundary-exception: record v0.2.0 package/build evidence for the Python wheel, local macOS arm64 + draft CLI artifact, and npm package metadata while keeping npm artifact candidacy blocked on + stale vendored `ethos 0.1.2` binaries and keeping publication, artifact upload, tags, installable + `0.2.0` wording, hosted, production, Windows, bundled PDFium, benchmark, `ethos-doc`, and + `ethos-rag` surfaces blocked. +- boundary-exception: activate v0.2.0 release-candidate source versions for Rust, Python, and npm + package metadata while keeping `cargo publish`, PyPI upload, npm publish, GitHub Release + artifact publication, release/package tags, installable `0.2.0` wording, hosted, production, + Windows, bundled PDFium, benchmark, `ethos-doc`, and `ethos-rag` surfaces blocked. +- boundary-exception: record decider approval for v0.2.0 release-candidate activation while + keeping package publication, artifact publication, tag creation, installable `0.2.0` wording, + hosted, production, Windows, bundled PDFium, benchmark, `ethos-doc`, and `ethos-rag` surfaces + blocked pending separate evidence records. - boundary-exception: add v0.2.0 source-preparation docs and gates for JSON verification and evidence anchoring, the v0.2.x compatibility policy, and the bring-your-own-parser tutorial; no `0.2.0` publication, package tag, GitHub Release artifact, or public install wording is approved diff --git a/Cargo.lock b/Cargo.lock index 8eab9c2..f574da7 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -193,7 +193,7 @@ dependencies = [ [[package]] name = "ethos-cli" -version = "0.1.2" +version = "0.2.0" dependencies = [ "clap", "ethos-doc-core", @@ -210,7 +210,7 @@ dependencies = [ [[package]] name = "ethos-doc-core" -version = "0.1.2" +version = "0.2.0" dependencies = [ "proptest", "serde", @@ -221,7 +221,7 @@ dependencies = [ [[package]] name = "ethos-grounding-opendataloader-json" -version = "0.1.2" +version = "0.2.0" dependencies = [ "ethos-doc-core", "serde", @@ -230,14 +230,14 @@ dependencies = [ [[package]] name = "ethos-layout" -version = "0.1.2" +version = "0.2.0" dependencies = [ "ethos-doc-core", ] [[package]] name = "ethos-pdf" -version = "0.1.2" +version = "0.2.0" dependencies = [ "ethos-doc-core", "serde", @@ -246,14 +246,14 @@ dependencies = [ [[package]] name = "ethos-tables" -version = "0.1.2" +version = "0.2.0" dependencies = [ "ethos-doc-core", ] [[package]] name = "ethos-verify" -version = "0.1.2" +version = "0.2.0" dependencies = [ "ethos-doc-core", "serde", diff --git a/Cargo.toml b/Cargo.toml index c9f08da..f33353d 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -15,7 +15,7 @@ members = [ ] [workspace.package] -version = "0.1.2" +version = "0.2.0" edition = "2021" rust-version = "1.87" license = "Apache-2.0" @@ -36,9 +36,9 @@ tempfile = "3" proptest = "1" # internal -ethos-core = { package = "ethos-doc-core", path = "crates/ethos-core", version = "0.1.2", default-features = false } -ethos-layout = { path = "crates/ethos-layout", version = "0.1.2" } -ethos-tables = { path = "crates/ethos-tables", version = "0.1.2" } +ethos-core = { package = "ethos-doc-core", path = "crates/ethos-core", version = "0.2.0", default-features = false } +ethos-layout = { path = "crates/ethos-layout", version = "0.2.0" } +ethos-tables = { path = "crates/ethos-tables", version = "0.2.0" } [profile.release] # Footprint discipline for the G2 gate (≤ 30 MB installed): strip + LTO + size-lean codegen. diff --git a/Makefile b/Makefile index ad902f9..407d554 100644 --- a/Makefile +++ b/Makefile @@ -343,7 +343,15 @@ release-candidate-prep: v0-2-release-prep: cargo test --locked --workspace $(MAKE) python-surface-test PYTHON=$(PYTHON) + $(PYTHON) .github/scripts/test_python_public_api_policy.py $(PYTHON) schemas/validate_examples.py + $(PYTHON) .github/scripts/test_validation_record_source.py + $(PYTHON) .github/scripts/test_v0_2_0_release_approval_request.py + $(PYTHON) .github/scripts/test_v0_2_0_release_approval_decision.py + $(PYTHON) .github/scripts/test_v0_2_0_version_activation.py + $(PYTHON) .github/scripts/test_v0_2_0_ethos_doc_core_cargo_publish_dry_run_evidence.py + $(PYTHON) .github/scripts/test_v0_2_0_package_build_evidence.py + $(PYTHON) .github/scripts/test_v0_2_0_draft_artifact_evidence.py $(PYTHON) .github/scripts/claims_gate.py $(PYTHON) .github/scripts/public_boundary_claims_gate.py git diff --check diff --git a/README.md b/README.md index c840d32..9d6a16a 100644 --- a/README.md +++ b/README.md @@ -15,9 +15,9 @@ > `@docushell/ethos-pdf@0.1.2` package, and GitHub Release `v0.1.2` macOS arm64/Linux x64 CLI > artifacts. PDFium-backed commands use caller-provided PDFium through > `ETHOS_PDFIUM_LIBRARY_PATH`. -> v0.2.0 is being prepared to publish registry install surfaces for JSON verification and -> evidence anchoring. No `0.2.0` registry install wording is approved until publication approval, -> registry availability, and clean smoke tests are recorded. +> v0.2.0 release-candidate source versions are activated for JSON verification and evidence +> anchoring. No `0.2.0` registry install wording is approved until publication, registry +> availability, artifact availability, and clean smoke tests are recorded. > Current execution status and release-scope notes live in `docs/execution-status.md`; > public-release hygiene gates live in `docs/public-release-checklist.md`. @@ -140,7 +140,7 @@ The Python wheel is a thin wrapper around a caller-provided local `ethos` CLI bi bundle the CLI or PDFium. Install or provide `ethos` separately, and keep `ETHOS_PDFIUM_LIBRARY_PATH` set for PDFium-backed commands. -The v0.2.0 preparation lane adds Python wrapper methods for JSON verification and evidence +The v0.2.0 release-candidate lane adds Python wrapper methods for JSON verification and evidence anchoring through that caller-provided CLI: ```python diff --git a/adapters/grounding/README.md b/adapters/grounding/README.md index 500f5c7..411e8a7 100644 --- a/adapters/grounding/README.md +++ b/adapters/grounding/README.md @@ -16,7 +16,7 @@ mapping (same input bytes, same output, same order); depend only on `ethos-core` `grounding` feature, never on parser internals; ship fixtures with every mapping change (request template: `.github/ISSUE_TEMPLATE/adapter_request.yml`). -For v0.2.0 preparation, OpenDataLoader remains the full reference adapter, not the minimum +For the v0.2.0 release-candidate lane, OpenDataLoader remains the full reference adapter, not the minimum onboarding example. It validates the documented subset strictly, downgrades unsupported capabilities explicitly, bounds element/table/cell boxes to page dimensions, rejects negative origins, and accepts exact page boundaries. New parser authors should start with diff --git a/crates/ethos-cli/Cargo.toml b/crates/ethos-cli/Cargo.toml index 284c108..301ea48 100644 --- a/crates/ethos-cli/Cargo.toml +++ b/crates/ethos-cli/Cargo.toml @@ -17,9 +17,9 @@ path = "src/main.rs" ethos-core = { workspace = true, features = ["full", "crop-element"] } ethos-layout = { workspace = true } ethos-tables = { workspace = true } -ethos-pdf = { path = "../ethos-pdf", version = "0.1.2" } -ethos-verify = { path = "../ethos-verify", version = "0.1.2" } -ethos-grounding-opendataloader-json = { path = "../../adapters/grounding/opendataloader-json", version = "0.1.2" } +ethos-pdf = { path = "../ethos-pdf", version = "0.2.0" } +ethos-verify = { path = "../ethos-verify", version = "0.2.0" } +ethos-grounding-opendataloader-json = { path = "../../adapters/grounding/opendataloader-json", version = "0.2.0" } clap = { workspace = true } serde = { workspace = true } serde_json = { workspace = true } diff --git a/crates/ethos-core/README.md b/crates/ethos-core/README.md index 22e17ba..354f486 100644 --- a/crates/ethos-core/README.md +++ b/crates/ethos-core/README.md @@ -8,7 +8,7 @@ ADR-0006 reserves the public crates.io identifier `ethos-doc-core` at `0.0.0-reserved.0`. The Rust library name remains `ethos_core` so existing source imports keep the same crate path while registry action remains blocked. -For the v0.2.0 preparation lane, parser authors should treat this as the Rust package for +For the v0.2.0 release-candidate lane, parser authors should treat this as the Rust package for `GroundingSource` implementations: ```toml @@ -32,8 +32,8 @@ library crate is `ethos_core`. - This README supports package-publication activation review only. - Source-only pre-alpha crop descriptor APIs are not part of the default public surface. They require the explicit Cargo feature `crop-element`, which exists for in-tree CLI contract work. -- v0.2.0 registry install wording remains in preparation status until publication approval, - registry availability, and clean smoke tests are recorded. +- v0.2.0 registry install wording remains in release-candidate status until publication, registry + availability, and clean smoke tests are recorded. ## Metadata Notes diff --git a/crates/ethos-pdf/README.md b/crates/ethos-pdf/README.md index ab40c47..7ae3a3f 100644 --- a/crates/ethos-pdf/README.md +++ b/crates/ethos-pdf/README.md @@ -5,7 +5,7 @@ traits. It normalizes parser output before data crosses the crate boundary. ADR-0006 reserves the public crates.io identifier `ethos-pdf` at `0.0.0-reserved.0`. -For the v0.2.0 preparation lane, this crate is a continuity crate for the lockstep Rust workspace. +For the v0.2.0 release-candidate lane, this crate is a continuity crate for the lockstep Rust workspace. The release headline remains JSON verification and evidence anchoring over caller-provided source evidence. `ethos-pdf` remains the parser/PDFium-facing crate. @@ -17,8 +17,8 @@ evidence. `ethos-pdf` remains the parser/PDFium-facing crate. - The reserved crates.io placeholder remains `0.0.0-reserved.0` until registry action is explicitly recorded. - This README supports package-publication activation review only. -- v0.2.0 registry install wording remains in preparation status until publication approval, - registry availability, and clean smoke tests are recorded. +- v0.2.0 registry install wording remains in release-candidate status until publication, registry + availability, and clean smoke tests are recorded. ## PDFium Boundary diff --git a/crates/ethos-verify/README.md b/crates/ethos-verify/README.md index 6ed74d5..6247a4d 100644 --- a/crates/ethos-verify/README.md +++ b/crates/ethos-verify/README.md @@ -6,7 +6,7 @@ verification limited to the current source-tree evidence-grounding contract. ADR-0006 reserves the public crates.io identifier `ethos-verify` at `0.0.0-reserved.0`. -For the v0.2.0 preparation lane, verification consumers should treat this as the Rust package for +For the v0.2.0 release-candidate lane, verification consumers should treat this as the Rust package for running checks over caller-provided `GroundingSource` evidence: ```toml @@ -29,8 +29,8 @@ unverifiable source fingerprints fail closed in the report. - The reserved crates.io placeholder remains `0.0.0-reserved.0` until registry action is explicitly recorded. - This README supports package-publication activation review only. -- v0.2.0 registry install wording remains in preparation status until publication approval, - registry availability, and clean smoke tests are recorded. +- v0.2.0 registry install wording remains in release-candidate status until publication, registry + availability, and clean smoke tests are recorded. ## Metadata Notes diff --git a/docs/bring-your-own-parser.md b/docs/bring-your-own-parser.md index 0e70e7a..b78fa72 100644 --- a/docs/bring-your-own-parser.md +++ b/docs/bring-your-own-parser.md @@ -1,6 +1,6 @@ # Bring Your Own Parser -Status: v0.2.0 preparation tutorial for parser authors. This document shows the intended trust-layer +Status: v0.2.0 release-candidate tutorial for parser authors. This document shows the intended trust-layer integration shape before any `0.2.0` publication claim is made. The integration model is: @@ -153,4 +153,3 @@ Parser adapters should: The OpenDataLoader JSON adapter remains the full reference adapter. It is useful for serious foreign-parser mapping, but the minimal integration surface is the `GroundingSource` trait above. - diff --git a/docs/execution-status.md b/docs/execution-status.md index b7ab48c..51b85dc 100644 --- a/docs/execution-status.md +++ b/docs/execution-status.md @@ -4,12 +4,61 @@ Date: 2026-06-25 Owner: product / decider Status: Public beta evaluation is approved for the GitHub source repository; the three bounded Rust library crates `ethos-doc-core`, `ethos-verify`, and `ethos-pdf` at `0.1.2`; the Python `ethos-pdf` wheel at `0.1.2`; the npm `@docushell/ethos-pdf` CLI package at `0.1.2`; and the GitHub Release `v0.1.2` macOS arm64 and Linux x64 CLI artifacts. Internal Milestone D source-only closeout remains complete, with Milestone E prep source-only closeout recorded for the internal prep boundary. Week 0 governance is accepted, WS-ENGINE Phase 1 has a real narrow PDFium path, WS-VERIFY-ALPHA has real deterministic evidence checks over native Ethos JSON and pinned OpenDataLoader output, WS-HARNESS has fail-closed readiness scaffolding, the Gate Zero corpus/hardware manifest and direct competitor lock are frozen/signed, ADR-0005 records an accepted `PROCEED` decision for internal Milestone B continuation, ADR-0006 closes package identifier/trademark validation, ADR-0007 locks the product direction, and patch `0.1.1` plus patch `0.1.2` publication/install wording closeouts are recorded for the approved evaluation surfaces. The exact current public sentence approved for source, Rust crate, Python wheel, npm package, macOS arm64 CLI artifact, and Linux x64 CLI artifact evaluation surfaces is: "Ethos is a deterministic document evidence layer for source-grounded verification and citation checking across native Ethos JSON and supported foreign parser outputs. The current beta includes the GitHub source repository, Rust library crates `ethos-doc-core`, `ethos-verify`, and `ethos-pdf` at `0.1.2`, the Python `ethos-pdf` wheel at `0.1.2`, the npm `@docushell/ethos-pdf@0.1.2` package, and GitHub Release `v0.1.2` macOS arm64/Linux x64 CLI artifacts. PDFium-backed commands use caller-provided PDFium through `ETHOS_PDFIUM_LIBRARY_PATH`." Milestone C has a source-tree internal artifact-validation closeout for the RAG chunk and security-report trust-loop checks. Milestone D has a source-tree internal source-only closeout recorded in `docs/validation/milestone-d-final-closeout-validation-2026-06-19.md`; the narrow `verify_citations` v1 contract in `docs/milestone-d-verify-citations-contract.md` remains carried by the existing `ethos verify` path and fixture-backed validation. The D `crop_element` v1 contract in `docs/milestone-d-crop-element-contract.md` is carried by the source-bound `ethos crop_element` CLI command plus existing `ethos verify --crop-dir` evidence artifacts; `ethos-core::crop_element` validates request identity, resolves one native document element, and emits descriptor/rendered crop metadata for that source-only contract when caller-provided source PDF bytes are bound. The `sandbox_subprocess` v1 contract in `docs/milestone-d-sandbox-subprocess-contract.md` classifies existing PDF worker-process timeout, memory-limit, stable-error, and diagnostics-gated stderr behavior without adding hardened sandbox rules. The first Milestone E prep boundary is recorded in `docs/milestone-e-prep-scope.md`, the internal fixture-candidate inventory is recorded in `docs/milestone-e-fixture-candidates.json`, internal fixture-promotion criteria are recorded in `docs/milestone-e-fixture-promotion-criteria.json`, the internal trust-loop walkthrough plan is recorded in `docs/milestone-e-internal-trust-loop-walkthrough.json`, the internal trust-loop use protocol is recorded in `docs/milestone-e-internal-trust-loop-use-protocol.json`, the internal trust-loop rehearsal/evidence matrix is recorded in `docs/milestone-e-internal-trust-loop-rehearsal-evidence-matrix.json`, and the internal trust-loop blocker ledger is recorded in `docs/milestone-e-internal-trust-loop-blocker-ledger.json`; these E prep JSON artifacts are schema-validated by `schemas/validate_examples.py` and only identify tracked trust-loop fixture candidates, internal promotion criteria, internal walkthrough sequencing, source-checkout rules for internal use, internal evidence-lane rehearsal planning, blocked-output alignment, evidence-lane alignment, diagnostic-boundary alignment, promotion-status alignment at `not_promoted_beyond_internal_fixture_planning`, source-status alignment at `source-only-pre-alpha-internal-milestone-e-prep`, applies-to binding alignment across current E source artifacts, required-before alignment for current readiness gates including `make milestone-e-prep remains green`, validation-record source-head alignment for each `Validated source HEAD before this record` line, and explicit blocker tracking that does not resolve or soften blockers. The Milestone E prep source-only closeout is recorded in `docs/validation/milestone-e-final-closeout-validation-2026-06-20.md` and does not resolve or soften blockers outside the approved public beta evaluation surfaces. Hosted surfaces, production positioning, Windows packaged artifacts, bundled project-maintained PDFium builds, `ethos-doc`, `ethos-rag`, public benchmark reports, public benchmark claims, and all speed/footprint/parser-quality/table-quality/production claims remain blocked. The controlled-run handoff remains `docs/gate-zero-evidence-runbook.md`; the accepted decision record is `docs/decisions/ADR-0005-gate-zero-decision.md`. -v0.2.0 source preparation is recorded in `docs/v0-2-0-release-prep.md`. The preparation lane adds -the bounded public sentence "v0.2.0 is being prepared to publish registry install surfaces for -JSON verification and evidence anchoring" while keeping `0.2.0` publication, package tags, GitHub -Release artifacts, PyPI upload, public install wording, hosted surfaces, production positioning, -public benchmark claims, Windows packaged artifacts, and bundled project-maintained PDFium blocked -until separate approval, operator evidence, and closeout records pass. +v0.2.0 release-candidate activation is recorded in `docs/v0-2-0-release-prep.md`. The +release-candidate lane adds the bounded public sentence "v0.2.0 release-candidate source versions +are activated for JSON verification and evidence anchoring" while keeping `0.2.0` publication, +package tags, GitHub Release artifacts, PyPI upload, public install wording, hosted surfaces, +production positioning, public benchmark claims, Windows packaged artifacts, and bundled +project-maintained PDFium blocked until separate approval, operator evidence, and closeout records +pass. + +v0.2.0 release approval request is recorded in +`docs/validation/v0-2-0-release-approval-request-validation-2026-06-25.md` for decider review +only. It requests exact review of the source commit, version-bump plan, `ethos-doc-core`, +`ethos-verify`, and `ethos-pdf` crate set, `ethos-pdf` continuity decision, Python scope, npm +fate, CLI artifact decision, tag/package-tag request, ADR-0006/name ownership confirmation, +`reserved_crates_io_version` handling, crates.io append-only risk, operator and closeout owner, +and retained blockers. Version bump, release-candidate branch creation, package publication, tag +creation, artifact publication, and installable `0.2.0` wording remain blocked until explicit +approval and later evidence records pass. + +v0.2.0 release approval decision is recorded in +`docs/validation/v0-2-0-release-approval-decision-validation-2026-06-25.md`. It accepts +release-candidate version activation on `dev/v0-2-approval-packet` for the exact `0.2.0` Rust +crate set, Python wheel scope, npm CLI package scope, CLI artifact preparation, `CHANGELOG.md`, +and release-candidate wording only. `cargo publish`, PyPI upload, `npm publish`, GitHub Release +artifact upload, release tag creation, package tag creation, and installable `0.2.0` public +wording remain blocked until separate evidence records and operator decisions pass. + +v0.2.0 version activation is recorded in +`docs/validation/v0-2-0-version-activation-validation-2026-06-25.md`. Rust workspace/package +dependency versions, Python package metadata and `ethos_pdf.__version__`, npm package metadata, +and release-candidate wording now point to `0.2.0` for candidate validation. Public install +commands and installable wording remain on the approved `0.1.2` evaluation baseline until +publication, registry/artifact availability, smoke evidence, and wording closeout records pass. + +v0.2.0 `ethos-doc-core` dry-run evidence is recorded in +`docs/validation/v0-2-0-ethos-doc-core-cargo-publish-dry-run-evidence-validation-2026-06-25.md`. +The first Rust registry dry-run gate passed for `ethos-doc-core 0.2.0` and recorded the generated +crate SHA256 +`de86ce74dd791b50d0722cddc878756cceabae2162f747e9e24902b88e5c7de1`. Actual `cargo publish`, +dependent-crate dry-runs, tag creation, artifact publication, and installable `0.2.0` wording +remain blocked until separate evidence and operator decisions pass. + +v0.2.0 package/build evidence is recorded in +`docs/validation/v0-2-0-package-build-evidence-validation-2026-06-25.md`. Python wheel local build, +install, and wrapper smoke passed; the local macOS arm64 draft CLI artifact smoke reported +`ethos 0.2.0`; npm tests and dry-run package metadata passed for `@docushell/ethos-pdf@0.2.0`. +npm v0.2.0 artifact candidacy remains blocked because the checked-in vendored binaries still +report `ethos 0.1.2`. Linux x64 CLI artifact evidence, npm vendor refresh, publication, tag +creation, artifact upload, and installable `0.2.0` wording remain blocked. + +v0.2.0 draft artifact evidence is recorded in +`docs/validation/v0-2-0-draft-artifact-evidence-validation-2026-06-25.md`. The release workflow +passed on `dev/v0-2-approval-packet` and produced macOS arm64 plus Linux x64 draft CLI artifacts +whose smoke sidecars report `ethos 0.2.0`. Artifact publication, npm vendor refresh, registry +publication, tag creation, and installable `0.2.0` wording remain blocked until separate records +and operator decisions pass. Older Milestone E paragraphs below preserve historical review records and their blockers at the time they were written. Patch `0.1.1` closeout records supersede those historical blockers only for the approved source, Rust crate, Python wheel, npm package, macOS arm64 CLI artifact, and Linux x64 CLI artifact evaluation surfaces. diff --git a/docs/public-boundary-claims.json b/docs/public-boundary-claims.json index 897618e..ddcc333 100644 --- a/docs/public-boundary-claims.json +++ b/docs/public-boundary-claims.json @@ -7,8 +7,8 @@ "Ethos is a deterministic document evidence layer for source-grounded verification and citation checking across native Ethos JSON and supported foreign parser outputs.", "The current beta includes the GitHub source repository, Rust library crates `ethos-doc-core`, `ethos-verify`, and `ethos-pdf` at `0.1.2`, the Python `ethos-pdf` wheel at `0.1.2`, the npm `@docushell/ethos-pdf@0.1.2` package, and GitHub Release `v0.1.2` macOS arm64/Linux x64 CLI artifacts.", "PDFium-backed commands use caller-provided PDFium through `ETHOS_PDFIUM_LIBRARY_PATH`.", - "v0.2.0 is being prepared to publish registry install surfaces for JSON verification and evidence anchoring.", - "No `0.2.0` registry install wording is approved until publication approval, registry availability, and clean smoke tests are recorded.", + "v0.2.0 release-candidate source versions are activated for JSON verification and evidence anchoring.", + "No `0.2.0` registry install wording is approved until publication, registry availability, artifact availability, and clean smoke tests are recorded.", "PDFium-backed commands require caller-provided PDFium through `ETHOS_PDFIUM_LIBRARY_PATH`.", "cargo add ethos-doc-core@0.1.2", "cargo add ethos-verify@0.1.2", diff --git a/docs/public-release-checklist.md b/docs/public-release-checklist.md index 88300fe..2e4c375 100644 --- a/docs/public-release-checklist.md +++ b/docs/public-release-checklist.md @@ -15,6 +15,48 @@ production positioning, Windows packaged artifacts, bundled project-maintained P public benchmark reports, public benchmark claims, speed, footprint, parser-quality, table-quality, `ethos-doc`, and `ethos-rag` remain blocked. +v0.2.0 release approval request is recorded in +`docs/validation/v0-2-0-release-approval-request-validation-2026-06-25.md` for decider review +only. It does not approve version bump, release-candidate branch creation, package publication, +tag creation, artifact publication, or installable `0.2.0` wording. The request keeps Python +public scope conditional on a PyPI wheel, matching `v0.2.0` CLI artifacts, and naming docs for +the historical `ethos-pdf` package. The request keeps npm scoped to a CLI binary distribution +decision, not a Node SDK/API claim. + +v0.2.0 release approval decision is recorded in +`docs/validation/v0-2-0-release-approval-decision-validation-2026-06-25.md`. It accepts +release-candidate version activation on the current branch for Rust, Python, npm, `CHANGELOG.md`, +and release-candidate wording only. It does not approve package publication, tag creation, +artifact publication, installable `0.2.0` wording, hosted surfaces, production positioning, +Windows packaged artifacts, bundled project-maintained PDFium builds, `ethos-doc`, `ethos-rag`, +or public benchmark claims. + +v0.2.0 version activation is recorded in +`docs/validation/v0-2-0-version-activation-validation-2026-06-25.md`. It moves Rust, Python, and +npm source/package metadata to `0.2.0` for release-candidate validation only. Public install +commands and installable wording remain on the approved `0.1.2` evaluation baseline until +publication, registry/artifact availability, smoke evidence, and wording closeout records pass. + +v0.2.0 `ethos-doc-core` dry-run evidence is recorded in +`docs/validation/v0-2-0-ethos-doc-core-cargo-publish-dry-run-evidence-validation-2026-06-25.md`. +It records a passing `cargo publish --dry-run --locked -p ethos-doc-core` for +`ethos-doc-core 0.2.0`; no publication, tag, artifact upload, dependent-crate dry-run, or +installable `0.2.0` wording is approved by that record. + +v0.2.0 package/build evidence is recorded in +`docs/validation/v0-2-0-package-build-evidence-validation-2026-06-25.md`. It records local package +build checks for the Python wheel, macOS arm64 draft CLI artifact, and npm package metadata. The +npm candidate remains blocked until v0.2.0 macOS arm64 and Linux x64 CLI artifacts both exist and +the vendored payload is refreshed from those artifacts; no publication, artifact upload, tag, or +installable `0.2.0` wording is approved by that record. + +v0.2.0 draft artifact evidence is recorded in +`docs/validation/v0-2-0-draft-artifact-evidence-validation-2026-06-25.md`. It records a successful +`release.yml` workflow run and downloaded macOS arm64/Linux x64 draft CLI artifacts whose smoke +sidecars report `ethos 0.2.0`. This evidence does not approve GitHub Release artifact +publication, npm vendor refresh, registry publication, tag creation, or installable `0.2.0` +wording. + Patch `0.1.1` readiness prep is recorded in `docs/validation/patch-0-1-1-readiness-prep-validation-2026-06-23.md` for review only. It records candidate onboarding contents after `ethos doctor`, synthetic fixture golden-change guarding, the diff --git a/docs/v0-2-0-release-prep.md b/docs/v0-2-0-release-prep.md index 23731b4..a69c4ff 100644 --- a/docs/v0-2-0-release-prep.md +++ b/docs/v0-2-0-release-prep.md @@ -1,11 +1,11 @@ # Ethos v0.2.0 Release Preparation -Status: source-preparation record. This document does not approve `cargo publish`, PyPI upload, -GitHub Release creation, package tags, or public `0.2.0` installability wording. +Status: release-candidate activation record. This document does not approve `cargo publish`, PyPI +upload, GitHub Release creation, package tags, or public `0.2.0` installability wording. Canonical preparation sentence: -> v0.2.0 is being prepared to publish registry install surfaces for JSON verification and evidence +> v0.2.0 release-candidate source versions are activated for JSON verification and evidence > anchoring. The release promise being prepared is narrow: @@ -42,54 +42,107 @@ The release promise being prepared is narrow: - hidden/off-page/low-contrast text detection; - annotation, link, embedded-file, or JavaScript inventories unless implemented. -## Publication Governance +## Release Sequence -Before any `0.2.0` publication action: +### 1. Decide Python Scope -- obtain explicit `0.2.0` publication approval; -- confirm crates.io ownership for `ethos-doc-core`, `ethos-verify`, and `ethos-pdf`; -- confirm ADR-0006 package-identifier governance; -- confirm publication metadata for intended crates; -- confirm how `reserved_crates_io_version` remains historical reservation metadata; -- confirm whether the Python wrapper is in the public promise; -- confirm the operator and closeout evidence owner. +Before approval, decide whether Python is public in `v0.2.0`. + +If Python is public in `v0.2.0`, commit to all of these surfaces together: + +- PyPI wheel; +- `v0.2.0` CLI artifacts usable by the wrapper; +- docs explaining that `ethos-pdf` is historical package naming and that JSON verify/anchor calls + use a caller-provided CLI binary. + +If Python is not public in `v0.2.0`, remove Python from the public promise before approval. + +### 2. Prepare Approval Packet + +The approval packet must bind: + +- exact source commit; +- version-bump plan; +- crates: `ethos-doc-core`, `ethos-verify`, and `ethos-pdf`; +- explicit `ethos-pdf` continuity decision; +- Python decision; +- npm `@docushell/ethos-pdf` fate; +- CLI artifact decision; +- tag and package-tag approval; +- ADR-0006/name ownership confirmation; +- `reserved_crates_io_version` handling; +- append-only crates.io risk; +- operator and closeout owner; +- retained blockers. If approval or registry publication is deferred, public wording must stay in preparation language and must not claim `0.2.0` registry installation. -## Publish Order - -The intended Rust order is: +### 3. Create Release-Candidate Branch -1. Run local tests and gates. -2. Dry-run only `ethos-doc-core` before any `0.2.0` crate is live. -3. Publish `ethos-doc-core`. -4. Wait for crates.io index availability and smoke a clean temp project. -5. Dry-run and publish `ethos-verify`. -6. Run a real API smoke with the bring-your-own-parser example. -7. Dry-run and publish `ethos-pdf` as a continuity crate. +After approval, create a release-candidate branch and make the versioned release-candidate changes: -`ethos-verify` and `ethos-pdf` registry-resolution dry-runs belong after -`ethos-doc-core 0.2.0` is live in the crates.io index. +- bump Rust workspace/package dependency versions to `0.2.0`; +- bump Python metadata and `__version__` if Python is included; +- bump npm if npm is included; +- finalize `CHANGELOG.md`; +- update version-pinned docs to release-candidate wording, not installable wording yet. -## Required Gates +### 4. Run Full Gates On The Bumped Tree -Source-prep gates: +Run: ```bash make v0-2-release-prep PYTHON=python3 +cargo publish --dry-run -p ethos-doc-core ``` -The target expands to the workspace Rust test suite, Python surface tests, schema example -validation, public claims gates, and whitespace diff checks. +Also run package/build checks for Python, npm, and CLI artifacts if those surfaces are included. + +### 5. Publish In Dependency Order + +Rust publication order: + +1. Publish `ethos-doc-core`. +2. Wait for crates.io index availability. +3. Dry-run and publish `ethos-verify`. +4. Dry-run and publish `ethos-pdf`. + +`ethos-verify` and `ethos-pdf` registry-resolution dry-runs belong after +`ethos-doc-core 0.2.0` is live in the crates.io index. + +Publish Python, npm, and CLI artifacts only if they are in scope. -Post-publication wording flip gates: +### 6. Smoke Real Usage + +Smoke: + +- Rust bring-your-own-parser API; +- CLI JSON verify; +- CLI evidence anchor; +- Python wrapper against the published CLI artifact if Python is in scope. + +### 7. Flip Docs Only After Smoke + +Only after registry/artifact availability and smoke evidence, flip docs to installable `0.2.0` +wording and rerun: ```bash python3 .github/scripts/claims_gate.py python3 .github/scripts/public_boundary_claims_gate.py ``` +## Source-Prep Gate + +Current source-prep gate before a release-candidate branch: + +```bash +make v0-2-release-prep PYTHON=python3 +``` + +The target expands to the workspace Rust test suite, Python surface tests, schema example +validation, public claims gates, and whitespace diff checks. + ## Python Wrapper Contract If Python remains included, the `ethos-pdf` package name is historical continuity naming. The @@ -116,3 +169,15 @@ ethos evidence anchor \ ``` Evidence anchor has no v0.2 fail flag. Non-bound anchor outcomes remain exit-0 structured reports. + +## DocuShell Design-Partner Pilot + +The DocuShell wedge should stay internal/design-partner scoped as "Evidence-Checked Answers", not a +public launch. + +The two learning goals are: + +- claim extraction quality; +- non-PDF ingestion. + +Those decide whether the deterministic trust layer is easy to apply outside Ethos' own parser path. diff --git a/docs/validation/README.md b/docs/validation/README.md index 6c4a904..025dfa7 100644 --- a/docs/validation/README.md +++ b/docs/validation/README.md @@ -10,6 +10,53 @@ in `docs/public-release-checklist.md`. Records: +v0.2.0 release approval request is recorded in +`v0-2-0-release-approval-request-validation-2026-06-25.md` for decider review only. It binds the +exact source commit, version-bump plan, Rust crate set, `ethos-pdf` continuity decision, Python +scope decision, npm fate, CLI artifact decision, tag/package-tag request, ADR-0006/name ownership +confirmation, `reserved_crates_io_version` handling, crates.io append-only risk, operator and +closeout owner, DocuShell internal/design-partner pilot boundary, and retained blockers. Version +bump, package publication, tag creation, artifact publication, and installable `0.2.0` wording +remain blocked until explicit approval and later evidence records pass. + +v0.2.0 release approval decision is recorded in +`v0-2-0-release-approval-decision-validation-2026-06-25.md`. It accepts release-candidate version +activation on `dev/v0-2-approval-packet` for Rust, Python, npm, `CHANGELOG.md`, and +release-candidate wording only. Package publication, tag creation, artifact publication, +installable `0.2.0` wording, hosted surfaces, production positioning, Windows packaged artifacts, +bundled project-maintained PDFium builds, `ethos-doc`, `ethos-rag`, and public benchmark claims +remain blocked until separate evidence records and operator decisions pass. + +v0.2.0 version activation is recorded in +`v0-2-0-version-activation-validation-2026-06-25.md`. It moves Rust, Python, and npm +source/package metadata to `0.2.0` for release-candidate validation only. Public install commands +and installable wording remain on the approved `0.1.2` evaluation baseline until publication, +registry/artifact availability, smoke evidence, and wording closeout records pass. + +v0.2.0 `ethos-doc-core` dry-run evidence is recorded in +`v0-2-0-ethos-doc-core-cargo-publish-dry-run-evidence-validation-2026-06-25.md`. The first Rust +registry dry-run gate passed for `ethos-doc-core 0.2.0`, producing +`ethos-doc-core-0.2.0.crate` with SHA256 +`de86ce74dd791b50d0722cddc878756cceabae2162f747e9e24902b88e5c7de1`. `cargo publish`, +dependent-crate dry-runs, tag creation, artifact publication, and installable `0.2.0` wording +remain blocked until separate evidence and operator decisions pass. + +v0.2.0 package/build evidence is recorded in +`v0-2-0-package-build-evidence-validation-2026-06-25.md`. It records a passing local Python +wheel build/install/wrapper smoke, a passing local macOS arm64 draft CLI artifact build/inventory +smoke with `ethos 0.2.0`, and passing npm tests plus `npm pack --dry-run` metadata for +`@docushell/ethos-pdf@0.2.0`. npm v0.2.0 artifact candidacy remains blocked because the +checked-in vendored binaries still expose `ethos 0.1.2`; Linux x64 CLI artifact evidence, npm +vendor refresh, publication, tag creation, artifact upload, and installable `0.2.0` wording remain +blocked. + +v0.2.0 draft artifact evidence is recorded in +`v0-2-0-draft-artifact-evidence-validation-2026-06-25.md`. It records a passing +`release.yml` workflow-dispatch run on `dev/v0-2-approval-packet`, downloaded macOS arm64 and +Linux x64 draft CLI artifacts, matching checksum sidecars, inventory sidecars, and smoke sidecars +that report `ethos 0.2.0`. Artifact publication, npm vendor refresh, registry publication, tag +creation, and installable `0.2.0` wording remain blocked until separate records pass. + Patch `0.1.2` closeout records now document that the approved patch `0.1.2` evaluation surfaces are closed for the GitHub source repository, Rust crates, Python wheel, npm package, macOS arm64/Linux x64 CLI artifacts, and the three approved annotated package tags. Hosted surfaces diff --git a/docs/validation/v0-2-0-draft-artifact-evidence-validation-2026-06-25.md b/docs/validation/v0-2-0-draft-artifact-evidence-validation-2026-06-25.md new file mode 100644 index 0000000..a2ba8b4 --- /dev/null +++ b/docs/validation/v0-2-0-draft-artifact-evidence-validation-2026-06-25.md @@ -0,0 +1,228 @@ +# v0.2.0 Draft Artifact Evidence Validation - 2026-06-25 + +Validated source HEAD before this record: `36955ca`. + +v0.2.0 draft artifact evidence source commit: +`36955cac69dc4eed624feb22b1a8c5e8a811d3bd`. + +v0.2.0 draft artifact evidence source tree: +`7ca47e76dfa2d23d40768dbcb88e2b97fba619b2`. + +Status: **draft CLI artifact evidence recorded; publication and installable wording remain blocked** + +This record captures a green v0.2.0 draft CLI artifact workflow run on the +`dev/v0-2-approval-packet` release-candidate branch. It records downloaded macOS arm64 and Linux +x64 draft artifact sidecars, archive checksums, and runtime smoke outputs. It satisfies the +two-platform draft CLI artifact evidence prerequisite for later artifact-publication and npm-vendor +decisions. It does not publish those artifacts, create a GitHub Release, update npm vendor +payloads, publish registries, create tags, or change public installation wording. + +## Workflow Run + +Workflow: + +```text +.github/workflows/release.yml +``` + +Run: + +```text +https://github.com/docushell/ethos/actions/runs/28175143857 +``` + +Observed run metadata: + +- status: `completed` +- conclusion: `success` +- event: `workflow_dispatch` +- branch: `dev/v0-2-approval-packet` +- head SHA: `36955cac69dc4eed624feb22b1a8c5e8a811d3bd` +- created at: `2026-06-25T13:52:38Z` +- updated at: `2026-06-25T13:53:52Z` + +Observed jobs: + +- `preflight`: passed. +- `cli-draft-artifacts (macos-arm64, macos-14, tar.gz)`: passed. +- `cli-draft-artifacts (linux-x64, ubuntu-latest, tar.gz)`: passed. + +Both artifact jobs passed build, draft artifact assembly, draft artifact runtime smoke, draft +artifact inventory validation, and artifact upload. + +## Downloaded Artifact Set + +The operator downloaded these workflow artifacts from run `28175143857`: + +- `ethos-cli-draft-macos-arm64/ethos-macos-arm64.tar.gz` +- `ethos-cli-draft-macos-arm64/ethos-macos-arm64.tar.gz.sha256` +- `ethos-cli-draft-macos-arm64/ethos-macos-arm64.inventory.json` +- `ethos-cli-draft-macos-arm64/ethos-macos-arm64.smoke.json` +- `ethos-cli-draft-linux-x64/ethos-linux-x64.tar.gz` +- `ethos-cli-draft-linux-x64/ethos-linux-x64.tar.gz.sha256` +- `ethos-cli-draft-linux-x64/ethos-linux-x64.inventory.json` +- `ethos-cli-draft-linux-x64/ethos-linux-x64.smoke.json` + +## Artifact Evidence + +macOS arm64: + +- archive: `ethos-macos-arm64.tar.gz` +- SHA256: `c588ee77bbaf99a7d933673e6cd9db190f5992e47d40955def803435a9f9fc5a` +- checksum sidecar matched the recomputed archive SHA256 +- archive members: `ethos-macos-arm64/`, `LICENSE`, `NOTICE`, `ethos`, `pdfium-manual-setup.md` +- inventory: + +```json +{ + "artifact": "ethos-macos-arm64.tar.gz", + "artifact_class": "github-release-binary", + "pdfium_policy": "caller-provided", + "publication": "blocked", + "required_notices": [ + "LICENSE", + "NOTICE", + "docs/pdfium-manual-setup.md" + ], + "schema": "ethos.release_artifact_inventory.v1", + "sha256": "c588ee77bbaf99a7d933673e6cd9db190f5992e47d40955def803435a9f9fc5a", + "status": "draft_not_release_ready", + "target": "macos-arm64" +} +``` + +- smoke: + +```json +{ + "artifact_dir": "ethos-macos-arm64", + "help_command_groups": [ + "doc", + "rag", + "security", + "verify", + "fingerprint" + ], + "missing_pdfium_exit_code": 12, + "missing_pdfium_message": "PDFium not found: set ETHOS_PDFIUM_LIBRARY_PATH to the caller-provided PDFium dynamic library path. Run ethos doctor for setup diagnostics, run ethos doctor --require-pdfium after setting it, and see docs/pdfium-manual-setup.md.", + "required_files": [ + "ethos", + "LICENSE", + "NOTICE", + "pdfium-manual-setup.md" + ], + "schema": "ethos.release_artifact_smoke.v1", + "target": "macos-arm64", + "version_stdout": "ethos 0.2.0" +} +``` + +Linux x64: + +- archive: `ethos-linux-x64.tar.gz` +- SHA256: `00137b20ca2c2a2d2089df1d135920b021b0905d779b1347d134e8a2fb7bfa23` +- checksum sidecar matched the recomputed archive SHA256 +- archive members: `ethos-linux-x64/`, `LICENSE`, `ethos`, `NOTICE`, `pdfium-manual-setup.md` +- inventory: + +```json +{ + "artifact": "ethos-linux-x64.tar.gz", + "artifact_class": "github-release-binary", + "pdfium_policy": "caller-provided", + "publication": "blocked", + "required_notices": [ + "LICENSE", + "NOTICE", + "docs/pdfium-manual-setup.md" + ], + "schema": "ethos.release_artifact_inventory.v1", + "sha256": "00137b20ca2c2a2d2089df1d135920b021b0905d779b1347d134e8a2fb7bfa23", + "status": "draft_not_release_ready", + "target": "linux-x64" +} +``` + +- smoke: + +```json +{ + "artifact_dir": "ethos-linux-x64", + "help_command_groups": [ + "doc", + "rag", + "security", + "verify", + "fingerprint" + ], + "missing_pdfium_exit_code": 12, + "missing_pdfium_message": "PDFium not found: set ETHOS_PDFIUM_LIBRARY_PATH to the caller-provided PDFium dynamic library path. Run ethos doctor for setup diagnostics, run ethos doctor --require-pdfium after setting it, and see docs/pdfium-manual-setup.md.", + "required_files": [ + "ethos", + "LICENSE", + "NOTICE", + "pdfium-manual-setup.md" + ], + "schema": "ethos.release_artifact_smoke.v1", + "target": "linux-x64", + "version_stdout": "ethos 0.2.0" +} +``` + +## Boundary + +This record does not approve GitHub Release artifact publication. This record does not approve +registry publication. This record does not approve PyPI upload. This record does not approve npm +publication. This record does not approve npm vendor refresh. This record does not create or +approve release tags or package tags. This record does not approve public installation wording for +`0.2.0`. + +The public install baseline remains `0.1.2` until separate registry/GitHub Release publication +decisions, operator actions, npm vendor refresh, and public wording closeout records pass. + +## Retained Blockers + +- GitHub Release artifact publication remains blocked. +- Registry publication remains blocked. +- PyPI upload remains blocked. +- npm vendor refresh remains blocked pending a separate refresh record. +- npm publication remains blocked. +- Release tag creation remains blocked. +- Package tag creation remains blocked. +- Public installation wording remains blocked. +- Hosted surfaces remain blocked. +- Production positioning remains blocked. +- Windows packaged artifacts remain blocked. +- Bundled project-maintained PDFium builds remain blocked. +- Public benchmark reports remain blocked. +- Public benchmark claims remain blocked. +- `ethos-doc` remains blocked. +- `ethos-rag` remains blocked. +- PDFium remains caller-provided through `ETHOS_PDFIUM_LIBRARY_PATH`. + +## Verification Commands + +```sh +make v0-2-release-prep PYTHON=python3 +git push origin dev/v0-2-approval-packet +GH_PROMPT_DISABLED=1 gh workflow run release.yml --repo docushell/ethos --ref dev/v0-2-approval-packet +GH_PROMPT_DISABLED=1 gh run watch 28175143857 --repo docushell/ethos --exit-status --interval 10 +GH_PROMPT_DISABLED=1 gh run view 28175143857 --repo docushell/ethos --json url,status,conclusion,event,headBranch,headSha,createdAt,updatedAt,jobs +GH_PROMPT_DISABLED=1 gh run download 28175143857 --repo docushell/ethos --dir +python3 .github/scripts/validate_release_artifact_inventory.py /*/*.inventory.json +shasum -a 256 /*/*.tar.gz +tar -tzf /ethos-cli-draft-linux-x64/ethos-linux-x64.tar.gz +tar -tzf /ethos-cli-draft-macos-arm64/ethos-macos-arm64.tar.gz +python3 .github/scripts/test_v0_2_0_draft_artifact_evidence.py +make v0-2-release-prep PYTHON=python3 +git diff --check +``` + +## Result + +```text +v0.2.0 draft CLI artifact evidence recorded +macOS arm64 and Linux x64 draft artifacts smoke as ethos 0.2.0 +public install baseline remains 0.1.2 +publication, npm vendor refresh, tags, and installable wording remain blocked pending separate approvals and evidence +``` diff --git a/docs/validation/v0-2-0-ethos-doc-core-cargo-publish-dry-run-evidence-validation-2026-06-25.md b/docs/validation/v0-2-0-ethos-doc-core-cargo-publish-dry-run-evidence-validation-2026-06-25.md new file mode 100644 index 0000000..aaf9126 --- /dev/null +++ b/docs/validation/v0-2-0-ethos-doc-core-cargo-publish-dry-run-evidence-validation-2026-06-25.md @@ -0,0 +1,117 @@ +# v0.2.0 ethos-doc-core Cargo Publish Dry-Run Evidence Validation - 2026-06-25 + +Validated source HEAD before this record: `9ca0147`. + +v0.2.0 ethos-doc-core dry-run source commit: +`9ca01477a14b9addd542e7aa9c5217a1b1df6831`. + +v0.2.0 ethos-doc-core dry-run source tree: +`095ce634fa0a90e81fbc4805574d75fa4d06bb71`. + +Status: **ethos-doc-core 0.2.0 cargo publish dry-run evidence recorded; cargo publish remains blocked** + +This record captures the first required Rust registry dry-run after `v0.2.0` version activation. +It does not approve or perform `cargo publish`, create tags, publish dependent crates, change +installable public wording, upload artifacts, publish Python or npm packages, approve hosted +surfaces, approve production positioning, approve Windows packaged artifacts, approve bundled +project-maintained PDFium builds, approve `ethos-doc`, approve `ethos-rag`, or approve public +benchmark reports or claims. + +## Subject + +- Repository: `docushell/ethos` +- Lane: v0.2.0 first Rust dry-run gate +- Package: `ethos-doc-core` +- Version: `0.2.0` +- Source commit: `9ca01477a14b9addd542e7aa9c5217a1b1df6831` +- Source tree: `095ce634fa0a90e81fbc4805574d75fa4d06bb71` +- Generated crate artifact: `ethos-doc-core-0.2.0.crate` +- Generated crate SHA256: + `de86ce74dd791b50d0722cddc878756cceabae2162f747e9e24902b88e5c7de1` + +## Command Evidence + +Command: + +```sh +cargo publish --dry-run --locked -p ethos-doc-core +``` + +Result: + +```text +Packaging ethos-doc-core v0.2.0 +Packaged 20 files, 162.1KiB (37.6KiB compressed) +Verifying ethos-doc-core v0.2.0 +Compiling ethos-doc-core v0.2.0 +Finished `dev` profile +Uploading ethos-doc-core v0.2.0 +warning: aborting upload due to dry run +RESULT: PASS (dry-run) +``` + +The dry run exited `0`. + +## Package File List + +```text +.cargo_vcs_info.json +Cargo.lock +Cargo.toml +Cargo.toml.orig +NOTICE.md +README.md +src/c14n.rs +src/codes.rs +src/config.rs +src/crop_element.rs +src/error.rs +src/evidence_anchor.rs +src/fingerprint.rs +src/geom.rs +src/grounding.rs +src/ids.rs +src/lib.rs +src/model.rs +src/traits.rs +src/verify_types.rs +``` + +## Boundary + +- `cargo publish` remains blocked until an explicit operator publication decision is recorded. +- `ethos-verify` and `ethos-pdf` dry-runs remain blocked until the required dependency-order lane + reaches them after `ethos-doc-core 0.2.0` is visible in the crates.io index. +- PyPI upload remains blocked. +- `npm publish` remains blocked. +- GitHub Release `v0.2.0` artifact upload remains blocked. +- Release tag creation remains blocked. +- Package tag creation remains blocked. +- Installable `0.2.0` public wording remains blocked. +- 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. + +## Commands + +```sh +cargo publish --dry-run --locked -p ethos-doc-core +shasum -a 256 target/package/ethos-doc-core-0.2.0.crate +cargo package --list --locked -p ethos-doc-core +python3 .github/scripts/test_v0_2_0_ethos_doc_core_cargo_publish_dry_run_evidence.py +make v0-2-release-prep PYTHON=python3 +git diff --check +``` + +## Result + +```text +ethos-doc-core 0.2.0 dry-run evidence recorded +The generated crate artifact and package file list are bound to source commit 9ca0147 +No publication, tag, artifact upload, or installable wording is approved by this record +``` diff --git a/docs/validation/v0-2-0-package-build-evidence-validation-2026-06-25.md b/docs/validation/v0-2-0-package-build-evidence-validation-2026-06-25.md new file mode 100644 index 0000000..5945a56 --- /dev/null +++ b/docs/validation/v0-2-0-package-build-evidence-validation-2026-06-25.md @@ -0,0 +1,214 @@ +# v0.2.0 Package Build Evidence Validation - 2026-06-25 + +Validated source HEAD before this record: `977306e`. + +v0.2.0 package/build source commit: +`977306eb19dbd4070a600bff36e6f52a1e26f776`. + +v0.2.0 package/build source tree: +`6ad9746f54c75985ad1069b73926e1877bf7d848`. + +Status: **local package/build evidence recorded; publication and installable wording remain blocked** + +This record captures local package/build checks after v0.2.0 source/package metadata activation and +after the draft CLI artifact workflow smoke expectation was aligned to `ethos 0.2.0`. It does not +approve PyPI upload, `npm publish`, GitHub Release artifact upload, release tags, package tags, +installable `0.2.0` public wording, hosted surfaces, production positioning, Windows packaged +artifacts, bundled project-maintained PDFium builds, public benchmark reports or claims, +`ethos-doc`, or `ethos-rag`. + +## Subject + +- Repository: `docushell/ethos` +- Lane: v0.2.0 local package/build checks +- Source commit: `977306eb19dbd4070a600bff36e6f52a1e26f776` +- Source tree: `6ad9746f54c75985ad1069b73926e1877bf7d848` +- Python wheel candidate: `ethos_pdf-0.2.0-py3-none-any.whl` +- Python wheel SHA256: + `5eb6eabb1d3e8a4d6d5f0280741a89103701c13706182e9004d5bf6ec2402ef4` +- macOS arm64 draft CLI archive: `ethos-macos-arm64.tar.gz` +- macOS arm64 draft CLI archive SHA256: + `2a41e3457cc074394ad0e347c967d8e90e353a1179d8beaa2dda82c2725ad84a` +- npm dry-run package id: `@docushell/ethos-pdf@0.2.0` +- npm dry-run shasum: `42a0d591e6dd55ee0a9b6ed9976e455786b643c0` +- npm dry-run integrity: + `sha512-98NfgYjTl49v+gahz+lrnOk3BDEvnDGYwHEIAWknksJIiwfZ7thzP0Q2WMZFJpwfVW1C/9oeccG5b5lbwmlFiA==` + +## Baseline Gates + +Commands: + +```sh +python3 .github/scripts/test_release_artifact_workflow_prep.py +cargo build --locked --release -p ethos-cli +make python-surface-test PYTHON=python3 +npm test --prefix packages/npm/ethos-pdf +``` + +Results: + +```text +test_release_artifact_workflow_prep.py: PASS (5 tests) +cargo build --locked --release -p ethos-cli: PASS +python-surface-test: PASS (34 tests) +npm test --prefix packages/npm/ethos-pdf: PASS +``` + +The draft artifact workflow now passes `--expected-version "ethos 0.2.0"` to +`smoke_release_cli_artifact.py`. + +## Python Wheel Evidence + +Command: + +```sh +SOURCE_DATE_EPOCH=0 python3 -m build --wheel --outdir +``` + +Result: + +```text +Successfully built ethos_pdf-0.2.0-py3-none-any.whl +``` + +Wheel metadata: + +```text +Name: ethos-pdf +Version: 0.2.0 +Summary: Python wrapper for the Ethos document evidence CLI. +License-Expression: Apache-2.0 +Requires-Python: >=3.8 +``` + +Wheel file list: + +```text +ethos_pdf-0.2.0.dist-info/METADATA +ethos_pdf-0.2.0.dist-info/RECORD +ethos_pdf-0.2.0.dist-info/WHEEL +ethos_pdf-0.2.0.dist-info/licenses/LICENSE +ethos_pdf-0.2.0.dist-info/licenses/NOTICE +ethos_pdf-0.2.0.dist-info/top_level.txt +ethos_pdf/__init__.py +ethos_pdf/_cli.py +``` + +Install and wrapper smoke: + +```sh +python3 -m pip install --no-deps --force-reinstall --target ethos_pdf-0.2.0-py3-none-any.whl +PYTHONPATH= python3 -c '' +``` + +Result: + +```text +Successfully installed ethos-pdf-0.2.0 +0.2.0 +True +bound +``` + +The wrapper smoke used the local macOS arm64 draft CLI artifact, verified +`examples/verify/native_grounded_citations.json` against `schemas/examples/document.example.json`, +and anchored `schemas/examples/evidence-anchor-request.example.json`. + +## CLI Artifact Evidence + +Commands: + +```sh +cargo build --locked --release -p ethos-cli +tar -C target/release-artifacts -czf target/release-artifacts/ethos-macos-arm64.tar.gz ethos-macos-arm64 +shasum -a 256 target/release-artifacts/ethos-macos-arm64.tar.gz +python3 .github/scripts/write_release_artifact_inventory.py --target macos-arm64 +python3 .github/scripts/smoke_release_cli_artifact.py --expected-version "ethos 0.2.0" --target macos-arm64 +python3 .github/scripts/validate_release_artifact_inventory.py target/release-artifacts/ethos-macos-arm64.inventory.json +``` + +Inventory result: + +```text +schema: ethos.release_artifact_inventory.v1 +status: draft_not_release_ready +artifact_class: github-release-binary +target: macos-arm64 +artifact: ethos-macos-arm64.tar.gz +sha256: 2a41e3457cc074394ad0e347c967d8e90e353a1179d8beaa2dda82c2725ad84a +pdfium_policy: caller-provided +publication: blocked +``` + +Smoke result: + +```text +schema: ethos.release_artifact_smoke.v1 +target: macos-arm64 +version_stdout: ethos 0.2.0 +missing_pdfium_exit_code: 12 +help_command_groups: doc, rag, security, verify, fingerprint +``` + +Linux x64 CLI artifact evidence remains required before any two-platform GitHub Release artifact +approval or npm vendor refresh decision. + +## npm Evidence And Blocker + +Commands: + +```sh +npm test --prefix packages/npm/ethos-pdf +npm pack --dry-run --json +node packages/npm/ethos-pdf/bin/ethos-pdf.js --version +``` + +Results: + +```text +platform selection ok +vendor assembly ok +name: @docushell/ethos-pdf +version: 0.2.0 +filename: docushell-ethos-pdf-0.2.0.tgz +entryCount: 11 +size: 1833226 +unpackedSize: 3934993 +shasum: 42a0d591e6dd55ee0a9b6ed9976e455786b643c0 +integrity: sha512-98NfgYjTl49v+gahz+lrnOk3BDEvnDGYwHEIAWknksJIiwfZ7thzP0Q2WMZFJpwfVW1C/9oeccG5b5lbwmlFiA== +node packages/npm/ethos-pdf/bin/ethos-pdf.js --version: ethos 0.1.2 +``` + +The npm package metadata and dry-run package structure are `0.2.0`, but npm artifact candidacy +remains blocked because the checked-in vendored binaries still expose `ethos 0.1.2`. Do not approve +`npm publish` for v0.2.0 until v0.2.0 macOS arm64 and Linux x64 CLI artifacts are both available, +`prepare-vendor` refreshes the payload from those artifacts, and the installed npm CLI smoke reports +`ethos 0.2.0`. + +## Boundary + +- PyPI upload remains blocked. +- `npm publish` remains blocked. +- GitHub Release `v0.2.0` artifact upload remains blocked. +- Linux x64 draft CLI artifact evidence remains required. +- npm vendor refresh remains blocked until both v0.2.0 CLI artifact payloads exist. +- Release tag creation remains blocked. +- Package tag creation remains blocked. +- Installable `0.2.0` public wording remains blocked. +- 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. + +## Result + +```text +Python wheel local build and wrapper smoke: PASS +macOS arm64 draft CLI artifact local build and smoke: PASS +npm metadata and dry-run package structure: PASS +npm v0.2.0 artifact candidacy: BLOCKED by vendored ethos 0.1.2 payload +``` diff --git a/docs/validation/v0-2-0-release-approval-decision-validation-2026-06-25.md b/docs/validation/v0-2-0-release-approval-decision-validation-2026-06-25.md new file mode 100644 index 0000000..a33972f --- /dev/null +++ b/docs/validation/v0-2-0-release-approval-decision-validation-2026-06-25.md @@ -0,0 +1,186 @@ +# v0.2.0 Release Approval Decision Validation - 2026-06-25 + +Validated source HEAD before this record: `bebc3b0`. + +v0.2.0 release approval decision source commit: +`bebc3b0a2a20fd762ad70351291222c162631eb6`. + +v0.2.0 release approval decision source tree: +`90b19b657df2df50a957a991dcde7b9474e1f758`. + +Status: **v0.2.0 release approval decision recorded; release-candidate version activation may +begin on the current branch; package publication, tag creation, artifact publication, and +installable wording remain blocked** + +This record accepts the exact `v0.2.0` release approval request packet after decider approval. It +authorizes release-candidate version activation on the current branch only. It does not run +`cargo publish`, publish any crate, upload to PyPI, run `npm publish`, create a GitHub Release, +upload CLI artifacts, create release tags, create package tags, change installable public wording, +approve hosted surfaces, approve production positioning, approve Windows packaged artifacts, +approve bundled project-maintained PDFium builds, approve `ethos-doc`, approve `ethos-rag`, or +approve public benchmark reports or claims. + +## Subject + +- Repository: `docushell/ethos` +- Lane: v0.2.0 release-candidate approval decision +- Approval owner: `docushell-admin` +- Approval request record: + `docs/validation/v0-2-0-release-approval-request-validation-2026-06-25.md` +- Approval request source commit accepted by this decision: + `fa15fa6f60993e30aa90540526903e4eb72c8252` +- Approval request source tree accepted by this decision: + `983f484ff1249ee3ea88da79ebafa9d2cd2410f5` +- Approval decision source commit: + `bebc3b0a2a20fd762ad70351291222c162631eb6` +- Approval decision source tree: + `90b19b657df2df50a957a991dcde7b9474e1f758` + +## Exact Decision Fields + +- Decision: accept the exact `v0.2.0` release approval request packet. +- Approver: `docushell-admin` acting as decider. +- Operator: `docushell-admin`. +- Closeout owner: `docushell-admin`. +- Date: 2026-06-25. +- Branch decision: continue on `dev/v0-2-approval-packet` as the release-candidate working branch + per operator instruction; do not create a separate branch for this lane unless a later explicit + instruction changes that. +- Exact target version accepted by this decision: `0.2.0`. +- Exact Rust crate set accepted by this decision: + - `ethos-doc-core = 0.2.0`; + - `ethos-verify = 0.2.0`; + - `ethos-pdf = 0.2.0`. +- Exact `ethos-pdf` continuity decision accepted by this decision: keep `ethos-pdf` as the + continuity crate for `v0.2.0`; JSON verify and evidence-anchor paths remain PDFium-free, while + parser, crop, and render paths continue to use caller-provided PDFium. +- Exact Python decision accepted by this decision: Python is public in `v0.2.0` only as + `ethos-pdf==0.2.0` with all of these required together: + - PyPI wheel; + - `v0.2.0` macOS arm64 and Linux x64 CLI artifacts usable by the Python wrapper; + - docs explaining that `ethos-pdf` is historical package naming and that JSON verify/anchor + methods call a caller-provided `ethos` CLI binary. +- Exact npm decision accepted by this decision: `@docushell/ethos-pdf@0.2.0` may remain in scope + only as a CLI binary distribution package. This decision does not approve a Node API, Node SDK, + N-API binding, or WASM package. +- Exact CLI artifact decision accepted by this decision: prepare macOS arm64 and Linux x64 GitHub + Release CLI artifacts for `v0.2.0`; Windows packaged artifacts remain blocked. +- Exact tag and package-tag decision accepted by this decision: + - release tag candidate: `v0.2.0`; + - package tag candidates: `ethos-package-ethos-doc-core-0.2.0`, + `ethos-package-ethos-verify-0.2.0`, and `ethos-package-ethos-pdf-0.2.0`; + - actual tag creation remains blocked until publication/smoke evidence and closeout records pass. +- ADR-0006/name ownership accepted by this decision: retain `docushell/ethos` as the canonical + source repository, `ethos-doc-core`, `ethos-verify`, and `ethos-pdf` as the requested priority + crates, `ethos-pdf` as the PyPI package/import surface `ethos_pdf`, and + `@docushell/ethos-pdf` as the npm CLI package identity. +- `reserved_crates_io_version` handling accepted by this decision: keep + `reserved_crates_io_version = "0.0.0-reserved.0"` as historical reservation metadata in the + crate manifests. The real package version comes from the workspace/package version and may be + bumped to `0.2.0` during release-candidate version activation. +- crates.io append-only risk accepted by this decision: once `0.2.0` is published to crates.io, + the exact version cannot be deleted or overwritten. A bad publish can only be yanked, so the + operator must stop on any version, source, artifact, README, license, dependency, or index + mismatch. + +## Approved Release-Candidate Work + +After this decision record is merged and validation passes, release-candidate work may begin on +`dev/v0-2-approval-packet` with only these changes: + +- bump Rust workspace/package dependency versions from `0.1.2` to `0.2.0`; +- bump Python metadata and `ethos_pdf.__version__` from `0.1.2` to `0.2.0`; +- bump npm `@docushell/ethos-pdf` from `0.1.2` to `0.2.0`; +- finalize `CHANGELOG.md`; +- update version-pinned docs to release-candidate wording, not installable wording. + +## Required Checks Before Publication Decisions + +The release-candidate tree must pass: + +```sh +make v0-2-release-prep PYTHON=python3 +cargo publish --dry-run -p ethos-doc-core +``` + +Python, npm, and CLI package/build checks must pass before those surfaces move to any separate +operator publication decision. `ethos-verify` and `ethos-pdf` dry-runs belong after +`ethos-doc-core 0.2.0` is visible in the crates.io index. + +## DocuShell Pilot Boundary + +The DocuShell wedge remains an internal/design-partner "Evidence-Checked Answers" pilot, not a +public launch. + +The accepted pilot learning goals are: + +- claim extraction quality; +- non-PDF ingestion. + +This decision does not approve hosted DocuShell surfaces, public SaaS positioning, or production +positioning. + +## Non-Actions + +- This decision record does not run `cargo publish`. +- This decision record does not publish any crate. +- This decision record does not upload to PyPI. +- This decision record does not publish any Python distribution. +- This decision record does not run `npm publish`. +- This decision record does not publish any npm package. +- This decision record does not create a GitHub Release. +- This decision record does not upload CLI artifacts. +- This decision record does not create a release tag. +- This decision record does not create package tags. +- This decision record does not approve installable `0.2.0` public wording. +- This decision record does not approve hosted surfaces. +- This decision record does not approve production positioning. +- This decision record does not approve Windows packaged artifacts. +- This decision record does not approve bundled project-maintained PDFium builds. +- This decision record does not approve public benchmark reports. +- This decision record does not approve public benchmark claims. +- This decision record does not approve `ethos-doc`. +- This decision record does not approve `ethos-rag`. + +## Retained Blockers + +- `cargo publish` remains blocked until release-candidate dry-runs pass and a separate operator + publication decision is recorded. +- PyPI upload remains blocked until deterministic wheel evidence and a separate operator + publication decision pass. +- `npm publish` remains blocked until package evidence and a separate operator publication decision + pass. +- GitHub Release `v0.2.0` CLI artifact publication remains blocked until artifact evidence and a + separate operator publication decision pass. +- Release tag and package tag creation remain blocked until explicit closeout evidence passes. +- Installable `0.2.0` public wording remains blocked until registry/artifact availability and + smoke closeout 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. + +## Commands + +```sh +python3 .github/scripts/test_v0_2_0_release_approval_decision.py +python3 .github/scripts/test_v0_2_0_release_approval_request.py +python3 .github/scripts/claims_gate.py +python3 .github/scripts/public_boundary_claims_gate.py +make v0-2-release-prep PYTHON=python3 +git diff --check +``` + +## Result + +```text +v0.2.0 release approval decision recorded +Release-candidate version activation may begin on dev/v0-2-approval-packet for Rust, Python, npm, +CHANGELOG, and release-candidate wording only +Package publication, tag creation, artifact publication, and installable wording remain blocked +pending separate evidence records and operator decisions +``` diff --git a/docs/validation/v0-2-0-release-approval-request-validation-2026-06-25.md b/docs/validation/v0-2-0-release-approval-request-validation-2026-06-25.md new file mode 100644 index 0000000..698c848 --- /dev/null +++ b/docs/validation/v0-2-0-release-approval-request-validation-2026-06-25.md @@ -0,0 +1,203 @@ +# v0.2.0 Release Approval Request Validation - 2026-06-25 + +Validated source HEAD before this record: `fa15fa6`. + +v0.2.0 release approval request source commit: +`fa15fa6f60993e30aa90540526903e4eb72c8252`. + +v0.2.0 release approval request source tree: +`983f484ff1249ee3ea88da79ebafa9d2cd2410f5`. + +Status: **v0.2.0 release approval request recorded; version bump, package publication, tag +creation, artifact publication, and installable wording remain blocked** + +This record requests decider review for the exact `v0.2.0` release-candidate scope. It does not +approve or perform a version bump, create a release-candidate branch, run `cargo publish`, upload +to PyPI, run `npm publish`, create a GitHub Release, upload CLI artifacts, create package tags, +change public install wording, approve hosted surfaces, approve production positioning, approve +Windows packaged artifacts, approve bundled project-maintained PDFium builds, approve `ethos-doc`, +approve `ethos-rag`, or approve public benchmark reports or claims. + +## Subject + +- Repository: `docushell/ethos` +- Lane: v0.2.0 release-candidate approval packet +- Approval source commit: `fa15fa6f60993e30aa90540526903e4eb72c8252` +- Approval source tree: `983f484ff1249ee3ea88da79ebafa9d2cd2410f5` +- Current source version baseline before approval: `0.1.2` +- Requested target version after approval: `0.2.0` +- Prepared release promise: + - JSON verification over caller-provided sources. + - JSON evidence anchoring over caller-provided sources. + - PDFium-free verify and evidence-anchor paths. + - PDFium remains caller-provided for parser, crop, and render paths that need PDF parsing. + +## Exact Request Fields + +- Decision requested: approve creation of a release-candidate branch for exact `v0.2.0` version + activation and artifact preparation. +- Approver requested: `docushell-admin` acting as decider. +- Operator requested: `docushell-admin`, or a named replacement explicitly accepted in the + approval decision. +- Closeout owner requested: `docushell-admin`, or a named replacement explicitly accepted in the + approval decision. +- Date requested: 2026-06-25. +- Exact source commit requested: `fa15fa6f60993e30aa90540526903e4eb72c8252`. +- Exact source tree requested: `983f484ff1249ee3ea88da79ebafa9d2cd2410f5`. +- Version-bump plan requested: + - bump Rust workspace/package dependency versions from `0.1.2` to `0.2.0`; + - bump Python metadata and `ethos_pdf.__version__` from `0.1.2` to `0.2.0` if Python is + accepted in scope; + - bump npm `@docushell/ethos-pdf` from `0.1.2` to `0.2.0` if npm is accepted in scope; + - finalize `CHANGELOG.md`; + - update version-pinned docs to release-candidate wording, not installable wording, until + registry/artifact smoke and closeout records pass. +- Exact Rust crate set requested: + - `ethos-doc-core = 0.2.0`; + - `ethos-verify = 0.2.0`; + - `ethos-pdf = 0.2.0`. +- Explicit `ethos-pdf` continuity decision requested: keep `ethos-pdf` in the `v0.2.0` crate set + as the continuity crate. The JSON verify and evidence-anchor promise does not require PDFium, + while PDF parser/crop/render paths continue to use caller-provided PDFium. +- Python decision requested: include Python `ethos-pdf==0.2.0` in public `v0.2.0` only if the + release includes all of these together: + - PyPI wheel for `ethos-pdf==0.2.0`; + - `v0.2.0` macOS arm64 and Linux x64 CLI artifacts usable by the Python wrapper; + - docs explaining that `ethos-pdf` is historical package naming and that JSON verify/anchor + methods call a caller-provided `ethos` CLI binary. +- npm `@docushell/ethos-pdf` fate requested: include `@docushell/ethos-pdf@0.2.0` only as a CLI + binary distribution package if `v0.2.0` CLI artifacts are approved. This request does not approve + a Node API, Node SDK, N-API binding, or WASM package. +- CLI artifact decision requested: prepare macOS arm64 and Linux x64 GitHub Release CLI artifacts + for `v0.2.0`; Windows packaged artifacts remain blocked. +- Tag and package-tag approval requested: + - release tag candidate: `v0.2.0`; + - package tag candidates: `ethos-package-ethos-doc-core-0.2.0`, + `ethos-package-ethos-verify-0.2.0`, and `ethos-package-ethos-pdf-0.2.0`; + - tag creation remains blocked until explicit decision, publication/smoke evidence, and closeout + records pass. +- ADR-0006/name ownership confirmation requested: retain `docushell/ethos` as the canonical + source repository, `ethos-doc-core`, `ethos-verify`, and `ethos-pdf` as the requested priority + crates, `ethos-pdf` as the PyPI package/import surface `ethos_pdf`, and + `@docushell/ethos-pdf` as the npm CLI package identity. +- `reserved_crates_io_version` handling requested: keep + `reserved_crates_io_version = "0.0.0-reserved.0"` as historical reservation metadata in the + crate manifests. The real package version comes from the workspace/package version and must be + bumped to `0.2.0` only on the release-candidate branch after approval. +- crates.io append-only risk accepted for review: once `0.2.0` is published to crates.io, the + exact version cannot be deleted or overwritten. A bad publish can only be yanked, so the operator + must stop on any version, source, artifact, README, license, dependency, or index mismatch. + +## Approval Sequence Requested + +1. Accept or reject this exact approval packet. +2. After acceptance, create a release-candidate branch. +3. Apply only release-candidate version and wording changes on that branch. +4. Run: + +```sh +make v0-2-release-prep PYTHON=python3 +cargo publish --dry-run -p ethos-doc-core +``` + +5. Run Python, npm, and CLI package/build checks only for accepted surfaces. +6. Publish Rust crates in dependency order: + - `ethos-doc-core`; + - wait for crates.io index availability; + - dry-run and publish `ethos-verify`; + - dry-run and publish `ethos-pdf`. +7. Publish Python, npm, and CLI artifacts only if accepted in scope. +8. Smoke: + - Rust bring-your-own-parser API; + - CLI JSON verify; + - CLI evidence anchor; + - Python wrapper against the published CLI artifact if Python is accepted in scope. +9. Only after publication and smoke evidence, flip docs to installable `0.2.0` wording and rerun + the claims gates. + +## DocuShell Pilot Boundary + +The DocuShell wedge remains an internal/design-partner "Evidence-Checked Answers" pilot, not a +public launch. + +The pilot learning goals are: + +- claim extraction quality; +- non-PDF ingestion. + +Those learning goals decide whether the deterministic trust layer is easy to apply outside Ethos' +own parser path. This request does not approve hosted DocuShell surfaces, public SaaS positioning, +or production positioning. + +## Non-Approvals + +- This request record does not approve a version bump. +- This request record does not create a release-candidate branch. +- This request record does not approve `cargo publish`. +- This request record does not publish any crate. +- This request record does not approve PyPI upload. +- This request record does not upload any Python distribution. +- This request record does not approve `npm publish`. +- This request record does not publish any npm package. +- This request record does not create a GitHub Release. +- This request record does not upload CLI artifacts. +- This request record does not create a release tag. +- This request record does not create package tags. +- This request record does not approve installable `0.2.0` public wording. +- This request record does not approve hosted surfaces. +- This request record does not approve production positioning. +- This request record does not approve Windows packaged artifacts. +- This request record does not approve bundled project-maintained PDFium builds. +- This request record does not approve public benchmark reports. +- This request record does not approve public benchmark claims. +- This request record does not approve `ethos-doc`. +- This request record does not approve `ethos-rag`. + +## Retained Blockers + +- Explicit decider approval remains required before a release-candidate branch. +- Rust workspace/package dependency version bump remains blocked until approval. +- Python metadata and `__version__` bump remain blocked until approval and Python scope acceptance. +- npm package version bump remains blocked until approval and npm scope acceptance. +- `CHANGELOG.md` final release wording remains blocked until approval. +- `cargo publish` remains blocked until release-candidate dry-runs pass and a separate operator + action is approved. +- PyPI upload remains blocked until Python scope is accepted and deterministic wheel evidence + passes. +- `npm publish` remains blocked until npm scope is accepted and package evidence passes. +- GitHub Release `v0.2.0` CLI artifact publication remains blocked until artifact evidence and + explicit approval pass. +- Release tag and package tag creation remain blocked until explicit approval and closeout evidence + pass. +- Installable `0.2.0` public wording remains blocked until registry/artifact availability and + smoke closeout 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. + +## Commands + +```sh +python3 .github/scripts/test_v0_2_0_release_approval_request.py +python3 .github/scripts/claims_gate.py +python3 .github/scripts/public_boundary_claims_gate.py +make v0-2-release-prep PYTHON=python3 +git diff --check +``` + +## Result + +```text +v0.2.0 release approval request recorded +Exact source commit, version-bump plan, Rust crate set, ethos-pdf continuity, Python decision, +npm fate, CLI artifact decision, tag/package-tag request, ADR-0006 ownership, reserved crates.io +metadata handling, crates.io append-only risk, operator and closeout owner, DocuShell pilot +boundary, and retained blockers were recorded +version bump, package publication, tag creation, artifact publication, and installable wording +remain blocked pending explicit approval and later evidence records +``` diff --git a/docs/validation/v0-2-0-version-activation-validation-2026-06-25.md b/docs/validation/v0-2-0-version-activation-validation-2026-06-25.md new file mode 100644 index 0000000..e9a86ac --- /dev/null +++ b/docs/validation/v0-2-0-version-activation-validation-2026-06-25.md @@ -0,0 +1,83 @@ +# v0.2.0 Version Activation Validation - 2026-06-25 + +Validated source HEAD before this record: `523e114`. + +v0.2.0 version activation source commit: +`523e1143bec52e16e596593f5dd649df741b4971`. + +v0.2.0 version activation source tree: +`8f13de3588a36927635a967cf120fba8f73a39f6`. + +Status: **v0.2.0 release-candidate source versions activated; package publication, tag creation, +artifact publication, and installable wording remain blocked** + +This record activates source/package metadata for the approved `v0.2.0` release-candidate lane +after `docs/validation/v0-2-0-release-approval-decision-validation-2026-06-25.md` landed on +`dev/v0-2-approval-packet`. + +## Activated Source Versions + +Rust, Python, and npm source/package metadata move to `0.2.0`: + +- Rust workspace package version and internal Rust path-dependency version pins. +- `Cargo.lock` workspace package entries. +- Python `pyproject.toml` metadata. +- Python `ethos_pdf.__version__`. +- npm `packages/npm/ethos-pdf/package.json` metadata. + +## Release-Candidate Wording + +Version-pinned public install commands remain on the approved `0.1.2` evaluation baseline until +publication, registry/artifact availability, smoke evidence, and wording closeout records pass. + +The allowed v0.2.0 wording is release-candidate wording only: + +> v0.2.0 release-candidate source versions are activated for JSON verification and evidence +> anchoring. + +No `0.2.0` registry install wording is approved until publication, registry availability, artifact +availability, and clean smoke tests are recorded. + +## Boundary + +This record does not approve a release, does not approve a tag, does not approve package publish, +does not approve npm publish, does not approve PyPI publish, does not approve crates.io publish, +does not approve a GitHub Release artifact, does not approve public installation wording for +`0.2.0`, does not approve hosted surfaces, does not approve production positioning, does not +approve Windows packaged artifacts, does not approve bundled project-maintained PDFium builds, +does not approve public benchmark reports, does not approve public benchmark claims, does not +approve speed, footprint, parser-quality, table-quality, or production claims, does not approve +`ethos-doc`, and does not approve `ethos-rag`. + +## Required Before Any Public 0.2.0 Install Wording + +- Build and smoke exact `0.2.0` CLI artifacts from the version-activated source commit. +- Record exact Rust crate package artifacts and dependency ordering for `0.2.0`. +- Record exact Python wheel artifacts for `0.2.0`. +- Record exact npm package artifact evidence for `@docushell/ethos-pdf@0.2.0`. +- Re-run public posture, claims, license/NOTICE, private-path, and source-binding checks after any + public-facing install wording changes. +- Record manual operator evidence for any credentialed publish or GitHub Release action. + +## Validation Commands + +```sh +python3 .github/scripts/test_v0_2_0_version_activation.py +python3 .github/scripts/test_v0_2_0_release_approval_decision.py +python3 .github/scripts/test_v0_2_0_release_approval_request.py +python3 .github/scripts/test_python_public_api_policy.py +python3 .github/scripts/test_npm_binary_package_scaffold.py +python3 .github/scripts/claims_gate.py +python3 .github/scripts/public_boundary_claims_gate.py +make v0-2-release-prep PYTHON=python3 +git diff --check +``` + +## Result + +```text +v0.2.0 release-candidate source versions activated +Rust, Python, and npm metadata now point to 0.2.0 for candidate validation +Public install commands and installable wording remain on approved 0.1.2 evaluation surfaces until +separate publication, smoke, and closeout records pass +``` diff --git a/packages/npm/ethos-pdf/package.json b/packages/npm/ethos-pdf/package.json index ad528be..2722c25 100644 --- a/packages/npm/ethos-pdf/package.json +++ b/packages/npm/ethos-pdf/package.json @@ -1,6 +1,6 @@ { "name": "@docushell/ethos-pdf", - "version": "0.1.2", + "version": "0.2.0", "description": "Ethos PDF CLI binary package for document evidence workflows.", "license": "Apache-2.0", "repository": { diff --git a/pyproject.toml b/pyproject.toml index 7fba5c0..408c17d 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta" [project] name = "ethos-pdf" -version = "0.1.2" +version = "0.2.0" description = "Python wrapper for the Ethos document evidence CLI." readme = "python/README.md" requires-python = ">=3.8" diff --git a/python/README.md b/python/README.md index 0c65c18..93f6c03 100644 --- a/python/README.md +++ b/python/README.md @@ -8,9 +8,10 @@ Install the published evaluation wheel from PyPI with: python3 -m pip install ethos-pdf==0.1.2 ``` -`v0.2.0` is being prepared to add JSON verification and evidence-anchor wrapper calls through a -caller-provided `ethos` CLI binary. Do not use `0.2.0` install wording until the package is -published and the post-publication smoke tests are recorded. +`v0.2.0` release-candidate source versions are activated for JSON verification and evidence-anchor +wrapper calls through a caller-provided `ethos` CLI binary. Do not use `0.2.0` install wording +until the package is published, matching CLI artifacts are available, and the post-publication +smoke tests are recorded. The package exposes a public semver API beginning at `0.1.0` for Python `>=3.8`. Patch releases must not break public function signatures, exception classes, or documented return shapes. Minor diff --git a/python/ethos_pdf/__init__.py b/python/ethos_pdf/__init__.py index f701040..9d72f93 100644 --- a/python/ethos_pdf/__init__.py +++ b/python/ethos_pdf/__init__.py @@ -35,7 +35,7 @@ verify, ) -__version__ = "0.1.2" +__version__ = "0.2.0" __all__ = [ "EthosCli",