- 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).
121 lines
4.3 KiB
Python
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"
|