@rig-c/core 2.39.0 → 2.40.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/docs/AUTHORING.md CHANGED
@@ -57,15 +57,15 @@ provenance for a reader of record, and following one can arrive at a stored cand
57
57
  or at the **derivation** of the gate a verdict is read against, none of which a run may
58
58
  open. ⭐ The gate's **clause statements** are a different matter and a run may read them:
59
59
  they are in
60
- [GATE.md](https://github.com/firejune/rigc/blob/main/docs/GATE.md), which is
60
+ [GATE.md](https://github.com/rig-c/core/blob/main/docs/GATE.md), which is
61
61
  repository material and not in the published package — item 11 of the allowed list — the
62
62
  measure, the comparator, the number and the SKIP semantics, with no recorded figure in it.
63
63
  So: read the
64
64
  document, take its numbered sections as the input, and leave its footprints to whoever
65
65
  is maintaining it. The rule this states is that an **allowed-reading surface has to be
66
66
  closed under reading**; the criterion behind it is under *The honesty rule* in
67
- [LADDER.md](https://github.com/firejune/rigc/blob/main/docs/LADDER.md), and the enumerated allowed and forbidden lists are in
68
- [bench/runs/README.md](https://github.com/firejune/rigc/blob/main/bench/runs/README.md),
67
+ [LADDER.md](https://github.com/rig-c/core/blob/main/docs/LADDER.md), and the enumerated allowed and forbidden lists are in
68
+ [bench/runs/README.md](https://github.com/rig-c/core/blob/main/bench/runs/README.md),
69
69
  which is also repository material and not in the published package,
70
70
  *What a run may read* — the prompt that starts a run quotes them
71
71
  outright, which is the copy that binds.
@@ -233,7 +233,7 @@ What the flags mean:
233
233
  | `--manifest` | a cut manifest. Only for a rig with **measured art** behind it; a foreign skeleton has none |
234
234
  | `--cut` | `build`, `explain` and `validate`: look up a named cut in `--cuts <cuts.json>`, **instead of** `--rig`/`--motion`/`--out` — the two spellings are one build stated two ways and are refused together. A `cuts.json` is `{ "<name>": { "rig": …, "motion": …, "out": …, "manifest"?: … } }`, every path in it relative to the table's own file, so the table lives with the project that owns the art |
235
235
  | `--cuts` | the `cuts.json` `--cut` names. Required beside it — `--cut` alone is refused, with no guess at where the table lives |
236
- | `--profile` | `spine` = the 34 validity rules (**the default**) · `spine-html` = all 50, opt-in |
236
+ | `--profile` | `spine` = the 35 validity rules (**the default**) · `spine-html` = all 51, opt-in |
237
237
  | `--report` | `build` and `repack`: also write the gate's report as a JSON document (`build-report/1`) to this file — every gate's `PASS`, `SKIP` and `FAIL` rows, its summary figures and stats line, which supplier judged it, and every `pack:` line's figures. Written when the command writes `--out` and when a gate is red, before the exit; a file already at the path is removed first, so a compile error or a refused repack leaves none. Never inside `--out`, refused by name. The printed lines are byte-identical with it and without it — **§5.3** |
238
238
  | `--candidate` | `check`, `bench`, `render`, `preview`, `chainfit` and `vote` only: a **compiled** artifact — the directory `build --out` wrote, or a `skeleton.json` path. A directory holding no `skeleton.json`, or a path with nothing at it, is refused `nothing at <path>`, exit 2, nothing written, by every command that takes a build — `validate <dir>` and a directory given to `diff` included (`RC27`). `--atlas <path>` names the atlas when it does not sit beside the skeleton. On `render` and `check` a rigc build whose `skeleton.model.json` is `rigc-compiled/3` or `/2` needs no atlas at all: with no `--atlas` and no atlas beside it, it is drawn from the document's `pages`, the same frames, geometry and figures as with it, and the `atlas` line says `— not there`. An atlas that is there is still read, and held to `pages`: one that differs (edited after the build) is drawn through spine-core, and the `poser` line names the first difference. A build drawn through an atlas that is not there — a `rigc-compiled/1` document, or a build the core refuses — is refused `nothing at <the atlas>: … is drawn through its atlas (<why>)`, exit 2, nothing written; a Spine export without its atlas is refused `nothing at <the atlas>` as it always was. A directory whose files are not one build — a `skeleton.json` or a `skeleton.atlas` from another build beside this one's `skeleton.model.json` — is drawn through spine-core where the runtime can load the pair, the `poser` line naming the two digests or the atlas's first difference; where it cannot, it is refused `<skeleton> does not load against <the atlas>: spine-core draws this pair (<the poser line's why>) and could not resolve it — "<the runtime's own words>". <dir> is not one build: …`, exit 2, nothing written (`RC18`) — another build's skeleton beside this build's atlas typically ends `"Region not found in atlas: <region> (attachment: <name>)"`. Rebuild, or put the build's own three files back beside each other. A Spine export beside an atlas it was not exported with is refused in the same words, ending `The skeleton and the atlas are not one pair`. An atlas copied from another build's directory usually stops earlier, at its pages — their paths are relative to the directory it was written in — and is refused naming the first one: `nothing at <the page>: <the atlas> names it as a page, and spine-core draws this pair through that atlas (<why>). … <dir> is not one build: …`, exit 2, nothing written (`RC18`); an export whose atlas names a page that is not there is refused the same way, ending `and the page has to be there`. A build moved or copied whole away from the directory it was built in stops at the same place, on either poser, and is refused naming the page and the file that names it: `nothing at <the page>: <the skeleton.model.json> names it as a page, and the core draws this build from it. A page path is relative to the build's directory, so a build moved or copied away from the directory it was built in names pages that are not here — build it again where it is, or build it with --copy-images, which writes its pages beside it` (under `--poser spine`, `<the atlas> names it as a page, and spine-core draws this pair through that atlas (--poser spine)` with the same ending), exit 2, nothing written (`RC21`); only where the core refused the files does a page refusal say the directory is not one build. `bench --bones` and `rigc bonedist` — on its `--candidate` and on its `--reference` — refuse a pair the runtime cannot load in the words above with `spine-core poses this pair (bonedist's candidate: …)` or `(bonedist's reference: …)`, exit 2, nothing written (`RC20`); they read no page image, so a build whose pages are elsewhere is measured as it is in place. A `skeleton.json` that is not JSON is refused `rigc: cannot read <the file>: <the parser's words> (line N, column N)`, exit 2, by `render`, `check`, `bench` and `bonedist` as by every other command that reads one (`RC22`). With the atlas gone, `check` on a `rigc-compiled/3` build reads its pages' `scale:` lines off the document, so its texture note and `check.json`'s `textureFrom.candidateScales` are what they are with the atlas; on a `/2` build its texture note says whether the atlas declares a `scale:` line is not read (that document does not state one), and `candidateScales` is `null` (`RC13`). **`vote` and `preview` take it more than once**: `vote` 2–4 times, one per pane, labelled A, B, C, D in the order given; `preview` any number of times, one pane per candidate in the order given, each headed by its path and its gate line, and the same skeleton twice (a directory and its `skeleton.json` are one) is refused by name. `--atlas` goes with one candidate only. Everywhere else a repeat is a typo and is refused |
239
239
  | `--animation` | `render`, `preview` and `vote` only: which animation to show. The default is **every** one for `render`, the **first** for `preview` (each candidate's own first, with several), and for `vote` the first of candidate A. A name the skeleton does not have is refused, with the ones it does have listed — and for `vote` and a several-candidate `preview`, so is a name that only *some* candidates have, naming the one that lacks it |
@@ -1076,7 +1076,7 @@ is a `BLOCK GENERATION_UNKNOWN`, never rounded to the nearest: a catalog that ro
1076
1076
  handed 19 skeletons labelled `3.8.99` a 4.2 runtime and every one of them posed as NaN
1077
1077
  (row 7). Reading a file with *that generation's own* defaults is not in this tool —
1078
1078
  re-export as 4.3, or transcribe by hand ([INGEST.md](INGEST.md) §2).
1079
- [GENERATIONS.md](https://github.com/firejune/rigc/blob/main/docs/GENERATIONS.md) is
1079
+ [GENERATIONS.md](https://github.com/rig-c/core/blob/main/docs/GENERATIONS.md) is
1080
1080
  the whole policy, and the editor migration it names.
1081
1081
 
1082
1082
  📝 **Do not delete the `note`.** Both written specs carry one saying the file is
@@ -1160,7 +1160,7 @@ bun cli.ts pose --images path/to/parts --frame poseA.png [--out pose.json]
1160
1160
  0.000 — is what says the shot is right and the name is yours. The measures, the
1161
1161
  refusals and what makes the block absent instead of guessed:
1162
1162
  [INGEST.md](INGEST.md) §1.3.1, and *The measure inventory* in
1163
- [BENCHMARK.md](https://github.com/firejune/rigc/blob/main/docs/BENCHMARK.md),
1163
+ [BENCHMARK.md](https://github.com/rig-c/core/blob/main/docs/BENCHMARK.md),
1164
1164
  which is repository material rather than part of this package.
1165
1165
 
1166
1166
  🔍 **The wiring is compared by name too, not only the rosters** (issue #1085).
@@ -1185,7 +1185,7 @@ bun cli.ts pose --images path/to/parts --frame poseA.png [--out pose.json]
1185
1185
  🚨 What it certifies is the **default skin** unless you pass `--skin <name>`:
1186
1186
  on a rig with named skins, a run with no skin draws none of their art on either
1187
1187
  side and reports a perfect `0.0000` about it (**§9**).
1188
- - **`bench <rung>`** runs one rung of [the benchmark ladder](https://github.com/firejune/rigc/blob/main/docs/LADDER.md): validate
1188
+ - **`bench <rung>`** runs one rung of [the benchmark ladder](https://github.com/rig-c/core/blob/main/docs/LADDER.md): validate
1189
1189
  under `--profile spine`, then diff against that rung's reference export, and with
1190
1190
  `--frames` the `check` table as well. Unlike the three above it is a **finish
1191
1191
  line, not a loop**: it opens the reference export, so a run that consults it and
@@ -1840,7 +1840,7 @@ column restated beside each row here, is what to act on.
1840
1840
  payloads in this guide are written to illustrate a field, never copied out of a
1841
1841
  reference export — an example lifted from one would be handing an authoring agent an
1842
1842
  answer to the rung it is standing on, which is the rule §10.6 states and the honesty
1843
- rule in [LADDER.md](https://github.com/firejune/rigc/blob/main/docs/LADDER.md) turns on. If a snippet here matches a reference file,
1843
+ rule in [LADDER.md](https://github.com/rig-c/core/blob/main/docs/LADDER.md) turns on. If a snippet here matches a reference file,
1844
1844
  that is a defect in this guide: report it.
1845
1845
 
1846
1846
  ### 3.1 `skeleton` — the header
@@ -2006,7 +2006,7 @@ is the half that is read exactly as stated: omit a `width` and the header carrie
2006
2006
  box, which `bounds_present` is the measure of. Both are reported and gate nothing, for
2007
2007
  the reason every reported measure is: no reading of the rendered frames could have
2008
2008
  decided a setup-pose bounding box. The measure inventory that says so lives in
2009
- [BENCHMARK.md](https://github.com/firejune/rigc/blob/main/docs/BENCHMARK.md), which
2009
+ [BENCHMARK.md](https://github.com/rig-c/core/blob/main/docs/BENCHMARK.md), which
2010
2010
  is repository material and not in the published package.
2011
2011
 
2012
2012
  ### 3.2 `bones` — Spine's bone list
@@ -2318,7 +2318,7 @@ there is nothing to measure it against.
2318
2318
  vertices, and an octagon's sides pass `R · cos(π/8)` from its centre — so rim
2319
2319
  vertices placed *on* a round silhouette leave the ink outline between the spokes
2320
2320
  undrawn, and every assertion passes.
2321
- [`gallery/squash`](https://github.com/firejune/rigc/tree/main/gallery/squash)'s
2321
+ [`gallery/squash`](https://github.com/rig-c/core/tree/main/gallery/squash)'s
2322
2322
  README carries the inradius arithmetic, both coverage readings, and the rim move
2323
2323
  that fixes it.
2324
2324
 
@@ -3557,7 +3557,7 @@ unresolved `spacingMode` fails the `=== Length` test and spaces bones as though
3557
3557
  `Fixed` had been asked for; an unresolved `rotateMode` is neither `Tangent` nor
3558
3558
  `ChainScale`, so bones follow the curve and never turn along it.
3559
3559
 
3560
- 🖼️ **Worked example: [`gallery/ride`](https://github.com/firejune/rigc/tree/main/gallery/ride)** — a trolley on a drawn rail, moved
3560
+ 🖼️ **Worked example: [`gallery/ride`](https://github.com/rig-c/core/tree/main/gallery/ride)** — a trolley on a drawn rail, moved
3561
3561
  by a `position` timeline, with `groups` + `stagger` keying the wheels.
3562
3562
 
3563
3563
  #### 3.5.2 `slider` — a value that drives an animation
@@ -3988,7 +3988,7 @@ shows at the top of it, where a 60° turn then applies at 1.00000008 s rather th
3988
3988
  one — pick `to`/`scale` so the endpoint lands **inside** the duration rather than
3989
3989
  exactly on it.
3990
3990
 
3991
- 🖼️ **Worked example: [`gallery/look`](https://github.com/firejune/rigc/tree/main/gallery/look)** — a face
3991
+ 🖼️ **Worked example: [`gallery/look`](https://github.com/rig-c/core/tree/main/gallery/look)** — a face
3992
3992
  whose yaw and pitch are two sliders sharing one bone, both `local: true` and both
3993
3993
  `additive: true`, with each range derived from the turn ceiling `build` reports
3994
3994
  for that mesh (§3.4) rather than chosen. [`docs/FACE.md`](FACE.md) §8's *The turn
@@ -4031,7 +4031,7 @@ is not an array. Every field is optional and each is the payload a firing
4031
4031
  Optional with one exception, and only meaningful for rigc's own formations:
4032
4032
  `meshSlots` and `meshTriangles` (the two halves of the mesh budget `A13` measures
4033
4033
  against), `axisBone`, `massBone`, `detached`, `deformMayFold`,
4034
- `consumerDrivenMix`, `idleDrivesMeshes`.
4034
+ `consumerDrivenMix`, `idleDrivesMeshes`, `axes`.
4035
4035
  Nothing in skeleton
4036
4036
  JSON records that a
4037
4037
  bone carries a cut's axis or that a parentage is forbidden, so the rig spec says it
@@ -4154,6 +4154,61 @@ onto it: on the first such rig that was 42 extra bones for an identical pose.
4154
4154
  - ⚠️ A15 is a renderer rule, so under `--profile spine` it reports `PROF` and the
4155
4155
  declaration is not read at all, stale or not.
4156
4156
 
4157
+ 📐 **`axes` holds bones to a line the rig declares.** A `scaleY` key squashes along
4158
+ the bone's own y axis, a `translateY` key moves along it — and so does whatever a
4159
+ consumer writes to the bone at run time. On a face painted upright with every bone
4160
+ at rotation 0, that is the face's axis. On a face painted **tilted** it is the
4161
+ page's: a blink squashes the eye along the page's vertical instead of across the
4162
+ eye line, a brow rises toward the page's top instead of the forehead, a mouth
4163
+ opens toward the page's bottom instead of the chin, an iris slides off the lid.
4164
+ The file loads, every other rule passes, and the picture is wrong. Measured on one
4165
+ seated rig: the line through the two eye bones sat 23.7° off the page's
4166
+ horizontal, all five face bones were at rotation 0, and the closed eyes came out
4167
+ level on a tilted face.
4168
+
4169
+ ```json
4170
+ "invariants": {
4171
+ "axes": [
4172
+ { "name": "face", "through": ["eye_r", "eye_l"],
4173
+ "bones": ["eye_r", "eye_l", "brow_r", "brow_l", "mouth"], "tolerance": 5 }
4174
+ ]
4175
+ }
4176
+ ```
4177
+
4178
+ For each entry the **line** runs through the setup-pose world origins of the two
4179
+ `through` bones, first to second, and every bone in `bones` must have its
4180
+ setup-pose **world x axis** within `tolerance` degrees of it — as a line, so a bone
4181
+ pointing the other way along it (turned 180°, or mirrored by a world scaleX of −1)
4182
+ lies along it too. The setup pose is the one `A50` reads: constraints applied, no
4183
+ physics, no skin set. `A51_BONES_LIE_ALONG_THEIR_DECLARED_AXIS` holds it (§5.2).
4184
+
4185
+ - **Held whether or not an animation keys the bone.** A consumer writes these bones
4186
+ at run time — `openTrack({ adjust })` on the core entry — which no animation in
4187
+ the file shows.
4188
+ - **The fix** for a bone that fails: rotate it in the rig spec onto the line (its
4189
+ `rotation`, so its world x axis lands on the axis angle the failure prints) and
4190
+ counter-rotate its attachments by the same angle, so the setup pose stays where
4191
+ it is. Its children inherit the rotation, so restate their `x`, `y` and
4192
+ `rotation` in its new frame too.
4193
+ - Compile errors, each naming `invariants.axes` and the entry: a missing `name`,
4194
+ a `name` twice, a `through` that is not two bone names, a bone in `through` or
4195
+ `bones` the rig does not declare, `through` naming one bone twice, an empty
4196
+ `bones`, a bone twice in `bones`, and a `tolerance` that is missing or not
4197
+ above 0 and below 90. **There is no default tolerance** — rigc does not invent
4198
+ one. Two lines meet at 0° to 90°, so 90 or more admits every orientation and
4199
+ could never fail; 0 would refuse a bone rotated exactly onto the line whenever
4200
+ the posed arithmetic rounds.
4201
+ - ⛔ **Two `through` bones on one world origin** draw no line. The rig spec cannot
4202
+ show that — a constraint can put them there — so it is the gate's refusal, not
4203
+ the compiler's: `axis "face": "eye_r" and "eye_l" share the world origin (…) at
4204
+ the setup pose, so they draw no line`.
4205
+ - Undeclared, `A51` reports **SKIP** — *the rig "X" declares no axes
4206
+ (invariants.axes)* — never a pass. It is a **validity** rule, unlike the
4207
+ archetype rules above: it can fire only on a declaration, so it never fires on
4208
+ foreign data, and it runs under the default `--profile spine`, where a
4209
+ declaration the profile did not read would be one nothing reads. The
4210
+ declaration is a statement to the gate and changes no emitted Spine byte.
4211
+
4157
4212
  ---
4158
4213
 
4159
4214
  ## 4. The motion spec, field by field
@@ -5046,7 +5101,7 @@ consumer's process, which is late. `A34_CONSTRAINT_TIMELINE_TARGETS` checks the
5046
5101
  same two from the other side, plus one thing the compiler cannot produce and a
5047
5102
  hand-edited file can: an **empty key array**, which the parser skips in silence.
5048
5103
 
5049
- 🖼️ **Worked example: [`gallery/walk`](https://github.com/firejune/rigc/tree/main/gallery/walk)** — two mirrored two-bone leg chains
5104
+ 🖼️ **Worked example: [`gallery/walk`](https://github.com/rig-c/core/tree/main/gallery/walk)** — two mirrored two-bone leg chains
5050
5105
  whose `mix`, `softness` and `bendPositive` are keyed through a stance and a swing,
5051
5106
  with the README's table of what each key is for.
5052
5107
 
@@ -5272,14 +5327,14 @@ field name twice.
5272
5327
  🖼️ **Worked examples, and they use a deform for four different things** — all
5273
5328
  four are repository material rather than part of the published package, so the
5274
5329
  links go to GitHub.
5275
- [`gallery/squash`](https://github.com/firejune/rigc/tree/main/gallery/squash) — a 9-vertex ball squashed about its contact point,
5330
+ [`gallery/squash`](https://github.com/rig-c/core/tree/main/gallery/squash) — a 9-vertex ball squashed about its contact point,
5276
5331
  from the two affine transforms its keys **state**.
5277
- [`gallery/portrait`](https://github.com/firejune/rigc/tree/main/gallery/portrait) — a 2.5D head turn, where the keys are the
5332
+ [`gallery/portrait`](https://github.com/rig-c/core/tree/main/gallery/portrait) — a 2.5D head turn, where the keys are the
5278
5333
  **projection of a yaw** rather than a squash: two grid meshes whose columns are
5279
5334
  placed to sample a cosine, and a measured account of the angle past which the
5280
- mesh folds. [`gallery/flex`](https://github.com/firejune/rigc/tree/main/gallery/flex) — a leaf whose blade bends on a
5335
+ mesh folds. [`gallery/flex`](https://github.com/rig-c/core/tree/main/gallery/flex) — a leaf whose blade bends on a
5281
5336
  `contour` mesh no bone can bend, and the measurement that picked the model.
5282
- [`gallery/nod`](https://github.com/firejune/rigc/tree/main/gallery/nod) — the
5337
+ [`gallery/nod`](https://github.com/rig-c/core/tree/main/gallery/nod) — the
5283
5338
  projection on the **other** axis (a `pitch`, §4.11.1), and a **travelling wave**
5284
5339
  whose only moving parameter is `phase`: each of its three meshes is laid out for
5285
5340
  the model that bends it, so the rows are the argument rather than the vertex
@@ -5599,7 +5654,7 @@ every ratio, so the two blocks can be lined up band against band instead of wors
5599
5654
  against worst — which on this build would have paired the inverted band with an
5600
5655
  untouched one and called the difference mild.
5601
5656
 
5602
- 📘 **[`gallery/nod`](https://github.com/firejune/rigc/tree/main/gallery/nod)'s
5657
+ 📘 **[`gallery/nod`](https://github.com/rig-c/core/tree/main/gallery/nod)'s
5603
5658
  README is a second reading of the same block** (repository material, hence the
5604
5659
  GitHub link), and it is the one where the figures are checked from two directions
5605
5660
  at once. Its `pitch` band ratios are *derived* from the mesh's own row table and
@@ -6256,7 +6311,7 @@ or the key's position in its own track. These are the frequent ones, verbatim:
6256
6311
  | `region "00_stage" of <pack>.atlas (declared 256x256 by its offsets) is 256x256 but the manifest window for "stage" is 250x256` | R5 — the row above under `--atlas-in`, and the prefix is the whole point: it says which of the two rigc **measured**, because the remedy differs. A bare path is a loose PNG it opened and you re-export; a `region … of <pack>` was read out of the pack, and you repack or aim the part at another region. This is the size row of §0.2's table, with the message it actually prints |
6257
6312
  | `duplicate region name "X"` | two PNGs share a basename; one part, one page, one name |
6258
6313
  | `"b/X.png" and the art already atlased as region "X" are two different files — … Rename one of the PNGs.` | R5 — the region name is the basename, so only one of the two can hold it. Rename a file (not a placeholder: the placeholder is free to repeat) |
6259
- | `the image "X.png" was never added to the atlas, so there is no region "X" …` | nothing in the spec — every image an attachment names is measured and atlased, one per file whichever skin names it, so this says rigc skipped one. Report it on [the tracker](https://github.com/firejune/rigc/issues). The `#555` the message itself cites is the change that made one-per-file an invariant, not a place to file against |
6314
+ | `the image "X.png" was never added to the atlas, so there is no region "X" …` | nothing in the spec — every image an attachment names is measured and atlased, one per file whichever skin names it, so this says rigc skipped one. Report it on [the tracker](https://github.com/rig-c/core/issues). The `#555` the message itself cites is the change that made one-per-file an invariant, not a place to file against |
6260
6315
  | `motion spec names archetype "A" but the rig spec at … is called "B"` | make `archetype` equal the rig's `name` |
6261
6316
  | `animation "A" declares duration Ns but its last key is at Ms` | R7 — fix whichever of the two you meant |
6262
6317
  | `animation "A" slot "X" attachment: key at Ns is Ms past the declared duration Ds` | §4.5 — the key is past the end of the animation and nothing will sample it. Move the key onto `duration`, or raise `duration` |
@@ -6427,9 +6482,9 @@ prints six rows and is one failure here.
6427
6482
  `A00_MODEL_READ` and `A00_MODEL_REGIONS_ON_PAGES` — the first two rows of the
6428
6483
  table below — which stand where `A00_ROUNDTRIP_PARSE`'s parse stands and have no
6429
6484
  round-trip counterpart, so they are not in the registry. That is the whole of the
6430
- difference between the two totals: `<N>` is 52 on the core entry and 50 on `cli.ts build`,
6431
- and the core report's last line states the split — 43 rules on the model side,
6432
- which are the 41 registry assertions the model side runs and those two; the 8 restated over the emitted text;
6485
+ difference between the two totals: `<N>` is 53 on the core entry and 51 on `cli.ts build`,
6486
+ and the core report's last line states the split — 44 rules on the model side,
6487
+ which are the 42 registry assertions the model side runs and those two; the 8 restated over the emitted text;
6433
6488
  and `A00_ROUNDTRIP_PARSE`, which it reports as a SKIP.
6434
6489
 
6435
6490
  🚨 **When `A00_ROUNDTRIP_PARSE` fails, read the report as a report about A00 and
@@ -6451,7 +6506,7 @@ Fix A00 and run it again.
6451
6506
  | `A04_MESH_TRIANGLES_AND_ENCODING` | both | authored mesh geometry: triangle count not a multiple of 3, an index out of range, or a `vertices` length that disagrees with `uvs` (the weighted/unweighted trap) **SKIP** when the skeleton carries no mesh attachment |
6452
6507
  | `A05_CURVE_ARRAY_LENGTH` | both | a raw `curve` with the wrong number of values, a non-finite number in one, or a curve on a timeline that cannot take one. Four numbers **per value channel**. **SKIP** when no animation carries a timeline at all. Timelines with no `curve` on any key still PASS: every timeline name is checked against the channel table whether or not a curve sits on one Under `cli_core.ts build` the same clause runs over the text the emitter wrote and prints this line in the same words (`RC28`). |
6453
6508
  | `A06_ATLAS_PAGE_SIZE_MATCHES_PNG` | both ◑ | the atlas `size:` disagrees with the PNG on disk — the message names the ratio it measured on **each** axis and the header that states the same art truthfully, and §0.2 has the whole of it: a runtime maps a region as a fraction of the DECLARED size and never reads the texture's own, so such a page draws, and what it breaks is every reader that addresses the page in texels. A uniform ratio is a `size:`/`scale:` pair away from honest and the message prints it; two ratios are not, because `scale:` carries one number, and the message says so rather than offering a header that would not work. **Or** a region's rectangle is not inside the page it names — rotation honoured, so a region at `rotate: 90` or `270` occupies `height x width` of the page and a region that fits only because it is turned is inside it. The message names the region, the rectangle it occupies, the page and the page's size. That clause is **validity** and runs under both profiles: a rectangle outside its page makes `u2 > 1` and samples whatever the wrap mode returns, and `--atlas-in` already refuses the same rectangle at compile time (§0.2). Under `spine-html` also: `pma` and rotation. Two regions on one page over the same texels was a clause here until issue #1099 and is `A49`'s now, read over what the two regions draw rather than over their rectangles. **Or** the page file is not a PNG at all, and then nothing about its size is measured: the message carries the page's path and the size the atlas declares, and names what the file is by its first bytes — one of WebP, JPEG, GIF, KTX, KTX2 by its signature, or no image format rigc recognises, the bytes in hex either way — or calls it **truncated** when it begins as a PNG and runs out before its `IEND`, or names the dimension when its `IHDR` states a width or height of **0**, which the format forbids (§0.2). **Or** the file reads as a PNG and its **image data cannot be decoded** — its compressed stream is inflated (not unfiltered) and the decoder refuses it, or it inflates short of the rows the header states — and the message names the file and carries the decoder's own words (`the decoder stops with "incorrect data check"`). That clause is validity too: nothing in rigc can read such a page back — and its failure is the **one** line that names such a page: `A19` SKIPs over it and points here rather than naming it again, the way `A17` alone names a missing file. **SKIP** when the atlas declares no page — and, rather than a PASS, when a page's file is **not on disk** and nothing else here failed: that page's size was not measured, so the reason names the pages not read (or says none was, when every file is missing) and points at `A17`, which fails naming each missing file — one missing file is named once, by the rule whose subject it is. With another `A06` failure standing, the missing page is `A17`'s alone |
6454
- | `A07_ATLAS_TEXT_SHAPE` | both | atlas text: a region name with stray whitespace, or a blank line splitting a page block. rigc writes the atlas, so this means a hand-edited file. Three sentences are about blank lines and name three different repairs: `line 1: the file begins with a blank line` (or `with N blank lines` for a run, one finding) is a blank before the first page name — delete it; `line N: the file ends with a blank line` (or `with N blank lines`, one finding) is a blank after the last page block's final line — delete it, since rigc ends the file on one newline; `line N: consecutive blank lines` is two blank lines side by side between the first page and the last — delete one. None of the three is `the last page block declares no region`, which means a page name and its fields with no region after them. rigc writes none of the three: under `--atlas-in` the pack's blank lines are put in rigc's shape on re-emission (§0.2), so any of them means a file rigc did not write, handed to `validate`. ⚠️ An atlas with **no page block at all** — no non-blank line — is not one of those: its subject is absent, so this reports **SKIP** naming the byte count it read, and so do the four rules below whose subject is a page ([#608](https://github.com/firejune/rigc/issues/608)). A rig whose skins need no art writes exactly that file (§3.4), and before #608 this row refused it with two findings naming a page block that was not there. What an empty atlas does **not** excuse is an attachment that wants a region out of it — that is `A08` Under `cli_core.ts build` the same clause runs over the text the emitter wrote and prints this line in the same words (`RC28`). |
6509
+ | `A07_ATLAS_TEXT_SHAPE` | both | atlas text: a region name with stray whitespace, or a blank line splitting a page block. rigc writes the atlas, so this means a hand-edited file. Three sentences are about blank lines and name three different repairs: `line 1: the file begins with a blank line` (or `with N blank lines` for a run, one finding) is a blank before the first page name — delete it; `line N: the file ends with a blank line` (or `with N blank lines`, one finding) is a blank after the last page block's final line — delete it, since rigc ends the file on one newline; `line N: consecutive blank lines` is two blank lines side by side between the first page and the last — delete one. None of the three is `the last page block declares no region`, which means a page name and its fields with no region after them. rigc writes none of the three: under `--atlas-in` the pack's blank lines are put in rigc's shape on re-emission (§0.2), so any of them means a file rigc did not write, handed to `validate`. ⚠️ An atlas with **no page block at all** — no non-blank line — is not one of those: its subject is absent, so this reports **SKIP** naming the byte count it read, and so do the four rules below whose subject is a page ([#608](https://github.com/rig-c/core/issues/608)). A rig whose skins need no art writes exactly that file (§3.4), and before #608 this row refused it with two findings naming a page block that was not there. What an empty atlas does **not** excuse is an attachment that wants a region out of it — that is `A08` Under `cli_core.ts build` the same clause runs over the text the emitter wrote and prints this line in the same words (`RC28`). |
6455
6510
  | `A08_REGION_NAMES_MATCH_ATTACHMENTS` | both | three things, and the message says which: an attachment whose `path` names **no region** of this atlas; a `path` carrying **stray whitespace**, printed quoted so you can see it; an **atlas region name** carrying stray whitespace (`A07` names that same line with its line number). The first two are read off the raw file **before** the loader is asked, so the miss is named here with the skin, the slot, the placeholder and the attachment's own name — the four things `AtlasAttachmentLoader`'s own `Region not found in atlas: <path> (attachment: <name>)` does not carry. There is no `spine-html` clause here — a placeholder is free to differ from the region its `path` names **SKIP** when no attachment names a region *and* the atlas declares none — both of its subjects at once |
6456
6511
  | `A09_ANIMATION_DURATION_MATCHES_SPEC` | both | the loaded duration ≠ the declared one, or the two sides disagree about which animations exist (R7). Asymmetric by design: a frame of slack for an animation that ends early, and none worth the name for a key *past* the declared end, which is the same rule §4.5 states at compile time — held here against a skeleton the compiler never saw. **SKIP** when neither side has an animation at all — a static rig has no duration |
6457
6512
  | `A10_NO_NAN_AFTER_STEPPING` | both | stepping the animation produced a pose that is not finite. It reads the **whole world transform** — all six terms `a`, `b`, `c`, `d`, `worldX`, `worldY` of every bone — then every vertex of every region and mesh the slots show, at the **setup pose** and at every stepped frame of every animation, through the same scan `render` refuses on, so the two cannot disagree. The detail names where (`the setup pose`, or `animation "x" frame i of 120 (t=…s)`), then the bone and the term (`bone "x" has a NaN`), or the slot, attachment and vertex (`vertex 3 has x Infinity`) when every bone is finite and a vertex is not — a `scaleX` chain whose product stays under the largest double does that. A bone at `rotation: 1e309` has a finite position and a `NaN` matrix, which is why the position alone is not read. Look for a number past the largest double, a degenerate curve or a zero scale. 🦴 **It also poses every bone `inherit` key at its own time**: the runtime resolves a mode by folding the case of its first letter and nothing else, and a spelling that misses is stored as **NaN** — the world position stays finite, `updateWorldTransform` matches no mode, and the bone keeps the rotation, scale and shear it had. The detail names the animation, the bone, the key's time and its spelling, beside the five. A bone whose *setup* spelling misses poses no mode at all, and is named the same way from the stepping loop. Neither is reachable from a rig spec — `build` refuses both spellings by name (§5.1) — so on a green build this clause is about files rigc did not write. Which mode a correct spelling poses is not judged here, because the lookup that resolved it is the one that would be checked. 🧍 **A skeleton with no animation is still read**: its setup pose is the whole of what it shows, so A10 reads it — the world transform and the vertices as above, and the bone's mode and the slot colours a stepped frame is read for, which on a static rig nothing else would reach — and names it `the setup pose` (`the setup pose: slot "x" colour is non-finite`). The stepping half had nothing, and a PASS row carries no detail, so the stats line says so: `nanStepping=skipped`, beside `animations=0`. `A09` does not follow — a declared duration is a fact about an animation and nothing else, so a static rig still gives it nothing. **SKIP** only when the skeleton carries **no bone and no animation**: there is then no pose to read and nothing to step |
@@ -6494,6 +6549,7 @@ Fix A00 and run it again.
6494
6549
  | `A48_TRANSFORM_CONSTRAINT_NOT_MUTED_THROUGHOUT` | both | a transform constraint none of whose mixes **for a property it drives** is away from 0 at setup or on any value an animation poses (§4.10, §4.12), or one whose `properties` name no `to` at all. A property is applied only when its own mix `!== 0`, and a key that omits a mix reads it as 1, so the six-mix early return of `TransformConstraint.update` would take a key of `mixRotate: 0` alone as a rescue — [measured] that key, and one keying `mixX` 1 on a rotate-only constraint, pose every bone exactly where no constraint does. A negative mix runs, and five transforms in the editor's example exports rest at −1. `transform constraint "C" drives rotate and has mixRotate 0 at setup, and none of the 1 animation keys its mix above 0; a mix is read only for a property the constraint drives, and update() skips each one at 0, so nothing ever moves "follower" — rest mixRotate above 0, or key its mix above 0 in an animation, or declare that the consumer drives its mix, in the rig spec as invariants.consumerDrivenMix: [{ "constraint": "C", "type": "transform", "why": … }]`. A constraint the rig spec declares in `invariants.consumerDrivenMix` (§3.7) is not measured, and one the file also switches on is refused as `A47`'s is; one that drives no property is refused with its own sentence declared or not, since no mix it carries is read by anybody. **SKIP** when the skeleton declares no transform constraint, and **SKIP by name** when every transform constraint it declares is declared consumer-driven: `every transform constraint here is declared in the rig spec as invariants.consumerDrivenMix, so its mix is the consumer's to set and nothing in this file shows it moving — "C" (why: …): it rests muted and none of the 1 animation keys its mix above 0`. With a live one beside it the stats line names the declared (`transformConsumerDriven`) |
6495
6550
  | `A49_PACKED_FOOTPRINTS_DO_NOT_OVERLAP` | renderer | two regions on one page whose rectangles overlap **and** whose footprints overlap — so one drawing samples texels that are the other's. A region's **footprint** is what samples it: a region a mesh names (its `path`, else its name, every frame of a `sequence` included) draws the mesh's hull loop and its triangles, its UVs placed on the page as `MeshAttachment.computeUVs` places them (a **linked mesh** draws its source's); a region a region attachment names, or that nothing names, draws its whole rectangle — and so does a mesh region whose UVs leave 0..1, whose hull or triangles do not index its vertices, or that is turned by `rotate:`, and every region when a `sequence` cannot be read. For two rectangles that is exactly the clause `A06` held until issue #1099 — an editor alias, a sequence frame, two rectangles moved over the same texels are refused as they were — and it accepts what `--pack-shape polygon` writes: a neighbour inside a mesh's rectangle where the hull is not (§0.1). **Touching is not overlapping**: interiors are compared, every comparison `FOOTPRINT_SLACK` (1e-6 texels) in favour of apart. `regions "ear_l" (341,696 112x420) and "mouth" (332,709 96x48) overlap on page "skeleton.png", and so do their footprints — "ear_l" draws the hull of mesh "ear_l" (skin "default", slot "ear_l"), "mouth" draws its whole rectangle, which region attachment "mouth" (skin "default", slot "mouth") draws; what they draw overlaps by 1916.5512 texel(s) of area inside 347,709 81x48. One drawing samples texels that are the other's: move one region clear of the other's footprint, or pack the page again` — `gallery/nod`'s polygon pack with `mouth` moved onto a triangle of the `ear_l` mesh (`PK88`). Nothing about the **padding** is read — the atlas and the model document do not state it — and the `2 · padding` the packer keeps is the packer's own (`PK81`). A renderer rule because two regions over the same texels is what an editor's packer writes for an alias or a sequence: 49 pairs on four of the ten atlases in `examples/`. **SKIP** when the atlas declares no page or no region, and when no page carries two regions — every unpacked build |
6496
6551
  | `A50_STAGE_BOX_IS_THE_STAGE` | both | the stage box a rig asked for (`skeleton.stageBox`, §3.1) is not the stage the model document states. Read on the data a consumer reads and on the pose it reads: the slot carries, in the `default` skin under the asked name, an unweighted bounding box of four vertices whose **stored** vertices are the stage's corners exactly — `(x, y)`, `(x + w, y)`, `(x + w, y + h)`, `(x, y + h)`, as the float32 the runtime loads — and whose **world** vertices at the setup pose (constraints applied, no physics, no skin set) are those corners as the runtime poses them on an unrotated root, compared on the header's 1e-6 grid at float32. `the stage box (slot "stage", attachment "stage"): vertex 1 is stored as (65, 0) and the stage's corner is (64, 0) — the stage 0,0 64x48, bottom-left first and counter-clockwise`; a root moved at setup reads *vertex 0 poses at (1, 0) at the setup pose and the stage's corner poses at (0, 0) on an unrotated root, on the header's grid — something moves the box's bone at setup (a constraint, or a pose the root does not state)*; a slot the skeleton lacks, no attachment of that name, another type, or a weighted box are each named too. Through spine-core it reads `findSlot`, `defaultSkin.getAttachment` and `computeWorldVertices` on a fresh skeleton after `setupPose()` and `updateWorldTransform(Physics.none)`; on the model side the document's record and rigc's core poser, held to spine-core's poses at tolerance 0 (`PG06`, `PG07`). **SKIP** where nothing asks: *nothing asks for a stage box: no rigc-compiled/3 model document beside this skeleton states one …* — a rig without the field, and any skeleton with no `/3` document beside it, an export among them |
6552
+ | `A51_BONES_LIE_ALONG_THEIR_DECLARED_AXIS` | both | a bone the rig holds to a line (`invariants.axes`, §3.7) whose world x axis at the setup pose — constraints applied, no physics, no skin set, the pose `A50` reads — is more than the entry's `tolerance` off the line through its two `through` bones' world origins, compared as lines (0° to 90°): `axis "face" (through "eye_r"→"eye_l", 30.0000°): bone "brow_l" world x axis at 36.0000°, 6.0000° off, tolerance 5° — rotate the bone in the rig spec onto the line and counter-rotate its attachments so the setup pose stays put`. Every angle is Spine world degrees (y up), counter-clockwise from +x. **Fix it in the rig spec**: set the bone's `rotation` so its world x axis lands on the axis angle the line prints, counter-rotate its attachments by the same angle so the setup pose does not move, and restate its children in its new frame. Also fails, by the entry's name: two `through` bones on one world origin (*share the world origin (x, y) at the setup pose, so they draw no line* — move one, or name another pair); a bone with no world x axis (a scale of 0); a bone inactive with no skin set (*so the setup pose places it nowhere*); a bone the skeleton does not have. Held whether or not an animation keys the bone: a consumer writes it at run time. Through spine-core it reads `appliedPose` on a fresh skeleton after `setupPose()` and `updateWorldTransform(Physics.none)`; on the model side rigc's core poser, held to spine-core's poses at tolerance 0 (`AX01`–`AX05`). **SKIP** when the rig declares no axes — *the rig "X" declares no axes (invariants.axes)* — and on a bare directory with no rig |
6497
6553
 
6498
6554
  `both ◑` marks a mixed assertion: its validity half always runs and its policy
6499
6555
  clauses are gated by profile.
@@ -7470,13 +7526,13 @@ that drew the reference frames, onto the same pixel grid, and reports what diffe
7470
7526
  you specifically: it means **you may run `check` as often as you like** without
7471
7527
  your run ceasing to be an honest authoring run. It is a loop, in the way `build` is
7472
7528
  a loop. `bench` and `diff` against a rung's export are not — they read the answer,
7473
- and [the ladder's honesty rule](https://github.com/firejune/rigc/blob/main/docs/LADDER.md) makes them a finish line you reach once.
7529
+ and [the ladder's honesty rule](https://github.com/rig-c/core/blob/main/docs/LADDER.md) makes them a finish line you reach once.
7474
7530
 
7475
7531
  📌 **That is also why the MAE figures quoted through this section stay.** Every one of
7476
7532
  them is a candidate's own reading against rendered frames — the exam question, not the
7477
7533
  answer key — so none of them narrows a reference-side measure, and a guide that censored
7478
7534
  them would be teaching less for no gain in honesty. The criterion is under *The honesty
7479
- rule* in [LADDER.md](https://github.com/firejune/rigc/blob/main/docs/LADDER.md); what it *does* seal is a score written
7535
+ rule* in [LADDER.md](https://github.com/rig-c/core/blob/main/docs/LADDER.md); what it *does* seal is a score written
7480
7536
  over a reference's own count, and no such figure appears here.
7481
7537
 
7482
7538
  🚨 **If you drive the runtime yourself, a bone's local transform lives on
@@ -9350,7 +9406,7 @@ read an absent `mixY` as that same `0`. *The key before's* is a sequence key's
9350
9406
  the same either way.
9351
9407
 
9352
9408
  ⚠️ **Why 4.3's defaults, when they are not every generation's** (row 4 of
9353
- [GENERATIONS.md](https://github.com/firejune/rigc/blob/main/docs/GENERATIONS.md)'s
9409
+ [GENERATIONS.md](https://github.com/rig-c/core/blob/main/docs/GENERATIONS.md)'s
9354
9410
  table).
9355
9411
  An omitted physics `inertia` is 0.5 to the 4.3 parser and 1 to the 4.2 one, and that
9356
9412
  is exactly why a key is left out only against the parser rigc links: the file states
@@ -9729,7 +9785,7 @@ two things.
9729
9785
  with the `visibleShare` it was computed on. ⚠️ **`visibleShare` is a per-frame
9730
9786
  diagnostic and not a summary statistic** — it is measured through the fitted
9731
9787
  placements, so a median or a mean of it is not comparable across fits. §12.3 says
9732
- what that costs and [the `visibleShare` study](https://github.com/firejune/rigc/blob/main/bench/studies/2026-09-03-visibleshare/README.md)
9788
+ what that costs and [the `visibleShare` study](https://github.com/rig-c/core/blob/main/bench/studies/2026-09-03-visibleshare/README.md)
9733
9789
  measures it — repository material, not shipped in the package.
9734
9790
  - **Hierarchy and attachment geometry**, so the search collapses. A child bone
9735
9791
  whose parent is already placed does not have four degrees of freedom: the rig
@@ -9909,7 +9965,7 @@ the answer landed and is *less* steady, not more (p99 0.6588 against 0.5592).
9909
9965
  **The field is reporting a bistable fit faithfully.** What is safe: reading it beside
9910
9966
  its own residual, on its own frame — which is what it exists for. What is safe as a
9911
9967
  corpus statistic: a **maximum**, which saturates. Full method and evidence:
9912
- [`bench/studies/2026-09-03-visibleshare`](https://github.com/firejune/rigc/blob/main/bench/studies/2026-09-03-visibleshare/README.md).
9968
+ [`bench/studies/2026-09-03-visibleshare`](https://github.com/rig-c/core/blob/main/bench/studies/2026-09-03-visibleshare/README.md).
9913
9969
 
9914
9970
  And per part:
9915
9971
 
@@ -9967,7 +10023,7 @@ simply unused; a name the directory lacks is refused `no-part-image` by name.
9967
10023
  floor. Read it per frame beside its own residual. If a corpus statistic is
9968
10024
  needed, a **maximum** is the one that survives (it saturates), and it should be
9969
10025
  quoted with its spread. §12.3 carries the figures;
9970
- [`bench/studies/2026-09-03-visibleshare`](https://github.com/firejune/rigc/blob/main/bench/studies/2026-09-03-visibleshare/README.md)
10026
+ [`bench/studies/2026-09-03-visibleshare`](https://github.com/rig-c/core/blob/main/bench/studies/2026-09-03-visibleshare/README.md)
9971
10027
  carries the method.
9972
10028
  - ⚠️ **Setup draw order, on one frame.** A `drawOrder` timeline reorders your slots
9973
10029
  at runtime and this cannot know the time, so a candidate that has one is masked in
package/docs/FACE.md CHANGED
@@ -33,11 +33,11 @@ hold its results.
33
33
  breath's `chest` is a **sibling** of the plate it must not scale, and §4.4 is the
34
34
  artless-parent pattern the shared-shift split is one instance of
35
35
  - The worked example every number below comes from:
36
- [`gallery/portrait`](https://github.com/firejune/rigc/tree/main/gallery/portrait),
36
+ [`gallery/portrait`](https://github.com/rig-c/core/tree/main/gallery/portrait),
37
37
  and its measurement half,
38
- [`FINDINGS.md`](https://github.com/firejune/rigc/tree/main/gallery/portrait/FINDINGS.md)
38
+ [`FINDINGS.md`](https://github.com/rig-c/core/tree/main/gallery/portrait/FINDINGS.md)
39
39
  - **The same closed forms on the other axis**:
40
- [`gallery/nod`](https://github.com/firejune/rigc/tree/main/gallery/nod) is a
40
+ [`gallery/nod`](https://github.com/rig-c/core/tree/main/gallery/nod) is a
41
41
  worked `pitch`, and this page stays written for a **yaw**. It re-derives §4.2's
42
42
  fold angle on uneven *rows* and brackets it against `A39` at 33°/34°, and it
43
43
  measures §5's foreshortening at **0.863–1.176** against the yaw's 0.892–1.064
@@ -46,7 +46,7 @@ hold its results.
46
46
  - **The turn driven by a value instead of played as a time** — the same keys, on
47
47
  an axis: §8's *The turn as a value rather than a time* derives the range,
48
48
  **AUTHORING §3.5.2** is the `slider` constraint that does it, and
49
- [`gallery/look`](https://github.com/firejune/rigc/tree/main/gallery/look) is
49
+ [`gallery/look`](https://github.com/rig-c/core/tree/main/gallery/look) is
50
50
  the worked case. Read it when the angle has to follow something outside the
51
51
  animation — a pointer, a gaze target, a game value
52
52
 
@@ -402,7 +402,7 @@ authority to say what it measured, and the step share is that.
402
402
 
403
403
  📐 Method, harness and the full ladders live in the repository rather than in
404
404
  this package, as
405
- [`bench/studies/2026-09-05-density`](https://github.com/firejune/rigc/tree/main/bench/studies/2026-09-05-density) —
405
+ [`bench/studies/2026-09-05-density`](https://github.com/rig-c/core/tree/main/bench/studies/2026-09-05-density) —
406
406
  the same study also measures why `contour` is the wrong generator for a turn: it
407
407
  saturates at 868 vertices however fine the tolerance, its vertices all sit on the
408
408
  silhouette so it samples **2 %** of the depth range, and its ear-clipped interior
@@ -424,7 +424,7 @@ step. ⇒ **Author the sheet so it changes by at least ~8 levels across one mesh
424
424
  cell**, and spend its whole 0–255 range on the part — a map using an eighth of
425
425
  the range describes the same surface and reports 5.8° less of it. Method and
426
426
  ladders:
427
- [`bench/studies/2026-09-05-noise`](https://github.com/firejune/rigc/tree/main/bench/studies/2026-09-05-noise).
427
+ [`bench/studies/2026-09-05-noise`](https://github.com/rig-c/core/tree/main/bench/studies/2026-09-05-noise).
428
428
 
429
429
  ⭐ **And you do not have to guess which of the three you are looking at.** The
430
430
  lines under the ceiling say it: the **1st percentile over
@@ -736,7 +736,7 @@ practical shape is checkable: **key the fade to 0 at or before
736
736
  the last angle that gates green, and let the fold happen after it.**
737
737
 
738
738
  📘 **The identity above, used forwards on a `pitch`.**
739
- [`gallery/nod`](https://github.com/firejune/rigc/tree/main/gallery/nod)
739
+ [`gallery/nod`](https://github.com/rig-c/core/tree/main/gallery/nod)
740
740
  (repository material) picks its ceiling first and solves the *rows* out of it —
741
741
  `|y|outer = R·cos θmax` with θmax chosen at 21° giving 140.037 — and then
742
742
  brackets the fold this section predicts against `A39` itself: **33° gates green
@@ -793,7 +793,7 @@ The far eye narrows to 89%, the near eye widens to 106% (**derived**: 0.8922 and
793
793
 
794
794
  📘 **How much this is worth depends on the axis, and there is a worked case for
795
795
  the other one.** The same closed form on the `pitch` of
796
- [`gallery/nod`](https://github.com/firejune/rigc/tree/main/gallery/nod)
796
+ [`gallery/nod`](https://github.com/rig-c/core/tree/main/gallery/nod)
797
797
  (repository material) spans **0.863 … 1.176** across its five features, against
798
798
  the **0.892 … 1.064** above — a wider span at the *identical* angle, because
799
799
  `α = atan2(coordinate, depth)` grows with the coordinate and a face is taller
@@ -883,7 +883,7 @@ are all timing:
883
883
  is a third drawing.
884
884
  - **A swap has to dodge the frame grid.** A stepped key exactly on a sample time
885
885
  can be missed by a player accumulating `1/fps`, which is why
886
- [`gallery/squash`](https://github.com/firejune/rigc/tree/main/gallery/squash)'s
886
+ [`gallery/squash`](https://github.com/rig-c/core/tree/main/gallery/squash)'s
887
887
  blink key sits at `0.399999` (AUTHORING §4.5). **A continuous channel does not
888
888
  care where the samples land.**
889
889
 
@@ -1112,7 +1112,7 @@ duration 2 x range x scale
1112
1112
  time to + (degrees - from) x scale the slider's own mapping
1113
1113
  ```
1114
1114
 
1115
- 📐 **Worked, on [`gallery/look`](https://github.com/firejune/rigc/tree/main/gallery/look)**,
1115
+ 📐 **Worked, on [`gallery/look`](https://github.com/rig-c/core/tree/main/gallery/look)**,
1116
1116
  whose face is a 21 × 9 `grid` over one depth sheet:
1117
1117
 
1118
1118
  ```bash
@@ -1323,7 +1323,7 @@ exactly as they do above. Write `"additive": true` on all of them.
1323
1323
 
1324
1324
  ✅ **The editor half, measured.** The round trip was taken with
1325
1325
  `tools/editor_roundtrip.ts` on a licensed editor (data version 4.3.26) against a 4.3.13 build of
1326
- [`gallery/look`](https://github.com/firejune/rigc/tree/main/gallery/look) — this
1326
+ [`gallery/look`](https://github.com/rig-c/core/tree/main/gallery/look) — this
1327
1327
  subsection's worked case, not the `gallery/portrait` this page names at the top,
1328
1328
  which declares no constraints at all and so can carry no slider. What it found:
1329
1329
 
@@ -1784,16 +1784,16 @@ substitute for looking at three scales.
1784
1784
 
1785
1785
  ## 10. The worked example
1786
1786
 
1787
- [`gallery/portrait`](https://github.com/firejune/rigc/tree/main/gallery/portrait)
1787
+ [`gallery/portrait`](https://github.com/rig-c/core/tree/main/gallery/portrait)
1788
1788
  is this page on real art — **22 parts, 27 bones, 22 slots, 2 meshes, 40
1789
1789
  vertices**, three animations, and a README that derives every number rather than
1790
1790
  listing it. Its
1791
- [`FINDINGS.md`](https://github.com/firejune/rigc/tree/main/gallery/portrait/FINDINGS.md)
1791
+ [`FINDINGS.md`](https://github.com/rig-c/core/tree/main/gallery/portrait/FINDINGS.md)
1792
1792
  is the measurement half: what it cost, the seven-angle sweep, the five tool gaps
1793
1793
  it filed.
1794
1794
 
1795
1795
  📘 **The `pitch` has its own worked case**, and this page deliberately stays a
1796
- yaw: [`gallery/nod`](https://github.com/firejune/rigc/tree/main/gallery/nod) is
1796
+ yaw: [`gallery/nod`](https://github.com/rig-c/core/tree/main/gallery/nod) is
1797
1797
  the same two closed forms on the other axis, plus a travelling `wave` (AUTHORING
1798
1798
  §4.11.1). §4.2 and §5 above point at the two places its figures are worth
1799
1799
  reading beside these — the fold angle bracketed against `A39`, and a
package/docs/INGEST.md CHANGED
@@ -644,7 +644,7 @@ it hands you, and every rule in them applies to a spec `ingest` wrote.
644
644
 
645
645
  A file from another Spine generation is the one case where transcription is not the
646
646
  first fallback: migrate it through the editor of its own generation first, as
647
- [GENERATIONS.md](https://github.com/firejune/rigc/blob/main/docs/GENERATIONS.md) §4
647
+ [GENERATIONS.md](https://github.com/rig-c/core/blob/main/docs/GENERATIONS.md) §4
648
648
  states.
649
649
 
650
650
  ### 2.1 The workflow
@@ -707,7 +707,7 @@ the second is the one that costs a day:
707
707
  all, and then you are debugging your own incomplete work rather than the format.
708
708
 
709
709
  ⚠️ **When a kind turns out not to be expressible, stop and say so — that is a finding,
710
- not a blocker to route around.** [SURVEY_2026-08-22.md](https://github.com/firejune/rigc/blob/main/docs/SURVEY_2026-08-22.md) is the
710
+ not a blocker to route around.** [SURVEY_2026-08-22.md](https://github.com/rig-c/core/blob/main/docs/SURVEY_2026-08-22.md) is the
711
711
  per-skeleton survey of exactly this. ⇒ Check the survey for your feature before
712
712
  concluding either way, and if it is genuinely absent, the shape of the answer is *"this export
713
713
  uses X, which the motion spec cannot say"* with a pointer — not a silent
@@ -869,7 +869,7 @@ catches a whole-figure scale error, and it is the only one that does.
869
869
  ### 2.4 The worked precedent, and what to take from it
870
870
 
871
871
  Three transcriptions of official Spine exports live in this repository under
872
- [`bench/transcriptions/`](https://github.com/firejune/rigc/tree/main/bench/transcriptions/)
872
+ [`bench/transcriptions/`](https://github.com/rig-c/core/tree/main/bench/transcriptions/)
873
873
  — `3-timing-and-spacing` (3 bones, 2 slots, 2 animations, 69 keys, every curve raw),
874
874
  `6-arcs` (weighted meshes, mesh `edges`, four 4.3 transform constraints) and
875
875
  `spineboy` in both `ess` and `pro`. They are **worked examples you are meant to read**,
@@ -1482,7 +1482,7 @@ every directory the fetch produced.
1482
1482
  the per-directory terms.
1483
1483
 
1484
1484
  The transcription these recipes start from is
1485
- [`bench/transcriptions/3-timing-and-spacing/`](https://github.com/firejune/rigc/tree/main/bench/transcriptions/3-timing-and-spacing/),
1485
+ [`bench/transcriptions/3-timing-and-spacing/`](https://github.com/rig-c/core/tree/main/bench/transcriptions/3-timing-and-spacing/),
1486
1486
  unmodified. The re-pivoted, renamed and extended variants in §4 were built from copies
1487
1487
  of it; none is committed, because each is an illustration of an edit rather than a
1488
1488
  transcription of anything.
package/docs/MOTION.md CHANGED
@@ -105,7 +105,7 @@ and each fails in its own way rather than merely reading oddly:**
105
105
  ⭐ **What is still this page's job is whatever moves the dial**, and that is an
106
106
  ordinary animation with §3 applying to it unchanged. The split is visible in the
107
107
  worked example: in
108
- [`gallery/look`](https://github.com/firejune/rigc/tree/main/gallery/look) the two
108
+ [`gallery/look`](https://github.com/rig-c/core/tree/main/gallery/look) the two
109
109
  lookup tables `turn` and `tilt` carry **no easing at all**, and `sweep` — the one
110
110
  animation there meant to be played — carries every `ease` in the file.
111
111
 
@@ -751,7 +751,7 @@ What *is* real: every command line below was run, and every figure printed in an
751
751
  output block is what the command actually printed.
752
752
 
753
753
  🖼️ **For the same recipe on art that ships, the
754
- [`gallery/`](https://github.com/firejune/rigc/tree/main/gallery) examples are worked
754
+ [`gallery/`](https://github.com/rig-c/core/tree/main/gallery) examples are worked
755
755
  in-betweening material** — `walk` is §3.5's arcs and §3.7's phase offsets on two leg
756
756
  chains, `ride` puts the same offsets in `groups` + `stagger`, and `squash` is §3.9's
757
757
  pivot written as a `deform` about a contact point. Each README says what every key is
package/docs/PROMPTING.md CHANGED
@@ -8,9 +8,9 @@ measured case: in August 2026 a deliberately small model — Gemini 3.7 Flash on
8
8
  the Antigravity harness — authored a benchmark rung over a **3 h 01 m** session,
9
9
  observed by the operator; the harness kept no tool transcript, so the run is
10
10
  recorded **Unverified** on honesty, with two of its four tiers *unevidenced*
11
- ([the verdict](https://github.com/firejune/rigc/blob/main/docs/pilot/verdicts/2026-08-24-rung3-1.md)
11
+ ([the verdict](https://github.com/rig-c/core/blob/main/docs/pilot/verdicts/2026-08-24-rung3-1.md)
12
12
  states all of that). Every stumble below is one it actually made, priced by the instruments in
13
- [PILOT.md](https://github.com/firejune/rigc/blob/main/docs/PILOT.md)'s protocol. A large model makes fewer of these mistakes
13
+ [PILOT.md](https://github.com/rig-c/core/blob/main/docs/PILOT.md)'s protocol. A large model makes fewer of these mistakes
14
14
  unprompted; the clauses cost you three lines and make the small model's run
15
15
  land and the large model's run land cleaner.
16
16
 
@@ -27,7 +27,7 @@ For ordinary use (not a benchmark), the whole prompt is short:
27
27
 
28
28
  Then add the clauses below. If you are running the **benchmark**, do not
29
29
  improvise a prompt at all — hand the agent
30
- [pilot/rung3-runner-prompt.md](https://github.com/firejune/rigc/blob/main/docs/pilot/rung3-runner-prompt.md) verbatim; it
30
+ [pilot/rung3-runner-prompt.md](https://github.com/rig-c/core/blob/main/docs/pilot/rung3-runner-prompt.md) verbatim; it
31
31
  carries the honesty rule, and an improvised summary of that rule is how a run
32
32
  stops being scorable.
33
33
 
@@ -101,7 +101,7 @@ prompt for the six clauses above and then let the agent work.
101
101
 
102
102
  ## If you are scoring the run
103
103
 
104
- That is a different document: [PILOT.md](https://github.com/firejune/rigc/blob/main/docs/PILOT.md) is the protocol,
104
+ That is a different document: [PILOT.md](https://github.com/rig-c/core/blob/main/docs/PILOT.md) is the protocol,
105
105
  `pilot/rung3-runner-prompt.md` is the exact prompt, and the evaluator sheet it
106
106
  names is for the judge only. The one rule that reaches this page: **never put
107
107
  baseline figures, reference structure, or previous runs' measures into the