rig-c 0.0.0-stage → 2.20.4
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/.claude-plugin/marketplace.json +19 -0
- package/.claude-plugin/plugin.json +13 -0
- package/LICENSE +30 -0
- package/NOTICE.md +145 -0
- package/README.md +817 -3
- package/bin/rigc.cjs +83 -0
- package/cli.ts +61 -0
- package/cli_core.ts +46 -0
- package/docs/AUTHORING.md +9923 -0
- package/docs/FACE.md +1948 -0
- package/docs/INGEST.md +1488 -0
- package/docs/MOTION.md +1241 -0
- package/docs/PROMPTING.md +109 -0
- package/docs/RIGGING.md +1441 -0
- package/docs/SPEC_COVERAGE.md +357 -0
- package/package.json +108 -4
- package/skills/rigc/SKILL.md +133 -0
- package/skills/rigc-face/SKILL.md +60 -0
- package/skills/rigc-ingest/SKILL.md +78 -0
- package/skills/rigc-motion/SKILL.md +51 -0
- package/skills/rigc-rigging/SKILL.md +49 -0
- package/src/areaband.ts +159 -0
- package/src/assertions/bodies/a01.ts +23 -0
- package/src/assertions/bodies/a02.ts +21 -0
- package/src/assertions/bodies/a03.ts +27 -0
- package/src/assertions/bodies/a04.ts +40 -0
- package/src/assertions/bodies/a05.ts +56 -0
- package/src/assertions/bodies/a06.ts +245 -0
- package/src/assertions/bodies/a07.ts +68 -0
- package/src/assertions/bodies/a08.ts +76 -0
- package/src/assertions/bodies/a09.ts +82 -0
- package/src/assertions/bodies/a10.ts +116 -0
- package/src/assertions/bodies/a11.ts +15 -0
- package/src/assertions/bodies/a12.ts +30 -0
- package/src/assertions/bodies/a13.ts +51 -0
- package/src/assertions/bodies/a14.ts +35 -0
- package/src/assertions/bodies/a15.ts +97 -0
- package/src/assertions/bodies/a16.ts +24 -0
- package/src/assertions/bodies/a17.ts +26 -0
- package/src/assertions/bodies/a18.ts +62 -0
- package/src/assertions/bodies/a19.ts +404 -0
- package/src/assertions/bodies/a20.ts +122 -0
- package/src/assertions/bodies/a21.ts +190 -0
- package/src/assertions/bodies/a22.ts +39 -0
- package/src/assertions/bodies/a23.ts +305 -0
- package/src/assertions/bodies/a24.ts +68 -0
- package/src/assertions/bodies/a25.ts +39 -0
- package/src/assertions/bodies/a26.ts +61 -0
- package/src/assertions/bodies/a27.ts +33 -0
- package/src/assertions/bodies/a28.ts +70 -0
- package/src/assertions/bodies/a29.ts +34 -0
- package/src/assertions/bodies/a30.ts +50 -0
- package/src/assertions/bodies/a31.ts +61 -0
- package/src/assertions/bodies/a32.ts +44 -0
- package/src/assertions/bodies/a33.ts +110 -0
- package/src/assertions/bodies/a34.ts +133 -0
- package/src/assertions/bodies/a35.ts +160 -0
- package/src/assertions/bodies/a36.ts +81 -0
- package/src/assertions/bodies/a37.ts +77 -0
- package/src/assertions/bodies/a38.ts +73 -0
- package/src/assertions/bodies/a39.ts +303 -0
- package/src/assertions/bodies/a40.ts +128 -0
- package/src/assertions/bodies/a42.ts +97 -0
- package/src/assertions/bodies/a43.ts +181 -0
- package/src/assertions/bodies/a44.ts +23 -0
- package/src/assertions/bodies/a45.ts +172 -0
- package/src/assertions/bodies/a46.ts +224 -0
- package/src/assertions/bodies/a47.ts +126 -0
- package/src/assertions/bodies/a48.ts +83 -0
- package/src/assertions/bodies/a49.ts +81 -0
- package/src/assertions/bodies/a50.ts +97 -0
- package/src/assertions/constraint_words.ts +169 -0
- package/src/assertions/emitted/index.ts +148 -0
- package/src/assertions/facts/animated_bones.ts +30 -0
- package/src/assertions/facts/animation_durations.ts +37 -0
- package/src/assertions/facts/atlas_pages.ts +19 -0
- package/src/assertions/facts/atlas_regions.ts +52 -0
- package/src/assertions/facts/bone_timelines.ts +37 -0
- package/src/assertions/facts/constraint_targets.ts +56 -0
- package/src/assertions/facts/constraints.ts +155 -0
- package/src/assertions/facts/deform_survey.ts +27 -0
- package/src/assertions/facts/event_keys.ts +55 -0
- package/src/assertions/facts/linked_meshes.ts +38 -0
- package/src/assertions/facts/mesh_attachments.ts +100 -0
- package/src/assertions/facts/region_joins.ts +34 -0
- package/src/assertions/facts/sequences.ts +85 -0
- package/src/assertions/facts/skeleton_roster.ts +45 -0
- package/src/assertions/facts/skin_entries.ts +37 -0
- package/src/assertions/facts/skin_members.ts +53 -0
- package/src/assertions/facts/slider_composition.ts +78 -0
- package/src/assertions/facts/slot_colour.ts +43 -0
- package/src/assertions/facts/stage.ts +27 -0
- package/src/assertions/facts/stage_box.ts +65 -0
- package/src/assertions/facts/stepped_poses.ts +74 -0
- package/src/assertions/facts/two_colour.ts +52 -0
- package/src/assertions/facts/vertex_polygons.ts +53 -0
- package/src/assertions/footprints.ts +367 -0
- package/src/assertions/harness.ts +109 -0
- package/src/assertions/inward_advance.ts +58 -0
- package/src/assertions/kinds.ts +105 -0
- package/src/assertions/mesh_kinds.ts +56 -0
- package/src/assertions/model/animated_bones.ts +38 -0
- package/src/assertions/model/animation_durations.ts +57 -0
- package/src/assertions/model/atlas_pages.ts +15 -0
- package/src/assertions/model/atlas_regions.ts +76 -0
- package/src/assertions/model/bone_timelines.ts +58 -0
- package/src/assertions/model/constraint_targets.ts +82 -0
- package/src/assertions/model/constraints.ts +233 -0
- package/src/assertions/model/declared.ts +125 -0
- package/src/assertions/model/deform_survey.ts +24 -0
- package/src/assertions/model/event_keys.ts +45 -0
- package/src/assertions/model/given.ts +45 -0
- package/src/assertions/model/index.ts +398 -0
- package/src/assertions/model/linked_meshes.ts +24 -0
- package/src/assertions/model/mesh_attachments.ts +119 -0
- package/src/assertions/model/parse.ts +146 -0
- package/src/assertions/model/region_joins.ts +67 -0
- package/src/assertions/model/runtime_timelines.ts +78 -0
- package/src/assertions/model/sequences.ts +157 -0
- package/src/assertions/model/skeleton_roster.ts +23 -0
- package/src/assertions/model/skin_entries.ts +69 -0
- package/src/assertions/model/skin_members.ts +64 -0
- package/src/assertions/model/slider_composition.ts +193 -0
- package/src/assertions/model/slot_colour.ts +81 -0
- package/src/assertions/model/stage.ts +28 -0
- package/src/assertions/model/stage_box.ts +51 -0
- package/src/assertions/model/stepped_poses.ts +105 -0
- package/src/assertions/model/two_colour.ts +61 -0
- package/src/assertions/model/vertex_polygons.ts +72 -0
- package/src/assertions/reasons.ts +129 -0
- package/src/assertions/region_lookups.ts +61 -0
- package/src/assertions/report.ts +189 -0
- package/src/assertions/values.ts +39 -0
- package/src/atlas.ts +2870 -0
- package/src/ballot.ts +866 -0
- package/src/bonedist.ts +643 -0
- package/src/chainfit.ts +2752 -0
- package/src/chains.ts +170 -0
- package/src/check.ts +4303 -0
- package/src/checkpics.ts +295 -0
- package/src/cli/core_commands.ts +1627 -0
- package/src/cli/repack.ts +414 -0
- package/src/cli/shared.ts +2776 -0
- package/src/cli/spine_commands.ts +820 -0
- package/src/compile.ts +9414 -0
- package/src/core/additive.ts +458 -0
- package/src/core/animation.ts +1050 -0
- package/src/core/clipping.ts +696 -0
- package/src/core/constraints.ts +1876 -0
- package/src/core/constraints_path.ts +964 -0
- package/src/core/constraints_physics.ts +881 -0
- package/src/core/constraints_slider.ts +635 -0
- package/src/core/deform.ts +613 -0
- package/src/core/draw_order.ts +125 -0
- package/src/core/events.ts +135 -0
- package/src/core/hooks.ts +249 -0
- package/src/core/index.ts +1400 -0
- package/src/core/raw.ts +739 -0
- package/src/core/skins.ts +129 -0
- package/src/core/uvs.ts +469 -0
- package/src/core/vertices.ts +490 -0
- package/src/core/walk.ts +197 -0
- package/src/core/world.ts +289 -0
- package/src/correspondence.ts +15 -0
- package/src/deformbuild.ts +60 -0
- package/src/deformgen.ts +630 -0
- package/src/deformmeasure.ts +732 -0
- package/src/deformreport.ts +373 -0
- package/src/deformstructure.ts +386 -0
- package/src/deformsurvey.ts +2162 -0
- package/src/depth.ts +784 -0
- package/src/diff.ts +2252 -0
- package/src/emit.ts +134 -0
- package/src/emit_spine.ts +854 -0
- package/src/errors.ts +53 -0
- package/src/framing.ts +819 -0
- package/src/generation.ts +139 -0
- package/src/ingest.ts +2293 -0
- package/src/json-position.ts +253 -0
- package/src/keyorder.ts +587 -0
- package/src/keys.ts +486 -0
- package/src/ladder.ts +121 -0
- package/src/mesh.ts +2382 -0
- package/src/meshcompare.ts +1188 -0
- package/src/meshquality.ts +2042 -0
- package/src/meshrasters.ts +944 -0
- package/src/meshreduce.ts +1425 -0
- package/src/model.ts +1245 -0
- package/src/motion.ts +809 -0
- package/src/nonfinite.ts +54 -0
- package/src/package_meta.ts +48 -0
- package/src/png.ts +297 -0
- package/src/pose.ts +2324 -0
- package/src/preview.ts +434 -0
- package/src/region_joins.ts +54 -0
- package/src/render.ts +1013 -0
- package/src/render_core.ts +871 -0
- package/src/render_shared.ts +2958 -0
- package/src/repack.ts +495 -0
- package/src/rig.ts +2941 -0
- package/src/slots.ts +892 -0
- package/src/spine_side.ts +138 -0
- package/src/timelines.ts +837 -0
- package/src/trackgen.ts +364 -0
- package/src/transform.ts +310 -0
- package/src/types.ts +1797 -0
- package/src/validate.ts +3875 -0
- package/tools/contact.ts +126 -0
- package/tools/editor_roundtrip.ts +1641 -0
- package/tools/font5x7.ts +101 -0
- package/tools/measure_contact_depth.ts +105 -0
- package/tools/plate.ts +508 -0
- package/tools/png_probe.mjs +72 -0
package/README.md
CHANGED
|
@@ -1,4 +1,818 @@
|
|
|
1
|
-
|
|
1
|
+
<p align="center">
|
|
2
|
+
<img src="https://raw.githubusercontent.com/firejune/rigc/main/assets/banner.svg" alt="rigc — AI-authored Spine 2D rigging and animation, verified before it is written" width="100%" />
|
|
3
|
+
</p>
|
|
2
4
|
|
|
3
|
-
|
|
4
|
-
|
|
5
|
+
<p align="center">
|
|
6
|
+
<a href="https://www.npmjs.com/package/spine-rigc"><img src="https://img.shields.io/npm/v/spine-rigc.svg?style=flat-square&color=FF6B4A" alt="npm version" /></a>
|
|
7
|
+
<a href="https://www.npmjs.com/package/spine-rigc"><img src="https://img.shields.io/npm/dm/spine-rigc.svg?style=flat-square&color=A855F7" alt="npm downloads" /></a>
|
|
8
|
+
<a href="LICENSE"><img src="https://img.shields.io/badge/license-MIT-38BDF8.svg?style=flat-square" alt="license" /></a>
|
|
9
|
+
</p>
|
|
10
|
+
|
|
11
|
+
**AI-authored Spine 2D rigging and animation, verified before it is written.** rigc is
|
|
12
|
+
a rig compiler for Spine: a rig spec and a motion spec in, Spine 4.3 skeleton data out,
|
|
13
|
+
gated by a list of named assertions before a byte is written — rigc's own validator in
|
|
14
|
+
the published package, held to a `spine-core` round trip's verdicts in this
|
|
15
|
+
repository's CI. Built so AI agents can author rigs and check their own work; it ships
|
|
16
|
+
as an agent skill.
|
|
17
|
+
|
|
18
|
+
## What you get
|
|
19
|
+
|
|
20
|
+
<p align="center">
|
|
21
|
+
<img src="https://raw.githubusercontent.com/firejune/rigc/main/assets/rigc-demo.gif" alt="Loose part PNGs assembling themselves into a character that breathes, blinks and waves" width="600" />
|
|
22
|
+
</p>
|
|
23
|
+
|
|
24
|
+
<p align="center"><em>Fourteen part PNGs drawn from scratch for this repo, one rig spec, one motion spec — the assembly,
|
|
25
|
+
the breathing and the wave are all rigc-compiled Spine animations, rendered with
|
|
26
|
+
<code>rigc render</code>.</em></p>
|
|
27
|
+
|
|
28
|
+
Loose part PNGs and two small JSON files in; **Spine 4.3 skeleton data out** — a
|
|
29
|
+
`skeleton.json` and a `skeleton.atlas` that load in any Spine runtime and **import
|
|
30
|
+
into the Spine editor**. Nothing is written unless a list of named assertions comes
|
|
31
|
+
back green — assertions this repository holds to the verdicts of a round-trip through
|
|
32
|
+
Spine's own parser, all but the parse itself, which runs only where the runtime is
|
|
33
|
+
installed.
|
|
34
|
+
|
|
35
|
+
| You have | You run | You get |
|
|
36
|
+
| --- | --- | --- |
|
|
37
|
+
| part PNGs, a rig spec and a motion spec | `rigc build` | `skeleton.json` + `skeleton.atlas` — or a failure named by rule, and **nothing on disk** |
|
|
38
|
+
| the same, and one texture instead of many | `rigc build --pack` | the parts arranged onto shared atlas pages, written beside the skeleton — losslessly, so the picture is the picture |
|
|
39
|
+
| a pack somebody already made | `rigc build --atlas-in` | the same skeleton, with every part resolved to a region of that atlas — or a named refusal, never a part that silently does not draw |
|
|
40
|
+
| a compiled rig | `rigc render` | every animation as PNG frames, plus one labelled contact sheet of the whole shot |
|
|
41
|
+
| a compiled rig | `rigc preview` | one self-contained `.html` that plays it in Spine's own web player |
|
|
42
|
+
| two to four compiled rigs | `rigc vote` | one ballot page a human picks from, and the answer checked into a ledger |
|
|
43
|
+
| 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)) |
|
|
44
|
+
| 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 |
|
|
45
|
+
|
|
46
|
+
Everything in that table needs Bun and this package — `preview` and `vote` also
|
|
47
|
+
`@esotericsoftware/spine-core` installed beside it (see below) — and no clone, no
|
|
48
|
+
reference art, no art pipeline, no server.
|
|
49
|
+
|
|
50
|
+
## What rigc is, and what it is not
|
|
51
|
+
|
|
52
|
+
rigc emits **Spine's own skeleton data format**. That is the whole positioning, and
|
|
53
|
+
it cuts both ways:
|
|
54
|
+
|
|
55
|
+
- The output loads in any Spine runtime, and it **imports into the Spine editor**.
|
|
56
|
+
A compiled rig is a starting point on a timeline, not a finished shot — **an AI
|
|
57
|
+
drafts, a human refines in the editor**. That hand-off is what emitting somebody
|
|
58
|
+
else's format buys, and `tools/editor_roundtrip.ts` measures that it survives
|
|
59
|
+
the trip in both directions.
|
|
60
|
+
- The published package **links no Spine runtime**. `@esotericsoftware/spine-core`
|
|
61
|
+
is a development dependency: this repository uses it to hold rigc's own validator
|
|
62
|
+
to the round trip's verdicts — in CI, on the public example recipes and every
|
|
63
|
+
selftest call, and on a private corpus of production rigs measured before each
|
|
64
|
+
release. Of the 50 assertions, 49 run on both; `A00_ROUNDTRIP_PARSE`, the official
|
|
65
|
+
parser's own parse, runs only where the runtime is installed and is reported as a
|
|
66
|
+
SKIP elsewhere. There, in the parse's place, the report also carries the model
|
|
67
|
+
side's own two parse rules, `A00_MODEL_READ` and `A00_MODEL_REGIONS_ON_PAGES`,
|
|
68
|
+
which no round-trip rule matches and the registry does not hold — so its summary
|
|
69
|
+
totals 52 on the core entry, and its last line counts 43 rules on the model side
|
|
70
|
+
([AUTHORING §5.2](docs/AUTHORING.md) has a row for each). Install it beside rigc
|
|
71
|
+
(`bun add -d @esotericsoftware/spine-core`) and the same `rigc` runs the round
|
|
72
|
+
trip on every build. The
|
|
73
|
+
[Spine Runtimes License Agreement](https://esotericsoftware.com/spine-runtimes-license)
|
|
74
|
+
applies to that dependency wherever it is installed.
|
|
75
|
+
|
|
76
|
+
### Licensing, stated plainly
|
|
77
|
+
|
|
78
|
+
rigc's own code is MIT (see [LICENSE](LICENSE)). That says nothing about Spine, and
|
|
79
|
+
the following is a restatement of Esoteric Software's terms, not a term of ours:
|
|
80
|
+
|
|
81
|
+
1. rigc's output **is Spine skeleton data**.
|
|
82
|
+
2. A product that plays it with **a Spine Runtime** — the official runtimes, the Web
|
|
83
|
+
Player — has integrated the Spine Runtimes. rigc also writes the model document
|
|
84
|
+
its own core poses, and a product may play that or anything else; what the terms
|
|
85
|
+
turn on is the integration.
|
|
86
|
+
3. Integrating a Spine Runtime into a product is permitted **under Section 2 of the
|
|
87
|
+
[Spine Editor License Agreement](https://esotericsoftware.com/spine-editor-license)**,
|
|
88
|
+
or otherwise on the condition that **each user of the product obtains their own
|
|
89
|
+
Spine editor licence** and the product carries the Runtimes licence and copyright
|
|
90
|
+
notice — the Runtimes licence's own two routes, in its words.
|
|
91
|
+
4. The published rigc **links no Spine runtime**. This repository does — `spine-core`
|
|
92
|
+
as a development dependency, for its own gate — and so does an install with the
|
|
93
|
+
runtime added beside it.
|
|
94
|
+
|
|
95
|
+
> **If a product integrates a Spine Runtime to play rigc's output, that integration
|
|
96
|
+
> is subject to the terms above** — rigc neither adds those terms nor removes them.
|
|
97
|
+
> Running the published rigc links no Spine runtime, and whether its output is then
|
|
98
|
+
> played by one, by rigc's own core, or by something else is the consumer's choice.
|
|
99
|
+
|
|
100
|
+
See [NOTICE.md](NOTICE.md) for the full notice.
|
|
101
|
+
|
|
102
|
+
📐 **What of Esoteric Software's is in this repository, and under what grant.** No
|
|
103
|
+
example asset is committed: `bun run fetch-examples` downloads the example projects
|
|
104
|
+
into a gitignored `examples/`. What *is* committed is `bench/reference/` — 1,293 PNG
|
|
105
|
+
frames **this project renders** from those examples' own exports, so a frame carries
|
|
106
|
+
those images' pixels and committing one **is** redistribution. Each example's own
|
|
107
|
+
`license.txt` permits exactly that, *"as long as they are accompanied by this license
|
|
108
|
+
file"*, and a verbatim copy of it sits at each example root here — all eight, written
|
|
109
|
+
there by the render script rather than left to memory. The same file's
|
|
110
|
+
**non-commercial** condition rides along with those images, and
|
|
111
|
+
[LICENSE](LICENSE) says so: rigc's MIT grant covers rigc's own code, documentation
|
|
112
|
+
and art, not this material. `7-anticipation` publishes no `license.txt` upstream, so
|
|
113
|
+
no such grant exists for it and its frames are never committed — they render only
|
|
114
|
+
into a gitignored directory, enforced by `git check-ignore`. Full reasoning:
|
|
115
|
+
[`bench/reference/README.md`](https://github.com/firejune/rigc/blob/main/bench/reference/README.md)
|
|
116
|
+
(repository material, not in the npm package).
|
|
117
|
+
|
|
118
|
+
The problem rigc is aimed at is narrow. An agent asked to author a rig has no way
|
|
119
|
+
to tell whether it succeeded: Spine's JSON parser accepts a great deal of nonsense
|
|
120
|
+
without a murmur — a constraint in the 4.2 shape simply vanishes, a `size:` that
|
|
121
|
+
disagrees with the PNG collapses every UV, a four-number curve array yields NaN,
|
|
122
|
+
a mesh whose vertex count happens to equal its UV count silently loses its bone
|
|
123
|
+
weights. Every one of those loads clean, plays, and is wrong. rigc's answer is to
|
|
124
|
+
make the failure legible: compile from a spec, run a list of named assertions — held
|
|
125
|
+
to the real parser's verdicts in CI — and **write nothing unless all of them are
|
|
126
|
+
green.**
|
|
127
|
+
|
|
128
|
+
## Install
|
|
129
|
+
|
|
130
|
+
📦 **rigc measures loose PNGs directly, and emits one atlas page per image unless
|
|
131
|
+
you ask otherwise.** `rigc build --pack` arranges every part onto shared pages and
|
|
132
|
+
writes them into `--out`; `--atlas-in` builds against a pack somebody else made.
|
|
133
|
+
Both are opt-in and both are narrow — no trimming, no rotation, no scaling — and
|
|
134
|
+
[AUTHORING §0.1–§0.2](docs/AUTHORING.md) states the limits before you hit them.
|
|
135
|
+
|
|
136
|
+
rigc runs on [Bun](https://bun.sh). The package ships its TypeScript sources and
|
|
137
|
+
Bun runs them, so there is no build step and no `dist/` that can drift from the
|
|
138
|
+
repository it was cut from. What is measured is the install itself — pack,
|
|
139
|
+
install into an empty directory, build, render — on Linux, macOS and Windows
|
|
140
|
+
runners and on Linux at the declared minimum, Bun 1.2.0 (the `installs` and
|
|
141
|
+
`installs-matrix` jobs in
|
|
142
|
+
[`ci.yml`](https://github.com/firejune/rigc/blob/main/.github/workflows/ci.yml),
|
|
143
|
+
repository material, not in the npm package); a platform
|
|
144
|
+
whose leg is red there is not one the package is known to run on.
|
|
145
|
+
|
|
146
|
+
**The npm package is `spine-rigc`; the command it installs is `rigc`.** npm
|
|
147
|
+
refuses the name `rigc` as too similar to packages that already exist, so the
|
|
148
|
+
project, this repository and the executable keep their name and only the
|
|
149
|
+
registry entry is spelled out.
|
|
150
|
+
|
|
151
|
+
```bash
|
|
152
|
+
bunx spine-rigc --help # run it without installing
|
|
153
|
+
bun add -g spine-rigc # or install the command
|
|
154
|
+
bun add -d spine-rigc # or pin it in a project
|
|
155
|
+
```
|
|
156
|
+
|
|
157
|
+
`npx spine-rigc` works too, as long as Bun is on `PATH` — the executable is a
|
|
158
|
+
Bun script, and npm only writes the shim that calls it.
|
|
159
|
+
|
|
160
|
+
Installed, the command is `rigc`. The examples below spell it `bun cli.ts`
|
|
161
|
+
because they are written from a clone of this repository (`bun install`, then run
|
|
162
|
+
the CLI in place). With `@esotericsoftware/spine-core` installed beside the package
|
|
163
|
+
the two are interchangeable — `rigc build …` is `bun cli.ts build …`. Without it,
|
|
164
|
+
`rigc` runs the entry that links none of the runtime (`bun cli_core.ts` in a clone):
|
|
165
|
+
`build` writes the same files gated without the parse, and a command that needs the
|
|
166
|
+
runtime is refused by name, saying how to get it (that entry's `--help` lists the
|
|
167
|
+
commands it runs). `rigc --version` names the entry that ran.
|
|
168
|
+
|
|
169
|
+
One command is a repository workflow rather than a package one: `bench` measures
|
|
170
|
+
against Spine's official example projects — fetched, never committed — and against
|
|
171
|
+
reference frames this project renders from them, which **are** committed, each
|
|
172
|
+
example's own `license.txt` beside them under the redistribution grant those files
|
|
173
|
+
carry; the images stay **non-commercial only**. The reasoning is in
|
|
174
|
+
[`bench/reference/README.md`](https://github.com/firejune/rigc/blob/main/bench/reference/README.md)
|
|
175
|
+
and the terms in [NOTICE.md](NOTICE.md). It needs a clone and `bun run
|
|
176
|
+
fetch-examples`, and says so by name when the corpus is absent. `check` is not one
|
|
177
|
+
of them: it reads whatever frames you point it at, so it runs from the installed
|
|
178
|
+
package on pictures of your own — which is what *Where to go next* below tells you
|
|
179
|
+
to do with it, and it is the one instrument here that can see a wrong animation.
|
|
180
|
+
|
|
181
|
+
### Install it into your agent
|
|
182
|
+
|
|
183
|
+
The guides under [Documentation](#documentation) also ship as
|
|
184
|
+
[Agent Skills](https://agentskills.io) — `skills/<name>/SKILL.md`, in this
|
|
185
|
+
repository and in the npm package — which a host reads once they are where it
|
|
186
|
+
looks: Claude Code through the plugin below, Codex, Gemini CLI and Antigravity
|
|
187
|
+
through `rigc skills install`. Each skill is a router and nothing more: when to load it, the
|
|
188
|
+
non-negotiables in a line apiece, and a link to the guide that owns every rule, so
|
|
189
|
+
a rule keeps living in exactly one place. The repository is also a Claude Code
|
|
190
|
+
plugin marketplace:
|
|
191
|
+
|
|
192
|
+
```shell
|
|
193
|
+
/plugin marketplace add firejune/rigc
|
|
194
|
+
/plugin install rigc@rigc
|
|
195
|
+
```
|
|
196
|
+
|
|
197
|
+
With the package already installed, `claude --plugin-dir node_modules/spine-rigc`
|
|
198
|
+
loads the same skills without a marketplace. The plugin carries no version of its
|
|
199
|
+
own — `/plugin update` follows `main` commit by commit, and the only version on
|
|
200
|
+
disk stays the one in `package.json`.
|
|
201
|
+
|
|
202
|
+
Codex, Gemini CLI and Antigravity read skills from one directory in the workspace,
|
|
203
|
+
`.agents/skills/` ([Codex](https://learn.chatgpt.com/docs/build-skills),
|
|
204
|
+
[Gemini CLI](https://github.com/google-gemini/gemini-cli/blob/main/docs/cli/skills.md),
|
|
205
|
+
[Antigravity](https://antigravity.google/docs/skills/)), and none of them reads
|
|
206
|
+
`node_modules`. With the package installed, one command puts every skill there:
|
|
207
|
+
|
|
208
|
+
```shell
|
|
209
|
+
bun add -d spine-rigc
|
|
210
|
+
bun rigc skills install # relative links: .agents/skills/rigc -> ../../node_modules/spine-rigc/skills/rigc
|
|
211
|
+
bun rigc skills install --copy # the folders themselves, for a host that does not follow a link
|
|
212
|
+
```
|
|
213
|
+
|
|
214
|
+
Run it through the project's own install, as above: `bunx spine-rigc skills install`
|
|
215
|
+
in a project that has the package was measured running the registry's copy instead
|
|
216
|
+
of the project's. A link reaches every upgrade of the package with no second run,
|
|
217
|
+
and a second run has nothing to do; an entry already there that this command did not
|
|
218
|
+
make is refused by name and nothing is written. Gemini CLI 0.41.1 was measured
|
|
219
|
+
listing a linked skill from both its workspace and its user directory — the
|
|
220
|
+
workspace one only in a folder it trusts. Codex's documentation says it follows a
|
|
221
|
+
symlinked skill folder, which is not measured here, and Antigravity CLI 1.1.9 has no
|
|
222
|
+
way to list skills without starting a session, so what it does with a link is not
|
|
223
|
+
measured either; `--copy` is the shape that asks nothing of a host. Gemini CLI can
|
|
224
|
+
also fetch a skill itself, one folder at a time:
|
|
225
|
+
`gemini skills install https://github.com/firejune/rigc.git --path skills/rigc --scope workspace`.
|
|
226
|
+
The routers are named `rigc-rigging`, `rigc-motion`, `rigc-face` and `rigc-ingest`
|
|
227
|
+
because that directory is flat — beside another tool's `motion`, a bare name is
|
|
228
|
+
whichever one the host picked — and under the Claude Code plugin they read
|
|
229
|
+
`rigc:rigc-motion` and so on.
|
|
230
|
+
|
|
231
|
+
## First rig in ten minutes
|
|
232
|
+
|
|
233
|
+
A whole rig, end to end, in a scratch directory: three tiny plates, two JSON
|
|
234
|
+
files, one `build`, one `validate`. No clone, no art pipeline, nothing fetched.
|
|
235
|
+
|
|
236
|
+
🚫 **Every value below is invented for this section** — a doll that exists
|
|
237
|
+
nowhere else in this repository. That is [AUTHORING.md](docs/AUTHORING.md) §3's
|
|
238
|
+
rule applied here: no example value in these documents is copied out of a
|
|
239
|
+
reference export, so nothing you read in a quickstart is an answer to anything
|
|
240
|
+
[the ladder](https://github.com/firejune/rigc/blob/main/docs/LADDER.md) measures.
|
|
241
|
+
|
|
242
|
+
**1. Install the command.**
|
|
243
|
+
|
|
244
|
+
```bash
|
|
245
|
+
bun add -g spine-rigc # installs `rigc`
|
|
246
|
+
```
|
|
247
|
+
|
|
248
|
+
Or skip the install and prefix every command below with `bunx `, e.g.
|
|
249
|
+
`bunx spine-rigc build …`.
|
|
250
|
+
|
|
251
|
+
**2. Make a directory and three plates.** rigc measures PNGs rather than trusting
|
|
252
|
+
a number you typed (R5), so the art has to exist. These three are solid colours a
|
|
253
|
+
few dozen pixels across — a hull, a mast and a lamp:
|
|
254
|
+
|
|
255
|
+
```bash
|
|
256
|
+
mkdir -p buoy/images && cd buoy
|
|
257
|
+
bun -e '
|
|
258
|
+
const parts = {
|
|
259
|
+
"images/hull.png": "iVBORw0KGgoAAAANSUhEUgAAADgAAAAMCAYAAAA3bX6lAAAAKElEQVR42mOI8bL6P5wxw6gHRz046sFRD456cNSDox4c9eCoBwcrBgDSZ+mdl2OiDgAAAABJRU5ErkJggg==",
|
|
260
|
+
"images/mast.png": "iVBORw0KGgoAAAANSUhEUgAAAAgAAAA0CAYAAAC3t3ldAAAAH0lEQVR42mO4dunIf3yYYVTBqIJRBaMKRhWMKhgcCgBGJo4s9YnopgAAAABJRU5ErkJggg==",
|
|
261
|
+
"images/lamp.png": "iVBORw0KGgoAAAANSUhEUgAAABIAAAASCAYAAABWzo5XAAAAHElEQVR42mP4v8HhPzUww6hBowaNGjRq0HAzCADvdrVmFPbc+QAAAABJRU5ErkJggg=="
|
|
262
|
+
};
|
|
263
|
+
for (const [p, b] of Object.entries(parts)) await Bun.write(p, Buffer.from(b, "base64"));
|
|
264
|
+
'
|
|
265
|
+
```
|
|
266
|
+
|
|
267
|
+
**3. The rig spec — `buoy.rig.json`.** Structure only: bones, the slots array in
|
|
268
|
+
draw order, and one skin mapping each slot to a plate.
|
|
269
|
+
|
|
270
|
+
```json
|
|
271
|
+
{
|
|
272
|
+
"spec": "rigc-rig/1",
|
|
273
|
+
"name": "buoy",
|
|
274
|
+
"images": "images",
|
|
275
|
+
"skeleton": { "width": 200, "height": 200 },
|
|
276
|
+
"bones": [
|
|
277
|
+
{ "name": "root" },
|
|
278
|
+
{ "name": "hull", "parent": "root", "x": 0, "y": 0 },
|
|
279
|
+
{ "name": "mast", "parent": "hull", "x": 0, "y": 4 },
|
|
280
|
+
{ "name": "lamp", "parent": "mast", "x": 0, "y": 52 }
|
|
281
|
+
],
|
|
282
|
+
"slots": [
|
|
283
|
+
{ "name": "mast", "bone": "mast", "attachment": "mast" },
|
|
284
|
+
{ "name": "hull", "bone": "hull", "attachment": "hull" },
|
|
285
|
+
{ "name": "lamp", "bone": "lamp", "attachment": "lamp" }
|
|
286
|
+
],
|
|
287
|
+
"skins": {
|
|
288
|
+
"default": {
|
|
289
|
+
"mast": { "mast": { "image": "mast.png", "y": 26 } },
|
|
290
|
+
"hull": { "hull": { "image": "hull.png" } },
|
|
291
|
+
"lamp": { "lamp": { "image": "lamp.png" } }
|
|
292
|
+
}
|
|
293
|
+
}
|
|
294
|
+
}
|
|
295
|
+
```
|
|
296
|
+
|
|
297
|
+
Three things in there are worth naming, because each is a rule rather than a
|
|
298
|
+
style: the **slots array is the setup draw order** (R4) — index 0 is furthest
|
|
299
|
+
back, so the mast is behind the hull; the attachment carries an **`image`
|
|
300
|
+
instead of a `width`/`height`** (R5), which is what makes the size in the
|
|
301
|
+
skeleton and the size in the atlas incapable of drifting apart; and the mast's
|
|
302
|
+
`"y": 26` offsets the plate *within* its slot so the bone sits at the mast's foot
|
|
303
|
+
rather than its middle.
|
|
304
|
+
|
|
305
|
+
**4. The motion spec — `buoy.motion.json`.** Time only, aimed at the rig by name:
|
|
306
|
+
|
|
307
|
+
```json
|
|
308
|
+
{
|
|
309
|
+
"spec": "rigc-motion/1",
|
|
310
|
+
"archetype": "buoy",
|
|
311
|
+
"cut": "buoy",
|
|
312
|
+
"easings": { "swing": [0.42, 0, 0.58, 1] },
|
|
313
|
+
"animations": {
|
|
314
|
+
"bob": {
|
|
315
|
+
"duration": 2,
|
|
316
|
+
"loop": true,
|
|
317
|
+
"tracks": [
|
|
318
|
+
{
|
|
319
|
+
"bone": "hull",
|
|
320
|
+
"property": "translatey",
|
|
321
|
+
"keys": [
|
|
322
|
+
{ "t": 0, "v": [0], "ease": "swing" },
|
|
323
|
+
{ "t": 0.5, "v": [5], "ease": "swing" },
|
|
324
|
+
{ "t": 1.5, "v": [-5], "ease": "swing" },
|
|
325
|
+
{ "t": 2, "v": [0] }
|
|
326
|
+
]
|
|
327
|
+
},
|
|
328
|
+
{
|
|
329
|
+
"bone": "mast",
|
|
330
|
+
"property": "rotate",
|
|
331
|
+
"keys": [
|
|
332
|
+
{ "t": 0, "v": [-6], "ease": "swing" },
|
|
333
|
+
{ "t": 1, "v": [6], "ease": "swing" },
|
|
334
|
+
{ "t": 2, "v": [-6] }
|
|
335
|
+
]
|
|
336
|
+
}
|
|
337
|
+
]
|
|
338
|
+
}
|
|
339
|
+
}
|
|
340
|
+
}
|
|
341
|
+
```
|
|
342
|
+
|
|
343
|
+
`archetype` must equal the rig's `name`. `duration` is declared and then checked
|
|
344
|
+
against what actually compiled (R7). The **last key of each track carries no
|
|
345
|
+
easing** — there is nothing after it to ease towards, and saying otherwise is a
|
|
346
|
+
compile error.
|
|
347
|
+
|
|
348
|
+
**5. Build, then re-gate what it wrote.**
|
|
349
|
+
|
|
350
|
+
```bash
|
|
351
|
+
rigc build --rig buoy.rig.json --motion buoy.motion.json --images images --out spine
|
|
352
|
+
rigc validate spine
|
|
353
|
+
```
|
|
354
|
+
|
|
355
|
+
`build` prints every assertion by name, then the shape of what it emitted, then
|
|
356
|
+
the three files it wrote:
|
|
357
|
+
|
|
358
|
+
```
|
|
359
|
+
.. pages=3 regions=3 bones=4 slots=3 animations=1 version=4.3.13 regionAttachments=3 meshAttachments=0 physicsConstraints=0 rig=buoy profile=spine
|
|
360
|
+
rigc: wrote …/buoy/spine/skeleton.json
|
|
361
|
+
rigc: wrote …/buoy/spine/skeleton.atlas
|
|
362
|
+
rigc: wrote …/buoy/spine/skeleton.model.json
|
|
363
|
+
rigc: look at it: rigc preview --candidate …/buoy/spine
|
|
364
|
+
```
|
|
365
|
+
|
|
366
|
+
`profile=spine` is the rulebook that judged it: *is this valid Spine 4.3 that any
|
|
367
|
+
runtime plays correctly?* That is the default, and the [Profiles](https://github.com/firejune/rigc/blob/main/docs/BENCHMARK.md#profiles--wrong-versus-not-how-we-do-it-here)
|
|
368
|
+
section of the benchmark dossier is where the other one lives. `validate` then re-reads those
|
|
369
|
+
artifacts from disk and ends `rigc: green`. That is a rig. `spine/skeleton.json`
|
|
370
|
+
is Spine 4.3 skeleton data — it loads in a Spine runtime and it imports into the
|
|
371
|
+
Spine editor.
|
|
372
|
+
|
|
373
|
+
**Try breaking it**, because the validator's messages are the interface here and
|
|
374
|
+
they are worth meeting once on purpose. With `spine/` built, rename
|
|
375
|
+
`images/hull.png` to `images/raft.png` and re-run `rigc validate spine`:
|
|
376
|
+
|
|
377
|
+
```
|
|
378
|
+
FAIL A17_ATLAS_PAGE_FILES_EXIST: page "../images/hull.png" is not on disk at …/images/hull.png
|
|
379
|
+
rigc: 1 assertion(s) failed
|
|
380
|
+
```
|
|
381
|
+
|
|
382
|
+
Put the name back and it is green again. The same gate runs inside `build`, and
|
|
383
|
+
a FAIL there stops it **before it writes** — a red build leaves no half-built
|
|
384
|
+
artifact on disk to mistake for a result, and there is no flag that changes that.
|
|
385
|
+
|
|
386
|
+
**6. See what you built.**
|
|
387
|
+
|
|
388
|
+
🚨 **Green is a claim about validity and about nothing else.** A rig whose head
|
|
389
|
+
sits visibly off its torso passes every assertion, loads in `spine-core` and steps
|
|
390
|
+
numerically clean — the offsets are the ones your spec asked for, and no
|
|
391
|
+
assertion can know you did not mean them. The only remedy is looking, and both
|
|
392
|
+
commands below need nothing you do not already have: no reference frames, no
|
|
393
|
+
second package, no server.
|
|
394
|
+
|
|
395
|
+
```bash
|
|
396
|
+
rigc render --candidate spine # PNG frames + a contact sheet, in render/
|
|
397
|
+
rigc preview --candidate spine # one .html file that plays it: preview.html
|
|
398
|
+
```
|
|
399
|
+
|
|
400
|
+
**`render`** samples every animation at 12 fps and writes `render/<animation>/f0000.png…`
|
|
401
|
+
with a `contact.png` beside them — every frame of the shot as one labelled grid,
|
|
402
|
+
which is the picture to open first, because spacing is a comparison *across*
|
|
403
|
+
frames. It draws with rigc's own rasteriser (the one `check` measures with), so
|
|
404
|
+
it needs no browser and no network, and the `frames.json` it leaves beside the
|
|
405
|
+
directories makes the result a frame set like any other — the world box every
|
|
406
|
+
frame is a picture of. `--animation <name>` narrows it to one, `--fps` and
|
|
407
|
+
`--max` change the rate and the frame size.
|
|
408
|
+
|
|
409
|
+
> 🚨 **`--skin <name>` if your rig has more than one.** With no `--skin` no skin
|
|
410
|
+
> is set at all, so every slot resolves through the **default** skin alone — and
|
|
411
|
+
> a slot whose art lives only in a named skin draws *nothing*. That is not a
|
|
412
|
+
> quirk of `render`: `check` compares `render`'s frames, so a multi-skin rig
|
|
413
|
+
> checked with no skin compares blank against blank and reports a perfect
|
|
414
|
+
> `0.0000` about art nobody drew. `render --skin` records the name in
|
|
415
|
+
> `frames.json`, `check --skin` poses the candidate under it, and a candidate
|
|
416
|
+
> scored against frames rendered under a *different* skin is refused by name
|
|
417
|
+
> rather than measured.
|
|
418
|
+
|
|
419
|
+
**`preview`** writes a single self-contained `.html`: your skeleton, your atlas
|
|
420
|
+
and every page's PNG bytes are embedded in it as data URIs, and it plays them in
|
|
421
|
+
the **official [Spine Web Player](https://esotericsoftware.com/spine-player)**.
|
|
422
|
+
Double-click it, or attach it to a message — the file carries the whole artifact.
|
|
423
|
+
It is also the strongest interop statement in this repository: a rig that plays
|
|
424
|
+
there has been played by Esoteric Software's own runtime rather than by ours.
|
|
425
|
+
|
|
426
|
+
> ⚖️ The player itself is **referenced, not embedded** — the page loads it from
|
|
427
|
+
> unpkg, so the first open needs a network, and rigc redistributes nothing
|
|
428
|
+
> Esoteric Software owns (see [NOTICE.md](NOTICE.md)). Everything the player
|
|
429
|
+
> draws is inside your file.
|
|
430
|
+
|
|
431
|
+
**7. Let someone choose.** Sooner or later you will have two builds that both pass
|
|
432
|
+
the gate and no instrument that can separate them. `vote` puts them in one page
|
|
433
|
+
side by side, labelled `A` and `B` with no paths on screen, and takes an answer
|
|
434
|
+
back:
|
|
435
|
+
|
|
436
|
+
```bash
|
|
437
|
+
rigc vote --candidate spine-a --candidate spine-b # -> ballot.html, open it and pick one
|
|
438
|
+
rigc vote --record vote-<id>.json # -> checks the answer into votes.jsonl
|
|
439
|
+
```
|
|
440
|
+
|
|
441
|
+
The voter picks a winner or says "tie / no preference"; the page hands them a
|
|
442
|
+
small JSON file to save; `--record` checks that file against the ballot's own
|
|
443
|
+
hashes and appends one line to an append-only ledger, refusing by name anything
|
|
444
|
+
that does not belong to it. See
|
|
445
|
+
[Letting someone choose](https://github.com/firejune/rigc/blob/main/docs/BENCHMARK.md#letting-someone-choose--rigc-vote).
|
|
446
|
+
|
|
447
|
+
**Where to go next.**
|
|
448
|
+
|
|
449
|
+
- 📘 **[docs/AUTHORING.md](docs/AUTHORING.md)** is the real guide — both files
|
|
450
|
+
field by field, the emission rules, every named failure mapped to the file that
|
|
451
|
+
has to change, and §8–§9 for reproducing a shot you were given as pictures. It
|
|
452
|
+
ships inside the npm package too, at
|
|
453
|
+
`node_modules/spine-rigc/docs/AUTHORING.md`.
|
|
454
|
+
- `rigc explain --rig buoy.rig.json --motion buoy.motion.json --out spine` prints
|
|
455
|
+
the compiled rig as a table — every bone with its resolved parent, the slots in
|
|
456
|
+
draw order, every timeline key by key — and writes nothing. It is what to reach
|
|
457
|
+
for when a rig compiles and still looks wrong.
|
|
458
|
+
- 🚨 **A green gate does not mean the animation is right**, and no assertion
|
|
459
|
+
could. If you have reference pictures of the shot,
|
|
460
|
+
`rigc check --candidate spine --frames <dir>` is the half of the loop that can
|
|
461
|
+
see a wrong animation — AUTHORING.md §9.
|
|
462
|
+
- 🧭 **No reference pictures, because the rig is your own?** Then make them:
|
|
463
|
+
`rigc render` the first build you are happy with and keep those frames. Every
|
|
464
|
+
later build is checked against them, and the first such check — the same build
|
|
465
|
+
against frames of itself — is the floor the rest are read against, because
|
|
466
|
+
`check` grades nothing and has no pass mark. It is the same instrument and the
|
|
467
|
+
same commands; what changes is that the reference is a build of yours you have
|
|
468
|
+
already looked at, so what it measures is **what your edit did**. Every drift
|
|
469
|
+
the report prints carries the bound its own match gives it on the line under
|
|
470
|
+
it, so a figure is read against that rather than against a number from a page;
|
|
471
|
+
AUTHORING.md §9.2 says what the two halves of that bound are and why the floor
|
|
472
|
+
is not zero.
|
|
473
|
+
- [docs/LADDER.md](https://github.com/firejune/rigc/blob/main/docs/LADDER.md) is the benchmark: the same job, from a brief
|
|
474
|
+
and rendered frames, scored. [docs/PILOT.md](https://github.com/firejune/rigc/blob/main/docs/PILOT.md) is how to run an
|
|
475
|
+
agent through it and score what comes back.
|
|
476
|
+
- 🤖 **Handing the authoring to an AI agent?**
|
|
477
|
+
[docs/PROMPTING.md](docs/PROMPTING.md) is the operator's page — the six prompt
|
|
478
|
+
clauses a measured pilot run paid for, and what you can leave unsaid.
|
|
479
|
+
|
|
480
|
+
## See what you built, and let someone choose
|
|
481
|
+
|
|
482
|
+
Steps 6 and 7 above are the three commands that need nothing but a compiled rig — no
|
|
483
|
+
reference frames, no second package, no server. **`render`** writes every frame as a
|
|
484
|
+
PNG plus one contact-sheet grid of the whole shot; **`preview`** writes one
|
|
485
|
+
self-contained `.html` that plays it in Spine's own web player; **`vote`** puts two to
|
|
486
|
+
four candidates in one page and takes a human's answer back. Reach for them the moment
|
|
487
|
+
a rig compiles green, because green says nothing at all about the picture.
|
|
488
|
+
|
|
489
|
+
<p align="center">
|
|
490
|
+
<img src="https://raw.githubusercontent.com/firejune/rigc/main/assets/rigc-keypose.gif" alt="Two key poses read back by rigc pose, two candidate in-between motions compiled from them, and a rigc vote ballot picking one" width="600" />
|
|
491
|
+
</p>
|
|
492
|
+
|
|
493
|
+
<p align="center"><em>The whole loop on one character: two key poses are the given
|
|
494
|
+
conditions, <code>rigc pose</code> reads where every part sits in each picture, two
|
|
495
|
+
candidate in-betweenings are compiled from the same pair — and a real
|
|
496
|
+
<code>rigc vote</code> ballot picks the winner, because the movement between the poses
|
|
497
|
+
is the one thing no instrument here will grade.</em></p>
|
|
498
|
+
|
|
499
|
+
Three properties of `vote` are worth stating, because they are what make its ledger
|
|
500
|
+
usable by the next agent rather than by a reader: **a tie is a recorded outcome, not a
|
|
501
|
+
missing one** — `both-unacceptable` is the tie that means *propose again*, and it is
|
|
502
|
+
unreachable if ties are not recordable; **the winner is a digest, not a label**, since
|
|
503
|
+
`B` means nothing outside one ballot while a digest identifies the same pixels
|
|
504
|
+
anywhere; and **every line carries a reason code** from a closed enumeration that is
|
|
505
|
+
enforced, so *"tie, because this one is better"* is refused.
|
|
506
|
+
|
|
507
|
+
🎞️ **Authoring the movement those pages show you** — key poses, in-betweening, and how
|
|
508
|
+
to spread candidates so a ballot informs — is [docs/MOTION.md](docs/MOTION.md).
|
|
509
|
+
|
|
510
|
+
📐 **`rigc pose --images parts/ --frame poseA.png` runs the other way.** Every command
|
|
511
|
+
above takes something you authored and tells you about it; this one takes a **picture
|
|
512
|
+
the user already has** — one key pose — and reports where each loose part PNG sits in
|
|
513
|
+
it, so those coordinates go into the rig and the motion **by construction** and the
|
|
514
|
+
effort goes into the part no instrument can measure: the movement between two poses.
|
|
515
|
+
A part that matches nowhere is refused by name, two near-equal placements are reported
|
|
516
|
+
as both, and nothing it prints is a score. Fields, the coordinate contract and the
|
|
517
|
+
limits: [AUTHORING.md §11](docs/AUTHORING.md). The parts it refuses because
|
|
518
|
+
something is drawn over them are `rigc chainfit`'s, once a candidate exists —
|
|
519
|
+
[§12](docs/AUTHORING.md).
|
|
520
|
+
|
|
521
|
+
## The gallery — seven complete rigs over art that ships with them
|
|
522
|
+
|
|
523
|
+
Each directory in [`gallery/`](https://github.com/firejune/rigc/tree/main/gallery) is
|
|
524
|
+
one rig spec, one motion spec and the PNGs they name, small enough to read in one
|
|
525
|
+
sitting. Each stars a single feature, so *how do I do X* has a working answer rather
|
|
526
|
+
than a field table, and each README carries the frame rate it was authored at, what
|
|
527
|
+
was verified, and what writing it cost. Repository material: a clone and
|
|
528
|
+
`bun install` runs them.
|
|
529
|
+
|
|
530
|
+
| Example | Stars | What it is |
|
|
531
|
+
| --- | --- | --- |
|
|
532
|
+
| [`gallery/walk`](https://github.com/firejune/rigc/tree/main/gallery/walk) | `ik` constraints + **`ik` timelines** | Two two-bone leg chains solved to foot targets — the planted leg nailed down, the swinging one let go at the top of its lift |
|
|
533
|
+
| [`gallery/squash`](https://github.com/firejune/rigc/tree/main/gallery/squash) | **`deform` timelines** | A ball squashed about its contact point and stretched along its travel, from two affine transforms the keys state rather than tabulate |
|
|
534
|
+
| [`gallery/flex`](https://github.com/firejune/rigc/tree/main/gallery/flex) | **`contour` meshes** | A swallow-tailed banner and a serrated leaf: four meshes traced off their own alpha, waved by bone timelines and rippled by a `deform` |
|
|
535
|
+
| [`gallery/ride`](https://github.com/firejune/rigc/tree/main/gallery/ride) | `path` attachments + **path constraints** | A trolley coasting down a drawn rail and rolling back, driven by a `position` timeline, with `groups` + `stagger` keying the wheels and the ears |
|
|
536
|
+
| [`gallery/portrait`](https://github.com/firejune/rigc/tree/main/gallery/portrait) | **deform `transform`** + `derive` group tracks | A 2.5D head turn: two meshes and six feature bones all keyed from one stated expression, `dx = x(cos t − 1) − z·sin t`, with the depths in the spec rather than a README |
|
|
537
|
+
| [`gallery/nod`](https://github.com/firejune/rigc/tree/main/gallery/nod) | the **`pitch`** and **`wave`** transform kinds | A head bowing and two lop ears rippling, on three meshes each laid out for the closed form that moves it — a fold angle solved for before authoring, and a shear whose winding no amplitude can reverse |
|
|
538
|
+
| [`gallery/look`](https://github.com/firejune/rigc/tree/main/gallery/look) | **`slider` constraints** | A head that turns because a **value** says so: two dials drive two sliders, and the rendered animation moves the needles rather than the face — with a depth map under the face mesh, a soft mask on the cowlick, and a slider range derived from the turn ceiling `build` reports rather than picked by eye |
|
|
539
|
+
|
|
540
|
+
<p align="center">
|
|
541
|
+
<img src="https://raw.githubusercontent.com/firejune/rigc/main/assets/rigc-scene.gif" alt="A portrait rig breathing, glancing aside, then turning its head in 2.5D — hair and features sliding at different depths" width="600" />
|
|
542
|
+
</p>
|
|
543
|
+
|
|
544
|
+
<p align="center"><em>The portrait rig playing its three animations in one take — the turn is
|
|
545
|
+
the shot: both silhouette edges move apart, which a flat slide cannot do, because every
|
|
546
|
+
feature carries its own depth. The rig guarantees the seams — <code>idle</code> loops
|
|
547
|
+
while <code>gaze</code> and <code>turn</code> return to rest, so the hand-offs meet at 0
|
|
548
|
+
differing pixels — and the composing is the consumer's. Authorable on plain Spine 4.3, no
|
|
549
|
+
plugin, no runtime patch; what the turn costs is authoring rather than runtime capability,
|
|
550
|
+
and that cost is one stated expression per key. Compiled and rendered entirely by the
|
|
551
|
+
published package.</em></p>
|
|
552
|
+
|
|
553
|
+
🎞️ **How the three films on this page were made** is kept with them, one directory per
|
|
554
|
+
film in [`films/`](https://github.com/firejune/rigc/tree/main/films) — a `run.sh` that
|
|
555
|
+
names every step, the assembler that cuts the shots and draws the type, and a README
|
|
556
|
+
saying what the film claims and which tool printed each figure on screen. Repository
|
|
557
|
+
material, like the gallery: a clone runs them.
|
|
558
|
+
|
|
559
|
+
## Commands
|
|
560
|
+
|
|
561
|
+
Every command takes its paths explicitly. `rigc <command> --help` prints its flags, and
|
|
562
|
+
[AUTHORING.md §0](docs/AUTHORING.md) is the same list with what each flag means, which
|
|
563
|
+
commands take it and what its default is.
|
|
564
|
+
|
|
565
|
+
| Command | Does |
|
|
566
|
+
| --- | --- |
|
|
567
|
+
| `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 |
|
|
568
|
+
| `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) |
|
|
569
|
+
| `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 |
|
|
570
|
+
| `validate <dir>` | re-gates artifacts already on disk |
|
|
571
|
+
| `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)) |
|
|
572
|
+
| `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 |
|
|
573
|
+
| `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 |
|
|
574
|
+
| `render --candidate <dir>` | PNG frames plus a contact sheet, in `render/`. `--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 |
|
|
575
|
+
| `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` |
|
|
576
|
+
| `vote --candidate a --candidate b` | one `.html` that asks a human which; `vote --record <file>` checks the answer into `votes.jsonl` |
|
|
577
|
+
| `pose --images <dir> --frame <png>` | reads part placements **out of** a picture |
|
|
578
|
+
| `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 |
|
|
579
|
+
| `diff <candidate.json> <reference.json>` | structural comparison of two skeletons, one ratio per measure and deliberately no combined score |
|
|
580
|
+
| `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 |
|
|
581
|
+
| `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 |
|
|
582
|
+
| `bench <rung> --candidate <dir>` | one rung of the benchmark ladder |
|
|
583
|
+
| `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 |
|
|
584
|
+
|
|
585
|
+
`diff`, `bonedist`, `check` and `bench` measure against something you were given; the
|
|
586
|
+
first three work on any reference you have, and `bench` is a repository workflow that needs a clone
|
|
587
|
+
and `bun run fetch-examples`. The reasoning behind them is in
|
|
588
|
+
[the benchmark dossier](https://github.com/firejune/rigc/blob/main/docs/BENCHMARK.md).
|
|
589
|
+
|
|
590
|
+
`build` and `validate` both default to `--profile spine` — the 34 validity rules, which
|
|
591
|
+
ask *is this valid Spine 4.3 that any runtime plays correctly?* `--profile spine-html`
|
|
592
|
+
adds all 50: the other 16 are one renderer's policy and one canvas budget's, and they
|
|
593
|
+
fire on perfectly correct editor-produced Spine data, which is why they are opt-in.
|
|
594
|
+
⇒ **That reason is about foreign data and does not carry to a rig you are authoring
|
|
595
|
+
yourself: author under `--profile spine-html` and read the extra 16 as findings, and
|
|
596
|
+
gate the release under `--profile spine`.** A `deform` key that folds a mesh inside
|
|
597
|
+
out is written out under the default and refused by name under `spine-html`, which
|
|
598
|
+
is the shape of what that split buys you. A report always names
|
|
599
|
+
the profile it ran and lists what that profile left out.
|
|
600
|
+
|
|
601
|
+
Several cuts can also be registered in a `cuts.json` and built by name
|
|
602
|
+
(`build --cut my_cut --cuts path/to/cuts.json`); every path in that table resolves
|
|
603
|
+
relative to the `cuts.json` file itself, so the table lives with the project that owns
|
|
604
|
+
the art. Its shape is under
|
|
605
|
+
[Usage](https://github.com/firejune/rigc/blob/main/docs/BENCHMARK.md#usage).
|
|
606
|
+
|
|
607
|
+
### Starting from a skeleton you already have
|
|
608
|
+
|
|
609
|
+
`rigc ingest` reads a Spine 4.3 `skeleton.json` and writes the two spec files that
|
|
610
|
+
rebuild it. It is the only command that runs against `build`'s direction, and the
|
|
611
|
+
only one whose contract is an equality rather than a rulebook:
|
|
612
|
+
|
|
613
|
+
```bash
|
|
614
|
+
rigc ingest hero.json --out specs/ --images parts/
|
|
615
|
+
rigc build --rig specs/rig.json --motion specs/motion.json --out build/
|
|
616
|
+
rigc diff build/skeleton.json hero.json
|
|
617
|
+
```
|
|
618
|
+
|
|
619
|
+
`--images parts/` is what makes the second line carry no flag: it writes the spec's
|
|
620
|
+
own `images` directory, spelled from `--out`, so the specs are self-contained from
|
|
621
|
+
there on. Leave it off and the `image` names resolve against `specs/` itself, which
|
|
622
|
+
holds no art — every rebuild then has to repeat `build --images parts/`.
|
|
623
|
+
|
|
624
|
+
**`build(ingest(x))` is `x`.** Over the eleven rigs this repository builds — the seven
|
|
625
|
+
gallery examples, the three generated probes and a coverage probe written for the
|
|
626
|
+
purpose — the rebuilt `skeleton.json` is byte for byte the file the decompiler read.
|
|
627
|
+
The atlas is held to a weaker
|
|
628
|
+
claim on purpose, and the weakening is measured rather than assumed: it comes back
|
|
629
|
+
equal as a **multiset of region blocks**, because the order the pages are collected in
|
|
630
|
+
is in no field of the skeleton.
|
|
631
|
+
|
|
632
|
+
**And over twelve skeletons nobody here wrote.** Every editor export in the fetched
|
|
633
|
+
example corpus, ingested, rebuilt through the pack beside it and `diff`ed against the
|
|
634
|
+
source, comes back **12 of 12, no blockers, 1.000 on every measure the report
|
|
635
|
+
carries** — and each rebuild is its export's own text in canonical form, apart from
|
|
636
|
+
the header's `hash` and `spine`: the editor's project hash and the runtime version
|
|
637
|
+
rigc stamps, the two keys the rig spec has no field for by design (12 of 12) — and
|
|
638
|
+
the header's box, which `build` computes rather than carries (issue #907): the rebuild
|
|
639
|
+
writes spine-core's `getBounds` over the export on the header's 1e-6 grid at float32, equal on all twelve,
|
|
640
|
+
where the editor wrote its own arithmetic, up to 0.0071 units away.
|
|
641
|
+
[INGEST.md §2.3](docs/INGEST.md) states that pass line and why those two are the
|
|
642
|
+
exceptions.
|
|
643
|
+
|
|
644
|
+
**What it reads is skeleton JSON and nothing else** — no `.spine` project, no binary
|
|
645
|
+
`.skel`, no atlas, no art. So it never invents, and the things it cannot get out of
|
|
646
|
+
the file are **findings** with codes rather than plausible values: a construct the
|
|
647
|
+
spec format cannot hold (`point`, a `sequence` block, an unknown field
|
|
648
|
+
on a bone, slot or constraint) is a blocker, the command exits non-zero, and both
|
|
649
|
+
specs are still written — a spec plus a list of what is missing from it beats no spec.
|
|
650
|
+
|
|
651
|
+
⚠️ **Two values are not in a skeleton at all.**
|
|
652
|
+
|
|
653
|
+
- **The stage.** `skeleton.width`/`height`. A skeleton that declares none is carried as
|
|
654
|
+
declaring none — the rig spec states `"width": null, "height": null` and the rebuild
|
|
655
|
+
carries no box either, byte for byte — and `--stage x,y,w,h` is how a
|
|
656
|
+
caller *adds* one, recorded as a judgement. It is not derivable — posing the rig gives
|
|
657
|
+
the *animated* extent, which is a different number from the setup box. ⛔ **And `--stage` beside a box the file already states is refused
|
|
658
|
+
too**, for the opposite reason: two sources for one value, where the file is the record
|
|
659
|
+
of what was measured. **All twelve exports in the example corpus carry a stage** and
|
|
660
|
+
none of them needs the flag. It is the value that costs least to get wrong, because
|
|
661
|
+
`diff` reports the box and gates nothing on it. 🔁 The box an export's header carries
|
|
662
|
+
is its setup-pose bounding box, and it becomes the rebuild's *stage*; the rebuild's own
|
|
663
|
+
header is computed again, because since issue #907 `build` writes the setup-pose
|
|
664
|
+
bounding box there — never the stage — held to spine-core's `getBounds`. 📦 A rig
|
|
665
|
+
that has to carry its stage in the Spine files asks for it as a bounding box
|
|
666
|
+
(`skeleton.stageBox`, written by `build` from the stage and held by
|
|
667
|
+
`A50_STAGE_BOX_IS_THE_STAGE`), and `ingest --stage-box <slot>` reads that box back as
|
|
668
|
+
the rebuild's stage — [AUTHORING §3.1](docs/AUTHORING.md) has the field and the
|
|
669
|
+
paragraph for a consumer of the files.
|
|
670
|
+
- **An animation's duration.** The format has no such field. The largest key time is
|
|
671
|
+
the only derivable answer and it is what a runtime plays to; it is wrong for an
|
|
672
|
+
animation that holds its last pose past its last key, so it is recorded as a finding
|
|
673
|
+
on every animation rather than chosen quietly.
|
|
674
|
+
|
|
675
|
+
Both specs carry a `note` that `ingest` writes itself, saying the file is decompiled
|
|
676
|
+
and naming the skeleton it came from — because a decompiled spec is indistinguishable
|
|
677
|
+
from an authored one by inspection, every gate here calls it green (it *is* green),
|
|
678
|
+
and no gate can catch a missing note.
|
|
679
|
+
|
|
680
|
+
[docs/INGEST.md](docs/INGEST.md) is the whole page on working from a file you were
|
|
681
|
+
handed; [docs/AUTHORING.md](docs/AUTHORING.md) §0.3 is the loop.
|
|
682
|
+
|
|
683
|
+
### The editor round trip — for a licence holder, never in CI
|
|
684
|
+
|
|
685
|
+
`tools/editor_roundtrip.ts` drives the loop the output's whole premise rests on:
|
|
686
|
+
build → **import into the Spine editor** → export back to JSON → gate, `diff`,
|
|
687
|
+
`render` and `check` the export against the build it came from.
|
|
688
|
+
|
|
689
|
+
```
|
|
690
|
+
bun cli.ts build --rig … --motion … --out build/ --copy-images
|
|
691
|
+
bun tools/editor_roundtrip.ts --build build/ --editor /Applications/Spine.app/Contents/MacOS/Spine
|
|
692
|
+
```
|
|
693
|
+
|
|
694
|
+
Of the editor's `--version` it records one line, the `Spine <x.y.z> …` line that
|
|
695
|
+
carries the version, and nothing else that command prints — the rest of it
|
|
696
|
+
names the licence holder — or `editor version: not found in --version output`
|
|
697
|
+
where no such line is there. That call carries the same `-u` as the import and
|
|
698
|
+
the export, so a trip pinned with `--editor-version <v>` names the editor it
|
|
699
|
+
pinned and says so on the line (`(read from --version under -u <v>, …)`); an
|
|
700
|
+
unpinned trip names the launcher's default. Once the export exists the version
|
|
701
|
+
is held against the export's own `skeleton.spine`, and a disagreement is a FAIL
|
|
702
|
+
naming both values — the line would otherwise name an editor the trip did not
|
|
703
|
+
run on. It prints the import and export exit codes, the
|
|
704
|
+
validator's verdict on the export, every `diff` measure that moved, `check`'s mean MAE and worst drift per
|
|
705
|
+
animation **for each skin the build and the export both declare** — one
|
|
706
|
+
render-and-check block per skin, with a per-skin roll-up under them, because a
|
|
707
|
+
rig's contested art lives in its named skins and a single un-skinned check draws
|
|
708
|
+
none of it; a skin only one side declares is a FAIL naming it as **lost** (or
|
|
709
|
+
**added**) **by the export**, with `diff`'s `attachments.skins` beside it, and is
|
|
710
|
+
rendered on neither side — and a
|
|
711
|
+
field-by-field list of what the editor rewrote. Every step quotes what its child
|
|
712
|
+
said when that child did not do what it was for, the renderers included — the
|
|
713
|
+
editor's words in full and in order, except its `Licensed to:` line, which names
|
|
714
|
+
the licence holder and is replaced by a line saying it was withheld; a skin
|
|
715
|
+
**neither** side can draw — a hit-box rig, say — is a **SKIP** naming that, not a
|
|
716
|
+
red, because `check` had nothing to compare and `diff` and `validate` have
|
|
717
|
+
already measured the rig. One side drawing where the other does not is the
|
|
718
|
+
divergence the trip exists to find and stays a failure. A human edit made in the
|
|
719
|
+
editor survives the trip back.
|
|
720
|
+
|
|
721
|
+
🔒 **It requires a licensed Spine editor on the machine, by construction**, and
|
|
722
|
+
drives only the [documented command line](https://esotericsoftware.com/spine-command-line-interface)
|
|
723
|
+
— never the UI, and it produces nothing the editor did not produce. With no
|
|
724
|
+
editor present it refuses by name and exits non-zero, and so does the **trial**:
|
|
725
|
+
the trial cannot save projects or export animation data, so the refusal names
|
|
726
|
+
what it found — the executable, the bundle, the `CFBundleName` that bundle
|
|
727
|
+
declares, or the banner the binary prints about itself — rather than starting it
|
|
728
|
+
and failing downstream. Both refusals point at `--exported <file>`, which
|
|
729
|
+
measures an export the editor already made and is the half of this tool that
|
|
730
|
+
needs no editor at all. A build whose `skeleton.json` is missing, is not JSON, or
|
|
731
|
+
holds JSON that is not one object is refused by name before the editor starts,
|
|
732
|
+
and so is a build with no atlas or an atlas that is a directory, or an
|
|
733
|
+
`--exported` file that is not one JSON object.
|
|
734
|
+
|
|
735
|
+
⛔ **Run the round trip by hand, on a machine that has the editor.** CI has no
|
|
736
|
+
editor, so no automated check runs it.
|
|
737
|
+
|
|
738
|
+
⚠️ Build with `--copy-images`. An ordinary build's atlas names its pages by a
|
|
739
|
+
relative path back to the art directory, and the round trip copies that atlas to
|
|
740
|
+
a directory at another depth — the tool refuses such a build by name rather than
|
|
741
|
+
letting `A17` blame the editor for the harness's own doing.
|
|
742
|
+
|
|
743
|
+
## Documentation
|
|
744
|
+
|
|
745
|
+
| Document | For |
|
|
746
|
+
| --- | --- |
|
|
747
|
+
| 📘 **[docs/AUTHORING.md](docs/AUTHORING.md)** | **the format guide, and the one to read before writing a spec.** Both input files field by field with a complete minimal example each, every field with its Spine meaning, the rules that decide what is emitted, the build → read the report → fix → repeat loop, the map from every named failure to the file that has to change, and the features rigc refuses by name so you do not spend a loop discovering them. It travels **inside the npm package**, at `node_modules/spine-rigc/docs/AUTHORING.md` |
|
|
748
|
+
| 🦴 **[docs/RIGGING.md](docs/RIGGING.md)** | **authoring the hierarchy.** Where a bone goes and why the art is pushed out on an offset, why a pivot in the wrong place looks like a search failure and what identifies one, moving a pivot and the child row that gets forgotten, gauges, siblings-not-a-chain, what a chain can reach and how many links it needs, why a local key is not a world key, duplicate art at mirrored pivots, and constraints as structure. Every section is a stumble the run records hold more than once, ranked by how often. Ships in the package too |
|
|
749
|
+
| 🎞️ **[docs/MOTION.md](docs/MOTION.md)** | **the key-pose recipe.** How to get two poses, what a pair of poses does and does not fix, the in-betweening rules and where each comes from, and how to spread candidates so a ballot informs. Ships in the package too |
|
|
750
|
+
| 🙂 **[docs/FACE.md](docs/FACE.md)** | **authoring a face.** A blink, a gaze and a 2.5D head turn on plain Spine data: the one line of yaw arithmetic every number in a turn comes from, depth as the parameter you are actually authoring, where to put a grid's columns and the angle at which any grid folds, what foreshortens and what does not, channel allocation before the first key, and the three cliffs with their angles. Also the deform audit gap, demonstrated — a folded mesh gates green — and the differential check that works today |
|
|
751
|
+
| 📥 **[docs/INGEST.md](docs/INGEST.md)** | **working with a skeleton you did not author.** What every command can and cannot do with a foreign `skeleton.json`, reading it with the toolchain, transcription as the route that makes it yours, what each validator complaint means on an export, and the re-pivot/rename/extend recipes. Ships in the package too |
|
|
752
|
+
| 🤖 **[docs/PROMPTING.md](docs/PROMPTING.md)** | **handing the authoring to an AI agent** — the prompt clauses a measured pilot run paid for, and what you can leave unsaid. Ships in the package too |
|
|
753
|
+
| 🔬 **[docs/SPEC_COVERAGE.md](docs/SPEC_COVERAGE.md)** | **the Spine 4.3 format, field by field.** Every key the 4.3 JSON parser and atlas reader take, what each defaults to, and the parser line it comes from — the rows rigc's refusals cite by part number. Ships in the package too |
|
|
754
|
+
| 🎓 **[the benchmark dossier](https://github.com/firejune/rigc/blob/main/docs/BENCHMARK.md)** | **why you can trust the output.** The yardstick, `diff` and `check` and what neither can see, the eight-rung ladder and the spineboy graduation exam, the run viewer, the 50 named assertions with their profiles, and the selftest that has watched every one of them fire. Repository material — it is not in the npm package |
|
|
755
|
+
| 📋 [LADDER.md](https://github.com/firejune/rigc/blob/main/docs/LADDER.md) · [GATE.md](https://github.com/firejune/rigc/blob/main/docs/GATE.md) · [PILOT.md](https://github.com/firejune/rigc/blob/main/docs/PILOT.md) | the live rung ledger, the clause statements a candidate is graded against, and how to run an agent through the ladder and score what comes back |
|
|
756
|
+
| 🔬 [SURVEY_2026-08-22.md](https://github.com/firejune/rigc/blob/main/docs/SURVEY_2026-08-22.md) | the survey the ladder was climbed by: rigc and the official examples as measured on 2026-08-22, and the gap list ordered by rung. A dated record, not kept current. Repository material |
|
|
757
|
+
| 🧬 [GENERATIONS.md](https://github.com/firejune/rigc/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 |
|
|
758
|
+
| 📓 [CASES.md](https://github.com/firejune/rigc/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 |
|
|
759
|
+
| 🗺️ [ROADMAP.md](https://github.com/firejune/rigc/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 |
|
|
760
|
+
| 📐 [CLAUDE.md](https://github.com/firejune/rigc/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/firejune/rigc/blob/main/CONTRIBUTING.md) calls it worth ten minutes before a first patch. Repository material |
|
|
761
|
+
|
|
762
|
+
## Why you can trust the output
|
|
763
|
+
|
|
764
|
+
rigc is measured against **Spine's own official example projects** — the
|
|
765
|
+
`1-weight-and-mass` … `8-follow-through` series as a difficulty ladder, with spineboy
|
|
766
|
+
as the graduation exam.
|
|
767
|
+
|
|
768
|
+
🎓 **The ladder is complete.** All eight numbered rungs and the
|
|
769
|
+
spineboy graduation exam are cleared and hold under the current gate, **v2.4**, every clause PASS or SKIP:
|
|
770
|
+
worst attributable slot drift **5.5550 px** against a 6.0 px bar — a **1.0801×**
|
|
771
|
+
margin, the thinnest of the ladder's **G2** figures, and **G5**'s 1.0376× is thinner
|
|
772
|
+
still — and **0 of 124** frame-change disagreements. Recompiling the same spec in a
|
|
773
|
+
different session reproduced every field of the measurement record **to the digit**.
|
|
774
|
+
The rungs stay in place as regression gates.
|
|
775
|
+
|
|
776
|
+
**Rung 7 clears on a read-down.** One of its three slots draws in every set and is
|
|
777
|
+
attributable in none; a read-down names the framing of its evidence, and a slot whose
|
|
778
|
+
attributability is **measured** to be capped below the bar reads down when everything
|
|
779
|
+
observable about it is independently verified strict. Every other rung and the
|
|
780
|
+
graduation exam pass on the clause itself. Recompiling a stored candidate reproduces its
|
|
781
|
+
record **to the digit within one gate**; across an instrument change the digits move,
|
|
782
|
+
and [docs/LADDER.md](https://github.com/firejune/rigc/blob/main/docs/LADDER.md) records
|
|
783
|
+
where. The standing figures quoted above are from its *gate-v2.4 re-inspection*, which
|
|
784
|
+
is the current sweep.
|
|
785
|
+
|
|
786
|
+
⚠️ **What that certifies, stated exactly.** That **the tool, the guide and the
|
|
787
|
+
protocol reach the bar across a bounded series of honest attempts, each residual
|
|
788
|
+
diagnosed and fixed** — spineboy took five, and the last inherited its
|
|
789
|
+
predecessor's specs under the run protocol's inheritance clause. It is **not**
|
|
790
|
+
that an agent authors a spineboy-scale rig from the brief alone in one run: the
|
|
791
|
+
ladder has not demonstrated that, and each row records which of the two it is.
|
|
792
|
+
|
|
793
|
+
🧪 **A separate series measures that harder question, and it has not been kind.**
|
|
794
|
+
From-zero attempts at spineboy — no inherited specs — have landed at **18.2, 18.8,
|
|
795
|
+
19.57, 7.86, 9.33 and 18.98 px** worst drift against the 6.0 px bar, a spread with no
|
|
796
|
+
monotone trend and every figure above the bar. They move no rung and reopen nothing —
|
|
797
|
+
a from-zero run is a tooling-progress measurement rather than a re-climb, which is
|
|
798
|
+
why the certification above is scoped to tool + guide + protocol. One of those
|
|
799
|
+
attempts states the residual in its own words: *"in motion it is not at editor
|
|
800
|
+
quality."* All six, with their verdicts, are in
|
|
801
|
+
[docs/LADDER.md](https://github.com/firejune/rigc/blob/main/docs/LADDER.md).
|
|
802
|
+
|
|
803
|
+
The whole dossier — the yardstick, `diff` and `check` and what neither of them can
|
|
804
|
+
see, every rung, the run viewer, the 50 assertions and the selftest behind them — is
|
|
805
|
+
[docs/BENCHMARK.md](https://github.com/firejune/rigc/blob/main/docs/BENCHMARK.md).
|
|
806
|
+
Live rung status is
|
|
807
|
+
[docs/LADDER.md](https://github.com/firejune/rigc/blob/main/docs/LADDER.md).
|
|
808
|
+
|
|
809
|
+
## Contributing
|
|
810
|
+
|
|
811
|
+
Issues are the ledger; see [CONTRIBUTING.md](https://github.com/firejune/rigc/blob/main/CONTRIBUTING.md) for what a change
|
|
812
|
+
has to clear before it lands. Releases are cut by release-please —
|
|
813
|
+
[RELEASING.md](https://github.com/firejune/rigc/blob/main/RELEASING.md).
|
|
814
|
+
|
|
815
|
+
## Licence
|
|
816
|
+
|
|
817
|
+
MIT — see [LICENSE](LICENSE). Third-party terms, including the Spine editor licence
|
|
818
|
+
requirement that this project inherits, are in [NOTICE.md](NOTICE.md).
|