v1.13.0 — actions that happen once, and ledgers that tell the truth about what they dropped
[1.13.0] - 2026-08-20
Added
-
concurrency: { mode: 'once' }— the receipt survives settlement
(Context Integrity, the choice seam).single-flightsuppresses a repeat
while the first occurrence is unresolved; the measured duplicate-execution
failure happened after it settled.'once'is a third mode at the same
declaration door: one EXECUTED occurrence per scope for the session, refused
DUPLICATE_EXECUTIONwith the receipt (priorTransitionId) and the authored
howToRepeatsentence. Two field lessons are the law's shape: only a person
acting on the screen (a user-attributed transition after the receipt) reopens
the action, and the reopened repeat FIRES carryingrepeatedon the result
AND the transition record — report, don't refuse. A refused first occurrence
minted no receipt; an'unobservable'one counts (unprovable non-execution
is not non-execution). While the first is unsettled,'once'answers exactly
as single-flight (PRIOR_FIRE_PENDING). The served row carries the verdict
(alreadyPerformed, action scope only) and Mode B greys the control out —
never send a person to approve a fire about to be turned away. Scopes,
payload identity and the matcher are single-flight's own (sameFire, one
owner of "what counts as the same fire again"). -
TransitionRecord.instance— the repeats-container card a fire named now
rides its receipt. A record that says what was pressed but not which row
cannot answer "did we already cancel order #57?". -
Bounded ledgers state their retention window, and every silent cut now
speaks (Context Integrity, phase 0).session.offersRetention()/
session.acknowledgementsRetention()answer{ minted, dropped, firstRetained, lastRetained }— the window that is still answerable, not
only the loss — andsession.offerStanding(id)asks the three-way
retained/evicted/unknown question without firing anything. Oversized
sensing trails join the house standard: evictions are counted
(sensedTrailsDropped()), andsensedTrail()on an evicted trail says
EVICTED with the surviving count instead of an answer indistinguishable
from "never sensed". The data channel's silent caps now announce
themselves the way its string cap always has: an array cut at 30 appends
… N more omitted, an object cut at 40 keys carries"…": "N more key(s) omitted". And a repeats row's render cap states its size:
instancesTotalrides the served row and both instance refusals, so 50
keys of 200 can never read as 50 of 50 (fireability was always uncapped;
now the served row says so). -
The unknown-instance refusal says which kind of wrong it is (Context
Integrity).INSTANCE_UNKNOWNnow carries
verdict: 'never-existed' | 'unsupported', decided by the coverage of the
set the id was compared against — the same source the served row's
enumerationstates. Absent from the app's declared existence source (a
selectorset enumerates everything that exists) →'never-existed', and
the refusal may finally say so. Absent from the mounted window →
'unsupported': not backed by anything served, and that is ALL the refusal
knows — a window has an edge, the world continues past it, and under a
capped window an id past the render cap is exactly the case
'unsupported'protects. A selector that throws or answers a non-array
falls back to the mounted window, and the verdict falls back WITH it: a
nonexistence claim never outlives its evidence.