v0.19.0
A slider reads one transform property of a bone and turns its value into a time. This release measures what those readers can actually produce, and gates the messages rigc prints when an author asks for a value they cannot.
test: and docs: commits, so the generated sections do not list them. They are the larger half of the release.
The reader table
Every one of the twelve readers a slider can use — six FromProperty classes over both branches of "local" — measured one at a time through spine-core, with the study and its evidence under bench/studies/2026-09-06-readers. No cell is a guess; ten controls re-derive the table from the runtime and fail if the guide and the measurement part.
Three cells carry more than a bound:
scaleX/scaleYunder"local": falselose the sign rather than clip it. A bone atscaleX: -1reads+1, so a range dipping below zero selects the mirror of the dial — a specific wrong frame, not a dead one.shearY's global bound is 720° wide and catches almost nothing. What matters is that the readable window is exactly 360° wide and slides with the bone's own orientation: it wraps like a rotation, with the seam wherever the bone points.rotateunder"local": falseis[0, 360], not[0, 360). The wrap rounds, from a threshold bisected off the runtime's own arithmetic. Nothing gates on it and the direction is friendly — a full-turn dial provably reaches its endpoint.
And one finding that changes advice rather than code: "local": true is a passthrough only on a free bone. On a bone another constraint drives, the local reader recomputes from the world matrix and has the same Math.sqrt floor. Any repair that says "just add local: true" has to name that condition.
A build sees a disputed dial
rigc names a slider's driving property twice — the artifact reads the skeleton data, a probe drives the bone and asks what moved — and cross-checks them. That verdict was printed by explain and by nothing else, so a build-only run never learned the two halves disagreed. It is now on A39's stats line, with what each answer reaches and which frames lie outside the artifact's own reach, posed through spine-core rather than predicted.
It reports rather than refuses, and the measurement decided that: the survey trusts neither half. It drives and then asks the runtime where the frame landed, so every posed key is confirmed and a key no field can select is already reported. Refusing would have refused rigs the runtime poses correctly at every key.
Refusal messages are quoted, and now they are checked
A README that quotes what rigc prints when it refuses a rig was outside the transcript gate entirely — the quote carried nothing to anchor on, and the command that produces it is not one a correct example runs. Changing a refusal's wording and leaving the quote stale passed green.
A block now states the edit that reproduces it, in a sentence the reader can see and act on:
Reproduce it: replace
"local": truewith"local": falseingallery/look/rig.json, then runbuild.
The bound is the grammar rather than a rule applied afterwards: the only file expressible is a discovered example's own rig or motion, the only operation is a literal replacement, the only verb is build. There is no path in it. Its sibling marker reads No run reproduces this: followed by the reason, so a block that is abridged now says so on the page instead of in an HTML comment.
First run found four stale or abridged quotes, one of them naming vertex indices its mesh no longer has.
Refusal arithmetic
Two figures in the slider wrap refusal were wrong in ways that only showed outside the range its fixtures happened to cover:
- The reading is a modulo, not one subtraction. A range further out than one turn named a value
FromRotate.valuecannot return — inside a refusal whose whole subject is which values it can. - The dead width of a range is a width. A range lying wholly outside the circle had the gap to the boundary counted too:
400°..500°was told 140° of it is dead, wider than the range.
Both were the same shape as the reader survey's own defect — a computation right about the case its fixture happened to be, and silent about the case beside it.
Verified as always by a round trip through @esotericsoftware/spine-core: 551 named assertions, 0 failures, 0 skips.