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

@ -6,6 +6,7 @@ from pathlib import Path
import pytest
from rein_aharness.metrics import external_metrics_dir
from rein_aharness.repository_grant import RepositoryGrant, RepositoryGrantError
from rein_aharness.repository_transaction import RepositoryTransaction
from rein_aharness.runner import run_task
@ -242,18 +243,129 @@ def test_taskspec_file_wraps_invalid_repository_grant(tmp_path: Path) -> None:
TaskSpec.from_file(task_file)
def test_runner_refuses_grant_before_adapter_dispatch_or_mutation(tmp_path: Path) -> None:
def test_runner_accepts_granted_commit_and_keeps_checkout_clean(tmp_path: Path) -> None:
repo = _make_repo(tmp_path)
class Adapter:
def execute_prompt(self, prompt: str, config: object):
docs = repo / "docs"
docs.mkdir()
(docs / "result.md").write_text("accepted\n", encoding="utf-8")
subprocess.run(["git", "add", "."], cwd=repo, check=True)
subprocess.run(
[
"git",
"-c",
"user.email=test@example.invalid",
"-c",
"user.name=test",
"commit",
"-qm",
"accepted result",
],
cwd=repo,
check=True,
)
from llm_connect.models import LLMResponse
return LLMResponse(
content="done",
model="fake",
usage={},
finish_reason="stop",
)
result = run_task(
TaskSpec(
title="bounded change",
description="update docs",
target_repo=repo,
repository_grant=RepositoryGrant.from_mapping(_grant()),
),
adapter=Adapter(),
report_to_hub=False,
write_metrics=True,
)
assert result.ok is True
assert result.committed is True
assert result.transaction is not None
assert result.transaction["acceptance"]["accepted"] is True
assert result.transaction["acceptance"]["changed_paths"] == ["docs/result.md"]
assert result.transaction["repository_grant"]["grant_id"]
assert result.transaction["metrics"] == {
"storage": "external",
"session_id": result.transaction["transaction_id"],
"projection_ready": True,
}
assert _git(repo, "status", "--porcelain=v2") == ""
assert not (repo / ".kaizen" / "metrics").exists()
metric_path = external_metrics_dir(repo, "coach") / "executions.jsonl"
metric_record = json.loads(metric_path.read_text(encoding="utf-8").strip())
assert metric_record["session_id"] == result.transaction["transaction_id"]
def test_runner_rejects_commit_outside_repository_grant(tmp_path: Path) -> None:
repo = _make_repo(tmp_path)
class Adapter:
def execute_prompt(self, prompt: str, config: object):
(repo / "UNRELATED.md").write_text("not granted\n", encoding="utf-8")
subprocess.run(["git", "add", "."], cwd=repo, check=True)
subprocess.run(
[
"git",
"-c",
"user.email=test@example.invalid",
"-c",
"user.name=test",
"commit",
"-qm",
"ungranted result",
],
cwd=repo,
check=True,
)
from llm_connect.models import LLMResponse
return LLMResponse(
content="done",
model="fake",
usage={},
finish_reason="stop",
)
result = run_task(
TaskSpec(
title="bounded change",
description="update docs",
target_repo=repo,
repository_grant=RepositoryGrant.from_mapping(_grant()),
),
adapter=Adapter(),
report_to_hub=False,
write_metrics=True,
)
assert result.ok is False
assert result.committed is True
assert result.reason.startswith("repository acceptance failed: path-not-granted")
assert result.transaction is not None
assert "acceptance" not in result.transaction
def test_runner_refuses_grant_when_durable_metrics_are_disabled(
tmp_path: Path,
) -> None:
repo = _make_repo(tmp_path)
called = False
class Adapter:
def execute_prompt(self, prompt: str, config: object) -> None:
def execute_prompt(self, prompt: str, config: object):
nonlocal called
called = True
raise AssertionError("adapter must not be dispatched")
head_before = _git(repo, "rev-parse", "HEAD")
status_before = _git(repo, "status", "--porcelain=v2")
result = run_task(
TaskSpec(
title="bounded change",
@ -267,11 +379,69 @@ def test_runner_refuses_grant_before_adapter_dispatch_or_mutation(tmp_path: Path
)
assert result.ok is False
assert result.committed is False
assert result.reason.startswith("refused: repository_grant enforcement")
assert "require durable external metrics" in result.reason
assert called is False
assert _git(repo, "rev-parse", "HEAD") == head_before
assert _git(repo, "status", "--porcelain=v2") == status_before
def test_runner_fails_granted_result_when_external_metrics_cannot_persist(
tmp_path: Path,
monkeypatch: pytest.MonkeyPatch,
) -> None:
repo = _make_repo(tmp_path)
class Adapter:
def execute_prompt(self, prompt: str, config: object):
docs = repo / "docs"
docs.mkdir()
(docs / "result.md").write_text("accepted\n", encoding="utf-8")
subprocess.run(["git", "add", "."], cwd=repo, check=True)
subprocess.run(
[
"git",
"-c",
"user.email=test@example.invalid",
"-c",
"user.name=test",
"commit",
"-qm",
"accepted result",
],
cwd=repo,
check=True,
)
from llm_connect.models import LLMResponse
return LLMResponse(
content="done",
model="fake",
usage={},
finish_reason="stop",
)
def fail_metrics(*args: object, **kwargs: object) -> None:
raise OSError("state volume unavailable")
monkeypatch.setattr(
"rein_aharness.runner.metrics.record_external_execution",
fail_metrics,
)
result = run_task(
TaskSpec(
title="bounded change",
description="update docs",
target_repo=repo,
repository_grant=RepositoryGrant.from_mapping(_grant()),
),
adapter=Adapter(),
report_to_hub=False,
write_metrics=True,
)
assert result.ok is False
assert result.reason == "required external metrics persistence failed (OSError)"
assert result.transaction is not None
assert result.transaction["acceptance"]["accepted"] is True
assert _git(repo, "status", "--porcelain=v2") == ""
def _git(repo: Path, *args: str) -> str: