@rig-c/core 2.82.1 → 2.83.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/README.md CHANGED
@@ -26,7 +26,7 @@ name. Both remain published alongside it with the same product files.
26
26
 
27
27
  <p align="center"><em>Fourteen part PNGs drawn from scratch for this repo, one rig spec, one motion spec — the assembly,
28
28
  the breathing and the wave are all rigc-compiled Spine animations, rendered with
29
- <code>rigc render</code>. A <a href="https://raw.githubusercontent.com/rig-c/core/main/assets/rigc-demo@2x.gif">2x version</a> (1200x810) sits beside it.</em></p>
29
+ <code>rigc render</code>. A <a href="https://raw.githubusercontent.com/rig-c/core/main/assets/rigc-demo@2x.gif">2x assembly film</a> (1200x810) sits beside it.</em></p>
30
30
 
31
31
  Loose part PNGs and two small JSON files in; **Spine 4.3 skeleton data out** — a
32
32
  `skeleton.json` and a `skeleton.atlas` that load in any Spine runtime and **import
@@ -44,7 +44,7 @@ installed.
44
44
  | a compiled rig and a question about its geometry | `rigc inspect` | one picture of its bones, axes, mesh edges and hulls, one bone's weights, the triangles folded over, the draw order, a world grid or a declared axis over the art — at the setup pose, one time or a sheet of several, zoomed to a bone or a slot, or beside a second build on one transform — and a legend that puts every mark back in world units |
45
45
  | a compiled rig | `rigc preview` | one self-contained `.html` that plays it in Spine's own web player |
46
46
  | two to four compiled rigs | `rigc vote` | one ballot page a human picks from, and the answer checked into a ledger |
47
- | a picture of a key pose | `rigc pose` | where each loose part PNG sits in it, in spec coordinates — the movement between two poses is then yours to key ([docs/MOTION.md](docs/MOTION.md)) |
47
+ | a picture of a key pose | `rigc pose` | where each loose part PNG sits in it, in spec coordinates — the movement between two poses is then yours to key ([the motion recipe](docs/MOTION.md)) |
48
48
  | the same picture, and a rig | `rigc chainfit` | the parts `pose` refuses because something is drawn over them — read through the candidate's own draw order and hierarchy, with the share of each part the answer was measured on |
49
49
 
50
50
  Everything in that table needs Bun and this package — `preview` and `vote` also
@@ -54,8 +54,8 @@ reference art, no art pipeline, no server.
54
54
  ### Depth-driven rigging
55
55
 
56
56
  Explicit depth, which a drawing alone does not state, makes facial features
