diff --git a/tests/test_completeness.py b/tests/test_completeness.py index 2103734..e5c1364 100644 --- a/tests/test_completeness.py +++ b/tests/test_completeness.py @@ -22,12 +22,14 @@ from __future__ import annotations import ast +import importlib.util import re import shutil from pathlib import Path from conftest import ( # noqa: F401 (sys.path side effect) - BASE_REPO, PINNED_REVISION, FakeGit, REPO_ROOT, staging_fragment, thin_fragment, + BASE_REPO, PINNED_REVISION, FakeGit, REPO_ROOT, carved_module_path, + staging_fragment, thin_fragment, ) from doc_health import corpus @@ -36,7 +38,13 @@ from openxdox import completeness as C from openxdox.generator import generate_snapshot, live_topic_health -SCRIPTS = REPO_ROOT / "scripts" +# WHERE THE PURITY AND GATING-BOUND CHECKS READ, after the carve (plan 038 +# T103). Before it, one import root, openxFactory's `scripts/`, held both the +# scoring module and `doc_health`. Now the scoring module and its importers are +# this leg's, under `src/` (the `openxdox` package's import root), and +# `doc_health` is openxFactory's, read where this process imports it from: the +# composed tree. +SRC = REPO_ROOT / "src" def _tree(tmp_path: Path) -> Path: @@ -174,7 +182,7 @@ def test_the_scoring_module_touches_nothing_outside_its_arguments(): # builtin escape hatch is called either. `doc_health.lines` is admitted # specifically because IT is itself pure by the same standard (asserted # below, not assumed): it imports only `re`, and defines no I/O. - tree = ast.parse((SCRIPTS / "ideation_dashboard" / "completeness.py").read_text("utf-8")) + tree = ast.parse(carved_module_path("completeness.py").read_text("utf-8")) modules: set[str] = set() for node in ast.walk(tree): if isinstance(node, ast.Import): @@ -187,7 +195,8 @@ def test_the_scoring_module_touches_nothing_outside_its_arguments(): if isinstance(node, ast.Call) and isinstance(node.func, ast.Name)} assert not called & {"open", "eval", "exec", "input", "print", "compile"}, called - lines_tree = ast.parse((SCRIPTS / "doc_health" / "lines.py").read_text("utf-8")) + lines_tree = ast.parse( + Path(importlib.util.find_spec("doc_health.lines").origin).read_text("utf-8")) lines_modules: set[str] = set() for node in ast.walk(lines_tree): if isinstance(node, ast.Import): @@ -501,14 +510,15 @@ def test_the_scoring_modules_only_importers_are_the_generator_and_the_guard(): r"|from\s+\.completeness\s+import\b" r"|import\s+\S*\bcompleteness\b)") importers = sorted( - p.relative_to(SCRIPTS).as_posix() - for p in SCRIPTS.rglob("*.py") + p.relative_to(SRC).as_posix() + for p in SRC.rglob("*.py") if p.name != "completeness.py" and import_re.search(p.read_text("utf-8"))) - assert importers == ["ideation_dashboard/generator.py", - "ideation_dashboard/kickoff.py"], importers + assert importers == ["openxdox/generator.py", + "openxdox/kickoff.py"], importers # ...and no doc-health / readiness path consults the score at all: the # per-document signal produces no finding and feeds no readiness tier. - for module in sorted((SCRIPTS / "doc_health").rglob("*.py")): + [doc_health] = importlib.util.find_spec("doc_health").submodule_search_locations + for module in sorted(Path(doc_health).rglob("*.py")): text = module.read_text("utf-8") assert not import_re.search(text), module assert "topic_health" not in text and "READY_MIN_SCORE" not in text, module diff --git a/tests/test_doxbench_packet.py b/tests/test_doxbench_packet.py index c097950..1b38f63 100644 --- a/tests/test_doxbench_packet.py +++ b/tests/test_doxbench_packet.py @@ -33,7 +33,7 @@ import pytest from conftest import REPO_ROOT # noqa: F401 (sys.path side effect) -from conftest import assert_not_at_this_leg, carved_module_path +from conftest import assert_not_at_this_leg, carved_module_path, openxfactory_root from opendox import doxbench_knowledge as kn # noqa: E402 from opendox import doxbench_packet as pk # noqa: E402 @@ -593,7 +593,10 @@ def test_the_status_read_agrees_with_the_repositorys_own_corpus_reader(): assert pk.STATUS_SCAN_LINES == 15 checked = 0 - for path in sorted((REPO_ROOT / "docs").rglob("*.md"))[:80]: + # THE REPOSITORY'S OWN CORPUS is openxFactory's, the one its corpus reader + # reads, so it is read from the composed tree (plan 038 T103): this + # checkout's own `docs/` holds too few documents for the check to be one. + for path in sorted((openxfactory_root() / "docs").rglob("*.md"))[:80]: text = path.read_text(encoding="utf-8", errors="replace") assert pk.lifecycle_status(text) == parse_status(text), path checked += 1 diff --git a/tests/test_gate_console.py b/tests/test_gate_console.py index 5ea209e..e66ee9c 100644 --- a/tests/test_gate_console.py +++ b/tests/test_gate_console.py @@ -42,7 +42,10 @@ import pytest import yaml -from conftest import BASE_REPO, PINNED_REVISION, REPO_ROOT, FakeGit, find_openxfactory_validator +from conftest import ( + BASE_REPO, PINNED_REVISION, REPO_ROOT, FakeGit, find_openxfactory_validator, + openxfactory_root, +) from doc_health import families from openxdox import gate_console as gc @@ -867,7 +870,7 @@ def test_the_cli_tells_the_human_the_snapshot_was_preserved(tmp_path, capsys): _fragment(root).parent.mkdir(parents=True, exist_ok=True) _fragment(root).write_text(LIVE, encoding="utf-8") - from ideation_dashboard import cli + from opendox import cli # Through the REAL parser, not a hand-built Namespace: a Namespace's field set # can drift from the CLI's while the test keeps passing. @@ -903,7 +906,9 @@ def test_the_cli_tells_the_human_the_snapshot_was_preserved(tmp_path, capsys): # demote that returned it. _SUPPORT_SPEC = importlib.util.spec_from_file_location( - "proposal_support_for_round_trip", REPO_ROOT / "scripts" / "proposal-support.py") + "proposal_support_for_round_trip", + # openxFactory's own forward gate, read from the composed tree (plan 038 T103). + openxfactory_root() / "scripts" / "proposal-support.py") support = importlib.util.module_from_spec(_SUPPORT_SPEC) sys.modules[_SUPPORT_SPEC.name] = support _SUPPORT_SPEC.loader.exec_module(support) @@ -1814,7 +1819,7 @@ def test_the_hostile_gate_descriptors_still_reach_the_real_cli(tmp_path): refuse it — inside the process, which is the layer that can refuse.""" import shlex - from ideation_dashboard import cli + from opendox import cli out = _hostile_gate_commands(tmp_path) parser = cli.build_parser() @@ -2251,22 +2256,35 @@ def test_new_verb_constants_are_the_contract_spellings(): import subprocess as _subprocess import sys as _sys +from conftest import CONTRACTS_DIR as _CONTRACTS_DIR from conftest import find_openxfactory_validator as _find_validator +from openxdox import contracts as _packaged_contracts _VALIDATOR = _find_validator() _NEW_VERBS = ("promote-to-staging", "derive-possibles", "research-brief") def _contract_carries_the_new_verbs() -> bool: - """True once the reachable openxFactory contract enumerates the new verbs.""" - if _VALIDATOR is None: - return False - schemas = _VALIDATOR.parent.parent / "contracts" / "schemas" - try: - intent = (schemas / "gate-intent.schema.yaml").read_text(encoding="utf-8") - record = (schemas / "gate-action-record.schema.yaml").read_text(encoding="utf-8") - except OSError: + """True once the reachable openxFactory contract enumerates the new verbs. + + Each schema is read where the pinned validator reads it (plan 038 T103, + OQ-R9-3). Before the carve both sat beside the validator; this leg carries + no `contracts/schemas/` there, so that read found nothing and the group + never ran. `gate-action-record`, one of the validator's own three, is the + installed distribution's packaged copy. `gate-intent`, one of the family's + other seven, is in the contracts directory `CONTRACTS_DIR` names, which + only a composed run supplies; a lone checkout has none, and there the + group still skips. + + Once a run supplies that directory, a schema it cannot read is a broken + composed source, not an unreleased contract: the read error is left to + fail collection, so the five cases cannot skip green over it.""" + if _VALIDATOR is None or _CONTRACTS_DIR is None: return False + intent = (_CONTRACTS_DIR / "schemas" / "gate-intent.schema.yaml").read_text( + encoding="utf-8") + record = _packaged_contracts.verified_path("gate-action-record").read_text( + encoding="utf-8") return all(v in intent and v in record for v in _NEW_VERBS) diff --git a/tests/test_round_trip.py b/tests/test_round_trip.py index 6a7307f..efa6243 100644 --- a/tests/test_round_trip.py +++ b/tests/test_round_trip.py @@ -26,7 +26,7 @@ import pytest -from conftest import REPO_ROOT +from conftest import carved_module_path, openxfactory_root from doc_health import families from openxdox import gate_console as gc @@ -461,7 +461,9 @@ def test_round_trip_and_doc_health_agree_about_every_fence(name): # fenced-flag vector each module computes, which is the thing all four actually # share. -_MOVER = REPO_ROOT / "scripts" / "proposal-support.py" +# openxFactory's own forward gate, which the carve left in openxFactory: read +# from the composed tree (plan 038 T103). +_MOVER = openxfactory_root() / "scripts" / "proposal-support.py" def _mover_module(): @@ -727,9 +729,11 @@ def test_the_shared_predicate_is_spelled_the_same_in_all_three(): """The algorithm, not just its outcome on these fixtures: all three lstrip (or trimStart) and then test the literal three backticks. A fixture set can only ever sample the input space; this pins the rule itself.""" - mine = (REPO_ROOT / "scripts/ideation_dashboard/round_trip.py").read_text( - encoding="utf-8") - fam = (REPO_ROOT / "scripts/doc_health/families.py").read_text(encoding="utf-8") + # Each read where its module now lives (plan 038 T103): `round_trip.py` at + # its carved home, this leg's `src/openxdox/`, and `families.py` where this + # process imports it from, openxFactory's composed tree. + mine = carved_module_path("round_trip.py").read_text(encoding="utf-8") + fam = Path(families.__file__).read_text(encoding="utf-8") js = OUTLINE_MODEL_JS.read_text(encoding="utf-8") assert 'return line.lstrip().startswith("```")' in mine assert 'if line.lstrip().startswith("```")' in fam