Implement REIN-A-0002 ops_run claim loop and approach registry.
Add activity-core ops_run client, approach selection (FI/Binky/mail/agent), claim-loop worker with lease heartbeat, CLI run --from-ops-run and claim-loop, install units, and docs demoting issue-core to legacy external tickets. T05 timer cutover remains operator after five clean cycles.
This commit is contained in:
parent
9644202eb2
commit
8200a672ea
15 changed files with 1807 additions and 73 deletions
97
tests/test_claim_loop.py
Normal file
97
tests/test_claim_loop.py
Normal file
|
|
@ -0,0 +1,97 @@
|
|||
"""Claim loop process_one tests (REIN-A-0002-T03)."""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
from unittest.mock import MagicMock, patch
|
||||
|
||||
from rein_aharness.approaches import ApproachResult, APPROACH_FI_RESEARCH_BRIEF
|
||||
from rein_aharness.claim_loop import process_one, poll_peek
|
||||
from rein_aharness.ops_run_client import OpsRun, OpsRunConfig, ActivityCoreOpsClient
|
||||
|
||||
|
||||
def _claimed_run() -> OpsRun:
|
||||
return OpsRun(
|
||||
id="run-1",
|
||||
activity_definition_id="def",
|
||||
idempotency_key="k",
|
||||
target_repo="freedom-intelligence",
|
||||
title="FI daily",
|
||||
description="d",
|
||||
labels=["automated", "research-brief"],
|
||||
state="claimed",
|
||||
claim_owner="worker-1",
|
||||
attempt=1,
|
||||
)
|
||||
|
||||
|
||||
def test_process_one_empty() -> None:
|
||||
client = MagicMock(spec=ActivityCoreOpsClient)
|
||||
client.config = OpsRunConfig(worker_id="w", lease_seconds=90)
|
||||
client.claim.return_value = []
|
||||
r = process_one(client)
|
||||
assert r.empty is True
|
||||
assert r.claimed is False
|
||||
|
||||
|
||||
def test_process_one_success_completes() -> None:
|
||||
client = MagicMock(spec=ActivityCoreOpsClient)
|
||||
client.config = OpsRunConfig(worker_id="w", lease_seconds=90)
|
||||
client.claim.return_value = [_claimed_run()]
|
||||
client.complete.return_value = OpsRun(
|
||||
id="run-1",
|
||||
activity_definition_id="def",
|
||||
idempotency_key="k",
|
||||
target_repo="freedom-intelligence",
|
||||
title="FI daily",
|
||||
description="",
|
||||
state="succeeded",
|
||||
labels=["automated", "research-brief"],
|
||||
)
|
||||
ar = ApproachResult(
|
||||
ok=True,
|
||||
approach=APPROACH_FI_RESEARCH_BRIEF,
|
||||
result={"path": "briefs/x.md"},
|
||||
reason="ok",
|
||||
)
|
||||
with patch("rein_aharness.claim_loop.execute_approach", return_value=ar):
|
||||
r = process_one(client)
|
||||
assert r.claimed is True
|
||||
assert r.ok is True
|
||||
assert r.approach == APPROACH_FI_RESEARCH_BRIEF
|
||||
client.complete.assert_called_once()
|
||||
client.fail.assert_not_called()
|
||||
|
||||
|
||||
def test_process_one_failure_reopens() -> None:
|
||||
client = MagicMock(spec=ActivityCoreOpsClient)
|
||||
client.config = OpsRunConfig(worker_id="w", lease_seconds=90)
|
||||
client.claim.return_value = [_claimed_run()]
|
||||
client.fail.return_value = OpsRun(
|
||||
id="run-1",
|
||||
activity_definition_id="def",
|
||||
idempotency_key="k",
|
||||
target_repo="freedom-intelligence",
|
||||
title="t",
|
||||
description="",
|
||||
state="open",
|
||||
labels=["automated", "research-brief"],
|
||||
)
|
||||
ar = ApproachResult(
|
||||
ok=False,
|
||||
approach=APPROACH_FI_RESEARCH_BRIEF,
|
||||
reason="llm timeout",
|
||||
reopen=True,
|
||||
)
|
||||
with patch("rein_aharness.claim_loop.execute_approach", return_value=ar):
|
||||
r = process_one(client)
|
||||
assert r.ok is False
|
||||
client.fail.assert_called_once()
|
||||
assert client.fail.call_args.kwargs["reopen"] is True
|
||||
|
||||
|
||||
def test_poll_peek() -> None:
|
||||
client = MagicMock(spec=ActivityCoreOpsClient)
|
||||
client.list_open.return_value = [_claimed_run()]
|
||||
rows = poll_peek(client)
|
||||
assert len(rows) == 1
|
||||
assert rows[0]["approach"] == APPROACH_FI_RESEARCH_BRIEF
|
||||
Loading…
Add table
Add a link
Reference in a new issue