feat(runtime): consume governed Activity Core closes
Assistant: codex Assistant-Model: gpt-5.6-sol Assistant-Session: 01a06ba0-10aa-7ea0-b20a-4f3fac39efe9
This commit is contained in:
parent
0e6d795aa4
commit
d00ffcb402
22 changed files with 1218 additions and 148 deletions
|
|
@ -2,6 +2,7 @@
|
|||
|
||||
from __future__ import annotations
|
||||
|
||||
import json
|
||||
import subprocess
|
||||
import time
|
||||
from pathlib import Path
|
||||
|
|
@ -14,14 +15,17 @@ from rein_aharness.claim_loop import (
|
|||
process_one,
|
||||
poll_peek,
|
||||
)
|
||||
from rein_aharness.close_outbox import CloseOutbox, CloseRequest
|
||||
from rein_aharness.execution_cancel import ExecutionCancel, active_cancel
|
||||
from rein_aharness.glas_execution import GLAS_APPROACH, GlasExecutionError
|
||||
from rein_aharness.metrics import external_metrics_dir
|
||||
from rein_aharness.ops_run_client import (
|
||||
ActivityCoreOpsClient,
|
||||
OpsRun,
|
||||
OpsRunConfig,
|
||||
OpsRunError,
|
||||
)
|
||||
from rein_aharness.repository_grant import RepositoryGrant
|
||||
from rein_aharness.repository_transaction import RepositoryTransaction
|
||||
|
||||
|
||||
|
|
@ -75,6 +79,45 @@ def _profiled_case(
|
|||
return repo, run, client
|
||||
|
||||
|
||||
def _grant(*paths: str) -> RepositoryGrant:
|
||||
return RepositoryGrant.from_mapping(
|
||||
{
|
||||
"version": "1",
|
||||
"allowed_paths": list(paths or ("docs/",)),
|
||||
"commit_count": {"min": 1, "max": 1},
|
||||
"publish": False,
|
||||
}
|
||||
)
|
||||
|
||||
|
||||
def _commit_file(repo: Path, relative: str, value: str = "result\n") -> str:
|
||||
path = repo / relative
|
||||
path.parent.mkdir(parents=True, exist_ok=True)
|
||||
path.write_text(value, encoding="utf-8")
|
||||
subprocess.run(["git", "add", relative], cwd=repo, check=True)
|
||||
subprocess.run(
|
||||
[
|
||||
"git",
|
||||
"-c",
|
||||
"user.email=test@example.invalid",
|
||||
"-c",
|
||||
"user.name=test",
|
||||
"commit",
|
||||
"-qm",
|
||||
"profiled result",
|
||||
],
|
||||
cwd=repo,
|
||||
check=True,
|
||||
)
|
||||
return subprocess.run(
|
||||
["git", "rev-parse", "HEAD"],
|
||||
cwd=repo,
|
||||
check=True,
|
||||
capture_output=True,
|
||||
text=True,
|
||||
).stdout.strip()
|
||||
|
||||
|
||||
def test_process_one_empty() -> None:
|
||||
client = MagicMock(spec=ActivityCoreOpsClient)
|
||||
client.config = OpsRunConfig(worker_id="w", lease_seconds=90)
|
||||
|
|
@ -150,6 +193,31 @@ def test_process_one_failure_reopens() -> None:
|
|||
assert client.fail.call_args.kwargs["reopen"] is True
|
||||
|
||||
|
||||
def test_grant_without_authoritative_profile_refuses_before_execution() -> None:
|
||||
client = MagicMock(spec=ActivityCoreOpsClient)
|
||||
client.config = OpsRunConfig(worker_id="w", lease_seconds=90)
|
||||
run = _claimed_run()
|
||||
run.repository_grant = _grant("docs/")
|
||||
client.claim.return_value = [run]
|
||||
client.fail.return_value = OpsRun(
|
||||
id=run.id,
|
||||
activity_definition_id="def",
|
||||
idempotency_key="k",
|
||||
target_repo=run.target_repo,
|
||||
title=run.title,
|
||||
description="",
|
||||
state="failed",
|
||||
)
|
||||
|
||||
with patch("rein_aharness.claim_loop.execute_approach") as execute:
|
||||
result = process_one(client)
|
||||
|
||||
assert result.ok is False
|
||||
assert "requires an authoritative harness_profile_ref" in result.reason
|
||||
execute.assert_not_called()
|
||||
assert client.fail.call_args.kwargs["reopen"] is False
|
||||
|
||||
|
||||
def test_process_one_refuses_close_after_lease_loss() -> None:
|
||||
client = MagicMock(spec=ActivityCoreOpsClient)
|
||||
client.config = OpsRunConfig(worker_id="w", lease_seconds=90)
|
||||
|
|
@ -340,7 +408,8 @@ def test_profiled_run_uses_glas_and_completes_with_full_result(
|
|||
complete_run_id = client.complete.call_args.args[0]
|
||||
complete_result = client.complete.call_args.kwargs["result"]
|
||||
assert complete_run_id == run.id
|
||||
assert complete_result["evidence"] == gateway_result["evidence"]
|
||||
assert complete_result["execution_evidence"] == gateway_result["evidence"]
|
||||
assert "tool_output" not in complete_result
|
||||
assert complete_result["repository_transaction"] == transaction
|
||||
client.fail.assert_not_called()
|
||||
select.assert_not_called()
|
||||
|
|
@ -379,7 +448,7 @@ def test_profile_refusal_fails_terminally_without_legacy_fallback(
|
|||
execute.assert_not_called()
|
||||
|
||||
|
||||
def test_profiled_signal_cancellation_releases_repository_lock(
|
||||
def test_profiled_signal_cancellation_releases_lock_and_durably_fails(
|
||||
tmp_path: Path,
|
||||
) -> None:
|
||||
repo, _run, client = _profiled_case(tmp_path)
|
||||
|
|
@ -397,7 +466,9 @@ def test_profiled_signal_cancellation_releases_repository_lock(
|
|||
assert result.ok is False
|
||||
assert result.reason == "execution cancelled (signal)"
|
||||
client.complete.assert_not_called()
|
||||
client.fail.assert_not_called()
|
||||
client.fail.assert_called_once()
|
||||
assert client.fail.call_args.kwargs["reopen"] is False
|
||||
assert "repository_transaction" in client.fail.call_args.kwargs["result"]
|
||||
with RepositoryTransaction(repo) as retry:
|
||||
assert retry.locked is True
|
||||
|
||||
|
|
@ -416,12 +487,240 @@ def test_profiled_close_failure_happens_after_repository_lock_release(
|
|||
result = process_one(client)
|
||||
|
||||
assert result.ok is False
|
||||
assert result.reason.startswith("close ops_run failed:")
|
||||
assert result.reason == "close evidence remains pending"
|
||||
assert "repository_transaction" in result.detail
|
||||
with RepositoryTransaction(repo) as retry:
|
||||
assert retry.locked is True
|
||||
|
||||
|
||||
def test_granted_profile_accepts_commit_records_metrics_and_durable_close(
|
||||
tmp_path: Path,
|
||||
) -> None:
|
||||
repo, run, client = _profiled_case(tmp_path)
|
||||
run.repository_grant = _grant("docs/")
|
||||
client.complete.return_value = OpsRun(
|
||||
id=run.id,
|
||||
activity_definition_id="def",
|
||||
idempotency_key="k",
|
||||
target_repo=run.target_repo,
|
||||
title=run.title,
|
||||
description="",
|
||||
state="succeeded",
|
||||
close_disposition="applied",
|
||||
)
|
||||
outbox = CloseOutbox(state_dir=tmp_path / "state")
|
||||
|
||||
def execute(*_args, **_kwargs):
|
||||
head = _commit_file(repo, "docs/result.md")
|
||||
return {
|
||||
"ok": True,
|
||||
"evidence": {
|
||||
"outcome": "succeeded",
|
||||
"commit_sha": head,
|
||||
"provider_response": {"secret": "must-not-persist"},
|
||||
},
|
||||
"tool_output": "must-not-persist",
|
||||
}
|
||||
|
||||
with patch("rein_aharness.claim_loop.execute_profiled_run", side_effect=execute):
|
||||
result = process_one(client, outbox=outbox)
|
||||
|
||||
assert result.ok is True
|
||||
transaction = result.detail["repository_transaction"]
|
||||
assert transaction["acceptance"]["accepted"] is True
|
||||
assert transaction["acceptance"]["changed_paths"] == ["docs/result.md"]
|
||||
assert transaction["repository_grant"]["grant_id"] == run.repository_grant.grant_id
|
||||
assert transaction["metrics"]["session_id"] == transaction["transaction_id"]
|
||||
assert outbox.status() == {"pending": 0, "delivered": 1, "quarantined": 0}
|
||||
|
||||
close_payload = client.complete.call_args.kwargs["result"]
|
||||
serialized = json.dumps(close_payload)
|
||||
assert "tool_output" not in serialized
|
||||
assert "provider_response" not in serialized
|
||||
assert close_payload["repository_transaction"]["acceptance"]["accepted"] is True
|
||||
|
||||
ledger = external_metrics_dir(repo, "rein-aharness") / "executions.jsonl"
|
||||
record = json.loads(ledger.read_text(encoding="utf-8").strip())
|
||||
assert record["success"] is True
|
||||
assert record["session_id"] == transaction["transaction_id"]
|
||||
|
||||
|
||||
def test_granted_profile_rejects_out_of_grant_commit_and_closes_failed(
|
||||
tmp_path: Path,
|
||||
) -> None:
|
||||
repo, run, client = _profiled_case(tmp_path)
|
||||
run.repository_grant = _grant("docs/")
|
||||
client.fail.return_value = OpsRun(
|
||||
id=run.id,
|
||||
activity_definition_id="def",
|
||||
idempotency_key="k",
|
||||
target_repo=run.target_repo,
|
||||
title=run.title,
|
||||
description="",
|
||||
state="failed",
|
||||
close_disposition="applied",
|
||||
)
|
||||
|
||||
def execute(*_args, **_kwargs):
|
||||
head = _commit_file(repo, "UNRELATED.md")
|
||||
return {"ok": True, "evidence": {"outcome": "succeeded", "commit_sha": head}}
|
||||
|
||||
with patch("rein_aharness.claim_loop.execute_profiled_run", side_effect=execute):
|
||||
result = process_one(client, outbox=CloseOutbox(state_dir=tmp_path / "state"))
|
||||
|
||||
assert result.ok is False
|
||||
assert result.reason.startswith("repository acceptance failed: path-not-granted")
|
||||
client.complete.assert_not_called()
|
||||
client.fail.assert_called_once()
|
||||
close_payload = client.fail.call_args.kwargs["result"]
|
||||
assert close_payload["repository_transaction"]["repository_grant"]["grant_id"]
|
||||
assert "acceptance" not in close_payload["repository_transaction"]
|
||||
assert client.fail.call_args.kwargs["reopen"] is False
|
||||
|
||||
|
||||
def test_granted_profile_metrics_failure_prevents_successful_close(
|
||||
tmp_path: Path,
|
||||
monkeypatch,
|
||||
) -> None:
|
||||
repo, run, client = _profiled_case(tmp_path)
|
||||
run.repository_grant = _grant("docs/")
|
||||
client.fail.return_value = OpsRun(
|
||||
id=run.id,
|
||||
activity_definition_id="def",
|
||||
idempotency_key="k",
|
||||
target_repo=run.target_repo,
|
||||
title=run.title,
|
||||
description="",
|
||||
state="failed",
|
||||
)
|
||||
|
||||
def execute(*_args, **_kwargs):
|
||||
head = _commit_file(repo, "docs/result.md")
|
||||
return {"ok": True, "evidence": {"outcome": "succeeded", "commit_sha": head}}
|
||||
|
||||
def fail_metrics(*_args, **_kwargs):
|
||||
raise OSError("state volume unavailable")
|
||||
|
||||
monkeypatch.setattr(
|
||||
"rein_aharness.claim_loop.metrics.record_external_execution",
|
||||
fail_metrics,
|
||||
)
|
||||
with patch("rein_aharness.claim_loop.execute_profiled_run", side_effect=execute):
|
||||
result = process_one(client, outbox=CloseOutbox(state_dir=tmp_path / "state"))
|
||||
|
||||
assert result.ok is False
|
||||
assert result.reason == "required external metrics persistence failed"
|
||||
client.complete.assert_not_called()
|
||||
close_payload = client.fail.call_args.kwargs["result"]
|
||||
assert close_payload["repository_transaction"]["acceptance"]["accepted"] is True
|
||||
assert "metrics" not in close_payload["repository_transaction"]
|
||||
|
||||
|
||||
def test_response_lost_close_replays_without_reexecuting_workload(
|
||||
tmp_path: Path,
|
||||
) -> None:
|
||||
repo, run, client = _profiled_case(tmp_path)
|
||||
run.repository_grant = _grant("docs/")
|
||||
outbox = CloseOutbox(state_dir=tmp_path / "state")
|
||||
reconciled = OpsRun(
|
||||
id=run.id,
|
||||
activity_definition_id="def",
|
||||
idempotency_key="k",
|
||||
target_repo=run.target_repo,
|
||||
title=run.title,
|
||||
description="",
|
||||
state="succeeded",
|
||||
close_disposition="reconciled",
|
||||
)
|
||||
client.complete.side_effect = [
|
||||
OpsRunError("complete transport failed", action="complete"),
|
||||
reconciled,
|
||||
]
|
||||
gateway_calls = 0
|
||||
|
||||
def execute(*_args, **_kwargs):
|
||||
nonlocal gateway_calls
|
||||
gateway_calls += 1
|
||||
head = _commit_file(repo, "docs/result.md")
|
||||
return {
|
||||
"ok": True,
|
||||
"evidence": {"outcome": "succeeded", "commit_sha": head},
|
||||
}
|
||||
|
||||
with patch("rein_aharness.claim_loop.execute_profiled_run", side_effect=execute):
|
||||
first = process_one(client, outbox=outbox)
|
||||
|
||||
assert first.reason == "close evidence remains pending"
|
||||
assert outbox.status()["pending"] == 1
|
||||
client.claim.return_value = []
|
||||
|
||||
second = process_one(client, outbox=outbox)
|
||||
|
||||
assert second.empty is True
|
||||
assert gateway_calls == 1
|
||||
assert client.complete.call_count == 2
|
||||
assert outbox.status() == {"pending": 0, "delivered": 1, "quarantined": 0}
|
||||
commit_count = subprocess.run(
|
||||
["git", "rev-list", "--count", "HEAD"],
|
||||
cwd=repo,
|
||||
check=True,
|
||||
capture_output=True,
|
||||
text=True,
|
||||
).stdout.strip()
|
||||
assert commit_count == "2"
|
||||
|
||||
|
||||
def test_pending_close_failure_blocks_new_claim(tmp_path: Path) -> None:
|
||||
client = MagicMock(spec=ActivityCoreOpsClient)
|
||||
client.config = OpsRunConfig(worker_id="w", lease_seconds=90)
|
||||
client.complete.side_effect = OpsRunError("transport failed", action="complete")
|
||||
outbox = CloseOutbox(state_dir=tmp_path / "state")
|
||||
outbox.enqueue(
|
||||
CloseRequest(
|
||||
run_id="run-1",
|
||||
transaction_id="tx-1",
|
||||
worker_id="w",
|
||||
action="complete",
|
||||
result={"ok": True},
|
||||
)
|
||||
)
|
||||
|
||||
result = process_one(client, outbox=outbox)
|
||||
|
||||
assert result.claimed is False
|
||||
assert result.retry_full_interval is True
|
||||
assert "remains pending" in result.reason
|
||||
client.claim.assert_not_called()
|
||||
|
||||
|
||||
def test_permanent_close_conflict_is_quarantined_before_claim(tmp_path: Path) -> None:
|
||||
client = MagicMock(spec=ActivityCoreOpsClient)
|
||||
client.config = OpsRunConfig(worker_id="w", lease_seconds=90)
|
||||
client.complete.side_effect = OpsRunError(
|
||||
"conflict",
|
||||
action="complete",
|
||||
status_code=409,
|
||||
code="terminal_conflict",
|
||||
)
|
||||
client.claim.return_value = []
|
||||
outbox = CloseOutbox(state_dir=tmp_path / "state")
|
||||
outbox.enqueue(
|
||||
CloseRequest(
|
||||
run_id="run-1",
|
||||
transaction_id="tx-1",
|
||||
worker_id="w",
|
||||
action="complete",
|
||||
result={"ok": True},
|
||||
)
|
||||
)
|
||||
|
||||
result = process_one(client, outbox=outbox)
|
||||
|
||||
assert result.empty is True
|
||||
assert outbox.status() == {"pending": 0, "delivered": 0, "quarantined": 1}
|
||||
client.claim.assert_called_once()
|
||||
|
||||
|
||||
def test_poll_peek_reports_authoritative_profile_route() -> None:
|
||||
client = MagicMock(spec=ActivityCoreOpsClient)
|
||||
run = _claimed_run()
|
||||
|
|
|
|||
|
|
@ -16,6 +16,7 @@ from rein_aharness.close_outbox import (
|
|||
InvalidCloseRequestError,
|
||||
OutboxConflictError,
|
||||
OutboxCorruptError,
|
||||
PermanentCloseDeliveryError,
|
||||
)
|
||||
|
||||
|
||||
|
|
@ -23,6 +24,7 @@ def _request(
|
|||
*,
|
||||
run_id: str = "run-1",
|
||||
transaction_id: str = "tx-1",
|
||||
worker_id: str = "worker-1",
|
||||
action: str = "complete",
|
||||
result: dict[str, object] | None = None,
|
||||
error: str = "",
|
||||
|
|
@ -31,6 +33,7 @@ def _request(
|
|||
return CloseRequest(
|
||||
run_id=run_id,
|
||||
transaction_id=transaction_id,
|
||||
worker_id=worker_id,
|
||||
action=action,
|
||||
result=result or {"ok": True, "accepted_commit": "a" * 40},
|
||||
error=error,
|
||||
|
|
@ -57,6 +60,7 @@ def test_enqueue_uses_private_external_atomic_record(tmp_path: Path) -> None:
|
|||
assert payload["state"] == "pending"
|
||||
assert payload["attempts"] == 0
|
||||
assert payload["entry_id"] == receipt.entry_id
|
||||
assert payload["worker_id"] == "worker-1"
|
||||
assert not tuple(outbox.pending_dir.glob("*.tmp"))
|
||||
|
||||
|
||||
|
|
@ -164,6 +168,21 @@ def test_process_interrupt_leaves_attempt_durable_and_pending(tmp_path: Path) ->
|
|||
assert payload["last_error"] is None
|
||||
|
||||
|
||||
def test_permanent_delivery_refusal_is_quarantined(tmp_path: Path) -> None:
|
||||
outbox = CloseOutbox(state_dir=tmp_path / "state")
|
||||
receipt = outbox.enqueue(_request())
|
||||
|
||||
def conflict(_: CloseRequest) -> None:
|
||||
raise PermanentCloseDeliveryError("terminal_conflict")
|
||||
|
||||
report = outbox.replay(conflict)
|
||||
|
||||
assert report.quarantined == 1
|
||||
assert report.remaining == 0
|
||||
assert outbox.entry_state(receipt.entry_id) == "quarantined"
|
||||
assert outbox.status() == {"pending": 0, "delivered": 0, "quarantined": 1}
|
||||
|
||||
|
||||
def test_corrupt_pending_record_is_preserved_in_quarantine(tmp_path: Path) -> None:
|
||||
outbox = CloseOutbox(state_dir=tmp_path / "state")
|
||||
request = _request()
|
||||
|
|
@ -232,6 +251,7 @@ outbox = CloseOutbox(state_dir=Path(sys.argv[1]))
|
|||
receipt = outbox.enqueue(CloseRequest(
|
||||
run_id="run-shared",
|
||||
transaction_id="tx-shared",
|
||||
worker_id="worker-1",
|
||||
action="complete",
|
||||
result={"ok": True},
|
||||
))
|
||||
|
|
@ -279,6 +299,7 @@ def test_replay_limit_is_bounded_and_leaves_remaining_entries(tmp_path: Path) ->
|
|||
[
|
||||
({"run_id": "bad/id"}, "run_id"),
|
||||
({"transaction_id": "with space"}, "transaction_id"),
|
||||
({"worker_id": "with space"}, "worker_id"),
|
||||
({"action": "cancel"}, "action"),
|
||||
({"action": "complete", "error": "not allowed"}, "cannot carry"),
|
||||
({"action": "complete", "reopen": True}, "cannot carry"),
|
||||
|
|
@ -292,6 +313,7 @@ def test_close_request_rejects_ambiguous_identity_or_action(
|
|||
values: dict[str, object] = {
|
||||
"run_id": "run-1",
|
||||
"transaction_id": "tx-1",
|
||||
"worker_id": "worker-1",
|
||||
"action": "complete",
|
||||
"result": {"ok": True},
|
||||
"error": "",
|
||||
|
|
@ -325,6 +347,7 @@ def test_close_request_rejects_unbounded_or_non_json_result(
|
|||
CloseRequest(
|
||||
run_id="run-1",
|
||||
transaction_id="tx-1",
|
||||
worker_id="worker-1",
|
||||
action="complete",
|
||||
result=result, # type: ignore[arg-type]
|
||||
)
|
||||
|
|
|
|||
70
tests/test_close_outbox_cli.py
Normal file
70
tests/test_close_outbox_cli.py
Normal file
|
|
@ -0,0 +1,70 @@
|
|||
from __future__ import annotations
|
||||
|
||||
import json
|
||||
from pathlib import Path
|
||||
|
||||
from rein_aharness.cli import main
|
||||
from rein_aharness.close_outbox import CloseOutbox, CloseRequest
|
||||
|
||||
|
||||
def test_close_outbox_status_reports_pending_and_sets_failure_exit(
|
||||
tmp_path: Path,
|
||||
capsys,
|
||||
) -> None:
|
||||
outbox = CloseOutbox(state_dir=tmp_path / "runtime-state")
|
||||
outbox.enqueue(
|
||||
CloseRequest(
|
||||
run_id="run-1",
|
||||
transaction_id="tx-1",
|
||||
worker_id="worker-1",
|
||||
action="complete",
|
||||
result={"ok": True},
|
||||
)
|
||||
)
|
||||
|
||||
assert main(["close-outbox", "status"]) == 1
|
||||
payload = json.loads(capsys.readouterr().out)
|
||||
assert payload == {"delivered": 0, "pending": 1, "quarantined": 0}
|
||||
|
||||
|
||||
def test_close_outbox_replay_delivers_pending_entry(
|
||||
tmp_path: Path,
|
||||
capsys,
|
||||
monkeypatch,
|
||||
) -> None:
|
||||
outbox = CloseOutbox(state_dir=tmp_path / "runtime-state")
|
||||
outbox.enqueue(
|
||||
CloseRequest(
|
||||
run_id="run-1",
|
||||
transaction_id="tx-1",
|
||||
worker_id="worker-1",
|
||||
action="complete",
|
||||
result={"ok": True},
|
||||
)
|
||||
)
|
||||
|
||||
class Client:
|
||||
class Config:
|
||||
worker_id = "worker-1"
|
||||
|
||||
config = Config()
|
||||
|
||||
def complete(self, run_id: str, *, result: dict):
|
||||
assert run_id == "run-1"
|
||||
assert result == {"ok": True}
|
||||
return None
|
||||
|
||||
monkeypatch.setattr(
|
||||
"rein_aharness.ops_run_client.ActivityCoreOpsClient",
|
||||
Client,
|
||||
)
|
||||
|
||||
assert main(["close-outbox", "replay"]) == 0
|
||||
payload = json.loads(capsys.readouterr().out)
|
||||
assert payload["delivered"] == 1
|
||||
assert payload["pending"] == 0
|
||||
|
||||
|
||||
def test_close_outbox_replay_rejects_invalid_limit(capsys) -> None:
|
||||
assert main(["close-outbox", "replay", "--limit", "0"]) == 2
|
||||
assert "between 1 and 1000" in capsys.readouterr().err
|
||||
|
|
@ -7,7 +7,12 @@ from pathlib import Path
|
|||
|
||||
import pytest
|
||||
|
||||
from rein_aharness.glas_execution import GLAS_ACTOR, GlasExecutionError, execute_profiled_run
|
||||
from rein_aharness.glas_execution import (
|
||||
GLAS_ACTOR,
|
||||
GlasExecutionError,
|
||||
execute_profiled_run,
|
||||
normalise_execution_evidence_for_close,
|
||||
)
|
||||
from rein_aharness.ops_run_client import OpsRun, OpsRunConfig
|
||||
|
||||
|
||||
|
|
@ -93,6 +98,29 @@ def test_profiled_run_rejects_invalid_gateway_result(tmp_path: Path) -> None:
|
|||
)
|
||||
|
||||
|
||||
def test_close_evidence_normalizer_drops_direct_and_unknown_values() -> None:
|
||||
result = normalise_execution_evidence_for_close(
|
||||
{
|
||||
"outcome": "succeeded",
|
||||
"profile_ref": "harness.agent-dev-local@1.0.0",
|
||||
"artifacts": ["docs/result.md", {"secret": "drop"}],
|
||||
"refs": {
|
||||
"assignment_ref": "assignment:1",
|
||||
"goal_refs": ["goal:1", {"secret": "drop"}],
|
||||
"api_key": "drop",
|
||||
},
|
||||
"provider_response": {"secret": "drop"},
|
||||
}
|
||||
)
|
||||
|
||||
assert result == {
|
||||
"profile_ref": "harness.agent-dev-local@1.0.0",
|
||||
"outcome": "succeeded",
|
||||
"artifacts": ["docs/result.md"],
|
||||
"refs": {"assignment_ref": "assignment:1", "goal_refs": ["goal:1"]},
|
||||
}
|
||||
|
||||
|
||||
def test_profiled_actor_validates_against_real_glas_and_sandboxer(tmp_path: Path) -> None:
|
||||
contract = pytest.importorskip("glas_harness.contract")
|
||||
sandbox_models = pytest.importorskip("sandboxer.models")
|
||||
|
|
|
|||
|
|
@ -36,6 +36,12 @@ def test_ops_run_from_api() -> None:
|
|||
"correlation_id": "corr-1",
|
||||
"goal_refs": ["goal:42@1"],
|
||||
},
|
||||
"repository_grant": {
|
||||
"version": "1",
|
||||
"allowed_paths": ["docs/", "README.md"],
|
||||
"commit_count": {"min": 1, "max": 1},
|
||||
"publish": False,
|
||||
},
|
||||
}
|
||||
)
|
||||
assert row.target_repo == "freedom-intelligence"
|
||||
|
|
@ -45,6 +51,25 @@ def test_ops_run_from_api() -> None:
|
|||
"correlation_id": "corr-1",
|
||||
"goal_refs": ["goal:42@1"],
|
||||
}
|
||||
assert row.repository_grant is not None
|
||||
assert row.repository_grant.grant_id == "af2e7c8275c9ba8c8f78485067f9608e"
|
||||
|
||||
|
||||
def test_ops_run_from_api_refuses_invalid_repository_grant() -> None:
|
||||
with pytest.raises(OpsRunError, match="invalid repository_grant") as exc_info:
|
||||
OpsRun.from_api(
|
||||
{
|
||||
"id": "run-1",
|
||||
"repository_grant": {
|
||||
"version": "1",
|
||||
"allowed_paths": ["../escape"],
|
||||
"commit_count": {"min": 1, "max": 1},
|
||||
"publish": False,
|
||||
},
|
||||
}
|
||||
)
|
||||
|
||||
assert exc_info.value.code == "invalid_repository_grant"
|
||||
|
||||
|
||||
def test_config_from_env(monkeypatch: pytest.MonkeyPatch) -> None:
|
||||
|
|
@ -126,6 +151,38 @@ def test_complete_and_fail() -> None:
|
|||
assert post.call_args.kwargs["json"]["reopen"] is True
|
||||
|
||||
|
||||
def test_complete_parses_reconciliation_and_machine_error_code() -> None:
|
||||
cfg = OpsRunConfig(base_url="http://example.test", worker_id="w")
|
||||
client = ActivityCoreOpsClient(cfg)
|
||||
accepted = MagicMock()
|
||||
accepted.raise_for_status = MagicMock()
|
||||
accepted.json.return_value = {
|
||||
"id": "r1",
|
||||
"activity_definition_id": "d",
|
||||
"idempotency_key": "k",
|
||||
"title": "t",
|
||||
"description": "",
|
||||
"state": "succeeded",
|
||||
"labels": [],
|
||||
"close_disposition": "reconciled",
|
||||
}
|
||||
with patch("rein_aharness.ops_run_client.httpx.post", return_value=accepted):
|
||||
out = client.complete("r1", result={"ok": True})
|
||||
assert out.close_disposition == "reconciled"
|
||||
|
||||
request = httpx.Request("POST", "http://example.test/ops-runs/r1/complete")
|
||||
conflict = httpx.Response(
|
||||
409,
|
||||
request=request,
|
||||
json={"detail": {"code": "terminal_conflict", "message": "conflict"}},
|
||||
)
|
||||
with patch("rein_aharness.ops_run_client.httpx.post", return_value=conflict):
|
||||
with pytest.raises(OpsRunError) as exc_info:
|
||||
client.complete("r1", result={"ok": True})
|
||||
assert exc_info.value.status_code == 409
|
||||
assert exc_info.value.code == "terminal_conflict"
|
||||
|
||||
|
||||
def test_claim_http_error() -> None:
|
||||
cfg = OpsRunConfig(base_url="http://example.test", worker_id="w")
|
||||
client = ActivityCoreOpsClient(cfg)
|
||||
|
|
@ -175,6 +232,30 @@ def test_ops_run_to_taskspec(tmp_path: Path) -> None:
|
|||
assert spec.hub_task_id == "r1"
|
||||
|
||||
|
||||
def test_ops_run_to_taskspec_carries_repository_grant(tmp_path: Path) -> None:
|
||||
import subprocess
|
||||
|
||||
repo = tmp_path / "controlled"
|
||||
repo.mkdir()
|
||||
subprocess.run(["git", "init", "-q"], cwd=repo, check=True)
|
||||
run = OpsRun.from_api(
|
||||
{
|
||||
"id": "r1",
|
||||
"target_repo": "controlled",
|
||||
"repository_grant": {
|
||||
"version": "1",
|
||||
"allowed_paths": ["docs/"],
|
||||
"commit_count": {"min": 1, "max": 1},
|
||||
"publish": False,
|
||||
},
|
||||
}
|
||||
)
|
||||
|
||||
spec = ops_run_to_taskspec(run, OpsRunConfig(repo_roots=(str(tmp_path),)))
|
||||
|
||||
assert spec.repository_grant is run.repository_grant
|
||||
|
||||
|
||||
def test_ops_run_to_taskspec_missing_repo() -> None:
|
||||
run = OpsRun(
|
||||
id="r1",
|
||||
|
|
|
|||
|
|
@ -285,6 +285,21 @@ def test_acceptance_rejects_dirty_post_state(tmp_path: Path) -> None:
|
|||
assert excinfo.value.code == "dirty-post-state"
|
||||
|
||||
|
||||
def test_acceptance_checks_dirty_post_state_before_unchanged_head(
|
||||
tmp_path: Path,
|
||||
) -> None:
|
||||
repo = _make_repo(tmp_path)
|
||||
|
||||
with RepositoryTransaction(repo, state_dir=tmp_path / "state") as tx:
|
||||
(repo / "uncommitted.txt").write_text("left dirty\n", encoding="utf-8")
|
||||
with pytest.raises(RepositoryAcceptanceError) as excinfo:
|
||||
tx.validate_acceptance(
|
||||
RepositoryAcceptancePolicy(allowed_paths=("docs/",))
|
||||
)
|
||||
|
||||
assert excinfo.value.code == "dirty-post-state"
|
||||
|
||||
|
||||
def test_acceptance_rejects_remote_tracking_ref_movement(tmp_path: Path) -> None:
|
||||
repo = _make_repo(tmp_path)
|
||||
subprocess.run(
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue