ouroboros/tests/test_evolution_commit_receipt.py
Anton Razzhigaev 031183e723
Some checks failed
Claudexor platform gate (API keys — subscription auth NOT covered) / fixture · macos-latest · exact managed runtime, fake harness, no model (push) Has been cancelled
Claudexor platform gate (API keys — subscription auth NOT covered) / fixture · ubuntu-latest · exact managed runtime, fake harness, no model (push) Has been cancelled
Claudexor platform gate (API keys — subscription auth NOT covered) / fixture · windows-latest · exact managed runtime, fake harness, no model (push) Has been cancelled
Claudexor platform gate (API keys — subscription auth NOT covered) / live · macos-latest · claude · API key only, subscription NOT covered (push) Has been cancelled
Claudexor platform gate (API keys — subscription auth NOT covered) / live · ubuntu-latest · claude · API key only, subscription NOT covered (push) Has been cancelled
Claudexor platform gate (API keys — subscription auth NOT covered) / live · windows-latest · claude · API key only, subscription NOT covered (push) Has been cancelled
Claudexor platform gate (API keys — subscription auth NOT covered) / live · macos-latest · codex · API key only, subscription NOT covered (push) Has been cancelled
Submit Claudexor dependency / Submit managed runtime snapshot (push) Has been cancelled
CI / quick-test (push) Has been cancelled
CI / benchmark-methodology (push) Has been cancelled
CI / full-test (macos-latest) (push) Has been cancelled
CI / full-test (ubuntu-latest) (push) Has been cancelled
CI / full-test (windows-latest) (push) Has been cancelled
CI / betterleaks-platform-smoke (macos-latest) (push) Has been cancelled
CI / betterleaks-platform-smoke (ubuntu-latest) (push) Has been cancelled
CI / betterleaks-platform-smoke (windows-latest) (push) Has been cancelled
CI / integration-test (push) Has been cancelled
CI / skill-smoke (macos-latest) (push) Has been cancelled
CI / skill-smoke (ubuntu-latest) (push) Has been cancelled
CI / skill-smoke (windows-latest) (push) Has been cancelled
CI / marker-guards (push) Has been cancelled
CI / ui-smoke (push) Has been cancelled
CI / docker-ui-smoke (push) Has been cancelled
CI / docker-portable-test (push) Has been cancelled
CI / system-e2e-mock (push) Has been cancelled
CI / e2e-live (SM1 x1 — largest subset feasible under the $30 cap) (push) Has been cancelled
Scorecard analysis workflow / Scorecard analysis (push) Has been cancelled
Sync Joi Lab Fork Mirror / sync (push) Has been cancelled
CI / release-preflight (push) Has been cancelled
CI / build (dmg, macos-latest, macos-arm64, syft_1.50.0_darwin_arm64.tar.gz, syft, e32fdb9d47823fa633748a1efca2528fd77c37469ea93c9e40ab835da44e4cce) (push) Has been cancelled
CI / build (tar.gz, ubuntu-latest, linux-x86_64, syft_1.50.0_linux_amd64.tar.gz, syft, bf7b29ff57f06da30918266a0e1c2885a8f99784798d1bdb1628886aa015d788) (push) Has been cancelled
CI / build (zip, windows-latest, windows-x64, syft_1.50.0_windows_amd64.zip, syft.exe, 815ee6973ec5dff6a671d7f41b0e78835a8c45b91d5a39f4743ea1cee833d3be) (push) Has been cancelled
CI / vendor-package-smoke (push) Has been cancelled
CI / release (push) Has been cancelled
test: synchronize rescue CAS interleaving without timing fixture I/O
Preserve real lock contention, the exact receipt and lost-update assertions while removing a scheduler-dependent timeout from test setup.

Co-authored-by: Ouroboros <311266734+ouroboros-agent@users.noreply.github.com>
2026-09-09 02:03:57 +03:00

376 lines
15 KiB
Python

