Releases: firejune/rigc
Release list
v0.20.0
release-please builds them from
commit type, so test: and docs: commits never appear in them. In this release
that is nine of fourteen commits, and it is where most of the work is. What follows
is the whole cut.
What this release is about
Two things that turn out to be one: the tool learned to say what it is not measuring,
and the repository stopped making claims about itself that nothing derives.
The depth report says what the sheet is not (#448, #449). sampleMeshDepth refused
a sheet that did not cover the mesh — but the check sat behind "does this sheet have a
transparent texel anywhere", so a full-frame opaque render skipped it entirely, which
is exactly what monocular depth estimation produces. The same defect was a named refusal
in one encoding and a green build in the other; the counts from both sides agree at 56
of 81 on the fixture that proves it. It now reports rather than refuses — a
full-frame sheet is a legitimate statement and rigc has no authority to guess an input
away — and what it adds is a measurement: how many of a mesh's vertices sample a texel the
part image does not draw.
stepShare separates a form from a discontinuity. A form's share halves under lattice
refinement while its ceiling converges; a cliff pins its share and halves its ceiling
instead, because there is no slope to converge to. Measured on synthetic sheets: form
0.358 → 0.100 with the angle steady, cliff 0.460 → 0.497 with the angle falling by half
each time. No threshold anywhere — the number is reported so an author can see which kind
of thing the ceiling read.
A line came back from the dead (#440). Giving the disputed dial the breadcrumb its
neighbours have exposed that the rollup identity was a literal at three sites, one of them
with a different separator — so N span(s) between consecutive keys scanned had printed
on no rig at all since #418. That line exists so "the scan ran and found nothing"
cannot be read as "the scan never ran", and it had silently become the second.
The run counts itself (#439, #451, #453). The selftest's summary used to restate every
suite's size by hand. Wrapping the calls put a number on the drift for the first time:
18 of 47 floor increments were wrong, the summed floor stood at 435 against a run of
551, and one suite had never been counted at all — a floor 116 cases below the truth
being the only thing between a vacuous run and a green one. Every figure is read off the
lines each suite prints now, and no hand-written number is left anywhere the summary
reads from, which made buildable a check twice rejected as unbuildable: a numeric
constant whose only reader is the summary is refused by name. ⇒ Adding a control needs
no summary edit, measured twice on the day it landed by two independent changes.
Two silences in the toolchain, closed (#457, #465). A .gitignore entry ending in a
slash covers one of the four things a path can be — a symlink, a file and an absent
path all slip past — and three paths here had already turned out to be symlinks, showing
as untracked in a public tree. All 31 entries are slashless, and CUR10 plants each path
in a throwaway repository as a directory, as a symlink and as a file and asks git each
time. Separately, two raw NUL bytes made cli.ts binary to the grep an agent has, so a
search over the file that owns the report text matched nothing, silently, with the same
exit code as an absence — it had already produced a wrong conclusion.
Doctrine (#445, #446, #447). rigc compiles an object; the scene belongs to the
consumer. The code had never crossed that line and the prose had — including a caption
claiming "scene direction" over a film whose own text states the boundary correctly and
measures it at 0 differing pixels across every hand-off. The honest claim is the
stronger one: the rig guarantees the seams, and the composing is the consumer's.
ROADMAP.md is new.
The selftest carries 577 named controls at this tag.
In this release but not in the sections below
| #450 | test: the run counts itself, and the floor is one per suite |
| #452 | test: the last figures come off the run, and a typed one is refused |
| #462 | test: a suite stated in halves, and no figure left in a constant |
| #446 | docs: rigc compiles an object, and the scene belongs to the consumer |
| #447 | docs: a rough roadmap to 1.0, and the road so far |
| #454 | docs: one fact spelled once, and the marker the reader can see |
| #459 | docs: the rig guarantees the seams, and the composing is the consumer's |
| #460 | docs: the doctrine that governs the product governs the work too |
| #461 | docs: what settles a thing, rather than who owns it |
| #469 | docs: two claims the tree contradicts, corrected against a run |
Features
- depth: what the sheet is not, and whether the ceiling read a form (#455) (dfcd808)
- explain: the disputed dial names its counter, and the scan line prints again (#467) (a42e9cf)
Bug Fixes
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.
v0.18.1
v0.18.0
0.18.0 (2026-09-05)
Features
- deform: a transform key over a multi-bone attachment, pushed into each bind space (#413) (7eb0a32)
- depth: the turn ceiling says whether it is describing the form or the sheet's grain (#416) (f1d6a67)
- gallery: look — a face whose angle is a value, not a time (#408) (c2a79d6)
- motion: the animator's words, mapped to the constructs that carry them (#397) (2ae784f)
- tools: the editor round trip, promoted out of a scratch shell script (#396) (19266b5)
- validate: A40 — two sliders on one target, and the yaw axis that dies at 0° (#405) (a546b20)
Bug Fixes
- tools: the round trip refuses the Spine trial by name, and says what it can still do (#414) (7467c1e)
- two claims the repository makes about itself, neither of them derived (#394) (e43aca9)
- validate: a triangle that draws no pixels cannot draw them backwards (#404) (554bfbe)
- validate: A39 poses a slider-applied animation at the slider's own mapping (#418) (ae6be2a)
- validate: A39 scans the span between keys, in closed form (#409) (7ebebc9)
v0.17.0
0.17.0 (2026-09-05)
A part can state its own depth, and rigc will tell you how far it turns.
Until now a 2.5D turn read one radius shared by a whole column of vertices — a
cylinder standing in for a surface. A generator can now name a depth map, and
yaw/pitch read a z per vertex off it. The lattice that carries those
vertices is generated rather than hand-numbered. A painted mask says
which region is soft, so a physics constraint answers an impact over exactly
that area. And build and explain now print the turn ceiling: the largest
angle this geometry on this sheet takes before a triangle reverses, per axis and
per direction, naming the triangle that goes first.
That last one changes the loop. The angle was previously found by writing a key,
building, reading A39's refusal and guessing again; it is closed form —
tan t = A₀/A_axis — and the compiler now says it up front.
Features
- deform: a depth map gives every vertex its own z, and yaw/pitch read it (#383) (e0371e8), closes #382
- mesh: a
gridgenerator, so the lattice stops being hand-numbered (#386) (b0ec75a), closes #382 - depth: a painted mask marks a soft region, and a physics constraint on its bone answers an impact over it (#390, #391) (843aad4)
- depth: report the turn a sheet supports, before a key is written (#393) (cc79286)
- docs:
FACE.md§2.2 andAUTHORING.md§3.4 — the fold angle belongs to the depth map's steepest slope, not to the mesh density (#392) (e409077)
The one rule to read before using any of it
The angle a part can turn through is a property of its depth map, not of how
finely it is meshed — approximately 1 / max|dz/du|, the reciprocal of the
sheet's steepest slope. Refining a lattice does not lower the ceiling; it finds
slopes that were always there. A map that reaches its floor with a vertical
edge — a dome, a hemisphere, anything traced straight off a rendered normal —
folds at any angle you like once it is meshed finely enough, while a map whose
slope is bounded holds the same angle at every density. Measured over a 1,300×
range of vertex counts: 62° → 14° for the first, a steady 63–64° for the second.
So a ceiling you cannot live with is fixed by editing the sheet, not the
mesh. rigc will not flatten a map for you; a depth map is a measurement, and the
compiler never invents a value that is not in the spec.
What this release does not do
- None of it has met real art. Every figure quoted for the depth work was
measured on generated fixtures — analytic ramps, domes and checkerboard blobs. - The angle and the jiggle cannot ride one attachment yet. A mesh carried by
a soft-region bone has two influences on some vertices, and atransformkey
needs one bind space, so per part it is one or the other
(#389). - A dense mesh is a large file. 449 bytes per vertex, plus ~1.77 MB per
deform key at 32,761 vertices. The gate itself stays linear and green to
64,800 triangles; the artifact is what grows.
Bug Fixes
- ignore scratch as a path, not only as a directory (#385) (87bb21f) — repository housekeeping, no effect on the published package
Withdrawn inside this release
Two commits in the log add things that do not exist in 0.17.0. They were
built and taken back out before release, and are listed here only so a reader of
the commit history is not left looking for them:
parallaxas a deform kind (#388, removed in #391) — it was ayawwith a term dropped, and baking a pointer-driven value into time keys is a category error. Parallax is camera work and belongs to whatever draws the result.- a soft region chosen by a depth threshold (#390, replaced in #391) — softness is not prominence. The most prominent thing on a face is the nose, and a nose does not wobble. The region is painted now.
v0.16.0
0.16.0 (2026-09-04)
Features
- compile: a curve on a hold segment is emitted stepped, as the editor writes it (#376) (04d6f27)
- compile: derive a mesh's hull and edges from its triangles and its size from the PNG (#375) (c62a432)
- emit: write skeleton.images so the editor finds the parts on import (#378) (dc16bf1)
- skills: package the authoring guides as an installable agent plugin (#371) (9030d5d)
v0.15.0
v0.14.1
v0.14.0
0.14.0 (2026-09-03)
Features
- motion: a deform key can state its transform instead of its table (#317) (3c13d7e), closes #294
- motion: a group track can key a value per member, stated or derived (#320) (1313b20), closes #295
Bug Fixes
- compile: the motion spec is parsed rather than cast (#321) (3e7238a), closes #307
- gallery: flex's leaf no longer folds, and its exemption is gone (#318) (fed3ba5), closes #313
- pose: the objective interpolates premultiplied, with a stated re-baseline (#322) (6c00c51), closes #306