refactor(tooling): T-1273 — the Blender carve-out, and a guard that keeps it carved

35 payloads move to tooling/scripts/blender/ and stay outside package scope.
They run under Blender's bundled Python, which cannot see the repo venv, so
they physically cannot import tooling.core — holding them to the D-263 contract
would either fail the gate forever or force the contract to be weakened for
everyone, and the second is how a gate stops meaning anything.

Count verified by import rather than filename: 33 import bpy/bmesh directly,
and the two that do not are still payloads per their own usage lines.
garment-fit/make_logo.py is the one genuine non-payload and stays for T-1290.

The bash wrapper is retired rather than kept. Keeping it would have put the
install-resolution logic in two places, which is the duplication T-1286 had
just finished collapsing three copies of. domains/blender/service.py owns the
decisions — resolve_blender (native beats flatpak, ordering preserved),
resolve_payload, absolutise — and only run_payload performs. test_blender.py
pins all of them without launching Blender, which matters here more than
usual: the thing being launched is a 200 MB GUI application that writes GLBs.

`reach blender run` takes a registered payload name OR a path to any script,
because the wrapper served both — the spikes and the glb-gen skill hand it
one-off scripts of their own. An unknown name enumerates all 35 and exits 2.

The exclusion now defends itself. check_carve_out_stays_carved fails if
`scripts` is added to PACKAGE_ROOTS, if the payload directory empties (an empty
exclusion proves nothing), or if an __init__.py appears there (which would make
the payloads importable — the coupling the carve-out exists to prevent). All
three arms mutation-proved.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
2026-09-02 20:55:52 +02:00
co-authored by Claude Opus 5
parent bb329e3294
commit 201dabd19b
63 changed files with 555 additions and 49 deletions
+49
View File
@@ -58,6 +58,18 @@ FORBIDDEN_IMPORTS = {"typer", "click"}
# needing to be widened by hand.
PACKAGE_ROOTS = ("main.py", "__init__.py", "core", "domains")
# tooling/scripts/blender/ is DELIBERATELY not in that tuple, and must never be
# added (T-1273). Those 35 files run under Blender's BUNDLED Python, which has
# no access to the repo venv — they physically cannot `import tooling.core`, so
# they cannot carry `@command` or print through `console`. Holding them to the
# contract would either fail this gate forever or force the contract to be
# weakened for everyone, and the second is how a gate stops meaning anything.
#
# It reads like an oversight, so `check_carve_out_stays_carved` below asserts
# the exclusion on purpose: widening PACKAGE_ROOTS to cover them fails loudly
# instead of quietly redefining what conformance means.
PAYLOAD_ROOTS = ("scripts",)
def _package_files() -> list[Path]:
"""Every .py that is part of the reach package — not the legacy scripts."""
@@ -303,6 +315,42 @@ def check_exit_codes(failures: list[str]) -> None:
)
def check_carve_out_stays_carved(failures: list[str]) -> None:
"""(7) The Blender payloads stay outside package scope, and stay populated.
Two failure modes, opposite directions:
- Someone widens PACKAGE_ROOTS to cover `scripts/` because the exclusion
looks like an oversight. Every payload then fails five invariants it
cannot satisfy, and the likely repair is to weaken the invariants.
- The payload directory quietly empties — a move, a bad merge — and the
exclusion goes on passing because excluding nothing is trivially fine.
An exception that guards nothing should not read as healthy.
"""
for root in PAYLOAD_ROOTS:
if root in PACKAGE_ROOTS:
failures.append(
f"[carve-out] '{root}' was added to PACKAGE_ROOTS — the Blender "
"payloads run under Blender's bundled Python and cannot import "
"tooling.core; see the comment above PAYLOAD_ROOTS (T-1273)"
)
payloads = list((PACKAGE / "scripts" / "blender").glob("*.py"))
if not payloads:
failures.append(
"[carve-out] tooling/scripts/blender/ holds no payloads — either the "
"carve-out was undone or they moved; an empty exclusion proves nothing"
)
# The payloads must not be reachable as modules either: an __init__.py would
# make them importable and invite exactly the coupling the carve-out prevents.
if (PACKAGE / "scripts" / "__init__.py").exists():
failures.append(
"[carve-out] tooling/scripts/__init__.py exists — that makes the "
"payload tree an importable package, which is what the carve-out avoids"
)
def main() -> int:
if shutil.which("reach") is None:
print("test_conformance: `reach` is not on PATH.\n Fix: make install-reach", file=sys.stderr)
@@ -316,6 +364,7 @@ def main() -> int:
check_commands_decorated(failures)
check_errors_name_a_remedy(failures)
check_exit_codes(failures)
check_carve_out_stays_carved(failures)
if failures:
print("test_conformance: FAIL", file=sys.stderr)