feat(cassettes): record and read operator operations
Append-only, and deliberately nothing here writes cassette_configs. That table now holds only what the machine has reported; letting an operation write it would put back the second writer this whole design exists to remove. get_cassette_ops_window returns oldest-first because order is meaning: a recount followed by a refill is not the same as the reverse. It takes the most recent N and reverses, so the window slides without the machine ever seeing them out of sequence. The window is what makes the channel self-healing, so it has to cover a plausible outage rather than just the newest change — a machine that missed one event still sees the operation in the next. _should_ack_op is extracted pure, the same way the state-event gate is, because three of its rules are easy to get wrong and none need a database to test: an unknown id closes out nothing, one machine must never be able to ack another machine's operation, and the FIRST acknowledgement is the one worth keeping. That last one matters because the machine echoes a window, so every id comes back many times over; overwriting would keep sliding the timestamp forward and lose when the operation actually landed. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
This commit is contained in:
parent
d8190375a6
commit
b2157db219
2 changed files with 160 additions and 0 deletions
|
|
@ -15,6 +15,7 @@ from datetime import datetime, timezone
|
|||
import pytest
|
||||
from pydantic import ValidationError
|
||||
|
||||
from ..crud import _should_ack_op
|
||||
from ..models import (
|
||||
CASSETTE_OP_TYPES,
|
||||
CassetteOp,
|
||||
|
|
@ -140,3 +141,28 @@ class TestWireShape:
|
|||
"schema_version": 2,
|
||||
"ops": [],
|
||||
}
|
||||
|
||||
|
||||
class TestShouldAckOp:
|
||||
"""The pure decision behind mark_cassette_ops_acked.
|
||||
|
||||
The machine echoes a WINDOW of applied ids on every state publish, so the
|
||||
same id arrives repeatedly and from a machine that may not own it.
|
||||
"""
|
||||
|
||||
def test_acks_an_unacked_op_for_the_reporting_machine(self):
|
||||
assert _should_ack_op(op(op_type="empty"), "m1") is True
|
||||
|
||||
def test_ignores_an_unknown_id(self):
|
||||
assert _should_ack_op(None, "m1") is False
|
||||
|
||||
def test_ignores_an_op_belonging_to_another_machine(self):
|
||||
"""Ids are unique, but a report from one machine must never close out
|
||||
another machine's operation."""
|
||||
assert _should_ack_op(op(op_type="empty", machine_id="m2"), "m1") is False
|
||||
|
||||
def test_keeps_the_first_acknowledgement(self):
|
||||
"""Every subsequent window carries the id again. Re-acking would slide
|
||||
the timestamp forward and lose when the operation actually landed."""
|
||||
already = op(op_type="empty", acked_at=AT)
|
||||
assert _should_ack_op(already, "m1") is False
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue