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

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
24 changes: 20 additions & 4 deletions .github/scripts/package_publication_candidate_activation.py
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,6 @@


ROOT = Path(__file__).resolve().parents[2]
VERSION = "0.1.1"
CORE_PACKAGE = "ethos-doc-core"
CANDIDATE_PACKAGES = (CORE_PACKAGE, "ethos-verify", "ethos-pdf")
CONSUMER_PACKAGE = "ethos-package-candidate-consumer"
Expand All @@ -50,6 +49,23 @@
}


def current_workspace_version() -> str:
manifest = (ROOT / "Cargo.toml").read_text(encoding="utf-8")
in_workspace_package = False
for line in manifest.splitlines():
if line == "[workspace.package]":
in_workspace_package = True
continue
if in_workspace_package and line.startswith("["):
break
if in_workspace_package and line.startswith('version = "'):
return line.split('"', 2)[1]
raise RuntimeError("unable to determine workspace package version")


VERSION = current_workspace_version()


def should_ignore(_: str, names: list[str]) -> set[str]:
return {name for name in names if name in IGNORE_NAMES}

Expand Down Expand Up @@ -140,8 +156,8 @@ def materialize_candidate_workspace(workspace: Path) -> None:

replace_once_if_needed(
workspace / "Cargo.toml",
'ethos-core = { path = "crates/ethos-core", version = "0.1.1", default-features = false }',
'ethos-core = { package = "ethos-doc-core", path = "crates/ethos-core", version = "0.1.1", default-features = false }',
f'ethos-core = {{ path = "crates/ethos-core", version = "{VERSION}", default-features = false }}',
f'ethos-core = {{ package = "ethos-doc-core", path = "crates/ethos-core", version = "{VERSION}", default-features = false }}',
)
root_manifest = workspace / "Cargo.toml"
root_text = root_manifest.read_text(encoding="utf-8")
Expand Down Expand Up @@ -547,7 +563,7 @@ def source_manifests_have_activation_shape() -> bool:
lockfile = (ROOT / "Cargo.lock").read_text(encoding="utf-8")
return all(
[
'ethos-core = { package = "ethos-doc-core", path = "crates/ethos-core", version = "0.1.1", default-features = false }'
f'ethos-core = {{ package = "ethos-doc-core", path = "crates/ethos-core", version = "{VERSION}", default-features = false }}'
in workspace,
'name = "ethos-doc-core"' in core,
'[lib]\nname = "ethos_core"' in core,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -128,7 +128,7 @@ def test_candidate_manifests_are_activated_and_non_candidates_stay_blocked(self)
pdf = read(ROOT / "crates/ethos-pdf/Cargo.toml")
self.assertIn(
'ethos-core = { package = "ethos-doc-core", path = "crates/ethos-core", '
'version = "0.1.1", default-features = false }',
'version = "0.1.2", default-features = false }',
workspace,
)
self.assertIn('name = "ethos-doc-core"', lockfile)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -493,7 +493,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.1"', read(ROOT / "Cargo.toml"))
self.assertIn('version = "0.1.2"', read(ROOT / "Cargo.toml"))

def test_evidence_status_matches_decider_input(self) -> None:
status = load_json(PREP)["evidence_review_status"]
Expand Down Expand Up @@ -611,7 +611,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.1"', cargo)
self.assertIn('version = "0.1.2"', 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)
Expand Down Expand Up @@ -667,7 +667,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.1"', cargo)
self.assertIn('version = "0.1.2"', cargo)

def test_package_publication_approval_request_packet_keeps_all_actions_blocked(self) -> None:
packet = load_json(PREP)["package_publication_approval_request_packet"]
Expand Down Expand Up @@ -741,7 +741,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.1"', cargo)
self.assertIn('version = "0.1.2"', cargo)

def test_pdfium_boundary_keeps_ethos_pdf_held_until_confirmed(self) -> None:
approved = load_json(PREP)["approved_package_publication_prep"]
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -121,7 +121,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.1", result["candidate_version"])
self.assertEqual("0.1.2", 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"])
Expand Down Expand Up @@ -151,7 +151,7 @@ def test_candidate_activation_preserves_import_and_dependency_shape(self) -> Non
self.assertEqual({"ethos-doc-core", "ethos-verify", "ethos-pdf"}, set(artifacts))
for artifact in artifacts.values():
self.assertRegex(artifact["sha256"], r"^[0-9a-f]{64}$")
self.assertTrue(artifact["crate_file"].endswith("-0.1.1.crate"))
self.assertTrue(artifact["crate_file"].endswith("-0.1.2.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")
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -115,7 +115,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.1", result["candidate_version"])
self.assertEqual("0.1.2", 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"])
Expand All @@ -141,7 +141,7 @@ def test_artifacts_and_manifest_shape_are_current(self) -> None:
self.assertEqual({"ethos-doc-core", "ethos-verify", "ethos-pdf"}, set(artifacts))
for artifact in artifacts.values():
self.assertRegex(artifact["sha256"], r"^[0-9a-f]{64}$")
self.assertTrue(artifact["crate_file"].endswith("-0.1.1.crate"))
self.assertTrue(artifact["crate_file"].endswith("-0.1.2.crate"))

def test_source_candidate_manifests_are_activated_while_tags_and_registry_stay_absent(self) -> None:
for manifest in (
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -176,7 +176,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.1"', root_manifest)
self.assertIn('version = "0.1.2"', 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)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -127,7 +127,7 @@ def test_source_manifests_have_activated_but_blocked_shape(self) -> None:

self.assertIn(
'ethos-core = { package = "ethos-doc-core", path = "crates/ethos-core", '
'version = "0.1.1", default-features = false }',
'version = "0.1.2", default-features = false }',
workspace,
)
self.assertIn('name = "ethos-doc-core"', core)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -114,7 +114,7 @@ def test_current_manifests_remain_unmigrated_source_tree_manifests(self) -> None
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.1", default-features = false }',
'ethos-core = { package = "ethos-doc-core", path = "crates/ethos-core", version = "0.1.2", default-features = false }',
workspace,
)
self.assertIn('ethos-core = { workspace = true, features = ["grounding", "verify-types"] }', verify)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -106,7 +106,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.1"', workspace)
self.assertIn('version = "0.1.2"', 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)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -111,7 +111,7 @@ def test_current_manifests_stay_source_tree_only(self) -> None:
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.1", default-features = false }',
'ethos-core = { package = "ethos-doc-core", path = "crates/ethos-core", version = "0.1.2", default-features = false }',
workspace,
)
self.assertIn('ethos-core = { workspace = true, features = ["grounding", "verify-types"] }', verify)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -138,7 +138,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.1"', read(ROOT / "Cargo.toml"))
self.assertIn('version = "0.1.2"', read(ROOT / "Cargo.toml"))

for manifest in (
ROOT / "crates/ethos-cli/Cargo.toml",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -105,7 +105,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.1"', workspace)
self.assertIn('version = "0.1.2"', 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)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -101,7 +101,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.1"', root_manifest)
self.assertIn('version = "0.1.2"', 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)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -140,16 +140,16 @@ def test_request_requires_decision_and_keeps_upload_blocked(self) -> None:
self.assertNotIn("saumildiwaker", raw)
self.assertNotIn("Desktop/Stuff", raw)

def test_source_metadata_remains_expected_for_python_wheel(self) -> None:
def test_source_metadata_keeps_current_python_surface_shape(self) -> None:
pyproject = read(PYPROJECT)
init = read(INIT)

self.assertIn('name = "ethos-pdf"', pyproject)
self.assertIn('version = "0.1.1"', pyproject)
self.assertIn('version = "0.1.2"', pyproject)
self.assertIn('requires-python = ">=3.8"', pyproject)
self.assertIn('license = "Apache-2.0"', pyproject)
self.assertIn('readme = "python/README.md"', pyproject)
self.assertIn('__version__ = "0.1.1"', init)
self.assertIn('__version__ = "0.1.2"', init)
self.assertIn('"EthosCli"', init)
self.assertIn('"EthosCommandError"', init)

Expand Down
9 changes: 4 additions & 5 deletions .github/scripts/test_patch_0_1_2_readiness_prep.py
Original file line number Diff line number Diff line change
Expand Up @@ -119,11 +119,10 @@ def test_public_readme_uses_professional_beta_wording_without_version_drift(self
for phrase in FORBIDDEN_RELEASE_CLAIMS:
self.assertNotIn(phrase, lower)

def test_package_manifests_remain_on_published_baseline(self) -> None:
self.assertIn('version = "0.1.1"', read(WORKSPACE_CARGO))
self.assertIn('version = "0.1.1"', read(PYPROJECT))
self.assertIn('__version__ = "0.1.1"', read(PYTHON_INIT))
self.assertIn('"version": "0.1.1"', read(NPM_PACKAGE))
def test_package_manifests_do_not_rewrite_prep_record_boundary(self) -> None:
self.assertIn("current public install baseline remains `0.1.1`", normalized(RECORD))
self.assertNotIn("python3 -m pip install ethos-pdf==0.1.2", read(README))
self.assertNotIn("npm install -g @docushell/ethos-pdf@0.1.2", read(README))

def test_record_is_indexed_and_status_docs_reference_it(self) -> None:
record_name = RECORD.name
Expand Down
151 changes: 151 additions & 0 deletions .github/scripts/test_patch_0_1_2_version_activation.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,151 @@
#!/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


ROOT = Path(__file__).resolve().parents[2]
RECORD = ROOT / "docs/validation/patch-0-1-2-version-activation-validation-2026-06-24.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"
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 = "0252cc7"
SOURCE_COMMIT = "0252cc7800d51cb1ec673698be5646b4fb945066"
SOURCE_TREE = "ec9e4481ab237192d10942e9ce8d0b4a908c15b8"

FORBIDDEN_RELEASE_CLAIMS = (
"0.1.2 is released",
"v0.1.2 is released",
"0.1.2 is published",
"v0.1.2 is published",
"publish 0.1.2",
"tag v0.1.2",
"npm install -g @docushell/ethos-pdf@0.1.2",
"python3 -m pip install ethos-pdf==0.1.2",
"cargo add ethos-doc-core@0.1.2",
"cargo add ethos-verify@0.1.2",
"cargo add ethos-pdf@0.1.2",
)


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 Patch012VersionActivationTests(unittest.TestCase):
def test_record_binds_source_and_declares_activation_scope(self) -> None:
record = normalized(RECORD)
raw = read(RECORD)

self.assertIn(f"Validated source HEAD before this record: `{SOURCE_SHORT}`", raw)
self.assertIn(f"Version-activation source commit: `{SOURCE_COMMIT}`", record)
self.assertIn(f"Version-activation source tree: `{SOURCE_TREE}`", record)
self.assertIn("Rust workspace and Python source/package metadata move to `0.1.2`", record)
self.assertIn("npm remains at `0.1.1` until matching `0.1.2` CLI artifacts exist", record)

def test_rust_and_python_versions_are_activated(self) -> None:
cargo = read(CARGO)
cli = read(CLI_CARGO)
lock = read(CARGO_LOCK)

self.assertIn('version = "0.1.2"', cargo)
self.assertIn('ethos-core = { package = "ethos-doc-core", path = "crates/ethos-core", version = "0.1.2"', cargo)
self.assertIn('ethos-layout = { path = "crates/ethos-layout", version = "0.1.2" }', cargo)
self.assertIn('ethos-tables = { path = "crates/ethos-tables", version = "0.1.2" }', cargo)
self.assertIn('ethos-pdf = { path = "../ethos-pdf", version = "0.1.2" }', cli)
self.assertIn('ethos-verify = { path = "../ethos-verify", version = "0.1.2" }', cli)
self.assertIn('ethos-grounding-opendataloader-json = { path = "../../adapters/grounding/opendataloader-json", version = "0.1.2" }', cli)
self.assertGreaterEqual(lock.count('version = "0.1.2"'), 7)
self.assertIn('version = "0.1.2"', read(PYPROJECT))
self.assertIn('__version__ = "0.1.2"', read(PYTHON_INIT))

def test_npm_and_public_install_wording_wait_for_artifacts(self) -> None:
npm = json.loads(read(NPM_PACKAGE))
readme = read(README)

self.assertEqual("0.1.1", npm["version"])
self.assertIn("cargo add ethos-doc-core@0.1.1", readme)
self.assertIn("cargo add ethos-verify@0.1.1", readme)
self.assertIn("cargo add ethos-pdf@0.1.1", readme)
self.assertIn("python3 -m pip install ethos-pdf==0.1.1", readme)
self.assertIn("npm install -g @docushell/ethos-pdf@0.1.1", readme)
for phrase in FORBIDDEN_RELEASE_CLAIMS:
self.assertNotIn(phrase, readme.lower())

def test_boundaries_remain_closed(self) -> None:
record = normalized(RECORD)
lower = record.lower()

for phrase in (
"does not approve a release",
"does not approve a tag",
"does not approve package publish",
"does not approve a GitHub Release artifact",
"does not approve public installation wording for `0.1.2`",
"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 `ethos-doc`",
"does not approve `ethos-rag`",
):
self.assertIn(phrase, record)
for phrase in FORBIDDEN_RELEASE_CLAIMS:
self.assertNotIn(phrase, lower)

def test_record_is_indexed_and_release_candidate_prep_runs_guard(self) -> None:
record_name = RECORD.name
block = target_block("release-candidate-prep")
readiness_guard = "$(PYTHON) .github/scripts/test_patch_0_1_2_readiness_prep.py"
activation_guard = "$(PYTHON) .github/scripts/test_patch_0_1_2_version_activation.py"

self.assertIn(record_name, read(VALIDATION_README))
self.assertIn(record_name, read(EXECUTION_STATUS))
self.assertIn(record_name, read(PUBLIC_RELEASE_CHECKLIST))
self.assertIn(activation_guard, block)
self.assertEqual(1, read(MAKEFILE).count(activation_guard))
self.assertLess(block.index(readiness_guard), block.index(activation_guard))

def test_record_avoids_local_private_paths(self) -> None:
text = read(RECORD)

for private in (
"/" + "Users/",
"/" + "private/tmp",
"/" + "private/var",
"/" + "var/folders",
"saumil" + "diwaker",
"Desktop/" + "Stuff",
"project/repo/" + "ethos",
):
self.assertNotIn(private, text)


if __name__ == "__main__":
unittest.main()
Loading
Loading