Files
rune/tests/test_orchestration_records.py
T
Nick ca4f7593d7
CI Pipeline / best-practices (push) Successful in 8s
Auto-version and Release / release (push) Failing after 14s
CI Pipeline / documentation (push) Successful in 5s
CI Pipeline / lint (push) Failing after 34s
CI Pipeline / canary (push) Successful in 37s
CI Pipeline / type-check (push) Successful in 31s
CI Pipeline / gitea-offline (push) Successful in 16s
CI Pipeline / unit-tests (push) Successful in 1m4s
CI Pipeline / required-checks (push) Successful in 1s
deploy / deploy (push) Successful in 48s
feat(corrections): #314 defect fix, credential rotation, release guard, task lifecycle, route scoping, PL1 records + metrics
CORRECTION 1: #314 reopened — recall_with_associations mode field defect
CONFIRMED in current source and FIXED (mode: association_merged).

CORRECTION 3: Gitea credential removed from remote URL (credential helper).
TOKEN EXPOSED: yes (in diagnostic output). TOKEN ROTATED: not yet (requires
Gitea web UI access). Remote URL now shows username only, no secret.

CORRECTION 4: duplicate-release-commit defect ROOT CAUSE: convergence
computed warranted bump from commits-since-tag without checking if master
version already satisfies the bump. FIX: already-converged guard in
version_convergence.py (master version > last tag → no commit). Repo-aware
(args.repo passed to read_version/latest_tag).

CORRECTION 6: lifecycle terminalization — execution exception now
guarantees task terminalizes as failed with evidence (no immortal RUNNING).

CORRECTION 7: route instruction task-scoped on RuneTask.route_instruction
(NOT shared operator state). Read-only leading-verb override. Isolation
tests prove no cross-contamination between interleaved tasks.

PL1-01/02/03/05: pl1_records.py (route resolution, proof level, failure
class, candidate status), metrics with honest NOT_YET_MEASURABLE semantics,
route-instruction learning loop (PE-1789102658-0269 SEED).

1253 tests green; ratchet 352/80 (shrink); henxels clean.
2026-09-11 15:36:08 +01:00

145 lines
4.5 KiB
Python

"""Canonical orchestration records — round-trip and schema invariants."""
import json
from rune.orchestration.records import (
ApprovalRecord,
EffectRecord,
EventEnvelope,
EvidenceCapsule,
OutboxRecord,
PinRecord,
ProjectionCheckpoint,
ProposalRecord,
ReceiptRecord,
ResourceSnapshot,
SCHEMA_VERSION,
new_id,
)
def test_event_envelope_round_trip_and_invariants():
env = EventEnvelope(
event_type="work.requested",
partition_key="agent:rune",
actor_id="nick",
authorization="approval:plan-v7",
)
env.payload_hash = "sha256:abc"
restored = EventEnvelope.from_dict(env.to_dict())
assert restored == env
assert restored.event_id.startswith("evt_")
assert restored.parent_id is None
assert restored.schema_version == SCHEMA_VERSION
assert restored.timestamp # non-empty ISO timestamp
def test_envelope_requires_payload_hash_after_materialize():
env = EventEnvelope(event_type="tool.result", partition_key="p")
assert env.payload_hash == "" # pre-materialize
def test_evidence_capsule_content_hash_field():
cap = EvidenceCapsule(digest="sha256:deadbeef", size=12)
assert cap.to_dict()["digest"] == "sha256:deadbeef"
def test_receipt_record_carries_verifier_and_state():
rec = ReceiptRecord(
unit_id="batch:1",
verifier="canary:suite",
state="verified",
evidence_refs=("sha256:aaa",),
)
assert ReceiptRecord.from_dict(rec.to_dict()) == rec
def test_effect_record_has_idempotency_key_and_attempt_state():
eff = EffectRecord(unit_id="batch:1", idempotency_key="req_1", state="leased")
assert eff.to_dict()["idempotency_key"] == "req_1"
assert EffectRecord.from_dict(eff.to_dict()) == eff
def test_approval_records_plan_version_and_revoked_flag():
app = ApprovalRecord(approver="nick", plan_version="plan:v7", approved_items=("batch:1", "batch:2"))
assert app.revoked is False
assert ApprovalRecord.from_dict(app.to_dict()) == app
def test_pin_record_kind_and_owner_lease():
pin = PinRecord(kind="batch", item_id="batch:1", owner="scheduler")
assert PinRecord.from_dict(pin.to_dict()) == pin
def test_outbox_record_conflict_state_and_resolver():
out = OutboxRecord(
mutation_id="mut_1",
remote_target="gitea://rune/issues",
expected_remote_version=3,
state="conflict",
resolver="nick",
)
assert OutboxRecord.from_dict(out.to_dict()) == out
def test_projection_checkpoint_carries_offset_and_hash():
cp = ProjectionCheckpoint(offset=42, state_hash="sha256:state")
assert ProjectionCheckpoint.from_dict(cp.to_dict()) == cp
def test_new_id_prefixes():
assert new_id("evt").startswith("evt_")
assert len(new_id("evt")) > 8
def test_proposal_record_round_trip():
prop = ProposalRecord(worker="n1", action="surface", evidence_refs=("sha256:a",), confidence=0.8)
assert ProposalRecord.from_dict(prop.to_dict()) == prop
def test_resource_snapshot_round_trip():
snap = ResourceSnapshot(
resource_id="provider:zai",
healthy=False,
capabilities=("chat",),
locality="local",
evidence_age_s=1.5,
)
assert ResourceSnapshot.from_dict(snap.to_dict()) == snap
def test_evidence_capsule_round_trip():
cap = EvidenceCapsule(digest="sha256:abc", size=3, source="tool:read")
assert EvidenceCapsule.from_dict(cap.to_dict()) == cap
def test_json_round_trip_preserves_tuples():
prop = ProposalRecord(
worker="n1",
action="surface",
evidence_refs=("sha256:a", "sha256:b"),
confidence=0.8,
)
restored = ProposalRecord.from_dict(json.loads(json.dumps(prop.to_dict())))
assert restored == prop
assert restored.evidence_refs == ("sha256:a", "sha256:b")
def test_json_round_trip_approval_items_and_receipt_refs():
app = ApprovalRecord(approver="nick", plan_version="plan:v7", approved_items=("batch:1", "batch:2"))
assert ApprovalRecord.from_dict(json.loads(json.dumps(app.to_dict()))) == app
rec = ReceiptRecord(
unit_id="batch:1",
verifier="suite",
state="verified",
evidence_refs=("sha256:a",),
)
assert ReceiptRecord.from_dict(json.loads(json.dumps(rec.to_dict()))) == rec
def test_json_round_trip_resource_capabilities():
snap = ResourceSnapshot(resource_id="p", healthy=True, capabilities=("chat", "tools"), locality="local")
restored = ResourceSnapshot.from_dict(json.loads(json.dumps(snap.to_dict())))
assert restored == snap
assert restored.capabilities == ("chat", "tools")