You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
feat(runner): state-drift Tier 2 funnel via storage tuples
Detect divergence between the runner's in-memory workitem cache and
the database without rescuing exceptions in the GenServer.
Storage layer:
* `transition_workitems/2` and `complete_workitems/4` now return
`{:error, {:state_drift, ctx}}` only when the Multi `:result` step
rejects an `:unexpected_updated_rows` mismatch. Other Multi failures
raise so they surface as crashes counted by the circuit breaker.
* `Storage.write_result()` and `Storage.state_drift_context()` types
document the new contract; the keyword `ctx` carries `expected:` /
`actual:` row counts that flow through to the persisted exception.
Enactment layer:
* `persist_or_drift!/3` funnels `{:error, {:state_drift, _}}` into
`Storage.exception_occurs(:state_drift, _)` plus
`exit({:shutdown, {:fatal, :state_drift}})`. The shutdown form keeps
the supervisor's `max_restarts` counter untouched, so a single
drifted enactment does not snowball into supervisor self-termination.
* `apply_calibration/1`, `start_workitems/2`, and `complete_workitems/3`
pipe their storage writes through `persist_or_drift!`.
New `Exceptions.StateDrift` carries the action and ctx so operators
see the row-count discrepancy in `enactment_logs.exception`.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
0 commit comments