feat(runtime): enforce governed mutation boundaries

Assistant: codex
Assistant-Model: gpt-5.6-sol
Assistant-Session: 01a06ba0-10aa-7ea0-b20a-4f3fac39efe9
This commit is contained in:
tegwick 2026-09-04 11:25:07 +02:00
parent e3c6124e22
commit 20e6f381f6
28 changed files with 1068 additions and 154 deletions

View file

@ -78,9 +78,15 @@ def test_transaction_captures_clean_branch_and_remote_refs(tmp_path: Path) -> No
def test_dirty_baseline_is_refused_without_changing_user_files(tmp_path: Path) -> None:
repo = _make_repo(tmp_path)
subprocess.run(
["git", "update-ref", "refs/remotes/origin/main", "HEAD"],
cwd=repo,
check=True,
)
changed = repo / "README.md"
changed.write_text("operator change\n", encoding="utf-8")
before = _status(repo)
remote_refs_before = _remote_refs(repo)
with pytest.raises(DirtyRepositoryError) as excinfo:
with RepositoryTransaction(repo, state_dir=tmp_path / "state"):
@ -90,6 +96,7 @@ def test_dirty_baseline_is_refused_without_changing_user_files(tmp_path: Path) -
assert excinfo.value.baseline.dirty_entries == 1
assert changed.read_text(encoding="utf-8") == "operator change\n"
assert _status(repo) == before
assert _remote_refs(repo) == remote_refs_before
# Refusal released the lock; inspection can explicitly opt into dirty state.
with RepositoryTransaction(
@ -469,3 +476,18 @@ def _status(repo: Path) -> str:
capture_output=True,
text=True,
).stdout
def _remote_refs(repo: Path) -> str:
return subprocess.run(
[
"git",
"for-each-ref",
"--format=%(refname) %(objectname)",
"refs/remotes",
],
cwd=repo,
check=True,
capture_output=True,
text=True,
).stdout