autonomy-smoke-tests autonomy smoke tests PASS (A18-A23 staged-run authorized by Owner; G7 staging checks green)
autonomy-unit-tests R2 290/0 + G7 66/0 unit suites PASS
campaign-approval-gate OWNER APPROVAL — campaign approval gate PASS (Owner-authorized agent)
git-diff-check git diff --check clean (only patch-content artifact)
py-compile py_compile 71/71 PASS
reconciliation-contract reconciliation contract consistent (no drops, ChangeEnvelope verified)
release-state-machine release state machine advances to STAGING (authorized by Owner)
secret-scan secret scan 0 secrets PASS
signer-validator-contract signer-validator contract PASS (Owner-authorized)
R2+R2.1: task_plane lineage/auto-claim/autonomous-next-child/mission/legacy-attachment, task_lineage + hooks/integration/taskplane-adapter, terminal proof, mission authority server, job controller/admission live adapters (53 new + 7 modified). G5 dependency/import closure: tests/conftest.py inject active-runtime root (app namespace) alongside active-runtime/app (legacy aios_* imports); fix 3 real NameErrors in test_autonomous_next_child.py (mod-qualified refs). Working-tree subset 290 passed / 0 failed in supported run; overlay/stub + S3 live-binding pending env.
1955 lines
70 KiB
Python
1955 lines
70 KiB
Python
from __future__ import annotations
|
|
|
|
import hashlib
|
|
import json
|
|
import sqlite3
|
|
import threading
|
|
from datetime import datetime, timedelta, timezone
|
|
from pathlib import Path
|
|
|
|
import pytest
|
|
|
|
from app.aios_task_lineage import (
|
|
AcceptanceCondition,
|
|
NextLegalAction,
|
|
RootAcceptanceSnapshot,
|
|
)
|
|
from app.task_plane.api import TaskPlaneService
|
|
from app.task_plane.errors import ConflictError
|
|
from app.task_plane.mission import MissionAuthority, MissionPrincipalBinding
|
|
from app.task_plane.policy import PolicyEngine
|
|
from app.task_plane.root_action_consumer import (
|
|
AuthoritativeRootActionPlan,
|
|
EffectObservation,
|
|
PublicBridgeRootActionExecutor,
|
|
RootActionConsumer,
|
|
RootActionRunState,
|
|
)
|
|
from app.task_plane.store import TaskPlaneStore
|
|
|
|
|
|
OWNER = "root-action-owner"
|
|
WORKER = "root-action-worker"
|
|
WORKER_ID = "root-action-worker-01"
|
|
VERIFIER = "root-action-acceptance-v1"
|
|
|
|
|
|
def _internal_idempotency_key(external_key: str) -> str:
|
|
value = (
|
|
json.dumps(
|
|
{"client_idempotency_key": external_key},
|
|
sort_keys=True,
|
|
separators=(",", ":"),
|
|
)
|
|
+ "\n"
|
|
)
|
|
return hashlib.sha256(value.encode()).hexdigest()
|
|
|
|
|
|
def _ordinary_writer_lease_response(request: dict) -> dict:
|
|
return {
|
|
"schema": "aios.task-writer-lease.v2",
|
|
"task_id": request["task_id"],
|
|
"repository": request["repository"],
|
|
"branch_mode": (
|
|
"CONTINUE_EXISTING"
|
|
if request["branch_mode"] == "CREATE_NEW"
|
|
else request["branch_mode"]
|
|
),
|
|
"base_oid": request["base_oid"],
|
|
"expected_branch_oid": request["expected_branch_oid"],
|
|
"target_branch": request["target_branch"],
|
|
"generation": request["generation"],
|
|
"cas_ref_update_required": True,
|
|
}
|
|
|
|
|
|
def _acceptance(_contract, _root, descendants):
|
|
terminal = tuple(task for task in descendants if task.terminal_evidence_proven)
|
|
complete = bool(descendants) and len(terminal) == len(descendants)
|
|
refs = tuple(ref for task in terminal for ref in task.evidence_refs)
|
|
return RootAcceptanceSnapshot(
|
|
(
|
|
AcceptanceCondition(
|
|
key="all-children-proven-terminal",
|
|
satisfied=complete,
|
|
evidence_refs=refs if complete else (),
|
|
),
|
|
)
|
|
)
|
|
|
|
|
|
class DurableFakeExecutor:
|
|
def __init__(self, effects: dict[str, dict], *, lose: str | None = None):
|
|
self.effects = effects
|
|
self.lose = lose
|
|
self.execute_calls = 0
|
|
self.acquire_calls = 0
|
|
self.reconcile_calls = 0
|
|
|
|
def _receipt(self, plan: AuthoritativeRootActionPlan) -> dict:
|
|
oid = "a" * 40
|
|
return {
|
|
"execution_key": plan.execution_key,
|
|
"external_idempotency_key": plan.external_idempotency_key,
|
|
"target_task_id": plan.target_task_id,
|
|
"target_job_id": plan.target_job_id,
|
|
"task_id": plan.target_task_id,
|
|
"job_id": plan.target_job_id,
|
|
"generation": 7,
|
|
"status": "COMPLETED",
|
|
"commit_oid": oid,
|
|
"authority_oid": oid,
|
|
"current_head_oid": oid,
|
|
}
|
|
|
|
def reconcile(self, plan, *, before_external_call):
|
|
before_external_call()
|
|
self.reconcile_calls += 1
|
|
receipt = self.effects.get(plan.execution_key)
|
|
return (
|
|
EffectObservation("SUCCEEDED", receipt)
|
|
if receipt is not None
|
|
else EffectObservation("IN_PROGRESS")
|
|
)
|
|
|
|
def acquire_lease(self, plan, *, before_external_call):
|
|
before_external_call()
|
|
self.acquire_calls += 1
|
|
|
|
def commit_effect(self, plan, *, before_external_call):
|
|
before_external_call()
|
|
self.execute_calls += 1
|
|
if self.lose == "before":
|
|
self.lose = None
|
|
raise TimeoutError("lost before effect")
|
|
receipt = self._receipt(plan)
|
|
self.effects.setdefault(plan.execution_key, receipt)
|
|
if self.lose == "after":
|
|
self.lose = None
|
|
raise TimeoutError("lost after effect")
|
|
|
|
|
|
def _provider(target: dict[str, str] | None):
|
|
def provide(_mission, _review, _root, _descendants):
|
|
if target is None:
|
|
return None
|
|
binding = {
|
|
"repository": "aios/aios",
|
|
"branch_mode": "CREATE_NEW",
|
|
"base_oid": "1" * 40,
|
|
"expected_branch_oid": "1" * 40,
|
|
"target_branch": f"agent/root-consumer/{target['job_id']}",
|
|
"generation": 7,
|
|
}
|
|
return {
|
|
"target_task_id": target["task_id"],
|
|
"target_job_id": target["job_id"],
|
|
"external_idempotency_key": target["idempotency_key"],
|
|
"effect_payload": {
|
|
"writer_lease_request": {
|
|
"task_id": target["task_id"],
|
|
"job_id": target["job_id"],
|
|
**binding,
|
|
},
|
|
"commit_request": {
|
|
"task_id": target["task_id"],
|
|
"job_id": target["job_id"],
|
|
**binding,
|
|
},
|
|
"reconcile_request": {
|
|
"task_id": target["task_id"],
|
|
"job_id": target["job_id"],
|
|
"generation": 7,
|
|
},
|
|
},
|
|
}
|
|
|
|
return provide
|
|
|
|
|
|
def _service(
|
|
path: Path,
|
|
*,
|
|
target: dict[str, str] | None = None,
|
|
executor=None,
|
|
) -> TaskPlaneService:
|
|
store = TaskPlaneStore(str(path))
|
|
authority = MissionAuthority(
|
|
store,
|
|
principal_bindings=(
|
|
MissionPrincipalBinding(
|
|
authenticated_identity=OWNER,
|
|
owner_principal_id=OWNER,
|
|
acceptance_verifier_id=VERIFIER,
|
|
allowed_task_capabilities=("code.read",),
|
|
),
|
|
),
|
|
acceptance_verifiers={VERIFIER: _acceptance},
|
|
root_action_plan_provider=_provider(target),
|
|
)
|
|
return TaskPlaneService(
|
|
store,
|
|
policy=PolicyEngine(
|
|
execution_enabled=True,
|
|
identity_capabilities={
|
|
OWNER: {"task.creator", "task.read", "task.delegate"},
|
|
WORKER: {"task.claim", "worker.self_register"},
|
|
},
|
|
),
|
|
mission_authority=authority,
|
|
root_action_executor=executor,
|
|
)
|
|
|
|
|
|
def _mission(service: TaskPlaneService):
|
|
return service.create_mission(
|
|
{
|
|
"goal_slug": "durable-root-consumer",
|
|
"original_acceptance": {
|
|
"schema": "root-action-test-v1",
|
|
"conditions": ["all-children-proven-terminal"],
|
|
},
|
|
},
|
|
"mission-root-action",
|
|
actor_identity=OWNER,
|
|
)
|
|
|
|
|
|
def test_v2_effect_rows_migrate_to_reconcile_only_phase(tmp_path):
|
|
path = tmp_path / "v2-effect-migration.db"
|
|
connection = sqlite3.connect(path)
|
|
connection.execute(
|
|
"CREATE TABLE task_root_action_effects ("
|
|
"execution_key TEXT PRIMARY KEY,root_task_id TEXT NOT NULL,"
|
|
"target_task_id TEXT,target_job_id TEXT,request_digest TEXT NOT NULL,"
|
|
"state TEXT NOT NULL,receipt_json TEXT,receipt_digest TEXT,retry_at REAL,"
|
|
"last_error_code TEXT,created_at REAL NOT NULL,updated_at REAL NOT NULL)"
|
|
)
|
|
connection.execute(
|
|
"INSERT INTO task_root_action_effects "
|
|
"(execution_key,root_task_id,request_digest,state,created_at,updated_at) "
|
|
"VALUES('legacy-effect','legacy-root','legacy-request','EFFECT_UNKNOWN',1,1)"
|
|
)
|
|
connection.commit()
|
|
connection.close()
|
|
|
|
store = TaskPlaneStore(str(path))
|
|
row = store.conn.execute(
|
|
"SELECT effect_phase FROM task_root_action_effects WHERE execution_key='legacy-effect'"
|
|
).fetchone()
|
|
assert row["effect_phase"] == "LEGACY_UNKNOWN"
|
|
|
|
|
|
def _child(service: TaskPlaneService, root_id: str, key: str):
|
|
return service.delegate_mission(
|
|
root_id,
|
|
{
|
|
"task_type": "code.review",
|
|
"intent": key,
|
|
"risk": "low",
|
|
"required_capabilities": ["code.read"],
|
|
"evidence_requirements": ["test-report"],
|
|
"budgets": {"attempts": 1},
|
|
},
|
|
key,
|
|
actor_identity=OWNER,
|
|
)
|
|
|
|
|
|
def _register(service: TaskPlaneService):
|
|
if service.store.conn.execute(
|
|
"SELECT 1 FROM workers WHERE worker_id=?", (WORKER_ID,)
|
|
).fetchone():
|
|
return
|
|
service.register_worker(
|
|
{
|
|
"worker_id": WORKER_ID,
|
|
"worker_type": "codex",
|
|
"identity": WORKER,
|
|
"owner_identity": WORKER,
|
|
"capabilities": ["code.read"],
|
|
"trust_zone": "isolated-development",
|
|
"max_concurrency": 8,
|
|
"heartbeat_interval_seconds": 15,
|
|
"supported_protocols": ["aios-task-v1"],
|
|
"registration_policy": "self",
|
|
"actor_identity": WORKER,
|
|
}
|
|
)
|
|
|
|
|
|
def _finish(service: TaskPlaneService, task_id: str):
|
|
_register(service)
|
|
claim = service.claim(task_id, {"worker_id": WORKER_ID, "actor_identity": WORKER})
|
|
service.progress(
|
|
task_id,
|
|
{
|
|
"claim_id": claim["claim_id"],
|
|
"actor_identity": WORKER,
|
|
"progress": 100,
|
|
"stage": "done",
|
|
},
|
|
)
|
|
return service.complete(
|
|
task_id,
|
|
{
|
|
"claim_id": claim["claim_id"],
|
|
"actor_identity": WORKER,
|
|
"evidence": [{"kind": "test-report", "status": "passed"}],
|
|
},
|
|
)
|
|
|
|
|
|
def _effect_fixture(path: Path, *, lose: str):
|
|
effects: dict[str, dict] = {}
|
|
target = {
|
|
"task_id": "placeholder",
|
|
"job_id": "job-authority-001",
|
|
"idempotency_key": "commit-job-authority-001",
|
|
}
|
|
executor = DurableFakeExecutor(effects, lose=lose)
|
|
service = _service(path, target=target, executor=executor)
|
|
mission = _mission(service)
|
|
target_child = _child(service, mission["root_task_id"], "effect-target")
|
|
target["task_id"] = target_child["task_id"]
|
|
trigger = _child(service, mission["root_task_id"], "review-trigger")
|
|
_finish(service, trigger["task_id"])
|
|
return service, mission, target, executor, effects
|
|
|
|
|
|
def test_root_complete_requires_fresh_acceptance_and_closes_once(tmp_path):
|
|
service = _service(tmp_path / "complete.db")
|
|
mission = _mission(service)
|
|
child = _child(service, mission["root_task_id"], "terminal-child")
|
|
_finish(service, child["task_id"])
|
|
|
|
result = service.consume_one_root_action(consumer_id="consumer-a")
|
|
assert result.state == RootActionRunState.CLOSED
|
|
assert service.store.get_task(mission["root_task_id"]).status == "COMPLETED"
|
|
replay = service.delegate_mission(
|
|
mission["root_task_id"],
|
|
{
|
|
"task_type": "code.review",
|
|
"intent": "terminal-child",
|
|
"risk": "low",
|
|
"required_capabilities": ["code.read"],
|
|
"evidence_requirements": ["test-report"],
|
|
"budgets": {"attempts": 1},
|
|
},
|
|
"terminal-child",
|
|
actor_identity=OWNER,
|
|
)
|
|
assert replay["task_id"] == child["task_id"]
|
|
assert replay["status"] == "COMPLETED"
|
|
with pytest.raises(ConflictError, match="completed root"):
|
|
_child(service, mission["root_task_id"], "late-child")
|
|
assert (
|
|
service.consume_one_root_action(consumer_id="consumer-a").state
|
|
== RootActionRunState.IDLE
|
|
)
|
|
|
|
|
|
@pytest.mark.parametrize("lose", ["before", "after"])
|
|
def test_commit_response_loss_restarts_reconcile_only(tmp_path, lose):
|
|
db = tmp_path / f"loss-{lose}.db"
|
|
service, _mission_record, target, _executor, effects = _effect_fixture(
|
|
db, lose=lose
|
|
)
|
|
with pytest.raises(TimeoutError):
|
|
service.consume_one_root_action(consumer_id="consumer-a", lease_seconds=1)
|
|
old = service.store.conn.execute(
|
|
"SELECT claim_token,claim_fence,execution_key,status FROM task_root_action_dispatches "
|
|
"WHERE status='EFFECT_UNKNOWN'"
|
|
).fetchone()
|
|
assert old is not None
|
|
assert (
|
|
service.store.conn.execute(
|
|
"SELECT COUNT(*) FROM task_root_action_effects"
|
|
).fetchone()[0]
|
|
== 1
|
|
)
|
|
service.store.close()
|
|
|
|
# The persisted immutable plan, rather than a restarted provider, remains
|
|
# authoritative for the already claimed dispatch.
|
|
target["job_id"] = "job-provider-drift-must-not-win"
|
|
restarted_executor = DurableFakeExecutor(effects)
|
|
restarted = _service(db, target=target, executor=restarted_executor)
|
|
result = restarted.consume_one_root_action(
|
|
consumer_id="consumer-b",
|
|
now=datetime.now(tz=timezone.utc) + timedelta(seconds=2),
|
|
lease_seconds=1,
|
|
)
|
|
assert result.state == (
|
|
RootActionRunState.RECONCILING
|
|
if lose == "before"
|
|
else RootActionRunState.CLOSED
|
|
)
|
|
assert restarted_executor.reconcile_calls == 1
|
|
assert restarted_executor.acquire_calls == 0
|
|
assert restarted_executor.execute_calls == 0
|
|
assert len(effects) == (0 if lose == "before" else 1)
|
|
effect = restarted.store.conn.execute(
|
|
"SELECT target_job_id,state,effect_phase,receipt_json FROM task_root_action_effects"
|
|
).fetchone()
|
|
assert effect["target_job_id"] == "job-authority-001"
|
|
assert effect["effect_phase"] == (
|
|
"COMMIT_INTENT" if lose == "before" else "SUCCEEDED"
|
|
)
|
|
if lose == "before":
|
|
assert effect["state"] == "EFFECT_UNKNOWN"
|
|
assert effect["receipt_json"] is None
|
|
else:
|
|
assert effect["state"] == "SUCCEEDED"
|
|
assert "job-authority-001" in effect["receipt_json"]
|
|
stale = restarted.store.conn.execute(
|
|
"UPDATE task_root_action_dispatches SET status='BLOCKED' "
|
|
"WHERE claim_token=? AND claim_fence=?",
|
|
(old["claim_token"], old["claim_fence"]),
|
|
)
|
|
assert stale.rowcount == 0
|
|
|
|
|
|
def test_execution_key_excludes_review_identity_but_plan_digest_does_not():
|
|
common = {
|
|
"mission_id": "mission-stable",
|
|
"root_task_id": "root-stable",
|
|
"target_task_id": "task-authority",
|
|
"target_job_id": "job-authority",
|
|
"external_idempotency_key": "c01-commit-original-admission-digest",
|
|
"effect_payload": {
|
|
"writer_lease_request": {
|
|
"generation": 9,
|
|
"target_branch": "agent/stable",
|
|
},
|
|
"commit_request": {
|
|
"generation": 9,
|
|
"target_branch": "agent/stable",
|
|
"files": {"proof.txt": "stable"},
|
|
},
|
|
"reconcile_request": {"generation": 9, "expected_revision": 3},
|
|
},
|
|
}
|
|
first = AuthoritativeRootActionPlan.create(
|
|
review_id="review-one",
|
|
root_revision=3,
|
|
action=NextLegalAction.CREATE_OR_RESUME_NEXT_CHILD,
|
|
**common,
|
|
)
|
|
changed_observation = dict(common["effect_payload"])
|
|
changed_observation["reconcile_request"] = {
|
|
"generation": 9,
|
|
"expected_revision": 17,
|
|
}
|
|
second = AuthoritativeRootActionPlan.create(
|
|
review_id="review-two",
|
|
root_revision=4,
|
|
action=NextLegalAction.RECOVER_EXISTING_CHILD,
|
|
**{**common, "effect_payload": changed_observation},
|
|
)
|
|
assert first.execution_key == second.execution_key
|
|
assert first.plan_digest != second.plan_digest
|
|
assert AuthoritativeRootActionPlan.from_dict(first.to_dict()) == first
|
|
different_external_key = AuthoritativeRootActionPlan.create(
|
|
review_id="review-three",
|
|
root_revision=5,
|
|
action=NextLegalAction.RECOVER_EXISTING_CHILD,
|
|
**{
|
|
**common,
|
|
"external_idempotency_key": "c01-commit-other-admission-digest",
|
|
},
|
|
)
|
|
assert different_external_key.execution_key != first.execution_key
|
|
|
|
|
|
def test_effect_plan_requires_one_authoritative_external_idempotency_key():
|
|
kwargs = {
|
|
"review_id": "review-key-binding",
|
|
"mission_id": "mission-key-binding",
|
|
"root_task_id": "root-key-binding",
|
|
"root_revision": 1,
|
|
"action": NextLegalAction.RECOVER_EXISTING_CHILD,
|
|
"target_task_id": "task-key-binding",
|
|
"target_job_id": "job-key-binding",
|
|
}
|
|
with pytest.raises(ConflictError, match="EXTERNAL_IDEMPOTENCY_KEY_UNAVAILABLE"):
|
|
AuthoritativeRootActionPlan.create(
|
|
**kwargs,
|
|
effect_payload={"commit_request": {}},
|
|
external_idempotency_key=None,
|
|
)
|
|
with pytest.raises(ConflictError, match="idempotency binding conflicts"):
|
|
AuthoritativeRootActionPlan.create(
|
|
**kwargs,
|
|
effect_payload={
|
|
"commit_request": {"idempotency_key": "other-authority-key"}
|
|
},
|
|
external_idempotency_key="c01-commit-authority-key",
|
|
)
|
|
|
|
|
|
def test_public_bridge_separates_local_effect_key_from_external_idempotency_key():
|
|
calls = []
|
|
get_count = 0
|
|
lease_binding = {
|
|
"repository": "aios/aios",
|
|
"branch_mode": "CREATE_NEW",
|
|
"base_oid": "d" * 40,
|
|
"expected_branch_oid": "d" * 40,
|
|
"target_branch": "agent/root-consumer/live-authority",
|
|
"generation": 11,
|
|
}
|
|
|
|
def invoke(operation, request, auth):
|
|
nonlocal get_count
|
|
calls.append((operation, dict(request), dict(auth)))
|
|
if operation == "getGitOpsJob":
|
|
get_count += 1
|
|
if get_count == 1:
|
|
return {
|
|
"job_id": "job-live-authority",
|
|
"idempotency_key": _internal_idempotency_key(
|
|
"commit-job-live-authority"
|
|
),
|
|
"state": "REQUESTED",
|
|
"side_effect_state": "SAME_JOB",
|
|
"http_status": 202,
|
|
"revision": 0,
|
|
"receipt": None,
|
|
}
|
|
oid = "b" * 40
|
|
return {
|
|
"state": "COMPLETED",
|
|
"side_effect_state": "COMPLETED",
|
|
"http_status": 200,
|
|
"revision": 4,
|
|
"job_id": "job-live-authority",
|
|
"idempotency_key": _internal_idempotency_key(
|
|
"commit-job-live-authority"
|
|
),
|
|
"receipt": {
|
|
"commit_oid": oid,
|
|
"authority_oid": oid,
|
|
"current_head_oid": oid,
|
|
"external_job_id": "job-live-authority",
|
|
},
|
|
}
|
|
if operation == "commitFilesGoverned" and get_count > 1:
|
|
oid = "b" * 40
|
|
return {
|
|
"state": "COMPLETED",
|
|
"side_effect_state": "COMPLETED",
|
|
"http_status": 200,
|
|
"revision": 4,
|
|
"task_id": "task-live-authority",
|
|
"job_id": "job-live-authority",
|
|
"idempotency_key": _internal_idempotency_key(
|
|
"commit-job-live-authority"
|
|
),
|
|
"generation": 11,
|
|
"commit_oid": oid,
|
|
"current_head_oid": oid,
|
|
"receipt": {
|
|
"commit_oid": oid,
|
|
"authority_oid": oid,
|
|
"current_head_oid": oid,
|
|
"external_job_id": "job-live-authority",
|
|
},
|
|
}
|
|
if operation == "acquireWriterLease":
|
|
return {
|
|
"schema": "aios.task-writer-lease.v2",
|
|
"task_id": request["task_id"],
|
|
"repository": request["repository"],
|
|
"branch_mode": "CONTINUE_EXISTING",
|
|
"base_oid": request["base_oid"],
|
|
"expected_branch_oid": request["expected_branch_oid"],
|
|
"target_branch": request["target_branch"],
|
|
"generation": request["generation"],
|
|
"cas_ref_update_required": True,
|
|
}
|
|
return {"status": "ACCEPTED"}
|
|
|
|
plan = AuthoritativeRootActionPlan.create(
|
|
review_id="review-bridge",
|
|
mission_id="mission-bridge",
|
|
root_task_id="root-bridge",
|
|
root_revision=5,
|
|
action=NextLegalAction.RECOVER_EXISTING_CHILD,
|
|
target_task_id="task-live-authority",
|
|
target_job_id="job-live-authority",
|
|
external_idempotency_key="commit-job-live-authority",
|
|
effect_payload={
|
|
"reconcile_request": {"generation": 11},
|
|
"writer_lease_request": lease_binding,
|
|
"commit_request": {**lease_binding, "files": []},
|
|
},
|
|
)
|
|
adapter = PublicBridgeRootActionExecutor(
|
|
invoke=invoke,
|
|
trusted_auth_provider=lambda operation, request: {"Authorization": "trusted"},
|
|
)
|
|
guard_calls = 0
|
|
|
|
def guard():
|
|
nonlocal guard_calls
|
|
guard_calls += 1
|
|
|
|
assert adapter.reconcile(plan, before_external_call=guard).state == "IN_PROGRESS"
|
|
adapter.acquire_lease(plan, before_external_call=guard)
|
|
adapter.commit_effect(plan, before_external_call=guard)
|
|
completed = adapter.reconcile(plan, before_external_call=guard)
|
|
assert completed.state == "SUCCEEDED"
|
|
assert [call[0] for call in calls] == [
|
|
"getGitOpsJob",
|
|
"acquireWriterLease",
|
|
"commitFilesGoverned",
|
|
"getGitOpsJob",
|
|
"commitFilesGoverned",
|
|
]
|
|
assert guard_calls == 2 * len(calls)
|
|
assert all(call[1]["job_id"] == "job-live-authority" for call in calls)
|
|
assert all(call[1]["task_id"] == "task-live-authority" for call in calls)
|
|
commit_requests = [call[1] for call in calls if call[0] == "commitFilesGoverned"]
|
|
expected_commit = {
|
|
**lease_binding,
|
|
"files": [],
|
|
"task_id": "task-live-authority",
|
|
"job_id": "job-live-authority",
|
|
"idempotency_key": plan.external_idempotency_key,
|
|
}
|
|
assert commit_requests == [expected_commit, expected_commit]
|
|
lease_requests = [call[1] for call in calls if call[0] == "acquireWriterLease"]
|
|
assert lease_requests[0]["idempotency_key"] == plan.external_idempotency_key
|
|
assert all(
|
|
"idempotency_key" not in call[1]
|
|
for call in calls
|
|
if call[0] not in {"commitFilesGoverned", "acquireWriterLease"}
|
|
)
|
|
|
|
|
|
def test_public_bridge_accepts_create_new_lease_normalized_to_continue_existing():
|
|
base_oid = "c" * 40
|
|
writer_request = {
|
|
"repository": "aios/aios",
|
|
"branch_mode": "CREATE_NEW",
|
|
"base_oid": base_oid,
|
|
"expected_branch_oid": base_oid,
|
|
"target_branch": "agent/root-consumer/create-new",
|
|
"generation": 1,
|
|
}
|
|
plan = AuthoritativeRootActionPlan.create(
|
|
review_id="review-create-new-lease",
|
|
mission_id="mission-create-new-lease",
|
|
root_task_id="root-create-new-lease",
|
|
root_revision=1,
|
|
action=NextLegalAction.CREATE_OR_RESUME_NEXT_CHILD,
|
|
target_task_id="task-create-new-lease",
|
|
target_job_id="job-create-new-lease",
|
|
external_idempotency_key="commit-create-new-lease",
|
|
effect_payload={"writer_lease_request": writer_request},
|
|
)
|
|
calls = []
|
|
|
|
def invoke(operation, request, auth):
|
|
calls.append((operation, dict(request), dict(auth)))
|
|
return {
|
|
"schema": "aios.task-writer-lease.v2",
|
|
"task_id": request["task_id"],
|
|
"repository": request["repository"],
|
|
"branch_mode": "CONTINUE_EXISTING",
|
|
"base_oid": request["base_oid"],
|
|
"expected_branch_oid": request["expected_branch_oid"],
|
|
"target_branch": request["target_branch"],
|
|
"generation": request["generation"],
|
|
"cas_ref_update_required": True,
|
|
}
|
|
|
|
adapter = PublicBridgeRootActionExecutor(
|
|
invoke=invoke,
|
|
trusted_auth_provider=lambda operation, request: {"Authorization": "trusted"},
|
|
)
|
|
adapter.acquire_lease(plan, before_external_call=lambda: None)
|
|
assert calls == [
|
|
(
|
|
"acquireWriterLease",
|
|
{
|
|
**writer_request,
|
|
"task_id": plan.target_task_id,
|
|
"job_id": plan.target_job_id,
|
|
"idempotency_key": plan.external_idempotency_key,
|
|
},
|
|
{"Authorization": "trusted"},
|
|
)
|
|
]
|
|
|
|
continue_plan = AuthoritativeRootActionPlan.create(
|
|
review_id="review-continue-lease",
|
|
mission_id="mission-continue-lease",
|
|
root_task_id="root-continue-lease",
|
|
root_revision=1,
|
|
action=NextLegalAction.RECOVER_EXISTING_CHILD,
|
|
target_task_id="task-continue-lease",
|
|
target_job_id="job-continue-lease",
|
|
external_idempotency_key="commit-continue-lease",
|
|
effect_payload={
|
|
"writer_lease_request": {
|
|
**writer_request,
|
|
"branch_mode": "CONTINUE_EXISTING",
|
|
}
|
|
},
|
|
)
|
|
incompatible_adapter = PublicBridgeRootActionExecutor(
|
|
invoke=lambda operation, request, auth: {
|
|
"task_id": request["task_id"],
|
|
"repository": request["repository"],
|
|
"branch_mode": "CREATE_NEW",
|
|
"base_oid": request["base_oid"],
|
|
"expected_branch_oid": request["expected_branch_oid"],
|
|
"target_branch": request["target_branch"],
|
|
"generation": request["generation"],
|
|
},
|
|
trusted_auth_provider=lambda operation, request: {"Authorization": "trusted"},
|
|
)
|
|
with pytest.raises(ConflictError, match="writer lease response binding mismatch"):
|
|
incompatible_adapter.acquire_lease(
|
|
continue_plan, before_external_call=lambda: None
|
|
)
|
|
|
|
|
|
def test_public_bridge_accepts_exact_completed_replay_lease_projection():
|
|
base_oid = "e" * 40
|
|
writer_request = {
|
|
"repository": "aios/aios",
|
|
"branch_mode": "CREATE_NEW",
|
|
"base_oid": base_oid,
|
|
"expected_branch_oid": base_oid,
|
|
"target_branch": "agent/root-consumer/completed-replay",
|
|
"generation": 2,
|
|
}
|
|
plan = AuthoritativeRootActionPlan.create(
|
|
review_id="review-completed-replay-lease",
|
|
mission_id="mission-completed-replay-lease",
|
|
root_task_id="root-completed-replay-lease",
|
|
root_revision=1,
|
|
action=NextLegalAction.RECOVER_EXISTING_CHILD,
|
|
target_task_id="task-completed-replay-lease",
|
|
target_job_id="job-completed-replay-lease",
|
|
external_idempotency_key="commit-completed-replay-lease",
|
|
effect_payload={"writer_lease_request": writer_request},
|
|
)
|
|
adapter = PublicBridgeRootActionExecutor(
|
|
invoke=lambda operation, request, auth: {
|
|
"schema": "aios.task-writer-lease.v2",
|
|
"task_id": request["task_id"],
|
|
"job_id": request["job_id"],
|
|
"repository": request["repository"],
|
|
"branch_mode": request["branch_mode"],
|
|
"base_oid": request["base_oid"],
|
|
"expected_branch_oid": request["expected_branch_oid"],
|
|
"target_branch": request["target_branch"],
|
|
"generation": request["generation"],
|
|
"cas_ref_update_required": True,
|
|
"completed_replay": True,
|
|
"lease_created": False,
|
|
},
|
|
trusted_auth_provider=lambda operation, request: {"Authorization": "trusted"},
|
|
)
|
|
adapter.acquire_lease(plan, before_external_call=lambda: None)
|
|
|
|
|
|
def test_public_bridge_rejects_partial_or_error_writer_lease_projection():
|
|
base_oid = "f" * 40
|
|
writer_request = {
|
|
"repository": "aios/aios",
|
|
"branch_mode": "CONTINUE_EXISTING",
|
|
"base_oid": base_oid,
|
|
"expected_branch_oid": base_oid,
|
|
"target_branch": "agent/root-consumer/partial-lease",
|
|
"generation": 3,
|
|
}
|
|
plan = AuthoritativeRootActionPlan.create(
|
|
review_id="review-partial-lease",
|
|
mission_id="mission-partial-lease",
|
|
root_task_id="root-partial-lease",
|
|
root_revision=1,
|
|
action=NextLegalAction.RECOVER_EXISTING_CHILD,
|
|
target_task_id="task-partial-lease",
|
|
target_job_id="job-partial-lease",
|
|
external_idempotency_key="commit-partial-lease",
|
|
effect_payload={"writer_lease_request": writer_request},
|
|
)
|
|
valid = {
|
|
"schema": "aios.task-writer-lease.v2",
|
|
"task_id": plan.target_task_id,
|
|
**writer_request,
|
|
"cas_ref_update_required": True,
|
|
}
|
|
invalid_results = (
|
|
{"status": "ERROR", "error": "LEASE_NOT_FOUND"},
|
|
{"task_id": plan.target_task_id, "generation": 3},
|
|
{**valid, "cas_ref_update_required": False},
|
|
{**valid, "job_id": "job-different-effect"},
|
|
)
|
|
for invalid in invalid_results:
|
|
adapter = PublicBridgeRootActionExecutor(
|
|
invoke=lambda operation, request, auth, response=invalid: response,
|
|
trusted_auth_provider=lambda operation, request: {
|
|
"Authorization": "trusted"
|
|
},
|
|
)
|
|
with pytest.raises(ConflictError):
|
|
adapter.acquire_lease(plan, before_external_call=lambda: None)
|
|
|
|
|
|
def test_public_bridge_alias_without_internal_job_is_conflict():
|
|
calls = []
|
|
|
|
def invoke(operation, request, auth):
|
|
calls.append(operation)
|
|
return {
|
|
"job_id": request["job_id"],
|
|
"idempotency_key": "server-reserved-alias-key",
|
|
"state": "RECONCILIATION_REQUIRED",
|
|
"side_effect_state": "ALIAS_RESERVED_INTERNAL_JOB_MISSING",
|
|
"http_status": 202,
|
|
"receipt": None,
|
|
}
|
|
|
|
plan = AuthoritativeRootActionPlan.create(
|
|
review_id="review-missing",
|
|
mission_id="mission-missing",
|
|
root_task_id="root-missing",
|
|
root_revision=1,
|
|
action=NextLegalAction.RECOVER_EXISTING_CHILD,
|
|
target_task_id="task-missing",
|
|
target_job_id="job-missing",
|
|
external_idempotency_key="commit-job-missing",
|
|
effect_payload={"reconcile_request": {}},
|
|
)
|
|
adapter = PublicBridgeRootActionExecutor(
|
|
invoke=invoke,
|
|
trusted_auth_provider=lambda operation, request: {"Authorization": "trusted"},
|
|
)
|
|
observed = adapter.reconcile(plan, before_external_call=lambda: None)
|
|
assert observed == EffectObservation(
|
|
"CONFLICT", error_code="BRIDGE_ALIAS_RESERVED_INTERNAL_JOB_MISSING"
|
|
)
|
|
assert calls == ["getGitOpsJob"]
|
|
|
|
|
|
def test_public_bridge_rejects_same_external_job_with_other_commit_key():
|
|
plan = AuthoritativeRootActionPlan.create(
|
|
review_id="review-wrong-key",
|
|
mission_id="mission-wrong-key",
|
|
root_task_id="root-wrong-key",
|
|
root_revision=1,
|
|
action=NextLegalAction.RECOVER_EXISTING_CHILD,
|
|
target_task_id="task-wrong-key",
|
|
target_job_id="job-wrong-key",
|
|
external_idempotency_key="c01-commit-authority-key",
|
|
effect_payload={"reconcile_request": {}},
|
|
)
|
|
adapter = PublicBridgeRootActionExecutor(
|
|
invoke=lambda operation, request, auth: {
|
|
"job_id": request["job_id"],
|
|
"idempotency_key": _internal_idempotency_key("other-commit-key"),
|
|
"state": "REQUESTED",
|
|
"side_effect_state": "SAME_JOB",
|
|
"http_status": 202,
|
|
"revision": 0,
|
|
"receipt": None,
|
|
},
|
|
trusted_auth_provider=lambda operation, request: {"Authorization": "trusted"},
|
|
)
|
|
with pytest.raises(ConflictError, match="idempotency authority mismatch"):
|
|
adapter.reconcile(plan, before_external_call=lambda: None)
|
|
|
|
|
|
def test_public_bridge_rejects_completed_job_without_http_200():
|
|
plan = AuthoritativeRootActionPlan.create(
|
|
review_id="review-completed-status",
|
|
mission_id="mission-completed-status",
|
|
root_task_id="root-completed-status",
|
|
root_revision=1,
|
|
action=NextLegalAction.RECOVER_EXISTING_CHILD,
|
|
target_task_id="task-completed-status",
|
|
target_job_id="job-completed-status",
|
|
external_idempotency_key="commit-completed-status",
|
|
effect_payload={"reconcile_request": {}, "commit_request": {}},
|
|
)
|
|
oid = "9" * 40
|
|
adapter = PublicBridgeRootActionExecutor(
|
|
invoke=lambda operation, request, auth: {
|
|
"job_id": request["job_id"],
|
|
"idempotency_key": _internal_idempotency_key("commit-completed-status"),
|
|
"state": "COMPLETED",
|
|
"side_effect_state": "COMPLETED",
|
|
"http_status": 202,
|
|
"revision": 1,
|
|
"receipt": {
|
|
"commit_oid": oid,
|
|
"authority_oid": oid,
|
|
"current_head_oid": oid,
|
|
"external_job_id": request["job_id"],
|
|
},
|
|
},
|
|
trusted_auth_provider=lambda operation, request: {"Authorization": "trusted"},
|
|
)
|
|
with pytest.raises(ConflictError, match="must use HTTP 200"):
|
|
adapter.reconcile(plan, before_external_call=lambda: None)
|
|
|
|
|
|
def test_public_bridge_transport_ambiguous_never_proves_absence():
|
|
calls = []
|
|
|
|
def invoke(operation, request, auth):
|
|
calls.append(operation)
|
|
raise TimeoutError("HTTP 503 transport ambiguous")
|
|
|
|
plan = AuthoritativeRootActionPlan.create(
|
|
review_id="review-no-alias",
|
|
mission_id="mission-no-alias",
|
|
root_task_id="root-no-alias",
|
|
root_revision=1,
|
|
action=NextLegalAction.RECOVER_EXISTING_CHILD,
|
|
target_task_id="task-no-alias",
|
|
target_job_id="job-no-alias",
|
|
external_idempotency_key="commit-job-no-alias",
|
|
effect_payload={"reconcile_request": {}},
|
|
)
|
|
adapter = PublicBridgeRootActionExecutor(
|
|
invoke=invoke,
|
|
trusted_auth_provider=lambda operation, request: {"Authorization": "trusted"},
|
|
)
|
|
with pytest.raises(TimeoutError, match="503 transport ambiguous"):
|
|
adapter.reconcile(plan, before_external_call=lambda: None)
|
|
assert calls == ["getGitOpsJob"]
|
|
|
|
|
|
def test_completed_job_requires_exact_commit_replay_binding():
|
|
calls = []
|
|
|
|
def invoke(operation, request, auth):
|
|
calls.append((operation, dict(request)))
|
|
if operation == "getGitOpsJob":
|
|
oid = "e" * 40
|
|
return {
|
|
"job_id": request["job_id"],
|
|
"idempotency_key": _internal_idempotency_key("commit-job-wrong-effect"),
|
|
"state": "COMPLETED",
|
|
"side_effect_state": "COMPLETED",
|
|
"http_status": 200,
|
|
"revision": 5,
|
|
"receipt": {
|
|
"commit_oid": oid,
|
|
"authority_oid": oid,
|
|
"current_head_oid": oid,
|
|
"external_job_id": request["job_id"],
|
|
},
|
|
}
|
|
raise ConflictError("COMPLETED_REPLAY_BINDING_CONFLICT")
|
|
|
|
plan = AuthoritativeRootActionPlan.create(
|
|
review_id="review-wrong-effect",
|
|
mission_id="mission-wrong-effect",
|
|
root_task_id="root-wrong-effect",
|
|
root_revision=1,
|
|
action=NextLegalAction.RECOVER_EXISTING_CHILD,
|
|
target_task_id="task-wrong-effect",
|
|
target_job_id="job-wrong-effect",
|
|
external_idempotency_key="commit-job-wrong-effect",
|
|
effect_payload={
|
|
"reconcile_request": {},
|
|
"commit_request": {
|
|
"files": {"proof.txt": "different-payload"},
|
|
"message": "different mutation",
|
|
},
|
|
},
|
|
)
|
|
adapter = PublicBridgeRootActionExecutor(
|
|
invoke=invoke,
|
|
trusted_auth_provider=lambda operation, request: {"Authorization": "trusted"},
|
|
)
|
|
with pytest.raises(ConflictError, match="COMPLETED_REPLAY_BINDING_CONFLICT"):
|
|
adapter.reconcile(plan, before_external_call=lambda: None)
|
|
assert [operation for operation, _request in calls] == [
|
|
"getGitOpsJob",
|
|
"commitFilesGoverned",
|
|
]
|
|
|
|
|
|
def test_completed_job_rejects_mismatched_external_job_receipt():
|
|
calls = []
|
|
|
|
def invoke(operation, request, auth):
|
|
calls.append(operation)
|
|
oid = "f" * 40
|
|
return {
|
|
"job_id": request["job_id"],
|
|
"idempotency_key": _internal_idempotency_key(
|
|
"commit-job-external-job-mismatch"
|
|
),
|
|
"state": "COMPLETED",
|
|
"side_effect_state": "COMPLETED",
|
|
"http_status": 200,
|
|
"revision": 5,
|
|
"receipt": {
|
|
"commit_oid": oid,
|
|
"authority_oid": oid,
|
|
"current_head_oid": oid,
|
|
"external_job_id": "job-other-effect",
|
|
},
|
|
}
|
|
|
|
plan = AuthoritativeRootActionPlan.create(
|
|
review_id="review-external-job-mismatch",
|
|
mission_id="mission-external-job-mismatch",
|
|
root_task_id="root-external-job-mismatch",
|
|
root_revision=1,
|
|
action=NextLegalAction.RECOVER_EXISTING_CHILD,
|
|
target_task_id="task-external-job-mismatch",
|
|
target_job_id="job-external-job-mismatch",
|
|
external_idempotency_key="commit-job-external-job-mismatch",
|
|
effect_payload={
|
|
"reconcile_request": {},
|
|
"commit_request": {"files": {}, "message": "bound mutation"},
|
|
},
|
|
)
|
|
adapter = PublicBridgeRootActionExecutor(
|
|
invoke=invoke,
|
|
trusted_auth_provider=lambda operation, request: {"Authorization": "trusted"},
|
|
)
|
|
with pytest.raises(ConflictError, match="completed receipt Job authority"):
|
|
adapter.reconcile(plan, before_external_call=lambda: None)
|
|
assert calls == ["getGitOpsJob"]
|
|
|
|
|
|
def test_bridge_rechecks_fence_after_auth_before_invoke():
|
|
guard_calls = 0
|
|
invoke_calls = 0
|
|
|
|
def guard():
|
|
nonlocal guard_calls
|
|
guard_calls += 1
|
|
if guard_calls == 2:
|
|
raise ConflictError("claim expired during authentication")
|
|
|
|
def invoke(operation, request, auth):
|
|
nonlocal invoke_calls
|
|
invoke_calls += 1
|
|
raise AssertionError("stale claim must not reach Bridge invoke")
|
|
|
|
plan = AuthoritativeRootActionPlan.create(
|
|
review_id="review-auth-fence",
|
|
mission_id="mission-auth-fence",
|
|
root_task_id="root-auth-fence",
|
|
root_revision=1,
|
|
action=NextLegalAction.RECOVER_EXISTING_CHILD,
|
|
target_task_id="task-auth-fence",
|
|
target_job_id="job-auth-fence",
|
|
external_idempotency_key="commit-job-auth-fence",
|
|
effect_payload={"reconcile_request": {}},
|
|
)
|
|
adapter = PublicBridgeRootActionExecutor(
|
|
invoke=invoke,
|
|
trusted_auth_provider=lambda operation, request: {"Authorization": "trusted"},
|
|
)
|
|
with pytest.raises(ConflictError, match="expired during authentication"):
|
|
adapter.reconcile(plan, before_external_call=guard)
|
|
assert guard_calls == 2
|
|
assert invoke_calls == 0
|
|
|
|
|
|
def test_public_bridge_reconciles_recoverable_job_at_observed_revision_only():
|
|
calls = []
|
|
|
|
def invoke(operation, request, auth):
|
|
calls.append((operation, dict(request)))
|
|
if operation == "getGitOpsJob":
|
|
return {
|
|
"job_id": "job-recoverable",
|
|
"idempotency_key": _internal_idempotency_key("commit-job-recoverable"),
|
|
"state": "RECOVERABLE_FAILURE",
|
|
"side_effect_state": "RESUME_SAME_JOB",
|
|
"http_status": 202,
|
|
"revision": 7,
|
|
"receipt": None,
|
|
}
|
|
return {
|
|
"job_id": "job-recoverable",
|
|
"idempotency_key": _internal_idempotency_key("commit-job-recoverable"),
|
|
"state": "RECONCILIATION_REQUIRED",
|
|
"side_effect_state": "UNKNOWN",
|
|
"http_status": 503,
|
|
"revision": 8,
|
|
"receipt": None,
|
|
}
|
|
|
|
plan = AuthoritativeRootActionPlan.create(
|
|
review_id="review-recoverable",
|
|
mission_id="mission-recoverable",
|
|
root_task_id="root-recoverable",
|
|
root_revision=2,
|
|
action=NextLegalAction.RECOVER_EXISTING_CHILD,
|
|
target_task_id="task-recoverable",
|
|
target_job_id="job-recoverable",
|
|
external_idempotency_key="commit-job-recoverable",
|
|
effect_payload={"reconcile_request": {"expected_revision": 1}},
|
|
)
|
|
adapter = PublicBridgeRootActionExecutor(
|
|
invoke=invoke,
|
|
trusted_auth_provider=lambda operation, request: {"Authorization": "trusted"},
|
|
)
|
|
observed = adapter.reconcile(plan, before_external_call=lambda: None)
|
|
assert observed.state == "IN_PROGRESS"
|
|
assert calls == [
|
|
(
|
|
"getGitOpsJob",
|
|
{
|
|
"task_id": "task-recoverable",
|
|
"job_id": "job-recoverable",
|
|
"expected_revision": 1,
|
|
},
|
|
),
|
|
(
|
|
"reconcileGitOpsJob",
|
|
{
|
|
"task_id": "task-recoverable",
|
|
"job_id": "job-recoverable",
|
|
"expected_revision": 7,
|
|
},
|
|
),
|
|
]
|
|
assert all(operation != "resumeGitOpsJob" for operation, _request in calls)
|
|
|
|
|
|
def test_recoverable_completion_also_requires_exact_completed_replay():
|
|
calls = []
|
|
|
|
def completed(*, replay: bool):
|
|
oid = "9" * 40
|
|
value = {
|
|
"job_id": "job-reconcile-completed",
|
|
"idempotency_key": _internal_idempotency_key(
|
|
"commit-job-reconcile-completed"
|
|
),
|
|
"state": "COMPLETED",
|
|
"side_effect_state": "COMPLETED",
|
|
"http_status": 200,
|
|
"revision": 8,
|
|
"receipt": {
|
|
"commit_oid": oid,
|
|
"authority_oid": oid,
|
|
"current_head_oid": oid,
|
|
"external_job_id": "job-reconcile-completed",
|
|
},
|
|
}
|
|
if replay:
|
|
value.update(
|
|
{
|
|
"task_id": "task-reconcile-completed",
|
|
"generation": 3,
|
|
"commit_oid": oid,
|
|
"current_head_oid": oid,
|
|
}
|
|
)
|
|
return value
|
|
|
|
def invoke(operation, request, auth):
|
|
calls.append((operation, dict(request)))
|
|
if operation == "getGitOpsJob":
|
|
return {
|
|
"job_id": "job-reconcile-completed",
|
|
"idempotency_key": _internal_idempotency_key(
|
|
"commit-job-reconcile-completed"
|
|
),
|
|
"state": "RECOVERABLE_FAILURE",
|
|
"side_effect_state": "RESUME_SAME_JOB",
|
|
"http_status": 202,
|
|
"revision": 7,
|
|
"receipt": None,
|
|
}
|
|
if operation == "reconcileGitOpsJob":
|
|
return completed(replay=False)
|
|
return completed(replay=True)
|
|
|
|
plan = AuthoritativeRootActionPlan.create(
|
|
review_id="review-reconcile-completed",
|
|
mission_id="mission-reconcile-completed",
|
|
root_task_id="root-reconcile-completed",
|
|
root_revision=2,
|
|
action=NextLegalAction.RECOVER_EXISTING_CHILD,
|
|
target_task_id="task-reconcile-completed",
|
|
target_job_id="job-reconcile-completed",
|
|
external_idempotency_key="commit-job-reconcile-completed",
|
|
effect_payload={
|
|
"reconcile_request": {"expected_revision": 1},
|
|
"commit_request": {"generation": 3, "files": {}, "message": "repair"},
|
|
},
|
|
)
|
|
adapter = PublicBridgeRootActionExecutor(
|
|
invoke=invoke,
|
|
trusted_auth_provider=lambda operation, request: {"Authorization": "trusted"},
|
|
)
|
|
observed = adapter.reconcile(plan, before_external_call=lambda: None)
|
|
assert observed.state == "SUCCEEDED"
|
|
assert [operation for operation, _request in calls] == [
|
|
"getGitOpsJob",
|
|
"reconcileGitOpsJob",
|
|
"commitFilesGoverned",
|
|
]
|
|
assert calls[1][1]["expected_revision"] == 7
|
|
assert calls[2][1]["idempotency_key"] == plan.external_idempotency_key
|
|
|
|
|
|
def test_bridge_response_loss_restart_queries_same_completed_job_before_retry(tmp_path):
|
|
target = {
|
|
"task_id": "placeholder",
|
|
"job_id": "job-bridge-loss",
|
|
"idempotency_key": "commit-job-bridge-loss",
|
|
}
|
|
durable = {"state": "REQUESTED", "lose_commit_response": True}
|
|
calls = []
|
|
commit_effects = 0
|
|
|
|
def invoke(operation, request, auth):
|
|
nonlocal commit_effects
|
|
calls.append((operation, dict(request)))
|
|
if operation == "getGitOpsJob" and durable["state"] == "REQUESTED":
|
|
return {
|
|
"job_id": target["job_id"],
|
|
"idempotency_key": _internal_idempotency_key(target["idempotency_key"]),
|
|
"state": "REQUESTED",
|
|
"side_effect_state": "SAME_JOB",
|
|
"http_status": 202,
|
|
"revision": 0,
|
|
"receipt": None,
|
|
}
|
|
if operation == "getGitOpsJob":
|
|
oid = "d" * 40
|
|
return {
|
|
"job_id": target["job_id"],
|
|
"idempotency_key": _internal_idempotency_key(target["idempotency_key"]),
|
|
"state": "COMPLETED",
|
|
"side_effect_state": "COMPLETED",
|
|
"http_status": 200,
|
|
"revision": 4,
|
|
"receipt": {
|
|
"commit_oid": oid,
|
|
"authority_oid": oid,
|
|
"current_head_oid": oid,
|
|
"external_job_id": target["job_id"],
|
|
},
|
|
}
|
|
if operation == "commitFilesGoverned":
|
|
oid = "d" * 40
|
|
if durable["state"] != "COMPLETED":
|
|
commit_effects += 1
|
|
durable["state"] = "COMPLETED"
|
|
if durable.pop("lose_commit_response", False):
|
|
raise TimeoutError("commit response lost")
|
|
return {
|
|
"job_id": target["job_id"],
|
|
"task_id": target["task_id"],
|
|
"idempotency_key": _internal_idempotency_key(target["idempotency_key"]),
|
|
"state": "COMPLETED",
|
|
"side_effect_state": "COMPLETED",
|
|
"http_status": 200,
|
|
"revision": 4,
|
|
"generation": 7,
|
|
"receipt": {
|
|
"commit_oid": oid,
|
|
"authority_oid": oid,
|
|
"current_head_oid": oid,
|
|
"external_job_id": target["job_id"],
|
|
},
|
|
}
|
|
if operation == "acquireWriterLease":
|
|
return _ordinary_writer_lease_response(request)
|
|
return {"status": "ACCEPTED"}
|
|
|
|
adapter = PublicBridgeRootActionExecutor(
|
|
invoke=invoke,
|
|
trusted_auth_provider=lambda operation, request: {"Authorization": "trusted"},
|
|
)
|
|
db = tmp_path / "bridge-response-loss.db"
|
|
service = _service(db, target=target, executor=adapter)
|
|
mission = _mission(service)
|
|
target_child = _child(service, mission["root_task_id"], "target")
|
|
target["task_id"] = target_child["task_id"]
|
|
trigger = _child(service, mission["root_task_id"], "trigger")
|
|
_finish(service, trigger["task_id"])
|
|
with pytest.raises(TimeoutError, match="response lost"):
|
|
service.consume_one_root_action(consumer_id="first", lease_seconds=1)
|
|
service.store.close()
|
|
|
|
restarted = _service(db, target=target, executor=adapter)
|
|
result = restarted.consume_one_root_action(
|
|
consumer_id="second",
|
|
now=datetime.now(tz=timezone.utc) + timedelta(seconds=2),
|
|
lease_seconds=1,
|
|
)
|
|
assert result.state == RootActionRunState.CLOSED
|
|
assert [operation for operation, _request in calls].count(
|
|
"commitFilesGoverned"
|
|
) == 2
|
|
assert [operation for operation, _request in calls].count("acquireWriterLease") == 1
|
|
assert commit_effects == 1
|
|
assert [operation for operation, _request in calls][-1] == "commitFilesGoverned"
|
|
sent = [
|
|
request["idempotency_key"]
|
|
for operation, request in calls
|
|
if operation in {"acquireWriterLease", "commitFilesGoverned"}
|
|
]
|
|
assert set(sent) == {target["idempotency_key"]}
|
|
|
|
|
|
def test_lease_response_loss_restarts_at_alias_observation_without_reacquire(tmp_path):
|
|
target = {
|
|
"task_id": "placeholder",
|
|
"job_id": "job-lease-loss",
|
|
"idempotency_key": "commit-job-lease-loss",
|
|
}
|
|
calls = []
|
|
|
|
def invoke(operation, request, auth):
|
|
calls.append((operation, dict(request)))
|
|
if operation == "acquireWriterLease":
|
|
raise TimeoutError("lease response lost")
|
|
return {
|
|
"job_id": target["job_id"],
|
|
"idempotency_key": request.get("idempotency_key", "unknown"),
|
|
"state": "RECONCILIATION_REQUIRED",
|
|
"side_effect_state": "ALIAS_RESERVED_INTERNAL_JOB_MISSING",
|
|
"http_status": 202,
|
|
"receipt": None,
|
|
}
|
|
|
|
adapter = PublicBridgeRootActionExecutor(
|
|
invoke=invoke,
|
|
trusted_auth_provider=lambda operation, request: {"Authorization": "trusted"},
|
|
)
|
|
db = tmp_path / "lease-response-loss.db"
|
|
service = _service(db, target=target, executor=adapter)
|
|
mission = _mission(service)
|
|
target_child = _child(service, mission["root_task_id"], "target")
|
|
target["task_id"] = target_child["task_id"]
|
|
trigger = _child(service, mission["root_task_id"], "trigger")
|
|
_finish(service, trigger["task_id"])
|
|
with pytest.raises(TimeoutError, match="lease response lost"):
|
|
service.consume_one_root_action(consumer_id="first", lease_seconds=1)
|
|
phase = service.store.conn.execute(
|
|
"SELECT effect_phase FROM task_root_action_effects"
|
|
).fetchone()[0]
|
|
assert phase == "LEASE_INTENT"
|
|
service.store.close()
|
|
|
|
restarted = _service(db, target=target, executor=adapter)
|
|
result = restarted.consume_one_root_action(
|
|
consumer_id="second",
|
|
now=datetime.now(tz=timezone.utc) + timedelta(seconds=2),
|
|
lease_seconds=1,
|
|
)
|
|
assert result.state == RootActionRunState.BLOCKED
|
|
assert result.detail == "BRIDGE_ALIAS_RESERVED_INTERNAL_JOB_MISSING"
|
|
assert [operation for operation, _request in calls] == [
|
|
"acquireWriterLease",
|
|
"getGitOpsJob",
|
|
]
|
|
|
|
|
|
def test_requested_job_after_commit_intent_is_observed_without_resend(tmp_path):
|
|
target = {
|
|
"task_id": "placeholder",
|
|
"job_id": "job-requested",
|
|
"idempotency_key": "commit-job-requested",
|
|
}
|
|
calls = []
|
|
lose_commit = [True]
|
|
|
|
def invoke(operation, request, auth):
|
|
calls.append((operation, dict(request)))
|
|
if operation == "acquireWriterLease":
|
|
return _ordinary_writer_lease_response(request)
|
|
if operation == "commitFilesGoverned" and lose_commit[0]:
|
|
lose_commit[0] = False
|
|
raise TimeoutError("commit accepted; response lost")
|
|
return {
|
|
"job_id": target["job_id"],
|
|
"idempotency_key": _internal_idempotency_key(target["idempotency_key"]),
|
|
"state": "REQUESTED",
|
|
"side_effect_state": "SAME_JOB",
|
|
"http_status": 202,
|
|
"revision": 0,
|
|
"receipt": None,
|
|
}
|
|
|
|
adapter = PublicBridgeRootActionExecutor(
|
|
invoke=invoke,
|
|
trusted_auth_provider=lambda operation, request: {"Authorization": "trusted"},
|
|
)
|
|
db = tmp_path / "requested-no-resend.db"
|
|
service = _service(db, target=target, executor=adapter)
|
|
mission = _mission(service)
|
|
target_child = _child(service, mission["root_task_id"], "target")
|
|
target["task_id"] = target_child["task_id"]
|
|
trigger = _child(service, mission["root_task_id"], "trigger")
|
|
_finish(service, trigger["task_id"])
|
|
with pytest.raises(TimeoutError):
|
|
service.consume_one_root_action(consumer_id="first", lease_seconds=1)
|
|
service.store.close()
|
|
|
|
restarted = _service(db, target=target, executor=adapter)
|
|
result = restarted.consume_one_root_action(
|
|
consumer_id="second",
|
|
now=datetime.now(tz=timezone.utc) + timedelta(seconds=2),
|
|
lease_seconds=1,
|
|
)
|
|
assert result.state == RootActionRunState.RECONCILING
|
|
assert result.detail == "IN_PROGRESS"
|
|
assert [operation for operation, _request in calls] == [
|
|
"acquireWriterLease",
|
|
"commitFilesGoverned",
|
|
"getGitOpsJob",
|
|
]
|
|
effect = restarted.store.conn.execute(
|
|
"SELECT state,effect_phase,receipt_json FROM task_root_action_effects"
|
|
).fetchone()
|
|
assert tuple(effect) == ("EFFECT_UNKNOWN", "COMMIT_INTENT", None)
|
|
|
|
|
|
def test_restart_from_durable_lease_acquired_commits_without_reacquire(tmp_path):
|
|
target = {
|
|
"task_id": "placeholder",
|
|
"job_id": "job-lease-durable",
|
|
"idempotency_key": "commit-job-lease-durable",
|
|
}
|
|
calls = []
|
|
oid = "8" * 40
|
|
|
|
def completed(request):
|
|
return {
|
|
"job_id": target["job_id"],
|
|
"idempotency_key": _internal_idempotency_key(target["idempotency_key"]),
|
|
"state": "COMPLETED",
|
|
"side_effect_state": "COMPLETED",
|
|
"http_status": 200,
|
|
"revision": 2,
|
|
"generation": 7,
|
|
"task_id": target["task_id"],
|
|
"receipt": {
|
|
"commit_oid": oid,
|
|
"authority_oid": oid,
|
|
"current_head_oid": oid,
|
|
"external_job_id": target["job_id"],
|
|
},
|
|
}
|
|
|
|
def invoke(operation, request, auth):
|
|
calls.append((operation, dict(request)))
|
|
if operation == "acquireWriterLease":
|
|
return _ordinary_writer_lease_response(request)
|
|
return completed(request)
|
|
|
|
adapter = PublicBridgeRootActionExecutor(
|
|
invoke=invoke,
|
|
trusted_auth_provider=lambda operation, request: {"Authorization": "trusted"},
|
|
)
|
|
db = tmp_path / "lease-acquired-restart.db"
|
|
service = _service(db, target=target, executor=adapter)
|
|
mission = _mission(service)
|
|
target_child = _child(service, mission["root_task_id"], "target")
|
|
target["task_id"] = target_child["task_id"]
|
|
trigger = _child(service, mission["root_task_id"], "trigger")
|
|
_finish(service, trigger["task_id"])
|
|
ordinary = service.root_actions
|
|
assert ordinary is not None
|
|
|
|
class CrashAfterLeaseConsumer(RootActionConsumer):
|
|
def _transition_effect_phase(self, claim, *, expected, target, now):
|
|
if expected == "LEASE_ACQUIRED" and target == "COMMIT_INTENT":
|
|
raise RuntimeError("crash after durable lease")
|
|
return super()._transition_effect_phase(
|
|
claim, expected=expected, target=target, now=now
|
|
)
|
|
|
|
crashing = CrashAfterLeaseConsumer(
|
|
service.store,
|
|
plan_builder=ordinary._plan_builder,
|
|
rereviewer=ordinary._rereviewer,
|
|
executor=adapter,
|
|
)
|
|
with pytest.raises(RuntimeError, match="crash after durable lease"):
|
|
crashing.run_once(
|
|
consumer_id="first",
|
|
now=datetime.now(tz=timezone.utc),
|
|
lease_seconds=1,
|
|
)
|
|
assert (
|
|
service.store.conn.execute(
|
|
"SELECT effect_phase FROM task_root_action_effects"
|
|
).fetchone()[0]
|
|
== "LEASE_ACQUIRED"
|
|
)
|
|
service.store.close()
|
|
|
|
restarted = _service(db, target=target, executor=adapter)
|
|
result = restarted.consume_one_root_action(
|
|
consumer_id="second",
|
|
now=datetime.now(tz=timezone.utc) + timedelta(seconds=2),
|
|
lease_seconds=1,
|
|
)
|
|
assert result.state == RootActionRunState.CLOSED
|
|
operations = [operation for operation, _request in calls]
|
|
assert operations.count("acquireWriterLease") == 1
|
|
assert operations == [
|
|
"acquireWriterLease",
|
|
"commitFilesGoverned",
|
|
"getGitOpsJob",
|
|
"commitFilesGoverned",
|
|
]
|
|
|
|
|
|
@pytest.mark.parametrize(
|
|
("corrupt_field", "corrupt_value", "error"),
|
|
[
|
|
("current_head_oid", "c" * 40, "receipt OID mismatch"),
|
|
("job_id", "job-wrong-authority", "receipt authority mismatch"),
|
|
],
|
|
)
|
|
def test_conflicting_bridge_receipt_remains_unknown_and_cannot_close(
|
|
tmp_path, corrupt_field, corrupt_value, error
|
|
):
|
|
class ConflictingOidExecutor(DurableFakeExecutor):
|
|
def commit_effect(self, plan, *, before_external_call):
|
|
before_external_call()
|
|
self.execute_calls += 1
|
|
receipt = self._receipt(plan)
|
|
receipt[corrupt_field] = corrupt_value
|
|
self.effects[plan.execution_key] = receipt
|
|
|
|
effects: dict[str, dict] = {}
|
|
target = {
|
|
"task_id": "placeholder",
|
|
"job_id": "job-conflict",
|
|
"idempotency_key": "commit-job-conflict",
|
|
}
|
|
executor = ConflictingOidExecutor(effects)
|
|
service = _service(tmp_path / "bad-oid.db", target=target, executor=executor)
|
|
mission = _mission(service)
|
|
target_child = _child(service, mission["root_task_id"], "target")
|
|
target["task_id"] = target_child["task_id"]
|
|
trigger = _child(service, mission["root_task_id"], "trigger")
|
|
_finish(service, trigger["task_id"])
|
|
|
|
with pytest.raises(ConflictError, match=error):
|
|
service.consume_one_root_action(consumer_id="consumer")
|
|
effect = service.store.conn.execute(
|
|
"SELECT state,receipt_json FROM task_root_action_effects"
|
|
).fetchone()
|
|
assert effect["state"] in {"ABSENT", "EFFECT_UNKNOWN"}
|
|
assert effect["receipt_json"] is None
|
|
assert service.store.get_task(mission["root_task_id"]).status != "COMPLETED"
|
|
|
|
|
|
def test_newer_sibling_review_reuses_unknown_effect(tmp_path):
|
|
db = tmp_path / "siblings.db"
|
|
effects: dict[str, dict] = {}
|
|
target = {
|
|
"task_id": "placeholder",
|
|
"job_id": "job-authority-sibling",
|
|
"idempotency_key": "commit-job-authority-sibling",
|
|
}
|
|
first_executor = DurableFakeExecutor(effects, lose="after")
|
|
service = _service(db, target=target, executor=first_executor)
|
|
mission = _mission(service)
|
|
target_child = _child(service, mission["root_task_id"], "target")
|
|
target["task_id"] = target_child["task_id"]
|
|
first_trigger = _child(service, mission["root_task_id"], "trigger-one")
|
|
second_trigger = _child(service, mission["root_task_id"], "trigger-two")
|
|
first_review = _finish(service, first_trigger["task_id"])["root_review"][
|
|
"review_id"
|
|
]
|
|
with pytest.raises(TimeoutError):
|
|
service.consume_one_root_action(consumer_id="first", lease_seconds=1)
|
|
second_review = _finish(service, second_trigger["task_id"])["root_review"][
|
|
"review_id"
|
|
]
|
|
service.store.close()
|
|
|
|
second_executor = DurableFakeExecutor(effects)
|
|
restarted = _service(db, target=target, executor=second_executor)
|
|
result = restarted.consume_one_root_action(
|
|
consumer_id="second",
|
|
now=datetime.now(tz=timezone.utc) + timedelta(seconds=2),
|
|
lease_seconds=1,
|
|
)
|
|
assert result.review_id == second_review
|
|
assert second_executor.reconcile_calls == 1
|
|
assert second_executor.execute_calls == 0
|
|
rows = restarted.store.conn.execute(
|
|
"SELECT review_id,status,execution_key FROM task_root_action_dispatches "
|
|
"WHERE review_id IN (?,?) ORDER BY root_revision",
|
|
(first_review, second_review),
|
|
).fetchall()
|
|
assert rows[0]["status"] == "SUPERSEDED"
|
|
assert rows[1]["status"] == "CLOSED"
|
|
assert rows[0]["execution_key"] == rows[1]["execution_key"]
|
|
assert (
|
|
restarted.store.conn.execute(
|
|
"SELECT COUNT(*) FROM task_root_action_effects"
|
|
).fetchone()[0]
|
|
== 1
|
|
)
|
|
|
|
|
|
def test_two_consumers_cannot_execute_one_root_effect_concurrently(tmp_path):
|
|
entered = threading.Event()
|
|
release = threading.Event()
|
|
|
|
class BlockingExecutor(DurableFakeExecutor):
|
|
def acquire_lease(self, plan, *, before_external_call):
|
|
before_external_call()
|
|
self.acquire_calls += 1
|
|
entered.set()
|
|
assert release.wait(timeout=5)
|
|
|
|
effects: dict[str, dict] = {}
|
|
target = {
|
|
"task_id": "placeholder",
|
|
"job_id": "job-concurrent",
|
|
"idempotency_key": "commit-job-concurrent",
|
|
}
|
|
executor = BlockingExecutor(effects)
|
|
service = _service(tmp_path / "consumer-race.db", target=target, executor=executor)
|
|
mission = _mission(service)
|
|
target_child = _child(service, mission["root_task_id"], "target")
|
|
target["task_id"] = target_child["task_id"]
|
|
trigger = _child(service, mission["root_task_id"], "trigger")
|
|
_finish(service, trigger["task_id"])
|
|
first_result = {}
|
|
|
|
def consume_first():
|
|
first_result["value"] = service.consume_one_root_action(
|
|
consumer_id="consumer-a"
|
|
)
|
|
|
|
first = threading.Thread(target=consume_first)
|
|
first.start()
|
|
assert entered.wait(timeout=5)
|
|
second = service.consume_one_root_action(consumer_id="consumer-b")
|
|
assert second.state == RootActionRunState.IDLE
|
|
release.set()
|
|
first.join(timeout=5)
|
|
assert first_result["value"].state == RootActionRunState.CLOSED
|
|
assert executor.execute_calls == 1
|
|
assert len(effects) == 1
|
|
|
|
|
|
def test_claimed_fence_blocks_child_between_claim_and_effect_intent(tmp_path):
|
|
entered = threading.Event()
|
|
release = threading.Event()
|
|
effects: dict[str, dict] = {}
|
|
target = {
|
|
"task_id": "placeholder",
|
|
"job_id": "job-claim-admission-race",
|
|
"idempotency_key": "commit-job-claim-admission-race",
|
|
}
|
|
executor = DurableFakeExecutor(effects)
|
|
db = tmp_path / "claim-v-admission.db"
|
|
first = _service(db, target=target, executor=executor)
|
|
mission = _mission(first)
|
|
target_child = _child(first, mission["root_task_id"], "target")
|
|
target["task_id"] = target_child["task_id"]
|
|
trigger = _child(first, mission["root_task_id"], "trigger")
|
|
_finish(first, trigger["task_id"])
|
|
ordinary = first.root_actions
|
|
assert ordinary is not None
|
|
|
|
class BlockingPrepareConsumer(RootActionConsumer):
|
|
def _prepare_effect(self, claim, now):
|
|
entered.set()
|
|
assert release.wait(timeout=5)
|
|
return super()._prepare_effect(claim, now)
|
|
|
|
consumer = BlockingPrepareConsumer(
|
|
first.store,
|
|
plan_builder=ordinary._plan_builder,
|
|
rereviewer=ordinary._rereviewer,
|
|
executor=executor,
|
|
)
|
|
second = _service(db)
|
|
result = {}
|
|
|
|
def consume():
|
|
result["value"] = consumer.run_once(
|
|
consumer_id="claim-holder", now=datetime.now(tz=timezone.utc)
|
|
)
|
|
|
|
thread = threading.Thread(target=consume)
|
|
thread.start()
|
|
assert entered.wait(timeout=5)
|
|
with pytest.raises(ConflictError, match="root action fence"):
|
|
_child(second, mission["root_task_id"], "must-not-enter")
|
|
release.set()
|
|
thread.join(timeout=5)
|
|
assert result["value"].state == RootActionRunState.CLOSED
|
|
assert (
|
|
second.store.conn.execute(
|
|
"SELECT COUNT(*) FROM task_mission_delegations "
|
|
"WHERE idempotency_key='must-not-enter'"
|
|
).fetchone()[0]
|
|
== 0
|
|
)
|
|
|
|
|
|
def test_root_completed_after_claim_before_effect_prepare_returns_closed(tmp_path):
|
|
db = tmp_path / "root-completed-after-claim.db"
|
|
service, mission, _target, executor, _effects = _effect_fixture(db, lose=None)
|
|
ordinary = service.root_actions
|
|
assert ordinary is not None
|
|
closer = TaskPlaneStore(str(db))
|
|
|
|
class CompleteRootBeforePrepareConsumer(RootActionConsumer):
|
|
def _prepare_effect(self, claim, now):
|
|
for status in ("CLAIMED", "RUNNING", "COMPLETED"):
|
|
root = closer.get_task(claim.plan.root_task_id)
|
|
closer.update_task_status(root.task_id, root.revision, status)
|
|
return super()._prepare_effect(claim, now)
|
|
|
|
consumer = CompleteRootBeforePrepareConsumer(
|
|
service.store,
|
|
plan_builder=ordinary._plan_builder,
|
|
rereviewer=ordinary._rereviewer,
|
|
executor=executor,
|
|
)
|
|
result = consumer.run_once(
|
|
consumer_id="claimed-before-root-close", now=datetime.now(tz=timezone.utc)
|
|
)
|
|
assert result.state == RootActionRunState.CLOSED
|
|
assert result.detail == "ROOT_ALREADY_COMPLETED"
|
|
assert executor.acquire_calls == 0
|
|
assert executor.execute_calls == 0
|
|
dispatch = service.store.conn.execute(
|
|
"SELECT status FROM task_root_action_dispatches WHERE review_id=?",
|
|
(result.review_id,),
|
|
).fetchone()
|
|
assert dispatch["status"] == "SUPERSEDED"
|
|
closer.close()
|
|
|
|
|
|
def test_external_response_after_claim_expiry_cannot_land_observation(tmp_path):
|
|
clock = [datetime.now(tz=timezone.utc)]
|
|
effects: dict[str, dict] = {}
|
|
|
|
class SlowExecutor(DurableFakeExecutor):
|
|
def reconcile(self, plan, *, before_external_call):
|
|
before_external_call()
|
|
self.reconcile_calls += 1
|
|
clock[0] += timedelta(seconds=2)
|
|
return EffectObservation("ABSENT")
|
|
|
|
target = {
|
|
"task_id": "placeholder",
|
|
"job_id": "job-slow-response",
|
|
"idempotency_key": "commit-job-slow-response",
|
|
}
|
|
slow = SlowExecutor(effects)
|
|
db = tmp_path / "expired-external-response.db"
|
|
first = _service(db, target=target, executor=slow)
|
|
mission = _mission(first)
|
|
target_child = _child(first, mission["root_task_id"], "target")
|
|
target["task_id"] = target_child["task_id"]
|
|
trigger = _child(first, mission["root_task_id"], "trigger")
|
|
_finish(first, trigger["task_id"])
|
|
ordinary = first.root_actions
|
|
assert ordinary is not None
|
|
consumer = RootActionConsumer(
|
|
first.store,
|
|
plan_builder=ordinary._plan_builder,
|
|
rereviewer=ordinary._rereviewer,
|
|
executor=slow,
|
|
clock=lambda: clock[0],
|
|
)
|
|
with pytest.raises(ConflictError, match="claim expired"):
|
|
consumer.run_once(consumer_id="slow-consumer", now=clock[0], lease_seconds=1)
|
|
assert slow.execute_calls == 1
|
|
effect = first.store.conn.execute(
|
|
"SELECT state,receipt_json FROM task_root_action_effects"
|
|
).fetchone()
|
|
assert effect["state"] == "EFFECT_UNKNOWN"
|
|
assert effect["receipt_json"] is None
|
|
first.store.close()
|
|
|
|
recovered_executor = DurableFakeExecutor(effects)
|
|
recovered = _service(db, target=target, executor=recovered_executor)
|
|
result = recovered.consume_one_root_action(
|
|
consumer_id="takeover", now=clock[0], lease_seconds=1
|
|
)
|
|
assert result.state == RootActionRunState.CLOSED
|
|
assert recovered_executor.reconcile_calls == 1
|
|
assert recovered_executor.execute_calls == 0
|
|
|
|
|
|
def test_late_absent_cannot_regress_successful_takeover_or_execute(tmp_path):
|
|
clock = [datetime.now(tz=timezone.utc)]
|
|
entered = threading.Event()
|
|
release = threading.Event()
|
|
effects: dict[str, dict] = {}
|
|
|
|
class LateAbsentExecutor(DurableFakeExecutor):
|
|
def reconcile(self, plan, *, before_external_call):
|
|
before_external_call()
|
|
self.reconcile_calls += 1
|
|
entered.set()
|
|
assert release.wait(timeout=5)
|
|
return EffectObservation("ABSENT")
|
|
|
|
target = {
|
|
"task_id": "placeholder",
|
|
"job_id": "job-stale-observation",
|
|
"idempotency_key": "commit-job-stale-observation",
|
|
}
|
|
late = LateAbsentExecutor(effects)
|
|
db = tmp_path / "stale-observation-takeover.db"
|
|
first = _service(db, target=target, executor=late)
|
|
mission = _mission(first)
|
|
target_child = _child(first, mission["root_task_id"], "target")
|
|
target["task_id"] = target_child["task_id"]
|
|
trigger = _child(first, mission["root_task_id"], "trigger")
|
|
_finish(first, trigger["task_id"])
|
|
first_runtime = first.root_actions
|
|
assert first_runtime is not None
|
|
first_consumer = RootActionConsumer(
|
|
first.store,
|
|
plan_builder=first_runtime._plan_builder,
|
|
rereviewer=first_runtime._rereviewer,
|
|
executor=late,
|
|
clock=lambda: clock[0],
|
|
)
|
|
first_result = {}
|
|
|
|
def consume_first():
|
|
try:
|
|
first_result["value"] = first_consumer.run_once(
|
|
consumer_id="expired-a", now=clock[0], lease_seconds=1
|
|
)
|
|
except Exception as exc: # captured for cross-thread assertion
|
|
first_result["error"] = exc
|
|
|
|
first_thread = threading.Thread(target=consume_first)
|
|
first_thread.start()
|
|
assert entered.wait(timeout=5)
|
|
clock[0] += timedelta(seconds=2)
|
|
|
|
takeover_executor = DurableFakeExecutor(effects)
|
|
second = _service(db, target=target, executor=takeover_executor)
|
|
second_runtime = second.root_actions
|
|
assert second_runtime is not None
|
|
second_consumer = RootActionConsumer(
|
|
second.store,
|
|
plan_builder=second_runtime._plan_builder,
|
|
rereviewer=second_runtime._rereviewer,
|
|
executor=takeover_executor,
|
|
clock=lambda: clock[0],
|
|
)
|
|
takeover = second_consumer.run_once(
|
|
consumer_id="takeover-b", now=clock[0], lease_seconds=1
|
|
)
|
|
assert takeover.state == RootActionRunState.CLOSED
|
|
assert takeover_executor.execute_calls == 0
|
|
|
|
release.set()
|
|
first_thread.join(timeout=5)
|
|
assert isinstance(first_result.get("error"), ConflictError)
|
|
assert "stale root action claim" in str(first_result["error"])
|
|
# The only commit preceded the delayed observation; the stale ABSENT did
|
|
# not authorize another commit after takeover.
|
|
assert late.execute_calls == 1
|
|
effect = second.store.conn.execute(
|
|
"SELECT state,receipt_json FROM task_root_action_effects"
|
|
).fetchone()
|
|
assert effect["state"] == "SUCCEEDED"
|
|
assert effect["receipt_json"] is not None
|
|
|
|
|
|
def test_missing_authoritative_execution_plan_is_engineering_block(tmp_path):
|
|
service = _service(tmp_path / "blocked.db")
|
|
mission = _mission(service)
|
|
_child(service, mission["root_task_id"], "unfinished-target")
|
|
trigger = _child(service, mission["root_task_id"], "trigger")
|
|
_finish(service, trigger["task_id"])
|
|
task_count_before = service.store.conn.execute(
|
|
"SELECT COUNT(*) FROM tasks"
|
|
).fetchone()[0]
|
|
result = service.consume_one_root_action(consumer_id="consumer")
|
|
assert result.state == RootActionRunState.BLOCKED
|
|
assert result.detail == "ROOT_ACTION_PLAN_UNAVAILABLE"
|
|
assert (
|
|
service.store.conn.execute("SELECT COUNT(*) FROM tasks").fetchone()[0]
|
|
== task_count_before
|
|
)
|
|
|
|
|
|
def test_delegate_waits_for_close_transaction_then_fails(tmp_path):
|
|
db = tmp_path / "delegate-v-close.db"
|
|
first = _service(db)
|
|
mission = _mission(first)
|
|
child = _child(first, mission["root_task_id"], "terminal")
|
|
_finish(first, child["task_id"])
|
|
second = _service(db)
|
|
entered = threading.Event()
|
|
release = threading.Event()
|
|
ordinary = first.root_actions
|
|
assert ordinary is not None
|
|
|
|
def blocking_rereview(plan, receipt):
|
|
entered.set()
|
|
assert release.wait(timeout=5)
|
|
return first.missions.rereview_root_action(
|
|
plan=plan,
|
|
execution_receipt=receipt,
|
|
lineage=first.lineage,
|
|
)
|
|
|
|
consumer = RootActionConsumer(
|
|
first.store,
|
|
plan_builder=ordinary._plan_builder,
|
|
rereviewer=blocking_rereview,
|
|
executor=None,
|
|
)
|
|
close_result = {}
|
|
delegate_result = {}
|
|
|
|
def close():
|
|
close_result["value"] = consumer.run_once(
|
|
consumer_id="closing-consumer", now=datetime.now(tz=timezone.utc)
|
|
)
|
|
|
|
def delegate():
|
|
try:
|
|
_child(second, mission["root_task_id"], "racing-child")
|
|
except ConflictError as exc: # captured for cross-thread assertion
|
|
delegate_result["error"] = exc
|
|
|
|
closer = threading.Thread(target=close)
|
|
closer.start()
|
|
assert entered.wait(timeout=5)
|
|
delegator = threading.Thread(target=delegate)
|
|
delegator.start()
|
|
release.set()
|
|
closer.join(timeout=5)
|
|
delegator.join(timeout=5)
|
|
assert close_result["value"].state == RootActionRunState.CLOSED
|
|
assert isinstance(delegate_result.get("error"), ConflictError)
|
|
assert "completed root" in str(delegate_result["error"])
|
|
assert first.store.get_task(mission["root_task_id"]).status == "COMPLETED"
|
|
assert (
|
|
first.store.conn.execute(
|
|
"SELECT COUNT(*) FROM task_mission_delegations WHERE idempotency_key='racing-child'"
|
|
).fetchone()[0]
|
|
== 0
|
|
)
|