57
- shift by different amounts during a small head turn: [`gallery/portrait`](https://github.com/rig-c/core/tree/main/gallery/portrait)
58
- uses per-part depths, while [`gallery/look`](https://github.com/rig-c/core/tree/main/gallery/look)
57
+ shift by different amounts during a small head turn: [the portrait depth example](https://github.com/rig-c/core/tree/main/gallery/portrait)
58
+ uses per-part depths, while [the mesh-depth look example](https://github.com/rig-c/core/tree/main/gallery/look)
59
59
  samples a depth map at the mesh vertices.
60
60
  With [`@rig-c/parts`' depth fields](https://github.com/rig-c/parts/blob/main/docs/AUTHORING.md#per-layer-depth-sheets-773),
61
61
  See-through's per-layer sheets are bound explicitly with `near` and `zScale`,
@@ -106,7 +106,7 @@ it cuts both ways:
106
106
 
107
107
  ### Licensing, stated plainly
108
108
 
109
- See [NOTICE.md](NOTICE.md) for licensing and third-party terms.
109
+ See [the licensing notice](NOTICE.md) for licensing and third-party terms.
110
110
 
111
111
  The problem rigc is aimed at is narrow. An agent asked to author a rig has no way
112
112
  to tell whether it succeeded: Spine's JSON parser accepts a great deal of nonsense
@@ -190,7 +190,7 @@ reference frames this project renders from them, which **are** committed, each
190
190
  example's own `license.txt` beside them under the redistribution grant those files
191
191
  carry; the images stay **non-commercial only**. The reasoning is in
192
192
  [`bench/reference/README.md`](https://github.com/rig-c/core/blob/main/bench/reference/README.md)
193
- and the terms in [NOTICE.md](NOTICE.md). It needs a clone and `bun run
193
+ and the terms in [the reference-image notice](NOTICE.md). It needs a clone and `bun run
194
194
  fetch-examples`, and says so by name when the corpus is absent. `check` is not one
195
195
  of them: it reads whatever frames you point it at, so it runs from the installed
196
196
  package on pictures of your own — which is what *Where to go next* below tells you
@@ -446,7 +446,7 @@ origin and tip in world units and in pixels, so a position read off the picture
446
446
  straight back into the spec. [AUTHORING §13](docs/AUTHORING.md#13-asking-the-picture-a-question--rigc-inspect)
447
447
  maps each question to its view.
448
448
 
449
- **`render`** writes PNG frames and a contact sheet; **`preview`** writes a playable HTML file — see [AUTHORING §0](docs/AUTHORING.md#0-the-loop).
449
+ **`render`** writes PNG frames and a contact sheet; **`preview`** writes a playable HTML file — see [the render and preview reference](docs/AUTHORING.md#0-the-loop).
450
450
 
451
451
  **7. Let someone choose.** Sooner or later you will have two builds that both pass
452
452
  the gate and no instrument that can separate them. `vote` puts them in one page
@@ -466,7 +466,7 @@ that does not belong to it. See
466
466
 
467
467
  **Where to go next.**
468
468
 
469
- - 📘 **[docs/AUTHORING.md](docs/AUTHORING.md)** is the real guide — both files
469
+ - 📘 **[the authoring guide](docs/AUTHORING.md)** is the real guide — both files
470
470
  field by field, the emission rules, every named failure mapped to the file that
471
471
  has to change, and §8–§9 for reproducing a shot you were given as pictures. It
472
472
  ships inside the npm package too, at
@@ -490,11 +490,11 @@ that does not belong to it. See
490
490
  it, so a figure is read against that rather than against a number from a page;
491
491
  AUTHORING.md §9.2 says what the two halves of that bound are and why the floor
492
492
  is not zero.
493
- - [docs/LADDER.md](https://github.com/rig-c/core/blob/main/docs/LADDER.md) is the benchmark: the same job, from a brief
493
+ - [the benchmark ladder](https://github.com/rig-c/core/blob/main/docs/LADDER.md) is the benchmark: the same job, from a brief
494
494
  and rendered frames, scored. [docs/PILOT.md](https://github.com/rig-c/core/blob/main/docs/PILOT.md) is how to run an
495
495
  agent through it and score what comes back.
496
496
  - 🤖 **Handing the authoring to an AI agent?**
497
- [docs/PROMPTING.md](docs/PROMPTING.md) is the operator's page — the six prompt
497
+ [the prompting guide](docs/PROMPTING.md) is the operator's page — the six prompt
498
498
  clauses a measured pilot run paid for, and what you can leave unsaid.
499
499
 
500
500
  ## See what you built, and let someone choose
@@ -514,7 +514,7 @@ a rig compiles green, because green says nothing at all about the picture.
514
514
  conditions, <code>rigc pose</code> reads where every part sits in each picture, two
515
515
  candidate in-betweenings are compiled from the same pair — and a real
516
516
  <code>rigc vote</code> ballot picks the winner, because the movement between the poses
517
- is the one thing no instrument here will grade. A <a href="https://raw.githubusercontent.com/rig-c/core/main/assets/rigc-keypose@2x.gif">2x version</a> (1200x810) sits beside it.</em></p>
517
+ is the one thing no instrument here will grade. A <a href="https://raw.githubusercontent.com/rig-c/core/main/assets/rigc-keypose@2x.gif">2x key-pose film</a> (1200x810) sits beside it.</em></p>
518
518
 
519
519
  Three properties of `vote` are worth stating, because they are what make its ledger
520
520
  usable by the next agent rather than by a reader: **a tie is a recorded outcome, not a
@@ -525,7 +525,7 @@ anywhere; and **every line carries a reason code** from a closed enumeration tha
525
525
  enforced, so *"tie, because this one is better"* is refused.
526
526
 
527
527
  🎞️ **Authoring the movement those pages show you** — key poses, in-betweening, and how
528
- to spread candidates so a ballot informs — is [docs/MOTION.md](docs/MOTION.md).
528
+ to spread candidates so a ballot informs — is [the motion authoring guide](docs/MOTION.md).
529
529
 
530
530
  📐 **`rigc pose --images parts/ --frame poseA.png` runs the other way.** Every command
531
531
  above takes something you authored and tells you about it; this one takes a **picture
@@ -568,7 +568,7 @@ while <code>gaze</code> and <code>turn</code> return to rest, so the hand-offs m
568
568
  differing pixels — and the composing is the consumer's. Authorable on plain Spine 4.3, no
569
569
  plugin, no runtime patch; what the turn costs is authoring rather than runtime capability,
570
570
  and that cost is one stated expression per key. Compiled and rendered entirely by the
571
- published package. A <a href="https://raw.githubusercontent.com/rig-c/core/main/assets/rigc-scene@2x.gif">2x version</a> (1200x810) sits beside it.</em></p>
571
+ published package. A <a href="https://raw.githubusercontent.com/rig-c/core/main/assets/rigc-scene@2x.gif">2x portrait film</a> (1200x810) sits beside it.</em></p>
572
572
 
573
573
  🎞️ **How the three films on this page were made** is kept with them, one directory per
574
574
  film in [`films/`](https://github.com/rig-c/core/tree/main/films) — a `run.sh` that
@@ -582,32 +582,34 @@ material, like the gallery: a clone runs them.
582
582
 
583
583
  | Command | Does |
584
584
  | --- | --- |
585
- | `build` | Compile, gate and write the rig. [Reference](docs/AUTHORING.md#0-the-loop). |
586
- | `validate` | Re-gate artifacts already on disk. [Reference](docs/AUTHORING.md#0-the-loop). |
587
- | `repack` | Repack a build from its output. [Reference](docs/AUTHORING.md#0-the-loop). |
588
- | `ingest` | Read a skeleton back into specs. [Reference](docs/AUTHORING.md#0-the-loop). |
589
- | `split` | Separate disconnected mesh pieces. [Reference](docs/AUTHORING.md#0-the-loop). |
590
- | `explain` | Print the compiled rig as a table. [Reference](docs/AUTHORING.md#0-the-loop). |
591
- | `render` | Write PNG frames and a contact sheet. [Reference](docs/AUTHORING.md#0-the-loop). |
592
- | `preview` | Write a playable HTML preview. [Reference](docs/AUTHORING.md#0-the-loop). |
593
- | `vote` | Make a ballot and record its answer. [Reference](docs/AUTHORING.md#0-the-loop). |
594
- | `pose` | Read part placements from a picture. [Reference](docs/AUTHORING.md#0-the-loop). |
595
- | `chainfit` | Read occluded parts through a candidate rig. [Reference](docs/AUTHORING.md#0-the-loop). |
596
- | `diff` | Compare two skeletons structurally. [Reference](docs/AUTHORING.md#0-the-loop). |
597
- | `bonedist` | Measure per-bone world-transform distance. [Reference](docs/AUTHORING.md#0-the-loop). |
598
- | `check` | Compare a candidate against reference pictures. [Reference](docs/AUTHORING.md#0-the-loop). |
599
- | `bench` | Run a rung of the benchmark ladder. [Reference](docs/AUTHORING.md#0-the-loop). |
600
- | `guide` | Read a named failure or guide section. [Reference](docs/AUTHORING.md#0-the-loop). |
601
- | `skills install` | Install the packaged agent skills. [Reference](docs/AUTHORING.md#0-the-loop). |
602
- | `review` | Hand a build to the review package. [Reference](docs/AUTHORING.md#0-the-loop). |
603
-
604
- ### Starting from a skeleton you already have
605
-
606
- See [INGEST: Starting from a skeleton you already have](docs/INGEST.md#starting-from-a-skeleton-you-already-have) for the ingest-and-rebuild loop.
607
-
608
- ### The editor round trip — for a licence holder, never in CI
609
-
610
- See [INGEST: The editor round trip](docs/INGEST.md#the-editor-round-trip--for-a-licence-holder-never-in-ci) for the licensed-editor workflow.
585
+ | `build` | Compile, gate and write the rig. [`build` in the CLI reference](docs/AUTHORING.md#cmd-build) |
586
+ | `validate` | Re-gate artifacts already on disk. [`validate` in the CLI reference](docs/AUTHORING.md#cmd-validate) |
587
+ | `repack` | Repack a build from its output. [`repack` in the CLI reference](docs/AUTHORING.md#cmd-repack) |
588
+ | `ingest` | Read a skeleton back into specs. [`ingest` in the CLI reference](docs/AUTHORING.md#cmd-ingest) |
589
+ | `split` | Separate disconnected mesh pieces. [`split` in the CLI reference](docs/AUTHORING.md#cmd-split) |
590
+ | `explain` | Print the compiled rig as a table. [`explain` in the CLI reference](docs/AUTHORING.md#cmd-explain) |
591
+ | `render` | Write PNG frames and a contact sheet. [`render` in the CLI reference](docs/AUTHORING.md#cmd-render) |
592
+ | `preview` | Write a playable HTML preview. [`preview` in the CLI reference](docs/AUTHORING.md#cmd-preview) |
593
+ | `vote` | Make a ballot and record its answer. [`vote` in the CLI reference](docs/AUTHORING.md#cmd-vote) |
594
+ | `pose` | Read part placements from a picture. [`pose` in the CLI reference](docs/AUTHORING.md#cmd-pose) |
595
+ | `chainfit` | Read occluded parts through a candidate rig. [`chainfit` in the CLI reference](docs/AUTHORING.md#cmd-chainfit) |
596
+ | `diff` | Compare two skeletons structurally. [`diff` in the CLI reference](docs/AUTHORING.md#cmd-diff) |
597
+ | `bonedist` | Measure per-bone world-transform distance. [`bonedist` in the CLI reference](docs/AUTHORING.md#cmd-bonedist) |
598
+ | `check` | Compare a candidate against reference pictures. [`check` in the CLI reference](docs/AUTHORING.md#cmd-check) |
599
+ | `bench` | Run a rung of the benchmark ladder. [`bench` in the CLI reference](docs/AUTHORING.md#cmd-bench) |
600
+ | `guide` | Read a named failure or guide section. [`guide` in the CLI reference](docs/AUTHORING.md#cmd-guide) |
601
+ | `skills install` | Install the packaged agent skills. [`skills install` in the CLI reference](docs/AUTHORING.md#cmd-skills-install) |
602
+ | `review` | Hand a build to the review package. [`review` in the CLI reference](docs/AUTHORING.md#cmd-review) |
603
+
604
+ ### Existing skeletons and the editor
605
+
606
+ <a id="starting-from-a-skeleton-you-already-have"></a>
607
+ <a id="the-editor-round-trip--for-a-licence-holder-never-in-ci"></a>
608
+
609
+ For a skeleton you already have, [the ingest-and-rebuild loop](docs/INGEST.md#starting-from-a-skeleton-you-already-have)
610
+ turns a Spine 4.3 export into rig and motion specs, then checks that rebuilding them reproduces it.
611
+ For a licence holder, [the editor round trip](docs/INGEST.md#the-editor-round-trip--for-a-licence-holder-never-in-ci)
612
+ imports a build into the Spine editor and checks the export against that build with the gate, structural diff and rendered frames; it never runs in CI.
611
613
 
612
614
  ## Documentation
613
615
 
@@ -625,7 +627,7 @@ See [INGEST: The editor round trip](docs/INGEST.md#the-editor-round-trip--for-a-
625
627
  | 🧬 [GENERATIONS.md](https://github.com/rig-c/core/blob/main/docs/GENERATIONS.md) | **Spine data from another generation.** Why a 3.8–4.2 file read as 4.3 fails in silence, the policy that follows (detect from `skeleton.spine`, never guess, play on the matching runtime), what `A16` and `ingest` do with such a file, and the editor as the migration path. Repository material |
626
628
  | 📓 [CASES.md](https://github.com/rig-c/core/blob/main/docs/CASES.md) | **converting a Live2D model to Spine, and verifying the conversion.** The method — the source's own player headless, parts from its texture along the drawables, motion baked as deform keys, reference frames first, `check` — run once end to end on two Live2D sample models with no editor and no converter, and verified against the source's renderer and Spine's own runtime: the three instruments, the two rigc defects verifying it led to and v1.2.1 fixed, and where the method stops. Figures only — the models' licence keeps every asset out. Repository material |
627
629
  | 🗺️ [ROADMAP.md](https://github.com/rig-c/core/blob/main/ROADMAP.md) | where this is going, and where it has been. What 1.0 had to mean before the number was claimed, and what it was claimed on — conditions rather than a feature list, because direction here comes from what users hit |
628
- | 📐 [CLAUDE.md](https://github.com/rig-c/core/blob/main/CLAUDE.md) | **the doctrine** — why the validator's messages are the product, why nothing reaches disk before green, why no number is ever invented, and what a change has to keep. [CONTRIBUTING.md](https://github.com/rig-c/core/blob/main/CONTRIBUTING.md) calls it worth ten minutes before a first patch. Repository material |
630
+ | 📐 [CLAUDE.md](https://github.com/rig-c/core/blob/main/CLAUDE.md) | **the doctrine** — why the validator's messages are the product, why nothing reaches disk before green, why no number is ever invented, and what a change has to keep. [the contribution guide](https://github.com/rig-c/core/blob/main/CONTRIBUTING.md) calls it worth ten minutes before a first patch. Repository material |
629
631
 
630
632
  [Dated survey](https://github.com/rig-c/core/blob/main/docs/SURVEY_2026-08-22.md).
631
633
 
@@ -649,7 +651,7 @@ attributability is **measured** to be capped below the bar reads down when every
649
651
  observable about it is independently verified strict. Every other rung and the
650
652
  graduation exam pass on the clause itself. Recompiling a stored candidate reproduces its
651
653
  record **to the digit within one gate**; across an instrument change the digits move,
652
- and [docs/LADDER.md](https://github.com/rig-c/core/blob/main/docs/LADDER.md) records
654
+ and [the gate re-inspection ledger](https://github.com/rig-c/core/blob/main/docs/LADDER.md) records
653
655
  where. The standing figures quoted above are from its *gate-v2.4 re-inspection*, which
654
656
  is the current sweep.
655
657
 
@@ -668,13 +670,13 @@ a from-zero run is a tooling-progress measurement rather than a re-climb, which
668
670
  why the certification above is scoped to tool + guide + protocol. One of those
669
671
  attempts states the residual in its own words: *"in motion it is not at editor
670
672
  quality."* All six, with their verdicts, are in
671
- [docs/LADDER.md](https://github.com/rig-c/core/blob/main/docs/LADDER.md).
673
+ [the from-zero attempt records](https://github.com/rig-c/core/blob/main/docs/LADDER.md).
672
674
 
673
675
  The whole dossier — the yardstick, `diff` and `check` and what neither of them can
674
676
  see, every rung, the run viewer, the 50 assertions and the selftest behind them — is
675
677
  [docs/BENCHMARK.md](https://github.com/rig-c/core/blob/main/docs/BENCHMARK.md).
676
678
  Live rung status is
677
- [docs/LADDER.md](https://github.com/rig-c/core/blob/main/docs/LADDER.md).
679
+ [recorded in the ladder ledger](https://github.com/rig-c/core/blob/main/docs/LADDER.md).
678
680
 
679
681
  ## Contributing
680
682
 
package/docs/AUTHORING.md CHANGED
@@ -120,27 +120,27 @@ commands take it and what its default is.
120
120
 
121
121
  | Command | Does |
122
122
  | --- | --- |
123
- | `build --rig … --motion … --out …` | compiles, gates, and **writes only if the gate is green**. `--images <dir>` says where the rig spec's `image` names resolve, `--manifest` adds measured art, and `--copy-images` copies every page PNG into `--out` so the directory is self-contained, with `skeleton.images` pointing at it so the editor's import finds the parts |
123
+ | `build --rig … --motion … --out …` | compiles, gates, and **writes only if the gate is green**. `--images <dir>` says where the rig spec's `image` names resolve, `--manifest` adds measured art, and `--copy-images` copies every page PNG into `--out` so the directory is self-contained, with `skeleton.images` pointing at it so the editor's import finds the parts <a id="cmd-build"></a> |
124
124
  | `build … --pack` | the same build with every part arranged onto **shared** atlas pages, written into `--out` — losslessly, and gated a second time as the pair that ships. `--page-size`, `--padding`, `--page-edges` and `--pack-shape` tune it — `--pack-shape polygon` lets a neighbour sit inside a mesh's rectangle where its hull is not, and with `--page-edges free` costs 12–56 times `rect`'s pack (3–11 s against 0.2–0.3 s on 90–144 parts over two to four pages; past 5 s from about 60 parts, a second or so below 45 on one page — AUTHORING §0.1) |
125
125
  | `build … --atlas-in <file.atlas>` | the same build with every part resolved to a **region of an existing pack** instead of a loose PNG; a name the atlas lacks, a size the spec disagrees with or a rectangle off its page is refused by name |
126
- | `validate <dir>` | re-gates artifacts already on disk |
127
- | `repack <dir> --out <dir>` | a packed build **repacked from its own output** — `skeleton.json`, `skeleton.atlas` and the pages, with no parts kept — under the packing flags `build --pack` takes, so a packer improvement reaches a build whose parts are gone. Every region is lifted off its page, the skeleton read back with `ingest`, and `build --pack` run over the lifted parts with the same gate; it writes only after every region is shown pixel-identical to the input's, the skeleton byte-identical and the gate green, and refuses by name an atlas it cannot lift exactly. A skeleton the rebuild does not reproduce byte for byte — a build written before 2.2.0, whose header box was its stage — is refused naming every differing path, and written, every path printed, only under `--accept-skeleton-differences`. `--stage-box <slot>` reads the stage from the box a build carries for it (`skeleton.stageBox`), the one way the three files alone carry the stage exactly ([AUTHORING §0.4](../docs/AUTHORING.md)) |
126
+ | `validate <dir>` | re-gates artifacts already on disk <a id="cmd-validate"></a> |
127
+ | `repack <dir> --out <dir>` | a packed build **repacked from its own output** — `skeleton.json`, `skeleton.atlas` and the pages, with no parts kept — under the packing flags `build --pack` takes, so a packer improvement reaches a build whose parts are gone. Every region is lifted off its page, the skeleton read back with `ingest`, and `build --pack` run over the lifted parts with the same gate; it writes only after every region is shown pixel-identical to the input's, the skeleton byte-identical and the gate green, and refuses by name an atlas it cannot lift exactly. A skeleton the rebuild does not reproduce byte for byte — a build written before 2.2.0, whose header box was its stage — is refused naming every differing path, and written, every path printed, only under `--accept-skeleton-differences`. `--stage-box <slot>` reads the stage from the box a build carries for it (`skeleton.stageBox`), the one way the three files alone carry the stage exactly ([AUTHORING §0.4](../docs/AUTHORING.md)) <a id="cmd-repack"></a> |
128
128
  | `repack <dir> --out <dir> --keep-pages` | **`skeleton.model.json` alone, for the pages already in place** — for a player that reads the document beside an atlas and pages that already shipped. The same read and stage rules, then `build --atlas-in` over the input atlas in place of the lift and the pack; it writes the document, and nothing else, only after its `pages` are shown to state the atlas as it stands, the rebuilt skeleton byte-identical to the one on disk (so `spine.sha256` names it — no acceptance flag here) and the gate green. `--out` is usually the build's own directory and must not already hold a `skeleton.model.json`; a page or region line the document has no field for is refused by name ([AUTHORING §0.4](../docs/AUTHORING.md)) |
129
- | `ingest <skeleton.json> --out <dir>` | `build` run backwards: reads a Spine 4.3 skeleton and writes the rig spec and motion spec that **rebuild it**, plus a findings report naming everything it could not carry. a skeleton that declares no stage is carried as declaring none, `--stage x,y,w,h` adds a box to one — and is refused, rather than ignored, beside one that declares a box — and `--images <dir>` writes the spec's own images directory — the opposite direction from `build --images`, which overrides it — so the rebuild carries no flag at all |
130
- | `split --rig … --motion … --slot <name> --out <dir>` | the answer to a mesh refused because its triangles are **several pieces that share no vertex** — two legs after the triangles bridging them were deleted. It reads the two specs and writes them back into an absent or empty `--out` with one mesh per piece: piece 1 stays in the slot, piece k gets a new slot `<slot>_<k>` after it, each with the original's uvs and weights unchanged, its triangles renumbered outline first where the hull rule needs it, and every deform key re-sliced to the new indices — plus `split.json`, where every original vertex and triangle went. One piece is said so and nothing is written; what it cannot carry is refused by name ([AUTHORING §3.4.11](../docs/AUTHORING.md)) |
131
- | `explain --rig … --motion …` | the compiled rig as a table — every bone with its resolved parent, the slots in draw order, every timeline key by key. Writes nothing. What to reach for when a rig compiles and still looks wrong |
132
- | `render --candidate <dir>` | PNG frames plus a contact sheet, in `render/`, one directory per set: `<anim>@<fps>fps/` rather than `<anim>/` at any rate but the default — `--fps 24` writes `<anim>@24fps/`, and the default 12 writes `<anim>/`. `--hide <slot,…>` or `--slot <slot,…>` draws part of the rig on the **same grid** as the whole, so the two frames overlay and the difference is the part; `frames.json` records the subset and `check` refuses such a set as a reference. `--geometry` adds a `geometry.json` per set: every frame's bone world transforms and skinned attachment vertices, with the rest geometry, on the frames' own grid. `--folds` adds a `folds.json` per set and prints one line per mesh with a triangle turned over against the setup pose: the frames, the worst frame's count and which triangles, their world box and their setup-pose area. `--pass-through <cloth>:<leg>` adds a `pass-through.json` per set and prints the frames where a cloth drawn over a leg has its side inside the leg's drawn outline, with the depth and the world box — a reading, not a gate. `--seam <slot>:<slot>` adds a `seam.json` per set and prints, per pair, the frames where two slots that meet open background inside the joint (a gap) or show the overlap band of one past the other (a ghost), with the worst frame's cell count and world box; `ghostA` and `ghostB` partition the same ghost by which named pair member drew it, each with its own frames, spans and worst; `gapOpen` and `gapCovered` partition `gap` by zero versus positive rendered alpha from the whole skeleton, including faint and clipped drawings ([AUTHORING §8.8](../docs/AUTHORING.md#88-where-two-slots-stop-meeting--render---seam)) — a reading, not a gate; the exit status never moves. `--overlaps` adds an `overlaps.json` per set and prints one `OVERLAP "<under>" under "<over>"` line per pair of slots where the one drawn later covers the earlier one and the motion moves it — the frames, and the most pixels covered and the frame of them — with an outline plate per covered frame; the render reports what was drawn, and the draw-order key is the author's remedy — a reading, not a gate. `--extremes` adds `extremes.json` and a native six-panel `extremes.png` (setup, geometry-pair min/max and three premultiplied RGBA diffs with a legend), plus one `EXTREMES` line per set. Per-channel minima/maxima and the farthest original-vertex geometry pair are distinct readings; valid channel extrema survive a geometry/image SKIP, with channel refusals reported separately; still or uncomparable geometry reports a named SKIP without changing render exit status. `--extremes-only` writes the same `extremes.json` and readable `extremes.png` bytes while skipping ordinary frame rasterization/encoding, `contact.png` and `frames.json`; it implies `--extremes` and takes no other diagnostic or sheet output flags. Look at min, max and diff before a person does ([AUTHORING §8.11](../docs/AUTHORING.md#811-min-max-and-diff-before-a-person-looks--render---extremes)). `--angle-range <bone>:<relative-to>` prints how far one bone turns against another over each set: the lowest and highest angle and the frames they fall on — a reading, not a gate. `--viewport <x,y,w,h>` or `--viewport-from <frames.json>` draws over a given box instead of a fitted one, so two builds land on one pixel grid; a slot that reaches past it is named by set and frame, and `frames.json` records it. `--environment <schedule.json>` draws the rig under the wind and gravity its physics answers, in time — the same frames on the core and on spine-core; an inspection of the response the rig states, not a scene. `--sheet [--sheet-format json-hash] [--page-edges pot\|free]` also packs each set's straight-alpha transparent frames into `sheet.png` + `sheet.json` (TexturePacker JSON Hash with a shared world-origin `anchor` and `2`-px clamped edge extrusion, verified through `pixi.js@8.22.0` and `phaser@3.90.0` by `tools/sheet_readers.ts`) and `sheet-manifest.json` (`rigc-sheet/1`), leaving diagnostic `f####.png` and `contact.png` unchanged ([AUTHORING §8.10](../docs/AUTHORING.md#810-sprite-sheets-from-a-render--render---sheet)) — a self-contained sprite object per animation set; when animations play and how a scene is composed remains the consumer's |
133
- | `preview --candidate <dir>` | one self-contained `.html` that plays it, headed by the line `validate <dir>` prints for it and the rigc version — a refused candidate is still previewed, and its header says so in the gate's words. Repeat `--candidate` for one page with a pane per candidate, in the order given; a green `build` ends by naming this command for its own `--out` |
134
- | `vote --candidate a --candidate b` | one `.html` that asks a human which; `vote --record <file>` checks the answer into `votes.jsonl` |
135
- | `pose --images <dir> --frame <png>` | reads part placements **out of** a picture |
136
- | `chainfit --candidate <dir> --images <dir> --frame <png>` | reads the parts `pose` refuses, through the candidate's own draw order and hierarchy: masked residuals over **visible** pixels, one hinge per child instead of four degrees of freedom, and the `rotate` key value each answer implies. A bone with two or more anchored descendants is **determined** rather than searched, and the residual that over-determination leaves is reported |
137
- | `diff <candidate.json> <reference.json>` | structural comparison of two skeletons, one ratio per measure and deliberately no combined score |
138
- | `bonedist --candidate … --reference … --bones …` | per-frame, per-bone world-transform distance against another skeleton — the ladder's stage 3, run on its own. `--bones <correspondence.json \| identity>` is required rather than defaulted: a candidate is entitled to its own bone names, so the pairing is stated |
139
- | `check --candidate <dir> --frames <dir> [--out <dir>]` | the candidate against reference pictures — the only instrument here that can see a *wrong animation*. `--out` also writes, for every frame the table lists, the picture its figures came from: reference, candidate, difference and overlay at native size |
140
- | `bench <rung> --candidate <dir>` | one rung of the benchmark ladder |
141
- | `guide <RULE>` / `guide §<n>[.<m>[.<k>]] [--all]` | the guide's row for the rule a failure line names — every `FAIL` line, `vote --record` refusal, `ingest` finding and coded compile error ends with ` (see rigc guide <RULE>)`; only legacy uncoded library errors point at `§5.1` — or a section's heading, own prose and subsection index, read from the installed `docs/AUTHORING.md` when it runs. `--all` includes the subsection bodies; a leaf prints its full text either way. Sizes describe full ledger spans. `--all` with a rule is refused, exit 2; an unknown name is refused with the nearest real ones, exit 1 |
142
- | `skills install [--dir …] [--copy]` | links every agent skill the package ships into `.agents/skills` (or `--dir`) as relative symlinks, or copies them with `--copy` — where Codex, Gemini CLI and Antigravity look. A second run has nothing to do; an entry already there that is not this command's is refused by name and nothing is written |
143
- | `review <dir> […]` | the human review of a build, handed to the `rig-review` bin of `@rig-c/review` installed beside the package; rigc does not depend on it and never installs it. `rigc review <dir> [--reference <video>] [--animation <name>] [--compare <dir>]` is `rig-review open <dir> …` with those flags 1:1 and prints the loopback URL to give a person; a first argument that is one of review's own words (`open`, `check`, `feedback`, `control`, `strip`, `compare`) or starts with `-` (`--help`, `--`) is handed over untouched. stdout is review's byte for byte, stderr review's plus the launcher's own refusals, the exit code review's. Refused by name, exit 2, before the bin starts: `REVIEW_OUTPUT_MISSING <path>` (the target is not a directory); `REVIEW_NOT_INSTALLED` with two lines, `bun add -d @rig-c/review` and `bunx @rig-c/review open <dir> …` carrying the flags given — the same on a terminal and off, with nothing installed or written; `REVIEW_BUN_TOO_OLD: Bun <found>, @rig-c/review needs <required>`, the floor read off the installed package's `engines.bun`; and an empty argument (`""`) under a Bun that drops one (Bun 1.2.0, measured by a probe), `rigc review: argument N is empty, and Bun <version>, the Bun running this, drops an empty argument when it starts a process — …`. docs/AUTHORING.md lists every line |
129
+ | `ingest <skeleton.json> --out <dir>` | `build` run backwards: reads a Spine 4.3 skeleton and writes the rig spec and motion spec that **rebuild it**, plus a findings report naming everything it could not carry. a skeleton that declares no stage is carried as declaring none, `--stage x,y,w,h` adds a box to one — and is refused, rather than ignored, beside one that declares a box — and `--images <dir>` writes the spec's own images directory — the opposite direction from `build --images`, which overrides it — so the rebuild carries no flag at all <a id="cmd-ingest"></a> |
130
+ | `split --rig … --motion … --slot <name> --out <dir>` | the answer to a mesh refused because its triangles are **several pieces that share no vertex** — two legs after the triangles bridging them were deleted. It reads the two specs and writes them back into an absent or empty `--out` with one mesh per piece: piece 1 stays in the slot, piece k gets a new slot `<slot>_<k>` after it, each with the original's uvs and weights unchanged, its triangles renumbered outline first where the hull rule needs it, and every deform key re-sliced to the new indices — plus `split.json`, where every original vertex and triangle went. One piece is said so and nothing is written; what it cannot carry is refused by name ([AUTHORING §3.4.11](../docs/AUTHORING.md)) <a id="cmd-split"></a> |
131
+ | `explain --rig … --motion …` | the compiled rig as a table — every bone with its resolved parent, the slots in draw order, every timeline key by key. Writes nothing. What to reach for when a rig compiles and still looks wrong <a id="cmd-explain"></a> |
132
+ | `render --candidate <dir>` | PNG frames plus a contact sheet, in `render/`, one directory per set: `<anim>@<fps>fps/` rather than `<anim>/` at any rate but the default — `--fps 24` writes `<anim>@24fps/`, and the default 12 writes `<anim>/`. `--hide <slot,…>` or `--slot <slot,…>` draws part of the rig on the **same grid** as the whole, so the two frames overlay and the difference is the part; `frames.json` records the subset and `check` refuses such a set as a reference. `--geometry` adds a `geometry.json` per set: every frame's bone world transforms and skinned attachment vertices, with the rest geometry, on the frames' own grid. `--folds` adds a `folds.json` per set and prints one line per mesh with a triangle turned over against the setup pose: the frames, the worst frame's count and which triangles, their world box and their setup-pose area. `--pass-through <cloth>:<leg>` adds a `pass-through.json` per set and prints the frames where a cloth drawn over a leg has its side inside the leg's drawn outline, with the depth and the world box — a reading, not a gate. `--seam <slot>:<slot>` adds a `seam.json` per set and prints, per pair, the frames where two slots that meet open background inside the joint (a gap) or show the overlap band of one past the other (a ghost), with the worst frame's cell count and world box; `ghostA` and `ghostB` partition the same ghost by which named pair member drew it, each with its own frames, spans and worst; `gapOpen` and `gapCovered` partition `gap` by zero versus positive rendered alpha from the whole skeleton, including faint and clipped drawings; ghost counts only visible contributions after draw order; `exposureA`/`exposureB` report each named slot’s whole-drawing background exposure even without a band ([AUTHORING §8.8](../docs/AUTHORING.md#88-where-two-slots-stop-meeting--render---seam)) — a reading, not a gate; the exit status never moves. `--overlaps` adds an `overlaps.json` per set and prints one `OVERLAP "<under>" under "<over>"` line per pair of slots where the one drawn later covers the earlier one and the motion moves it — the frames, and the most pixels covered and the frame of them — with an outline plate per covered frame; the render reports what was drawn, and the draw-order key is the author's remedy — a reading, not a gate. `--extremes` adds `extremes.json` and a native six-panel `extremes.png` (setup, geometry-pair min/max and three premultiplied RGBA diffs with a legend), plus one `EXTREMES` line per set. Per-channel minima/maxima and the farthest original-vertex geometry pair are distinct readings; valid channel extrema survive a geometry/image SKIP, with channel refusals reported separately; still or uncomparable geometry reports a named SKIP without changing render exit status. `--extremes-only` writes the same `extremes.json` and readable `extremes.png` bytes while skipping ordinary frame rasterization/encoding, `contact.png` and `frames.json`; it implies `--extremes` and takes no other diagnostic or sheet output flags. Look at min, max and diff before a person does ([AUTHORING §8.11](../docs/AUTHORING.md#811-min-max-and-diff-before-a-person-looks--render---extremes)). `--angle-range <bone>:<relative-to>` prints how far one bone turns against another over each set: the lowest and highest angle and the frames they fall on — a reading, not a gate. `--viewport <x,y,w,h>` or `--viewport-from <frames.json>` draws over a given box instead of a fitted one, so two builds land on one pixel grid; a slot that reaches past it is named by set and frame, and `frames.json` records it. `--environment <schedule.json>` draws the rig under the wind and gravity its physics answers, in time — the same frames on the core and on spine-core; an inspection of the response the rig states, not a scene. `--sheet [--sheet-format json-hash] [--page-edges pot\|free]` also packs each set's straight-alpha transparent frames into `sheet.png` + `sheet.json` (TexturePacker JSON Hash with a shared world-origin `anchor` and `2`-px clamped edge extrusion, verified through `pixi.js@8.22.0` and `phaser@3.90.0` by `tools/sheet_readers.ts`) and `sheet-manifest.json` (`rigc-sheet/1`), leaving diagnostic `f####.png` and `contact.png` unchanged ([AUTHORING §8.10](../docs/AUTHORING.md#810-sprite-sheets-from-a-render--render---sheet)) — a self-contained sprite object per animation set; when animations play and how a scene is composed remains the consumer's <a id="cmd-render"></a> |
133
+ | `preview --candidate <dir>` | one self-contained `.html` that plays it, headed by the line `validate <dir>` prints for it and the rigc version — a refused candidate is still previewed, and its header says so in the gate's words. Repeat `--candidate` for one page with a pane per candidate, in the order given; a green `build` ends by naming this command for its own `--out` <a id="cmd-preview"></a> |
134
+ | `vote --candidate a --candidate b` | one `.html` that asks a human which; `vote --record <file>` checks the answer into `votes.jsonl` <a id="cmd-vote"></a> |
135
+ | `pose --images <dir> --frame <png>` | reads part placements **out of** a picture <a id="cmd-pose"></a> |
136
+ | `chainfit --candidate <dir> --images <dir> --frame <png>` | reads the parts `pose` refuses, through the candidate's own draw order and hierarchy: masked residuals over **visible** pixels, one hinge per child instead of four degrees of freedom, and the `rotate` key value each answer implies. A bone with two or more anchored descendants is **determined** rather than searched, and the residual that over-determination leaves is reported <a id="cmd-chainfit"></a> |
137
+ | `diff <candidate.json> <reference.json>` | structural comparison of two skeletons, one ratio per measure and deliberately no combined score <a id="cmd-diff"></a> |
138
+ | `bonedist --candidate … --reference … --bones …` | per-frame, per-bone world-transform distance against another skeleton — the ladder's stage 3, run on its own. `--bones <correspondence.json \| identity>` is required rather than defaulted: a candidate is entitled to its own bone names, so the pairing is stated <a id="cmd-bonedist"></a> |
139
+ | `check --candidate <dir> --frames <dir> [--out <dir>]` | the candidate against reference pictures — the only instrument here that can see a *wrong animation*. `--out` also writes, for every frame the table lists, the picture its figures came from: reference, candidate, difference and overlay at native size <a id="cmd-check"></a> |
140
+ | `bench <rung> --candidate <dir>` | one rung of the benchmark ladder <a id="cmd-bench"></a> |
141
+ | `guide <RULE>` / `guide §<n>[.<m>[.<k>]] [--all]` | the guide's row for the rule a failure line names — every `FAIL` line, `vote --record` refusal, `ingest` finding and coded compile error ends with ` (see rigc guide <RULE>)`; only legacy uncoded library errors point at `§5.1` — or a section's heading, own prose and subsection index, read from the installed `docs/AUTHORING.md` when it runs. `--all` includes the subsection bodies; a leaf prints its full text either way. Sizes describe full ledger spans. `--all` with a rule is refused, exit 2; an unknown name is refused with the nearest real ones, exit 1 <a id="cmd-guide"></a> |
142
+ | `skills install [--dir …] [--copy]` | links every agent skill the package ships into `.agents/skills` (or `--dir`) as relative symlinks, or copies them with `--copy` — where Codex, Gemini CLI and Antigravity look. A second run has nothing to do; an entry already there that is not this command's is refused by name and nothing is written <a id="cmd-skills-install"></a> |
143
+ | `review <dir> […]` | the human review of a build, handed to the `rig-review` bin of `@rig-c/review` installed beside the package; rigc does not depend on it and never installs it. `rigc review <dir> [--reference <video>] [--animation <name>] [--compare <dir>]` is `rig-review open <dir> …` with those flags 1:1 and prints the loopback URL to give a person; a first argument that is one of review's own words (`open`, `check`, `feedback`, `control`, `strip`, `compare`) or starts with `-` (`--help`, `--`) is handed over untouched. stdout is review's byte for byte, stderr review's plus the launcher's own refusals, the exit code review's. Refused by name, exit 2, before the bin starts: `REVIEW_OUTPUT_MISSING <path>` (the target is not a directory); `REVIEW_NOT_INSTALLED` with two lines, `bun add -d @rig-c/review` and `bunx @rig-c/review open <dir> …` carrying the flags given — the same on a terminal and off, with nothing installed or written; `REVIEW_BUN_TOO_OLD: Bun <found>, @rig-c/review needs <required>`, the floor read off the installed package's `engines.bun`; and an empty argument (`""`) under a Bun that drops one (Bun 1.2.0, measured by a probe), `rigc review: argument N is empty, and Bun <version>, the Bun running this, drops an empty argument when it starts a process — …`. docs/AUTHORING.md lists every line <a id="cmd-review"></a> |
144
144
 
145
145
  `diff`, `bonedist`, `check` and `bench` measure against something you were given; the
146
146
  first three work on any reference you have, and `bench` is a repository workflow that needs a clone
@@ -381,7 +381,7 @@ What the flags mean:
381
381
  | `--extremes` | `render` only: add `extremes.json` and an `EXTREMES` line per set; a moving set with comparable geometry and readable images also gets a native six-panel `extremes.png`: setup, geometry-pair min/max and three premultiplied RGBA diffs with a legend. Per-channel extrema are independent of that geometry pair; valid channels survive geometry/image SKIPs, and channel refusals are named separately. Still or uncomparable geometry is a named SKIP, with no invented pair or PNG; observation failures do not change a successful render's exit status. Supports `--slot`/`--hide` on both posers, on the whole rig's viewport. Without the flag, no extremes observations are added — **§8.11** |
382
382
  | `--folds` | `render` only: also read which mesh triangles **fold** — turn over against the setup pose — and write a `folds.json` into each frame directory: per mesh, the frames with a folded triangle, the worst frame's count and which triangles, by index, with their world box and setup-pose area. One printed line per mesh that folds; a triangle degenerate or inverted at the setup pose is said once and never counted. A reading, not a gate. Every other file is byte for byte what the render writes without it, `geometry.json` included. Refused with `--slot`/`--hide`, for `--geometry`'s reason — **§8.3** |
383
383
  | `--pass-through` | `render` only: `<cloth>:<leg>[,<cloth>:<leg>…]` — also read where each cloth slot **passes through** its leg slot, and write a `pass-through.json` into each frame directory: the frames where the cloth draws over the leg and its drawn side lies inside the leg's drawn outline by 2 steps or more, each with the depth and the world box. One printed line per pair, and one per frame that passes through. The first name must draw over the second for a frame to be read — the declared order, never both. A reading, not a gate. Every other file is byte for byte what the render writes without it. An unknown slot, a slot that draws on no frame, a malformed list and a pair named twice are refused by name, and so is `--slot`/`--hide` beside it — **§8.5** |
384
- | `--seam` | `render` only: `<slot>:<slot>[,<slot>:<slot>…]` — also read, for each pair, where the two drawings **stop meeting**, and write a `seam.json` into each frame directory: per pair, the frames with a **gap** (background inside the joint, where the two covered together at the setup pose) and the frames with a **ghost** (the overlap band of one showing where, from its partner's point of view, nothing was drawn), each with its cell count, area and world box, and as runs of frames with their times. One printed line per pair; a pair whose drawings share no cell at the setup pose is a **SKIP**. A reading, not a gate: the exit status never moves. Every other file is byte for byte what the render writes without it. An unknown slot, a slot with itself, a slot that draws on no frame, a malformed list and a pair named twice in either order are refused by name, and so is `--slot`/`--hide` beside it — **§8.8** |
384
+ | `--seam` | `render` only: `<slot>:<slot>[,<slot>:<slot>…]` — also read, for each pair, where the two drawings **stop meeting**, and write a `seam.json` into each frame directory: per pair, the frames with a **gap** (background inside the joint, where the two covered together at the setup pose) and the frames with a **ghost** (the visible overlap band of one showing where, from its partner's point of view, nothing was drawn), plus `exposureA`/`exposureB` for each named slot’s positive-alpha drawing on otherwise empty skeleton background, each with its cell count, area and world box, and as runs of frames with their times. One printed line per pair; a pair whose drawings share no cell at the setup pose is a **SKIP**. A reading, not a gate: the exit status never moves. Every other file is byte for byte what the render writes without it. An unknown slot, a slot with itself, a slot that draws on no frame, a malformed list and a pair named twice in either order are refused by name, and so is `--slot`/`--hide` beside it — **§8.8** |
385
385
  | `--overlaps` | `render` only: also read **which slot each frame drew over which**, and write an `overlaps.json` into each frame directory: per frame, every pair of slots where the slot drawn later covers pixels the earlier one drew (drawn at 128 of 255 or more, on the frames' own pixels), with the pixel count and the pixel box. One `OVERLAP` line per pair that covers anything — its frames as runs, how many of the set, and the most pixels and the frame of them — and one line counting the pairs that are layering the motion does not move. Each frame with a moving pair also gets an **outline plate**, `overlaps-f<NNNN>.png`: every slot in a moving pair as its contour in its own colour, dashed where a slot drawn later covers it, named at its top. The render reports what was drawn; who should be in front is the author's, and the draw-order key in the motion spec is the remedy. A reading, not a gate. Every other file is byte for byte what the render writes without it, and `frames.json` gains each set's `overlaps` record. Refused beside `--slot`/`--hide` — **§8.9** |
386
386
  | `--angle-range` | `render` only: `<bone>:<relative-to>[,<bone>:<relative-to>…]` — also read, for each pair, the range of the first bone's world direction against the second's over each set's frames, and print one line per pair per set: the lowest and the highest angle in degrees, the span, and the frame each was first reached on. A reading, not a gate: nothing is written, and every file is the bytes the render writes without it. An unknown bone (with the bones there are), a bone against itself, a malformed list and a pair named twice are refused by name, and so is a bone with no direction on a frame — **§8.7** |
387
387
  | `--sheet` | `render` only: also pack each rendered animation set as a straight-alpha transparent sprite sheet (`sheet.png` + `sheet.json`, plus `sheet2.png` / `sheet2.json` on multi-page spill, and `sheet-manifest.json` (`rigc-sheet/1`)) inside its set directory, and record `sheet` on each set in `frames.json`. Diagnostic `f####.png` and `contact.png` stay byte for byte what the render writes without it — **§8.10** |
@@ -9185,15 +9185,15 @@ inside it, and a second copy of the leg on the same bone):
9185
9185
  ```bash
9186
9186
  bun cli.ts render --candidate path/to/spine --animation swing --seam skirt:leg,leg_front:leg [--skin …] [--fps 12]
9187
9187
  # rigc render
9188
- # .. swing 25 frame(s), 2.000s + contact.png + seam.json (1 of 2 pair(s) open) -> …/swing
9189
- # .. seam "swing": seam "skirt" | "leg" opens on 23 of 25 frame(s) read [1-23] at 12 fps, max 1606 px — gap on 13 frame(s) [6-18], worst frame 12 (t=1.0000s): 616 px (616.00 units² at 1.0000 units a px) in world box x [-7.00, 19.00] y [105.99, 196.00] (26.00 × 90.00); ghost on 23 frame(s) [1-23], worst frame 12 (t=1.0000s): 1606 px (1606.00 units² at 1.0000 units a px) in world box x [-27.00, 41.00] y [104.99, 198.00] (68.00 × 93.00)
9190
- # .. seam "swing": seam "leg_front" | "leg" stays closed on all 25 frame(s) read at 12 fps — no gap, no ghost
9188
+ # One seam line per pair, including no gap/no ghost or SKIP, followed by
9189
+ # exposure "<slot>" for each named member: frames, worst cell count and box.
9191
9190
  ```
9192
9191
 
9193
9192
  The skirt swinging off the leg opens background between the leg's right edge and
9194
9193
  the skirt's swung side (the gap, widest at the top of the swing), and uncovers the
9195
- top of the leg the skirt hid at rest (the ghost). The two copies of the leg move
9196
- on one bone and never open. A pair with nothing to read says so —
9194
+ top of a visible leg drawing the skirt hid at rest (the ghost). A leg drawing
9195
+ hidden by its opaque front copy contributes no ghost; reading `skirt:leg_front`
9196
+ measures that front copy instead. The two copies move on one bone and never open. A pair with nothing to read says so —
9197
9197
  `seam "a" | "b": SKIP — the two drawings share no cell at the setup pose on any of
9198
9198
  the N frame(s) both draw, so there is no band to read` — and a closed pair says
9199
9199
  `stays closed on all N frame(s) read`; the two are never the same line.
@@ -9225,17 +9225,31 @@ table's vertices, and the atlas alpha:
9225
9225
  were painted to overlap. It has **no width of its own**: it is the overlap the
9226
9226
  art states, dilated by nothing. A frame whose band is empty is not read; a pair
9227
9227
  whose band is empty on every frame both draw is a SKIP.
9228
- - **gap** — a cell inside the convex hull of the band's cell centres, carried to
9229
- the frame by both pieces (the region the joint occupies now, as both place it),
9230
- that neither piece draws at the frame, and that at least one piece, carrying it
9231
- back, finds drawn by either drawing at the setup pose: the two no longer cover
9232
- what they covered together. The hull is the joint's own extent, which is what
9233
- keeps a piece's far end swinging away from reading as a tear; the rest test is
9234
- what keeps a band that is concave at the setup pose from reading its own notch.
9228
+ - **gap** — a cell inside the outer convex hull of the band's centres carried
9229
+ through both pieces, **and inside a local sweep of the same setup band cell**,
9230
+ that neither piece draws now, and that at least one inverse attachment map
9231
+ finds drawn by either setup drawing. A local sweep is the convex hull of one
9232
+ overlap cell's four corners carried through each attachment's triangle maps
9233
+ (eight points); each corner uses its own setup triangle, extending the nearest
9234
+ affine map outside a mesh as above. Take the union of these local sweeps,
9235
+ never a hull joining unrelated overlap cells. The outer hull and inverse OR
9236
+ test remain required. This derives joint support from the rig's art, texel
9237
+ grid and deformation, without a pivot distance or a joint-disc limit. A
9238
+ connected branch with two separated overlap rows can have plain background
9239
+ between those rows: the outer hull alone fills it, while local correspondence
9240
+ excludes it. A genuine opening between opposed overlap cells remains a gap.
9241
+ The rectangle controls derive eight real tear cells; a notched branch derives
9242
+ sixteen adjacent background cells excluded by this rule, including when both
9243
+ kinds occur in the same frame.
9235
9244
  - **ghost** — a cell one piece draws at the frame and the other does not, which
9236
9245
  the drawing piece, carrying it back, lands in the band, and which the other,
9237
- carrying it back, finds neither drawing at at the setup pose: the overlap swung
9238
- clear of its partner.
9246
+ carrying it back, finds neither drawing at the setup pose: the overlap swung
9247
+ clear of its partner. It must also retain nonzero **source-over contribution**
9248
+ from that named slot after every drawing in frame draw order. Later alpha 255
9249
+ hides earlier contributions; partial alpha leaves a contribution, and earlier
9250
+ art cannot hide later art. Every fragment of every slot participates, with
9251
+ renderer alpha, tint, UV windows and clipping. This tests contribution, not a
9252
+ colour difference or an artistic judgment.
9239
9253
  - **ghostA**, **ghostB** — that same ghost partitioned by the piece drawing the
9240
9254
  cell: `a` only or `b` only, respectively, in the flag's named pair order. On
9241
9255
  each frame `ghost.px = ghostA.px + ghostB.px` (an absent hit contributes 0),
@@ -9257,9 +9271,22 @@ Each partition's box lies in the union's. A gap in the pair can be covered by a
9257
9271
  third drawing and need not be a visible opening. Reversing the pair preserves
9258
9272
  both gap coverage readings.
9259
9273
 
9260
- Draw order does not enter: a cell is covered or bare whichever piece is in front.
9261
- An owner says which piece drew a ghost cell; it does not say which piece is over
9262
- or under the other, or whether that exposure should fail a consumer's policy.
9274
+ - **exposureA**, **exposureB** — cells of positive rendered alpha from each
9275
+ named slot where removing **all fragments of that slot** leaves alpha zero
9276
+ from every other skeleton slot. The entire drawing enters, including art
9277
+ outside the band, at setup as well as in motion. A positive-alpha drawing of
9278
+ another slot excludes exposure regardless of its draw order or opacity: this
9279
+ asks what the named slot adds on background, separately from ghost visibility.
9280
+ No presentation background enters. Hits, spans and worst frames use the same
9281
+ shape and units as gap; the grid uses the coarser pair texel when both draw,
9282
+ or the present member's texel when its partner is absent. Exposure is measured
9283
+ even when there is no band, while the seam still says SKIP. It is **not** a
9284
+ change-from-setup measure, does not enter `opens` or `maxPx`, and can be nonzero
9285
+ on a clean rig. Pair reversal exchanges the complete exposure readings.
9286
+
9287
+ Draw order enters ghost visibility. Gap coverage and background exposure depend
9288
+ on zero versus positive alpha and do not depend on order. Named owners describe
9289
+ contributions; none of these fields decides whether a consumer should fail.
9263
9290
 
9264
9291
  **Outside the overlap hull is outside the gap reading.** Both `gap` and its
9265
9292
  `gapOpen` partition are judged only inside the carried setup-overlap hull. Art
@@ -9278,13 +9305,14 @@ cells on the joint rim. The [measurement receipt](https://github.com/rig-c/core/
9278
9305
  pins the inputs, commands and frame digest; these are seam cells, not display
9279
9306
  pixels. See [#1575](https://github.com/rig-c/core/issues/1575) for the reading.
9280
9307
 
9281
- **Its reach, stated.** A piece folded right back over its partner carries the band
9282
- across the region it vacated, so a hull that wide reads the vacated cells its
9283
- partner's view still finds drawn. And no size is too small to print: on
9284
- `gallery/walk`, built as its README says, `--seam thigh_f:shin_f,arm_f:hand_f`
9285
- reads gaps of 1–2 px on 5 of the 12 frames at the knee and of 1 px on 2 at the
9286
- wrist — the wrist's, drawn at 20 pixels a unit, is the V where the two outlines
9287
- meet at an angle, one cell deep. Whether that is a tear is yours.
9308
+ **Its reach, stated.** Local material sweeps replace the old global hull's
9309
+ bridges across unrelated overlap cells. They still describe a sampled geometric
9310
+ support region, not an oracle for intended silhouette or an anatomical joint
9311
+ inferred from bone names. Folding or severe deformation can carry a local cell
9312
+ across vacated background; a result requires inspection. No minimum count, pivot
9313
+ radius or acceptance threshold is added. The controls include ordinary tears,
9314
+ adjacent background, hidden and partly visible ghosts, absent partners and
9315
+ whole-drawing exposures; they do not certify every possible art shape.
9288
9316
 
9289
9317
  **The file.** `seam.json`, `rigc-seam/1`, beside each set's frames — the contract
9290
9318
  a consumer reads it by. Every unit is in this table; the file's own `reading`
@@ -9296,7 +9324,7 @@ block carries the definitions above in words.
9296
9324
  | `animation` | string or `null` | — | the set's animation; `null` for a skeleton with none (its one frame is the setup pose) |
9297
9325
  | `skin` | string | — | the skin asked for; **absent** when none was |
9298
9326
  | `fps` | number | frames per second | the rate the set was sampled at |
9299
- | `reading` | object | — | `read`, `cell`, `drawn`, `band`, `carried`, `gap`, `ghost`, `box`, `status`, `ghostA`, `ghostB`, `gapOpen`, `gapCovered`: the definitions, in words |
9327
+ | `reading` | object | — | `read`, `cell`, `drawn`, `band`, `carried`, `gap`, `ghost`, `box`, `status`, `ghostA`, `ghostB`, `gapOpen`, `gapCovered`, `exposureA`, `exposureB`: the definitions, in words |
9300
9328
  | `pairs[]` | array | — | one per pair, in the flag's order |
9301
9329
  | `pairs[].a`, `pairs[].b` | string | — | the two slots, as the flag names them |
9302
9330
  | `pairs[].drawn` | integer | frames | frames on which both slots draw |
@@ -9307,6 +9335,7 @@ block carries the definitions above in words.
9307
9335
  | `pairs[].gap`, `pairs[].ghost` | object | — | one per kind, each `{ frames, spans, worst }` |
9308
9336
  | `pairs[].ghostA`, `pairs[].ghostB` | object | — | the same `{ frames, spans, worst }` shape for ghost cells drawn by `a` only and `b` only; an empty owner has `frames: []`, `spans: []`, `worst: null` |
9309
9337
  | `pairs[].gapOpen`, `pairs[].gapCovered` | object | — | the same `{ frames, spans, worst }` shape for gap cells with zero or positive rendered skeleton-composite alpha, respectively; an empty kind has `frames: []`, `spans: []`, `worst: null` |
9338
+ | `pairs[].exposureA`, `pairs[].exposureB` | object | — | `{ frames, spans, worst }` for the whole named slot’s positive-alpha cells on otherwise zero-alpha skeleton background; measured independently of the band, including SKIP pairs; not part of `opens` or `maxPx` |
9310
9339
  | `<kind>.frames[]` | hit[] | — | only the frames with a cell of that kind, in frame order |
9311
9340
  | `<kind>.spans[]` | array | — | `<kind>.frames` as runs of consecutive indices: `{ frames: [first, last], times: [first, last], maxPx }` — frame indices, seconds, px |
9312
9341
  | `<kind>.worst` | hit or `null` | — | the frame with the most cells, the earliest on a tie; `null` when none |
@@ -9323,15 +9352,15 @@ span.times, maxPx: span.maxPx, kind }`, and one pair over one animation is
9323
9352
  `opens.length` frame(s) `[opens]`, max `maxPx` px — with `status: "skip"` the case
9324
9353
  to say SKIP for.
9325
9354
 
9326
- The owner keys and their two legend entries are additive in `rigc-seam/1`.
9327
- Removing just those four keys leaves the previous file's bytes after the same
9328
- two-space JSON serialization and newline. The union, its spans and worst, and
9329
- the printed summary retain their existing meanings; an owner may have different
9330
- spans or a different worst frame from the union.
9331
- Likewise `gapOpen`, `gapCovered` and their two legend entries are additive:
9332
- removing only those entries leaves the previous file's bytes after the same
9333
- serialization, including `gap`, `ghostA` and `ghostB`. The coverage partition may
9334
- have different spans and worst frames from the union; it changes no seam policy.
9355
+ `rigc-seam/1` retains its hit and summary shapes and adds `exposureA/B` and
9356
+ their legend entries. The reader semantics intentionally change: ghosts hidden
9357
+ by later opaque art disappear, and global-hull-only gap cells disappear. This
9358
+ can reduce `ghost`, its owners, `gap`, its partitions, `opens` and `maxPx`;
9359
+ consumers using those readings as their own policy must account for the change.
9360
+ The seam remains a reading: no compiler assertion or CLI exit verdict changes.
9361
+ Clean stationary/shared-motion pair fields remain unchanged apart from exposure;
9362
+ the `reading` legend states the revised definitions. Historical measurements
9363
+ above pin their old reader and must not be treated as current measurements.
9335
9364
 
9336
9365
  **Refused, nothing written:** a slot the skeleton does not have (with the slots it
9337
9366
  does), an entry without exactly one `:`, an empty entry or name, a slot paired with
@@ -12026,7 +12055,7 @@ ambiguous one ends with a sentence saying which reading its figures support:
12026
12055
 
12027
12056
  - **below the floor** — a 40x28 part of one flat colour, cut from a plain block it fits anywhere inside:
12028
12057
 
12029
- > part is 40x28 px (span 40 frame px, opaque 1) with texture 0, detail 0; 17 candidate(s), best 0.0000, next 0.0000, spread 0.0000 — below the measured floor (AUTHORING §11.5: detail 0.5, measured from 24 px up): pose placed 7% of the measured parts this plain, so this part is too plain for pose to tell its placements apart; that says nothing about whether the cut is right
12058
+ > part is 40x28 px (span 40 frame px, opaque 1) with texture 0, detail 0; 34 candidate(s), best 0.0000, next 0.0000, spread 0.0000 — below the measured floor (AUTHORING §11.5: detail 0.5, measured from 24 px up): pose placed 7% of the measured parts this plain, so this part is too plain for pose to tell its placements apart; that says nothing about whether the cut is right
12030
12059
 
12031
12060
  - **above the floor** — a 200x150 black-and-white checker the frame does not hold:
12032
12061
 
@@ -12034,7 +12063,7 @@ ambiguous one ends with a sentence saying which reading its figures support:
12034
12063
 
12035
12064
  - **above the floor, one size in two places** — two copies of an 18x34 checker 0.6 of its span and 6° apart, the shape a left/right pair cut from one drawing reads as:
12036
12065
 
12037
- > part is 18x34 px (span 36.2 frame px, opaque 1) with texture 0.0221, detail 0.752; 14 candidate(s), best 0.0160, next 0.0162, spread 0.0002 — above the measured floor (AUTHORING §11.5: detail 0.5, measured from 24 px up), so size and texture do not explain this; every placement in the margin is at the best's scale (within 0.30%, bar 2%) and in another place (the nearest 0.62 span(s) away, bar 0.45), which is the part — or a near-copy, such as a left/right pair cut from one drawing — drawn more than once: read the placements as an unordered set and order it across frames (MOTION.md §2.2 🔀); a --rotation window that excludes one place chooses the answer rather than measuring it
12066
+ > part is 18x34 px (span 36.2 frame px, opaque 1) with texture 0.0221, detail 0.752; 40 candidate(s), best 0.0160, next 0.0162, spread 0.0002 — above the measured floor (AUTHORING §11.5: detail 0.5, measured from 24 px up), so size and texture do not explain this; every placement in the margin is at the best's scale (within 0.30%, bar 2%) and in another place (the nearest 0.62 span(s) away, bar 0.45), which is the part — or a near-copy, such as a left/right pair cut from one drawing — drawn more than once: read the placements as an unordered set and order it across frames (MOTION.md §2.2 🔀); a --rotation window that excludes one place chooses the answer rather than measuring it
12038
12067
 
12039
12068
  Read the figures, not only the verdict. *Below* says plainness alone explains the
12040
12069
  failure: the cut may be right, and `pose` cannot tell. *Above* says it does not,
@@ -12124,6 +12153,22 @@ two anchors — and only 3 had it ranked under the old cutoff of three. The fine
12124
12153
  took the grid from 185 to 227 trials found and those misses from 65 to 14, for about
12125
12154
  10–14% more CPU over the grid (615 s of user time before it, 678 s and 704 s on two runs after).
12126
12155
 
12156
+ For repeated parts, an independent coverage lane also keeps up to five **new**
12157
+ places per rung. It uses the same fields and stride, but bounds suppression by
12158
+ half the material box's **short** side at that rung's scale: a long thin part's
12159
+ half-length cannot erase a different nearby position. Positions already covered
12160
+ by the original lane do not consume that extra budget. The coverage lane keeps
12161
+ its coarse pool through the first finer, rotation-regridded level before comparing
12162
+ scales together; only then does it narrow to the usual refinement budget. Both
12163
+ completed pools use the same full-resolution objective, dedupe and ambiguity
12164
+ margin. More coverage can expose an ambiguity; it does not prove that every
12165
+ visible copy, or every subpixel placement, will be recovered. Public repeated-part
12166
+ controls hold the final reported position, rotation and scale, not merely a coarse
12167
+ seed. The programmatic `coarsePolicy: 'legacy-only'` seam reproduces the original
12168
+ search for those negative controls; the CLI uses short-support coverage. An unknown
12169
+ runtime `coarsePolicy` is refused by name with the supplied value and both accepted
12170
+ policies; omit it to use the current search.
12171
+
12127
12172
  The refinement then polishes each place down the pyramid, and at full resolution a
12128
12173
  polish that has stopped tries its scale **up by half a coarse rung** (2^(1/6)), re-fits
12129
12174
  the position there, and restarts if that scores better; fifteen places survive each
package/docs/INGEST.md CHANGED
@@ -1405,11 +1405,11 @@ rigc pose
1405
1405
  .. ground rgb(232, 232, 232) over 100% of the border ring
1406
1406
  .. parts …/packed-only (1 png)
1407
1407
  .. search scale 0.5–2 in 7 step(s) · rotation -180°–180° in 24 step(s) of 15° · refuse above residual 0.25
1408
- PLACE 3-timing-and-spacing.png x= 324.3 y= 188.3 rot= -91.6° scale=0.630 residual=0.2085 unexplained= 30%
1408
+ PLACE 3-timing-and-spacing.png x= 324.6 y= 189.8 rot= -91.1° scale=0.625 residual=0.2080 unexplained= 29%
1409
1409
  found on a 14x7 anchor grid, step 4 at 16x reduction
1410
1410
  ```
1411
1411
 
1412
- ⚠️ **`residual=0.2085` is *under* the default 0.25 refusal bar**, so nothing refused
1412
+ ⚠️ **`residual=0.2080` is *under* the default 0.25 refusal bar**, so nothing refused
1413
1413
  it, and `PLACE` rather than `AMBIG` means nothing flagged it either. With the same frame
1414
1414
  and the two real loose parts, the answer is what it should be:
1415
1415
 
@@ -1424,7 +1424,7 @@ rigc pose
1424
1424
  .. ground rgb(232, 232, 232) over 100% of the border ring
1425
1425
  .. parts …/examples/3-timing-and-spacing/images (2 png)
1426
1426
  .. search scale 0.3–0.6 in 4 step(s) · rotation -180°–180° in 24 step(s) of 15° · refuse above residual 0.25
1427
- PLACE pendulum.png x= 319.3 y= 214.6 rot= -88.9° scale=0.411 residual=0.0428 unexplained= 7%
1427
+ PLACE pendulum.png x= 319.4 y= 214.4 rot= -88.9° scale=0.412 residual=0.0421 unexplained= 7%
1428
1428
  found on a 28x13 anchor grid, step 2 at 16x reduction
1429
1429
  PLACE square.png x= 437.1 y= 343.1 rot= 0.0° scale=0.410 residual=0.0334 unexplained= 4%
1430
1430
  found on a 113x51 anchor grid, step 2 at 4x reduction