v0.7.1
A patch on the hostile-input path of the seen: token.
The token is rendered into a form and comes back from the browser, so a
tampered seen=abc or an array from seen[]=1 is hostile input — not a
programmer error. Until now Integer() let its ArgumentError and
TypeError escape the whole Statecraft hierarchy: a forged field slipped
past every rescue_from and became a 500.
Both are now refused as Statecraft::StaleTransition with
expected_version: nil — nothing was compared — so the controller that
already maps staleness to 409 handles them for free:
rescue Statecraft::StaleTransition
head :conflict
The error message stays honest about which refusal happened: an unreadable
token reads "the token "abc" is not a readable version" instead of
claiming the row moved on.
Fixed alongside: token normalization ran before the pipeline's telemetry
block, so this refusal would have been invisible to
transition_failed.statecraft subscribers. It now publishes with reason
:stale like every other staleness.
The example app also drops two literal reads of the state column in favour
of the mounting's own column, renders the token through its reader as the
README recipe shows, and the statesman runbook now points at the versioning
section for the one metadata-only migration that buys the protection after
a conversion.