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
118 changes: 118 additions & 0 deletions .github/scripts/test_milestone_e_package_publication_approval_prep.py
Original file line number Diff line number Diff line change
Expand Up @@ -90,6 +90,7 @@
"package_registry_assembly_activation_prep": "docs/validation/milestone-e-package-publication-registry-assembly-activation-prep-validation-2026-06-21.md",
"package_real_version_selection_prep": "docs/validation/milestone-e-package-publication-real-version-selection-prep-validation-2026-06-21.md",
"package_tag_creation_prep": "docs/validation/milestone-e-package-publication-tag-creation-prep-validation-2026-06-21.md",
"package_decision_bundle_validation": "docs/validation/milestone-e-package-publication-decision-bundle-validation-2026-06-21.md",
}
EXPECTED_PUBLICATION_DECISION_INPUTS = {
"decision_status": "not_approved_pending_exact_decision",
Expand Down Expand Up @@ -212,6 +213,65 @@
"package publication remains blocked",
],
}
EXPECTED_PACKAGE_PUBLICATION_APPROVAL_REQUEST_PACKET = {
"packet_state": "approval_request_packet_recorded_publication_blocked",
"candidate_crates": [
"ethos-doc-core mapped from crates/ethos-core; package-name migration remains pending",
"ethos-verify mapped from crates/ethos-verify; dependency manifest activation remains pending",
"ethos-pdf mapped from crates/ethos-pdf; dependency manifest activation and PDFium boundary confirmation must remain current",
],
"package_version_map": [
"ethos-doc-core has no selected package publication version",
"ethos-verify has no selected package publication version",
"ethos-pdf has no selected package publication version",
],
"package_tag_name": "not selected; package tag creation remains blocked",
"package_tag_source_commit": "not selected; package tag binding remains blocked",
"package_tag_source_tree": "not selected; package source tree binding remains blocked",
"manifest_activation_diff": "not prepared; current Cargo manifests remain unchanged",
"registry_assembly_evidence": "not activated; registry-backed dependent package assembly remains blocked",
"public_installation_wording": "No public installation wording is approved; public installation remains blocked.",
"explicit_exclusions": [
"wheels",
"npm packages",
"binaries",
"hosted surfaces",
"production positioning",
"public benchmark reports",
"public benchmark claims",
"release artifacts",
"project-maintained PDFium builds",
],
"required_before_approval": [
"exact package publication approval decision record",
"exact candidate crate list",
"exact SemVer package version or per-crate version map",
"exact package tag name and package_tag_source_commit",
"exact package-name migration diff for ethos-doc-core",
"exact dependency manifest activation diff for ethos-verify and ethos-pdf",
"exact registry-backed dependent package assembly evidence",
"posture and claims gates after exact public installation wording changes",
],
"non_approvals": [
"this packet does not select a package publication version",
"this packet does not create a package tag",
"this packet does not change Cargo manifests",
"this packet does not activate package dependency manifests",
"this packet does not create a registry",
"this packet does not activate registry-backed dependent package assembly",
"this packet does not invite public installation",
"this packet does not approve package publication",
],
"retained_blockers": [
"no package publication version is selected",
"no package tag is created",
"no package dependency manifest activation is approved",
"no registry-backed dependent package assembly activation is approved",
"public installation remains blocked",
"package publication remains blocked",
"real-version cargo publish remains blocked",
],
}

