Files
rune/tests/test_auto_execution_context.py
Nick be6bc17537 feat(t21.4): post-restart final V1 proof — governed rollback API, automatic project execution context, material-change closure, structural coverage
- scripts/rollback_generation.py: canonical rollback_generation() on the
  deploy_promotion machinery (shared lock, refuse-before-mutation,
  ancestor-only eligibility, identity+health qualification, durable
  history, revert on failed health). Live B->A->B proven with refusals.
- tool_executor._resolve_execution_cwd: automatic project execution
  context (workdir > project registry resolve_cwd > process); unknown
  projects refused pre-execution. Live A-F proven.
- construction_interface.close(): auto material_change + revision +
  drift/revise flow (rule 19Q.2 integrated, proven live).
- spine: entry-point seeding (__main__ is a runtime path invisible to
  the import graph); subsystem belongs_to membership via coverage audit.
- scripts/structural_coverage_audit.py: 237 modules A157/B41/C28/D11/E0,
  100% meaningful coverage, 100% impl ownership, 0 significant unknowns.
- Scaffolding internal 1.0.2 -> 1.0.3 (PATCH, contract unchanged).
- Gate object T21.4 persisted (12 mandatory gates, GateAccounting).
2026-09-17 01:38:39 +01:00

121 lines
4.3 KiB
Python

"""test_auto_execution_context.py — T21.4 §21.4E: automatic project
execution context.
The model names the PROJECT; the execution layer resolves cwd through
ProjectRegistry.resolve_cwd (repo wins over scaffold; active worktree wins
over repo). Unknown projects are refused BEFORE execution. No manual `cd`.
"""
from __future__ import annotations
import json
import sys
from pathlib import Path
import pytest
_SRC = str(Path(__file__).resolve().parent.parent / "src")
if _SRC not in sys.path:
sys.path.insert(0, _SRC)
from rune import tool_executor as te
@pytest.fixture
def two_projects(tmp_path, monkeypatch):
"""rune @ repo-a (with an active worktree seam) + ext @ repo-b."""
store = tmp_path / "store" / "scaffold"
store.mkdir(parents=True)
repo_a = tmp_path / "repo-a"
repo_b = tmp_path / "repo-b"
worktree_a = tmp_path / "wt-a"
for p in (repo_a, repo_b, worktree_a):
p.mkdir()
reg = {"schema_version": 1, "updated_at": "t", "projects": {
"rune": {
"project_id": "rune",
"scaffold_location": str(tmp_path / "scaffold-a"), # NOT the repo
"repo_identity": f"git:{repo_a}",
"active_worktree": "",
},
"ext": {
"project_id": "ext",
"scaffold_location": str(tmp_path / "scaffold-b"),
"repo_identity": f"git:{repo_b}",
"active_worktree": "",
},
}}
(store / "project_registry.json").write_text(json.dumps(reg))
monkeypatch.setenv("RUNE_SCAFFOLD_STORE", str(store))
return {"repo_a": repo_a, "repo_b": repo_b, "worktree_a": worktree_a,
"store": store, "reg": reg}
def _pwd(args: dict) -> dict:
# _run_command directly: the approval flow (prepare/approve) wraps it
# in production and is exercised by the tool-authority tests.
return te._run_command({"command": "pwd", **args})
# A. known project → executes in its repo automatically
def test_rune_auto_binding(two_projects):
r = _pwd({"project": "rune"})
assert r["success"] and r["cwd_source"] == "project_registry"
assert r["result"].strip() == str(two_projects["repo_a"])
# B. disposable external project → executes in ITS repo
def test_external_auto_binding(two_projects):
r = _pwd({"project": "ext"})
assert r["result"].strip() == str(two_projects["repo_b"])
# C. switch rune → ext → rune: no cwd leakage
def test_switch_no_leakage(two_projects):
r1 = _pwd({"project": "rune"})
r2 = _pwd({"project": "ext"})
r3 = _pwd({"project": "rune"})
assert r1["result"].strip() == str(two_projects["repo_a"])
assert r2["result"].strip() == str(two_projects["repo_b"])
assert r3["result"].strip() == str(two_projects["repo_a"])
# D. active worktree wins over repo root
def test_worktree_precedence(two_projects):
store = two_projects["store"]
reg = json.loads((store / "project_registry.json").read_text())
reg["projects"]["rune"]["active_worktree"] = str(two_projects["worktree_a"])
(store / "project_registry.json").write_text(json.dumps(reg))
r = _pwd({"project": "rune"})
assert r["result"].strip() == str(two_projects["worktree_a"])
# E. unknown project mutation refused BEFORE execution
def test_unknown_project_refused(two_projects):
r = te._run_command({
"command": "touch /tmp/pwned-marker-t214", "project": "ghost"})
assert not r["success"]
assert "refused" in r["error"].lower()
assert not Path("/tmp/pwned-marker-t214").exists()
# F. repo path wins over scaffold path (scaffold is never an execution cwd)
def test_repo_wins_over_scaffold(two_projects):
r = _pwd({"project": "rune"})
assert r["result"].strip() == str(two_projects["repo_a"])
assert r["result"].strip() != str(two_projects["reg"]["projects"]["rune"]["scaffold_location"])
# explicit governed workdir outranks project resolution
def test_explicit_workdir_precedence(two_projects):
other = two_projects["repo_a"].parent / "governed-dir"
other.mkdir()
r = _pwd({"workdir": str(other), "project": "rune"})
assert r["result"].strip() == str(other)
assert r["cwd_source"] == "explicit"
# project-less commands keep process cwd (nothing to bind)
def test_project_less_uses_process_cwd(two_projects):
r = te._run_command({"command": "pwd"})
assert r["success"] and r["cwd_source"] == "process"