"""The exact commit receipt: what binds it, what may read it, and what may never erase it.
Split out of ``tests/test_evolution_state_integrity_v3.py`` by theme: the receipt bound to
campaign, transaction and task; the second commit blocked before review; the receipt race
ahead of the git commit; revoked authority; the rescue link and campaign sidecar that share
the CAS; and the stale, terminal and panicking writers that must not overwrite it.
"""
from __future__ import annotations
import pathlib
import threading
from types import SimpleNamespace
import pytest
from tests._evolution_state_shared import (
_active_transaction,
_patch_commit_seam,
)
def test_exact_commit_receipt_is_bound_to_campaign_transaction_and_task(tmp_path):
from supervisor import evolution_lifecycle
campaign, tx = _active_transaction(tmp_path)
claim = {
"campaign_id": campaign["id"],
"transaction_id": tx["transaction_id"],
"task_id": tx["task_id"],
}
assert evolution_lifecycle.check_evolution_authority(**claim)["ok"] is True
receipt = evolution_lifecycle.record_evolution_commit(**claim, commit_sha="a" * 40)
assert receipt["ok"] is True
assert receipt["commit_sha"] == "a" * 40
stored = evolution_lifecycle._read_evolution_campaign()["active_transaction"]
assert stored["commit_receipt"] == receipt
assert evolution_lifecycle.check_evolution_authority(
**claim, commit_sha="b" * 40,
)["reason"] == "commit_receipt_mismatch"
campaign_state = evolution_lifecycle._read_evolution_campaign()
campaign_state["active_transaction"].pop("commit_receipt")
assert evolution_lifecycle._write_evolution_campaign(campaign_state) is True
assert evolution_lifecycle.check_evolution_authority(
**claim, commit_sha="a" * 40,
)["reason"] == "commit_receipt_missing"
def test_second_evolution_commit_is_blocked_before_review(tmp_path, monkeypatch):
from ouroboros.tools import git as git_tools
from supervisor import evolution_lifecycle
campaign, tx = _active_transaction(tmp_path)
assert evolution_lifecycle.record_evolution_commit(
campaign["id"], tx["transaction_id"], tx["task_id"], "a" * 40,
)["ok"] is True
review_calls = []
monkeypatch.setattr(git_tools, "_task_attributed_commit_paths", lambda *a, **k: (None, None, "", None))
monkeypatch.setattr(git_tools, "_check_overlapping_review_attempt", lambda *a, **k: "")
_patch_commit_seam(monkeypatch, "_record_commit_attempt", lambda *a, **k: None)
monkeypatch.setattr(git_tools, "_acquire_git_lock", lambda *a, **k: pathlib.Path("lock"))
monkeypatch.setattr(git_tools, "_release_git_lock", lambda *a, **k: None)
monkeypatch.setattr(git_tools, "_prepare_review_commit_worktree", lambda *a, **k: (False, ""))
_patch_commit_seam(monkeypatch, "_run_reviewed_stage_cycle",
lambda *a, **k: review_calls.append(True) or {"status": "passed"},
)
ctx = SimpleNamespace(
repo_dir=tmp_path,
drive_root=tmp_path,
current_task_type="evolution",
task_id=tx["task_id"],
task_metadata={"evolution_transaction": tx},
)
result = git_tools._repo_commit_push(ctx, "second commit")
assert "transaction_already_committed" in result
assert "No reviewer was called" in result
assert review_calls == []
def test_receipt_race_blocks_evolution_before_git_commit(tmp_path, monkeypatch):
from ouroboros.tools import git as git_tools
from supervisor import evolution_lifecycle
campaign, tx = _active_transaction(tmp_path)
ctx = SimpleNamespace(
repo_dir=tmp_path,
current_task_type="evolution",
task_id=tx["task_id"],
task_metadata={"evolution_transaction": tx},
)
_patch_commit_seam(monkeypatch, "_record_commit_attempt", lambda *a, **k: None)
claim, error = git_tools._check_evolution_commit_stage(
ctx, "commit", 0.0, phase="pre_review_authority",
)
assert error == ""
assert evolution_lifecycle.record_evolution_commit(
**claim, commit_sha="b" * 40,
)["ok"] is True
_claim, error = git_tools._check_evolution_commit_stage(
ctx, "commit", 0.0, phase="pre_commit_authority",
)
assert "transaction_already_committed" in error
assert "Nothing was committed" in error
def test_revoked_authority_leaves_commit_unrecorded(tmp_path):
from supervisor import evolution_lifecycle, state
campaign, tx = _active_transaction(tmp_path)
live = state.load_state()
live["evolution_mode_enabled"] = False
live["evolution_owner_stopped"] = True
state.save_state(live)
receipt = evolution_lifecycle.record_evolution_commit(
campaign["id"], tx["transaction_id"], tx["task_id"], "c" * 40,
)
assert receipt == {"ok": False, "reason": "owner_stopped", "commit_sha": "c" * 40}
assert evolution_lifecycle._read_evolution_campaign()["active_transaction"]["commit_sha"] == ""
def test_exact_receipt_remains_authority_after_post_task_autostop(tmp_path):
from supervisor import evolution_lifecycle, state
campaign, tx = _active_transaction(tmp_path)
sha = "9" * 40
claim = {
"campaign_id": campaign["id"],
"transaction_id": tx["transaction_id"],
"task_id": tx["task_id"],
}
assert evolution_lifecycle.record_evolution_commit(**claim, commit_sha=sha)["ok"] is True
state.update_state(lambda live: live.update(
evolution_mode_enabled=False,
post_task_autostop=False,
))
assert evolution_lifecycle.check_evolution_authority(
**claim, commit_sha=sha,
)["ok"] is True
assert evolution_lifecycle.check_evolution_authority(**claim)["reason"] == "evolution_disabled"
@pytest.mark.parametrize("held_lock", ["state", "campaign"])
def test_rescue_link_uses_shared_campaign_cas_and_preserves_commit_receipt(
tmp_path, monkeypatch, held_lock,
):
from ouroboros import platform_layer
from ouroboros.platform_layer import (
acquire_exclusive_file_lock,
release_exclusive_file_lock,
)
from ouroboros.utils import atomic_write_json
from supervisor import evolution_lifecycle, git_ops, state
campaign, tx = _active_transaction(tmp_path)
sha = "3" * 40
assert evolution_lifecycle.record_evolution_commit(
campaign["id"], tx["transaction_id"], tx["task_id"], sha,
)["ok"] is True
monkeypatch.setattr(evolution_lifecycle, "EVOLUTION_CAMPAIGN_CAS_TIMEOUT_SEC", 1.0)
monkeypatch.setattr(git_ops, "DRIVE_ROOT", tmp_path)
campaign_path = tmp_path / "state" / "evolution_campaign.json"
if held_lock == "state":
lock_path = tmp_path / "locks" / "state.lock"
lock_fd = state.acquire_file_lock(lock_path, timeout_sec=1.0)
release = state.release_file_lock
else:
lock_path = campaign_path.with_name(campaign_path.name + ".lock")
lock_fd = acquire_exclusive_file_lock(lock_path, timeout_sec=1.0)
release = release_exclusive_file_lock
assert lock_fd is not None
done = threading.Event()
acquiring = threading.Event()
released = threading.Event()
lock_owner = state if held_lock == "state" else platform_layer
acquire = lock_owner.acquire_exclusive_file_lock
def _acquire_after_interleave(path, **kwargs):
if path == lock_path:
acquiring.set()
released.wait()
return acquire(path, **kwargs)
# Prove contention separately, then hand off at the real acquisition seam.
# File I/O and thread scheduling must not consume the CAS timeout while the
# fixture deliberately holds the lock needed by the successful update.
monkeypatch.setattr(lock_owner, "acquire_exclusive_file_lock", _acquire_after_interleave)
def _link() -> None:
git_ops._link_rescue_to_evolution_transaction(
{"rescue_ref": "rescue/test", "path": "/tmp/rescue-test"},
"test",
)
done.set()
thread = threading.Thread(target=_link, daemon=True)
thread.start()
try:
assert acquire_exclusive_file_lock(lock_path, timeout_sec=0.001) is None
assert acquiring.wait(2.0) is True
assert done.wait(0.1) is False
current = evolution_lifecycle._read_evolution_campaign()
current["active_transaction"]["interleaved"] = held_lock
atomic_write_json(campaign_path, current, trailing_newline=True)
finally:
release(lock_path, lock_fd)
released.set()
thread.join(timeout=1.0)
assert done.wait(2.0) is True
assert not thread.is_alive()
stored = evolution_lifecycle._read_evolution_campaign()["active_transaction"]
assert stored["commit_sha"] == sha
assert stored["commit_receipt"]["commit_sha"] == sha
assert stored["rescue_ref"] == "rescue/test"
assert stored["interleaved"] == held_lock
def test_commit_receipt_uses_campaign_sidecar_before_rescue(tmp_path, monkeypatch):
from ouroboros.platform_layer import (
acquire_exclusive_file_lock,
release_exclusive_file_lock,
)
from supervisor import evolution_lifecycle
campaign, tx = _active_transaction(tmp_path)
monkeypatch.setattr(evolution_lifecycle, "EVOLUTION_CAMPAIGN_CAS_TIMEOUT_SEC", 1.0)
campaign_path = tmp_path / "state" / "evolution_campaign.json"
lock_path = campaign_path.with_name(campaign_path.name + ".lock")
lock_fd = acquire_exclusive_file_lock(lock_path, timeout_sec=1.0)
assert lock_fd is not None
done = threading.Event()
result = {}
def _record() -> None:
result.update(evolution_lifecycle.record_evolution_commit(
campaign["id"], tx["transaction_id"], tx["task_id"], "4" * 40,
))
done.set()
thread = threading.Thread(target=_record, daemon=True)
thread.start()
try:
assert done.wait(0.1) is False
finally:
release_exclusive_file_lock(lock_path, lock_fd)
assert done.wait(2.0) is True
thread.join(timeout=1.0)
assert result["ok"] is True
assert evolution_lifecycle._read_evolution_campaign()["active_transaction"][
"commit_receipt"
]["commit_sha"] == "4" * 40
def test_campaign_sidecar_contention_releases_state_lock_quickly(tmp_path):
from ouroboros.platform_layer import (
acquire_exclusive_file_lock,
release_exclusive_file_lock,
)
from supervisor import evolution_lifecycle, state
campaign, tx = _active_transaction(tmp_path)
campaign_path = tmp_path / "state" / "evolution_campaign.json"
sidecar = campaign_path.with_name(campaign_path.name + ".lock")
sidecar_fd = acquire_exclusive_file_lock(sidecar, timeout_sec=1.0)
assert sidecar_fd is not None
try:
result = evolution_lifecycle.record_evolution_commit(
campaign["id"], tx["transaction_id"], tx["task_id"], "6" * 40,
)
assert result["ok"] is False
state_fd = state.acquire_file_lock(state.STATE_LOCK_PATH, timeout_sec=0.2)
assert state_fd is not None
state.release_file_lock(state.STATE_LOCK_PATH, state_fd)
finally:
release_exclusive_file_lock(sidecar, sidecar_fd)
def test_sent_owner_report_clear_cannot_erase_concurrent_commit_receipt(tmp_path, monkeypatch):
from supervisor import evolution_lifecycle, queue
campaign, tx = _active_transaction(tmp_path)
report = {"cycle_outcome": "absorbed", "task_id": "previous"}
current = evolution_lifecycle._read_evolution_campaign()
current["pending_owner_report"] = report
assert evolution_lifecycle._write_evolution_campaign(current) is True
def _send_then_record(*args, **kwargs):
receipt = evolution_lifecycle.record_evolution_commit(
campaign["id"], tx["transaction_id"], tx["task_id"], "5" * 40,
)
assert receipt["ok"] is True
monkeypatch.setattr(queue, "notify_owner_cycle_outcome", _send_then_record)
queue._deliver_pending_owner_report()
stored = evolution_lifecycle._read_evolution_campaign()
assert "pending_owner_report" not in stored
assert stored["active_transaction"]["commit_receipt"]["commit_sha"] == "5" * 40
def test_terminal_campaign_cannot_be_resurrected_by_a_stale_writer(tmp_path):
from supervisor import evolution_lifecycle
campaign, _ = _active_transaction(tmp_path)
stale = dict(campaign)
evolution_lifecycle.complete_evolution_campaign("owner stop", cleanup_worktree=False)
stale["status"] = "active"
assert evolution_lifecycle._write_evolution_campaign(stale) is False
assert evolution_lifecycle._read_evolution_campaign()["status"] == "stopped"
def test_stale_campaign_cannot_overwrite_a_new_campaign(tmp_path):
from supervisor import evolution_lifecycle, queue, state
state.init(tmp_path)
queue.init(tmp_path)
first = evolution_lifecycle.start_evolution_campaign("First", source="test")
stale = dict(first)
evolution_lifecycle.complete_evolution_campaign("done", cleanup_worktree=False)
second = evolution_lifecycle.start_evolution_campaign("Second", source="test")
stale["status"] = "active"
assert evolution_lifecycle._write_evolution_campaign(stale) is False
assert evolution_lifecycle._read_evolution_campaign()["id"] == second["id"]
def test_panic_campaign_close_uses_nonblocking_state_lock(tmp_path, monkeypatch):
from supervisor import evolution_lifecycle, queue, state
state.init(tmp_path)
queue.init(tmp_path)
evolution_lifecycle.start_evolution_campaign("Improve", source="test")
timeouts = []
monkeypatch.setattr(
state,
"acquire_file_lock",
lambda path, timeout_sec=4.0, **kw: timeouts.append(timeout_sec) or None,
)
evolution_lifecycle.complete_evolution_campaign(
"panic stop", status="stopped", cleanup_worktree=False,
)
assert timeouts == [0.001]
def test_unreadable_campaign_file_refuses_writes_instead_of_treating_it_as_absent(tmp_path, monkeypatch):
"""A campaign file that EXISTS but cannot be read (a transient Windows sharing
violation, corruption) is not "no campaign": the stale-write guard cannot compare
ids, so the write is refused — never allowed as if the slot were free. An absent
file stays writable (the first campaign of a fresh data root)."""
from supervisor import evolution_lifecycle, queue, state
state.init(tmp_path)
queue.init(tmp_path)
path = evolution_lifecycle._evolution_campaign_path()
assert not path.exists()
fresh = {"schema_version": 1, "id": "abc12345", "status": "active", "objective": "First"}
assert evolution_lifecycle._write_evolution_campaign(dict(fresh)) is True
assert path.is_file()
monkeypatch.setattr(evolution_lifecycle, "read_json_dict", lambda _p: None)
assert evolution_lifecycle._write_evolution_campaign({**fresh, "id": "other001"}) is False
monkeypatch.undo()
assert evolution_lifecycle._read_evolution_campaign()["id"] == "abc12345"