FORBIDDEN_PREP_WORDING = [
"public beta is approved",
Expand Down Expand Up @@ -541,6 +601,48 @@ def test_combined_package_publication_decision_prep_bundle_blocks_all_actions(se
self.assertIn('name = "ethos-pdf"', pdf_manifest)
self.assertIn('version = "0.1.0"', cargo)

def test_package_publication_approval_request_packet_keeps_all_actions_blocked(self) -> None:
packet = load_json(PREP)["package_publication_approval_request_packet"]
cargo = read(ROOT / "Cargo.toml")
core_manifest = read(ROOT / "crates/ethos-core/Cargo.toml")
verify_manifest = read(ROOT / "crates/ethos-verify/Cargo.toml")
pdf_manifest = read(ROOT / "crates/ethos-pdf/Cargo.toml")

self.assertEqual(EXPECTED_PACKAGE_PUBLICATION_APPROVAL_REQUEST_PACKET, packet)
self.assertEqual("approval_request_packet_recorded_publication_blocked", packet["packet_state"])
self.assertEqual(3, len(packet["candidate_crates"]))
self.assertIn("ethos-doc-core has no selected package publication version", packet["package_version_map"])
self.assertEqual(
"not selected; package tag creation remains blocked",
packet["package_tag_name"],
)
self.assertEqual(
"not selected; package tag binding remains blocked",
packet["package_tag_source_commit"],
)
self.assertEqual(
"not prepared; current Cargo manifests remain unchanged",
packet["manifest_activation_diff"],
)
self.assertIn("public installation remains blocked", packet["public_installation_wording"])
self.assertIn("release artifacts", packet["explicit_exclusions"])
self.assertIn("project-maintained PDFium builds", packet["explicit_exclusions"])
self.assertIn("exact package tag name and package_tag_source_commit", packet["required_before_approval"])
self.assertIn("posture and claims gates after exact public installation wording changes", packet["required_before_approval"])
self.assertIn("this packet does not change Cargo manifests", packet["non_approvals"])
self.assertIn("this packet does not invite public installation", packet["non_approvals"])
self.assertIn("this packet does not approve package publication", packet["non_approvals"])
self.assertIn("real-version cargo publish remains blocked", packet["retained_blockers"])
self.assertIn('"crates/ethos-core"', cargo)
self.assertIn('"crates/ethos-verify"', cargo)
self.assertIn('"crates/ethos-pdf"', cargo)
self.assertIn("publish = false", core_manifest)
self.assertIn("publish = false", verify_manifest)
self.assertIn("publish = false", pdf_manifest)
self.assertIn('name = "ethos-core"', core_manifest)
self.assertIn('name = "ethos-verify"', verify_manifest)
self.assertIn('name = "ethos-pdf"', pdf_manifest)

def test_pdfium_boundary_keeps_ethos_pdf_held_until_confirmed(self) -> None:
approved = load_json(PREP)["approved_package_publication_prep"]
pdfium_boundary = " ".join(approved["pdfium_boundary"])
Expand Down Expand Up @@ -602,6 +704,10 @@ def test_schema_validation_covers_package_publication_prep(self) -> None:
False,
schema["$defs"]["package_publication_decision_prep_bundle"]["additionalProperties"],
)
self.assertEqual(
False,
schema["$defs"]["package_publication_approval_request_packet"]["additionalProperties"],
)
self.assertEqual(9, schema["properties"]["required_evidence"]["minItems"])
self.assertEqual(13, schema["properties"]["explicit_blockers"]["minItems"])
self.assertEqual(
Expand All @@ -628,6 +734,18 @@ def test_schema_validation_covers_package_publication_prep(self) -> None:
"required_decision_inputs"
]["minItems"],
)
self.assertEqual(
8,
schema["$defs"]["package_publication_approval_request_packet"]["properties"][
"required_before_approval"
]["minItems"],
)
self.assertEqual(
9,
schema["$defs"]["package_publication_approval_request_packet"]["properties"][
"explicit_exclusions"
]["minItems"],
)
self.assertIn("ethos-milestone-e-package-publication-approval-prep.schema.json", validate_examples)
self.assertIn("docs\" / \"milestone-e-package-publication-approval-prep.json", validate_examples)
self.assertIn("ethos-milestone-e-package-publication-approval-prep.schema.json", schemas_readme)
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,200 @@
#!/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 json
import re
import unittest
from pathlib import Path

from makefile_guard import target_block


ROOT = Path(__file__).resolve().parents[2]
PREP = ROOT / "docs/milestone-e-package-publication-approval-prep.json"
RECORD = (
ROOT
/ "docs/validation/"
"milestone-e-package-publication-decision-bundle-validation-2026-06-21.md"
)
VALIDATION_README = ROOT / "docs/validation/README.md"
CI_WORKFLOW = ROOT / ".github/workflows/ci.yml"

FORBIDDEN_SCOPE_EXPANSION = [
"public reports are approved",
"public result wording approved",
"release-ready",
"release artifact approved",
"package-ready",
"package publication is approved",
"package publication approved",
"packages are published",
"published packages",
"production-ready",
"production positioning approved",
"benchmark-validated",
"public benchmark pass",
"speed validated",
"fastest",
"launch-ready",
"hosted surface approved",
"hosted demo approved",
"demo-ready",
"performance validated",
"quality validated",
"footprint validated",
"table-quality validated",
"parser-quality validated",
]


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 load_json(path: Path) -> dict:
return json.loads(path.read_text(encoding="utf-8"))


class MilestoneEPackagePublicationDecisionBundleValidationRecordTests(unittest.TestCase):
def test_decision_bundle_record_is_indexed(self) -> None:
readme = read(VALIDATION_README)
normalized_readme = re.sub(r"\s+", " ", readme)

self.assertIn(RECORD.name, readme)
self.assertIn(
"package publication decision-bundle validation for the combined decision inputs",
normalized_readme,
)

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

self.assertIn("Validated source HEAD before this record: `63d8647`", text)
self.assertIn(
"python3 .github/scripts/test_milestone_e_package_publication_approval_prep.py",
text,
)
self.assertIn(
"python3 .github/scripts/"
"test_milestone_e_package_publication_decision_bundle_validation_record.py",
text,
)
self.assertIn("python3 .github/scripts/test_public_surface_posture.py", text)
self.assertIn("python3 .github/scripts/claims_gate.py", text)
self.assertIn("cargo build --locked -p ethos-cli", text)
self.assertIn("make milestone-e-prep PYTHON=<jsonschema-venv>/bin/python", text)
self.assertIn("git diff --check", text)

def test_record_matches_combined_decision_bundle_scope(self) -> None:
prep = load_json(PREP)
bundle = prep["package_publication_decision_prep_bundle"]
record = normalized(RECORD)

self.assertEqual("combined_decision_inputs_recorded_actions_blocked", bundle["decision_state"])
for boundary in bundle["review_boundary"]:
self.assertIn(boundary, record)
for decision_input in bundle["required_decision_inputs"]:
self.assertIn(decision_input, record)
for non_approval in bundle["non_approvals"]:
self.assertIn(non_approval, record)
for blocker in bundle["retained_blockers"]:
self.assertIn(blocker, record)
self.assertIn("Ethos remains source-only pre-alpha", record)
self.assertIn("Package publication remains blocked", record)
self.assertIn("Public installation remains blocked", record)

def test_record_matches_non_activating_approval_request_packet(self) -> None:
prep = load_json(PREP)
packet = prep["package_publication_approval_request_packet"]
record = normalized(RECORD)

self.assertEqual("approval_request_packet_recorded_publication_blocked", packet["packet_state"])
self.assertIn(packet["packet_state"], record)
for candidate in packet["candidate_crates"]:
self.assertIn(candidate, record)
for version in packet["package_version_map"]:
self.assertIn(version, record)
self.assertIn(packet["package_tag_name"], record)
self.assertIn(packet["package_tag_source_commit"], record)
self.assertIn(packet["package_tag_source_tree"], record)
self.assertIn(packet["manifest_activation_diff"], record)
self.assertIn(packet["registry_assembly_evidence"], record)
self.assertIn(packet["public_installation_wording"], record)
for exclusion in packet["explicit_exclusions"]:
self.assertIn(exclusion, record)
for required in packet["required_before_approval"]:
self.assertIn(required, record)
for non_approval in packet["non_approvals"]:
self.assertIn(non_approval, record)
for blocker in packet["retained_blockers"]:
self.assertIn(blocker, record)

def test_record_keeps_public_boundaries_explicit(self) -> None:
record = normalized(RECORD)

self.assertIn("Public reports remain blocked", record)
self.assertIn("Public result wording remains blocked", record)
self.assertIn("Release artifacts remain blocked", record)
self.assertIn("Binaries remain blocked", record)
self.assertIn("Wheels remain blocked", record)
self.assertIn("Npm packages remain blocked", record)
self.assertIn("Hosted surfaces remain blocked", record)
self.assertIn("Production positioning remains blocked", record)
self.assertIn("Public benchmark reports remain blocked", record)
self.assertIn("Public benchmark claims remain blocked", record)
self.assertIn("Project-maintained PDFium builds remain blocked", record)

def test_make_and_ci_run_record_guard_after_combined_prep(self) -> None:
make_block = target_block("milestone-e-prep")
ci = read(CI_WORKFLOW)
registry_activation_guard = (
"test_milestone_e_package_publication_registry_assembly_activation_prep.py"
)
record_guard = "test_milestone_e_package_publication_decision_bundle_validation_record.py"
command_guard = "test_milestone_e_validation_command_index.py"

for text, prefix in ((make_block, "$(PYTHON) .github/scripts/"), (ci, "python3 .github/scripts/")):
self.assertIn(prefix + record_guard, text)
self.assertEqual(1, text.count(prefix + record_guard))
self.assertLess(text.index(prefix + registry_activation_guard), text.index(prefix + record_guard))
self.assertLess(text.index(prefix + record_guard), text.index(prefix + command_guard))

def test_record_avoids_scope_expansion_language_or_private_paths(self) -> None:
lower = normalized(RECORD).lower()
raw = read(RECORD)

for phrase in FORBIDDEN_SCOPE_EXPANSION:
self.assertNotIn(phrase, lower)
self.assertNotIn("/Users/", raw)
self.assertNotIn("/private/tmp", raw)
self.assertNotIn("/private/var", raw)
self.assertNotIn("/var/folders", raw)
self.assertNotIn("saumildiwaker", raw)
self.assertNotIn("Desktop/Stuff", raw)
self.assertNotIn("project/repo/ethos", raw)
self.assertNotIn("docs/.roadmap.md.swp", raw)
self.assertNotIn("web/", raw)


if __name__ == "__main__":
unittest.main()
Original file line number Diff line number Diff line change
Expand Up @@ -104,6 +104,7 @@
"$(PYTHON) .github/scripts/test_milestone_e_package_publication_tag_creation_prep.py",
"$(PYTHON) .github/scripts/test_milestone_e_package_publication_manifest_activation_prep.py",
"$(PYTHON) .github/scripts/test_milestone_e_package_publication_registry_assembly_activation_prep.py",
"$(PYTHON) .github/scripts/test_milestone_e_package_publication_decision_bundle_validation_record.py",
"$(PYTHON) .github/scripts/test_milestone_e_validation_command_index.py",
"$(PYTHON) .github/scripts/test_milestone_e_validation_command_index_validation_record.py",
"$(PYTHON) .github/scripts/test_milestone_e_validation_record_index.py",
Expand Down
1 change: 1 addition & 0 deletions .github/scripts/test_milestone_e_prep_scope.py
Original file line number Diff line number Diff line change
Expand Up @@ -403,6 +403,7 @@ def test_make_target_is_narrow_and_guarded(self) -> None:
"$(PYTHON) .github/scripts/test_milestone_e_package_publication_tag_creation_prep.py",
"$(PYTHON) .github/scripts/test_milestone_e_package_publication_manifest_activation_prep.py",
"$(PYTHON) .github/scripts/test_milestone_e_package_publication_registry_assembly_activation_prep.py",
"$(PYTHON) .github/scripts/test_milestone_e_package_publication_decision_bundle_validation_record.py",
"$(PYTHON) .github/scripts/test_milestone_e_validation_command_index.py",
"$(PYTHON) .github/scripts/test_milestone_e_validation_command_index_validation_record.py",
"$(PYTHON) .github/scripts/test_milestone_e_validation_record_index.py",
Expand Down
Loading
Loading