ruling-2026-08-26-deferral-unit4-fencingA durable addressed mailbox with claim leases. Claiming is a compare-and-set:
UPDATE ... WHERE state = 'NEW' OR the lease has expired, which must affect
exactly one row. Two distinct actors contending for one message produce
exactly one winner, verified by a two-connection barrier test.
A second connection using the same actor id is granted the claim, because a claim already held by that actor short-circuits and returns it. That is actor-level idempotency — it is what makes a retried worker harmless — and it is not process-level exclusivity. Two processes hosting one actor can both proceed.
Reclaiming an expired lease works through the inbox() sweep, which resets an
expired CLAIMED message to NEW. Calling claim() directly on your own
expired lease hits the same-owner short-circuit and returns the stale lease
without renewing it, so the compare-and-set’s expired branch is unreachable by
the owner.
Benign under holding 1 — only the addressed recipient can reach that path, so no other actor can steal a lease — but it is dead code implying an intent the code does not have. Recorded rather than silently fixed, because the fix belongs with the fencing work in Unit 8.
tests/test_concurrency.py::test_only_one_contender_wins_a_contested_lease and
::test_the_same_actor_from_two_processes_is_idempotent_not_exclusive. The
second asserts the current behaviour precisely so that a future change to it is
visible rather than silent.
F-U4-1 is closed. relay.claim()’s same-owner short-circuit now fires
only when the lease is both same-owner and unexpired; a same-owner
expired reclaim falls through into the compare-and-set exactly like a
takeover attempt would, and wins it the same way, minting a fresh fencing
token from a new monotonic counter (lease_tokens, migration 13). The
previously-unreachable expired branch is now reachable by the owner, not
only by a different actor, and is covered directly by
tests/test_fencing.py::test_same_owner_expired_claim_mints_a_fresh_token_fu4_1.
complete() and dead_letter() verify that token atomically against the
durable row before writing, closing the actual consequence F-U4-1 named:
docs/rulings/2026-08-26-one-process-per-actor.md’s holding 1
(“actor-level idempotency, not process-level exclusivity”) is superseded by
holding 3 there, closed the same way. This section is additive – the
holdings and the named limit above describe accurately what was true before
Unit 8 landed, and are left unedited as the historical record of what F-U4-1
was. See docs/v1-unit8-supervisor-fencing-recovery.md for the full
fencing contract and its complete proof matrix.