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.
Files changed (213) hide show
  1. package/.claude-plugin/marketplace.json +19 -0
  2. package/.claude-plugin/plugin.json +13 -0
  3. package/LICENSE +30 -0
  4. package/NOTICE.md +145 -0
  5. package/README.md +817 -3
  6. package/bin/rigc.cjs +83 -0
  7. package/cli.ts +61 -0
  8. package/cli_core.ts +46 -0
  9. package/docs/AUTHORING.md +9923 -0
  10. package/docs/FACE.md +1948 -0
  11. package/docs/INGEST.md +1488 -0
  12. package/docs/MOTION.md +1241 -0
  13. package/docs/PROMPTING.md +109 -0
  14. package/docs/RIGGING.md +1441 -0
  15. package/docs/SPEC_COVERAGE.md +357 -0
  16. package/package.json +108 -4
  17. package/skills/rigc/SKILL.md +133 -0
  18. package/skills/rigc-face/SKILL.md +60 -0
  19. package/skills/rigc-ingest/SKILL.md +78 -0
  20. package/skills/rigc-motion/SKILL.md +51 -0
  21. package/skills/rigc-rigging/SKILL.md +49 -0
  22. package/src/areaband.ts +159 -0
  23. package/src/assertions/bodies/a01.ts +23 -0
  24. package/src/assertions/bodies/a02.ts +21 -0
  25. package/src/assertions/bodies/a03.ts +27 -0
  26. package/src/assertions/bodies/a04.ts +40 -0
  27. package/src/assertions/bodies/a05.ts +56 -0
  28. package/src/assertions/bodies/a06.ts +245 -0
  29. package/src/assertions/bodies/a07.ts +68 -0
  30. package/src/assertions/bodies/a08.ts +76 -0
  31. package/src/assertions/bodies/a09.ts +82 -0
  32. package/src/assertions/bodies/a10.ts +116 -0
  33. package/src/assertions/bodies/a11.ts +15 -0
  34. package/src/assertions/bodies/a12.ts +30 -0
  35. package/src/assertions/bodies/a13.ts +51 -0
  36. package/src/assertions/bodies/a14.ts +35 -0
  37. package/src/assertions/bodies/a15.ts +97 -0
  38. package/src/assertions/bodies/a16.ts +24 -0
  39. package/src/assertions/bodies/a17.ts +26 -0
  40. package/src/assertions/bodies/a18.ts +62 -0
  41. package/src/assertions/bodies/a19.ts +404 -0
  42. package/src/assertions/bodies/a20.ts +122 -0
  43. package/src/assertions/bodies/a21.ts +190 -0
  44. package/src/assertions/bodies/a22.ts +39 -0
  45. package/src/assertions/bodies/a23.ts +305 -0
  46. package/src/assertions/bodies/a24.ts +68 -0
  47. package/src/assertions/bodies/a25.ts +39 -0
  48. package/src/assertions/bodies/a26.ts +61 -0
  49. package/src/assertions/bodies/a27.ts +33 -0
  50. package/src/assertions/bodies/a28.ts +70 -0
  51. package/src/assertions/bodies/a29.ts +34 -0
  52. package/src/assertions/bodies/a30.ts +50 -0
  53. package/src/assertions/bodies/a31.ts +61 -0
  54. package/src/assertions/bodies/a32.ts +44 -0
  55. package/src/assertions/bodies/a33.ts +110 -0
  56. package/src/assertions/bodies/a34.ts +133 -0
  57. package/src/assertions/bodies/a35.ts +160 -0
  58. package/src/assertions/bodies/a36.ts +81 -0
  59. package/src/assertions/bodies/a37.ts +77 -0
  60. package/src/assertions/bodies/a38.ts +73 -0
  61. package/src/assertions/bodies/a39.ts +303 -0
  62. package/src/assertions/bodies/a40.ts +128 -0
  63. package/src/assertions/bodies/a42.ts +97 -0
  64. package/src/assertions/bodies/a43.ts +181 -0
  65. package/src/assertions/bodies/a44.ts +23 -0
  66. package/src/assertions/bodies/a45.ts +172 -0
  67. package/src/assertions/bodies/a46.ts +224 -0
  68. package/src/assertions/bodies/a47.ts +126 -0
  69. package/src/assertions/bodies/a48.ts +83 -0
  70. package/src/assertions/bodies/a49.ts +81 -0
  71. package/src/assertions/bodies/a50.ts +97 -0
  72. package/src/assertions/constraint_words.ts +169 -0
  73. package/src/assertions/emitted/index.ts +148 -0
  74. package/src/assertions/facts/animated_bones.ts +30 -0
  75. package/src/assertions/facts/animation_durations.ts +37 -0
  76. package/src/assertions/facts/atlas_pages.ts +19 -0
  77. package/src/assertions/facts/atlas_regions.ts +52 -0
  78. package/src/assertions/facts/bone_timelines.ts +37 -0
  79. package/src/assertions/facts/constraint_targets.ts +56 -0
  80. package/src/assertions/facts/constraints.ts +155 -0
  81. package/src/assertions/facts/deform_survey.ts +27 -0
  82. package/src/assertions/facts/event_keys.ts +55 -0
  83. package/src/assertions/facts/linked_meshes.ts +38 -0
  84. package/src/assertions/facts/mesh_attachments.ts +100 -0
  85. package/src/assertions/facts/region_joins.ts +34 -0
  86. package/src/assertions/facts/sequences.ts +85 -0
  87. package/src/assertions/facts/skeleton_roster.ts +45 -0
  88. package/src/assertions/facts/skin_entries.ts +37 -0
  89. package/src/assertions/facts/skin_members.ts +53 -0
  90. package/src/assertions/facts/slider_composition.ts +78 -0
  91. package/src/assertions/facts/slot_colour.ts +43 -0
  92. package/src/assertions/facts/stage.ts +27 -0
  93. package/src/assertions/facts/stage_box.ts +65 -0
  94. package/src/assertions/facts/stepped_poses.ts +74 -0
  95. package/src/assertions/facts/two_colour.ts +52 -0
  96. package/src/assertions/facts/vertex_polygons.ts +53 -0
  97. package/src/assertions/footprints.ts +367 -0
  98. package/src/assertions/harness.ts +109 -0
  99. package/src/assertions/inward_advance.ts +58 -0
  100. package/src/assertions/kinds.ts +105 -0
  101. package/src/assertions/mesh_kinds.ts +56 -0
  102. package/src/assertions/model/animated_bones.ts +38 -0
  103. package/src/assertions/model/animation_durations.ts +57 -0
  104. package/src/assertions/model/atlas_pages.ts +15 -0
  105. package/src/assertions/model/atlas_regions.ts +76 -0
  106. package/src/assertions/model/bone_timelines.ts +58 -0
  107. package/src/assertions/model/constraint_targets.ts +82 -0
  108. package/src/assertions/model/constraints.ts +233 -0
  109. package/src/assertions/model/declared.ts +125 -0
  110. package/src/assertions/model/deform_survey.ts +24 -0
  111. package/src/assertions/model/event_keys.ts +45 -0
  112. package/src/assertions/model/given.ts +45 -0
  113. package/src/assertions/model/index.ts +398 -0
  114. package/src/assertions/model/linked_meshes.ts +24 -0
  115. package/src/assertions/model/mesh_attachments.ts +119 -0
  116. package/src/assertions/model/parse.ts +146 -0
  117. package/src/assertions/model/region_joins.ts +67 -0
  118. package/src/assertions/model/runtime_timelines.ts +78 -0
  119. package/src/assertions/model/sequences.ts +157 -0
  120. package/src/assertions/model/skeleton_roster.ts +23 -0
  121. package/src/assertions/model/skin_entries.ts +69 -0
  122. package/src/assertions/model/skin_members.ts +64 -0
  123. package/src/assertions/model/slider_composition.ts +193 -0
  124. package/src/assertions/model/slot_colour.ts +81 -0
  125. package/src/assertions/model/stage.ts +28 -0
  126. package/src/assertions/model/stage_box.ts +51 -0
  127. package/src/assertions/model/stepped_poses.ts +105 -0
  128. package/src/assertions/model/two_colour.ts +61 -0
  129. package/src/assertions/model/vertex_polygons.ts +72 -0
  130. package/src/assertions/reasons.ts +129 -0
  131. package/src/assertions/region_lookups.ts +61 -0
  132. package/src/assertions/report.ts +189 -0
  133. package/src/assertions/values.ts +39 -0
  134. package/src/atlas.ts +2870 -0
  135. package/src/ballot.ts +866 -0
  136. package/src/bonedist.ts +643 -0
  137. package/src/chainfit.ts +2752 -0
  138. package/src/chains.ts +170 -0
  139. package/src/check.ts +4303 -0
  140. package/src/checkpics.ts +295 -0
  141. package/src/cli/core_commands.ts +1627 -0
  142. package/src/cli/repack.ts +414 -0
  143. package/src/cli/shared.ts +2776 -0
  144. package/src/cli/spine_commands.ts +820 -0
  145. package/src/compile.ts +9414 -0
  146. package/src/core/additive.ts +458 -0
  147. package/src/core/animation.ts +1050 -0
  148. package/src/core/clipping.ts +696 -0
  149. package/src/core/constraints.ts +1876 -0
  150. package/src/core/constraints_path.ts +964 -0
  151. package/src/core/constraints_physics.ts +881 -0
  152. package/src/core/constraints_slider.ts +635 -0
  153. package/src/core/deform.ts +613 -0
  154. package/src/core/draw_order.ts +125 -0
  155. package/src/core/events.ts +135 -0
  156. package/src/core/hooks.ts +249 -0
  157. package/src/core/index.ts +1400 -0
  158. package/src/core/raw.ts +739 -0
  159. package/src/core/skins.ts +129 -0
  160. package/src/core/uvs.ts +469 -0
  161. package/src/core/vertices.ts +490 -0
  162. package/src/core/walk.ts +197 -0
  163. package/src/core/world.ts +289 -0
  164. package/src/correspondence.ts +15 -0
  165. package/src/deformbuild.ts +60 -0
  166. package/src/deformgen.ts +630 -0
  167. package/src/deformmeasure.ts +732 -0
  168. package/src/deformreport.ts +373 -0
  169. package/src/deformstructure.ts +386 -0
  170. package/src/deformsurvey.ts +2162 -0
  171. package/src/depth.ts +784 -0
  172. package/src/diff.ts +2252 -0
  173. package/src/emit.ts +134 -0
  174. package/src/emit_spine.ts +854 -0
  175. package/src/errors.ts +53 -0
  176. package/src/framing.ts +819 -0
  177. package/src/generation.ts +139 -0
  178. package/src/ingest.ts +2293 -0
  179. package/src/json-position.ts +253 -0
  180. package/src/keyorder.ts +587 -0
  181. package/src/keys.ts +486 -0
  182. package/src/ladder.ts +121 -0
  183. package/src/mesh.ts +2382 -0
  184. package/src/meshcompare.ts +1188 -0
  185. package/src/meshquality.ts +2042 -0
  186. package/src/meshrasters.ts +944 -0
  187. package/src/meshreduce.ts +1425 -0
  188. package/src/model.ts +1245 -0
  189. package/src/motion.ts +809 -0
  190. package/src/nonfinite.ts +54 -0
  191. package/src/package_meta.ts +48 -0
  192. package/src/png.ts +297 -0
  193. package/src/pose.ts +2324 -0
  194. package/src/preview.ts +434 -0
  195. package/src/region_joins.ts +54 -0
  196. package/src/render.ts +1013 -0
  197. package/src/render_core.ts +871 -0
  198. package/src/render_shared.ts +2958 -0
  199. package/src/repack.ts +495 -0
  200. package/src/rig.ts +2941 -0
  201. package/src/slots.ts +892 -0
  202. package/src/spine_side.ts +138 -0
  203. package/src/timelines.ts +837 -0
  204. package/src/trackgen.ts +364 -0
  205. package/src/transform.ts +310 -0
  206. package/src/types.ts +1797 -0
  207. package/src/validate.ts +3875 -0
  208. package/tools/contact.ts +126 -0
  209. package/tools/editor_roundtrip.ts +1641 -0
  210. package/tools/font5x7.ts +101 -0
  211. package/tools/measure_contact_depth.ts +105 -0
  212. package/tools/plate.ts +508 -0
  213. package/tools/png_probe.mjs +72 -0
@@ -0,0 +1,357 @@
1
+ # Spine 4.3 export-format surface
2
+
3
+ This page is the Spine 4.3 skeleton JSON and atlas text surface, read off the `spine-core`
4
+ this package pins (`@esotericsoftware/spine-core@4.3.13`): every field the parser reads, what it
5
+ defaults to, and the parser line each row cites. rigc's refusals point at its rows by part
6
+ number — `part 1-6` is §1.6. It is not a statement of what rigc emits; that is
7
+ [AUTHORING.md](AUTHORING.md).
8
+
9
+ **Sources.** S1 is [`spine-core/src/SkeletonJson.ts`](https://github.com/EsotericSoftware/spine-runtimes/blob/4.3/spine-ts/spine-core/src/SkeletonJson.ts),
10
+ S2 [`SkeletonBinary.ts`](https://github.com/EsotericSoftware/spine-runtimes/blob/4.3/spine-ts/spine-core/src/SkeletonBinary.ts)
11
+ and S3 [`TextureAtlas.ts`](https://github.com/EsotericSoftware/spine-runtimes/blob/4.3/spine-ts/spine-core/src/TextureAtlas.ts),
12
+ all on branch `4.3`; S6 is `SkeletonJson.ts` on branches `4.0`, `4.1` and `4.2`, for the
13
+ timeline below. Bare `:NNN` line numbers are into those TypeScript sources — ⚠️ the published npm
14
+ package ships **`dist/` only**, so the local copy to read is
15
+ `node_modules/@esotericsoftware/spine-core/dist/<File>.js`, whose line numbers differ; the
16
+ parser's full `case` label set is identical in both. 📘 = the field is described on an official
17
+ Esoteric docs page. 🔍 = source-only; the parser reads it but no public doc page describes it.
18
+
19
+ ---
20
+
21
+ ## Format-change timeline, verified from the four parsers (S1, S6)
22
+
23
+ | Change | 4.0 | 4.1 | 4.2 | 4.3 |
24
+ | --- | --- | --- | --- | --- |
25
+ | Constraints location | `root.ik` / `root.transform` / `root.path` arrays | same | same **+ `root.physics`** | **single `root.constraints[]`, `type` discriminator** |
26
+ | Bone inheritance field | `"transform"` | `"transform"` | **`"inherit"`** | `"inherit"` |
27
+ | `sequence` on region/mesh | ✗ | **✓** | ✓ | ✓ |
28
+ | `skeleton.referenceScale` | ✗ | ✗ | **✓** | ✓ |
29
+ | `physics` constraint + timelines | ✗ | ✗ | **✓** | ✓ |
30
+ | `slider` constraint + timelines | ✗ | ✗ | ✗ | **✓** |
31
+ | `drawOrderFolder` timeline | ✗ | ✗ | ✗ | **✓** |
32
+ | Bone `icon`/`iconSize`/`iconRotation` (JSON) | ✗ | ✗ | ✗ | **✓** |
33
+ | Transform constraint model | `target` + `local`/`relative` + `offsetRotation` + mix\* | same | same | **`source` + `properties{from→to}` + `localSource`/`localTarget`/`additive`/`clamp`** |
34
+ | IK `uniform` bool | ✓ | ✓ | ✓ | **replaced by `scaleY` (`ScaleYMode`)** |
35
+
36
+ Evidence: `root.ik` present in 4.0/4.1/4.2 and absent in 4.3; `root.constraints` absent in
37
+ 4.0/4.1/4.2 and present in 4.3; `uniform` at `4.2 SkeletonJson.ts:161`, gone in 4.3; `target` at
38
+ `4.2:182,222` vs `source` at `4.3:188`.
39
+
40
+ ⇒ **A file exported by a pre-4.3 editor carries the *legacy* shape, and the 4.3 parser drops
41
+ those constraints on the floor with no error.**
42
+
43
+ ---
44
+
45
+ ## Part 1 — the full 4.3 JSON surface
46
+
47
+ All line numbers refer to **S1**, `SkeletonJson.ts` (branch `4.3`, 1406 lines), unless prefixed.
48
+ Everything reaches the parser through `getValue(map, property, defaultValue)` at `:1404-1406`, so
49
+ **"required" below means "the parser dereferences it unconditionally and produces `NaN`/`undefined`
50
+ if absent"** — never "the parser complains".
51
+
52
+ ### 1.1 Skeleton header — `root.skeleton` (`:75-87`)
53
+
54
+ | Field | Default | Notes | Doc |
55
+ | --- | --- | --- | --- |
56
+ | `hash` | `undefined` | opaque; tools use it for change detection | 📘 |
57
+ | `spine` | `undefined` | stored as `skeletonData.version`, **never compared** in spine-ts JSON | 📘 |
58
+ | `x`, `y`, `width`, `height` | `undefined` | the setup-pose bounding box | 📘 |
59
+ | `referenceScale` | `100` (× `scale`) | **4.2+**; drives runtime physics/scale reference | 🔍 |
60
+ | `fps` | `undefined` → `SkeletonData.fps` stays `30` | nonessential | 📘 |
61
+ | `images` | `null` | nonessential | 📘 |
62
+ | `audio` | `null` | nonessential | 📘 |
63
+
64
+ The whole `skeleton` block is optional (`if (skeletonMap)` at `:76`).
65
+
66
+ ### 1.2 Bones — `root.bones[]` (`:90-118`)
67
+
68
+ | Field | Default | Notes | Doc |
69
+ | --- | --- | --- | --- |
70
+ | `name` | — | required in practice (`new BoneData(…, boneMap.name, …)`, `:97`) | 📘 |
71
+ | `parent` | `null` | resolved by name; **must be declared earlier in the array** | 📘 |
72
+ | `length` | `0` (× scale) | | 📘 |
73
+ | `x`, `y` | `0` (× scale) | local to parent | 📘 |
74
+ | `rotation` | `0` | degrees, CCW, y-up | 📘 |
75
+ | `scaleX`, `scaleY` | `1` | | 📘 |
76
+ | `shearX`, `shearY` | `0` | | 📘 |
77
+ | `inherit` | `"Normal"` | enum `Normal \| OnlyTranslation \| NoRotationOrReflection \| NoScale \| NoScaleOrReflection` (`BoneData.ts:80`). Resolved by `Utils.enumValue` which upper-cases the first letter (`Utils.ts:392-394`), so `"noScale"` and `"NoScale"` both work. **4.2+ name; 4.0/4.1 called it `transform`** | 🔍 |
78
+ | `skin` | `false` | → `data.skinRequired` | 📘 (as "skin") |
79
+ | `color` | none | hex string `rrggbbaa`, nonessential | 📘 |
80
+ | `icon` | `undefined` | **4.3, editor-only affordance** | 🔍 |
81
+ | `iconSize` | `1` | 4.3 | 🔍 |
82
+ | `iconRotation` | `0` | 4.3 | 🔍 |
83
+ | ~~`visible`~~ | — | **NOT read from JSON.** `SkeletonBinary.ts:126` reads it (nonessential); the JSON parser has no equivalent. See §1.11. | 🔍 |
84
+
85
+ ### 1.3 Slots — `root.slots[]` (`:121-141`)
86
+
87
+ | Field | Default | Notes | Doc |
88
+ | --- | --- | --- | --- |
89
+ | `name` | — | required | 📘 |
90
+ | `bone` | — | **required**; a miss throws `Couldn't find bone … for slot …` (`:127`) | 📘 |
91
+ | `color` | white | `rrggbbaa` | 📘 |
92
+ | `dark` | none | two-colour tint; only set when present, and `darkColor` stays `null` otherwise (`:133-134`) | 📘 |
93
+ | `attachment` | `null` | setup-pose attachment name | 📘 |
94
+ | `blend` | `"normal"` | enum `Normal \| Additive \| Multiply \| Screen` (`SlotData.ts:64`) | 📘 |
95
+ | `visible` | `true` | 4.3, nonessential-ish; **JSON reads this one** (`:138`) | 🔍 |
96
+
97
+ **Draw order is the array order of `slots`.** There is no separate setup draw-order field.
98
+
99
+ ### 1.4 Constraints — `root.constraints[]` (`:144-369`) — **4.3 shape**
100
+
101
+ Common to every entry: `name`, `type`, `skin` (default `false` → `skinRequired`, `:147`).
102
+ `type` is read with `getValue(constraintMap, "type", false)`, so **an entry with no `type` matches
103
+ no case and is silently dropped** (`:148-367`, no `default:` branch).
104
+
105
+ **`type: "ik"`** (`:149-176`) 📘 (3.8 shape only)
106
+
107
+ | Field | Default |
108
+ | --- | --- |
109
+ | `bones[]` | required, ≥1, resolved by name (throws on miss) |
110
+ | `target` | required (throws on miss) |
111
+ | `scaleY` | absent → `ScaleYMode.None`; enum `None \| Uniform \| Volume` (`ConstraintData.ts:50`). **4.3 replacement for 4.2's `uniform: bool`** |
112
+ | `mix` | `1` |
113
+ | `softness` | `0` (× scale) |
114
+ | `bendPositive` | `true` → `bendDirection = ±1` |
115
+ | `compress` | `false` |
116
+ | `stretch` | `false` |
117
+
118
+ **`type: "transform"`** (`:177-268`) 🔍 — completely rebuilt in 4.3
119
+
120
+ | Field | Default |
121
+ | --- | --- |
122
+ | `bones[]` | required |
123
+ | `source` | required (4.2 called this `target`) |
124
+ | `localSource`, `localTarget`, `additive`, `clamp` | `false` |
125
+ | `properties` | `{}` — a map `fromName → { offset, to: { toName → { offset, max, scale } } }`. `fromName`/`toName` ∈ `rotate \| x \| y \| scaleX \| scaleY \| shearY`; anything else **throws** (`:241`, `:521`). `x`/`y` offsets are scaled (`propertyScale`, `:526-532`) |
126
+ | `rotation`, `x`, `y`, `scaleX`, `scaleY`, `shearY` | `0` — the constraint's offsets array |
127
+ | `mixRotate`, `mixX`, `mixScaleX`, `mixShearY` | `1`; `mixY` defaults to `mixX`, `mixScaleY` to `mixScaleX`. **Each mix is only read if the matching `to` property was declared** (`:259-264`) |
128
+
129
+ **`type: "path"`** (`:269-300`) 📘 (3.8 shape)
130
+
131
+ | Field | Default |
132
+ | --- | --- |
133
+ | `bones[]`, `slot` | required |
134
+ | `positionMode` | `"Percent"` — `Fixed \| Percent` (`PathConstraintData.ts:77`) |
135
+ | `spacingMode` | `"Length"` — `Length \| Fixed \| Percent \| Proportional` (`:82`) |
136
+ | `rotateMode` | `"Tangent"` — `Tangent \| Chain \| ChainScale` (`:87`) |
137
+ | `rotation` | `0` → `offsetRotation` |
138
+ | `position` | `0`; × scale iff `positionMode == Fixed` |
139
+ | `spacing` | `0`; × scale iff `spacingMode ∈ {Length, Fixed}` |
140
+ | `mixRotate`, `mixX` | `1`; `mixY` defaults to `mixX` |
141
+
142
+ **`type: "physics"`** (`:301-339`) 🔍 — 4.2+
143
+
144
+ | Field | Default |
145
+ | --- | --- |
146
+ | `bone` | required (throws) |
147
+ | `x`, `y`, `rotate`, `scaleX`, `shearX` | `0` — **the components. All zero = a constraint that parses and does nothing** |
148
+ | `scaleY` | absent → `ScaleYMode.None` (4.3) |
149
+ | `limit` | `5000` (× scale) |
150
+ | `fps` | `60` → `step = 1/fps` |
151
+ | `inertia` | `0.5` |
152
+ | `strength` | `100` |
153
+ | `damping` | `0.85` |
154
+ | `mass` | `1` → stored as `massInverse = 1/mass` |
155
+ | `wind`, `gravity` | `0` |
156
+ | `mix` | `1` |
157
+ | `inertiaGlobal`, `strengthGlobal`, `dampingGlobal`, `massGlobal`, `windGlobal`, `gravityGlobal`, `mixGlobal` | `false` |
158
+
159
+ **`type: "slider"`** (`:340-366`) 🔍 — **new in 4.3, undocumented anywhere public**
160
+
161
+ | Field | Default |
162
+ | --- | --- |
163
+ | `additive`, `loop` | `false` |
164
+ | `mix` | `1` |
165
+ | `bone` | optional. **Presence switches the whole model**: with a bone it is a property-driven slider, without one it is a time slider (`time`, default `0`, `:361`) |
166
+ | `property` | required when `bone` is set; same six `from` names as the transform constraint |
167
+ | `from` | `0` (× propertyScale) → `data.property.offset` |
168
+ | `to` | `0` → `data.offset` |
169
+ | `scale` | `1` ÷ propertyScale |
170
+ | `max` | `0` |
171
+ | `local` | `false` |
172
+ | `animation` | resolved in a **second pass over `root.constraints`** after animations are read (`:495-507`); a miss throws `Slider animation not found` |
173
+
174
+ ### 1.5 Skins — `root.skins[]` (`:372-443`)
175
+
176
+ | Field | Default | Notes |
177
+ | --- | --- | --- |
178
+ | `name` | — | the skin named `"default"` becomes `skeletonData.defaultSkin` (`:441`) |
179
+ | `bones[]` | none | bone names this skin activates (`:377-384`) |
180
+ | `ik[]`, `transform[]`, `path[]`, `physics[]`, `slider[]` | none | **constraint names, still split per type inside a skin** even though the top-level array was unified (`:386-429`) |
181
+ | `attachments` | `{}` | `slotName → { placeholderName → attachmentMap }` (`:431-439`) |
182
+ | ~~`color`~~ | — | **not read from JSON.** `Skin.color` exists with a default of `fe9e4fff` (`Skin.ts:71-72`) and only `SkeletonBinary.ts:448` sets it. |
183
+
184
+ The **placeholder** (the key) and the attachment's own `name` are different things: `name` defaults
185
+ to the placeholder (`:537`), and `path` defaults to `name` (`:541`, `:570`). Three-level indirection:
186
+ placeholder → name → path → atlas region.
187
+
188
+ ### 1.6 Attachments (`readAttachment`, `:535-654`) — `type` defaults to `"region"` (`:539`)
189
+
190
+ | Type | Fields (default) | Line | Doc |
191
+ | --- | --- | --- | --- |
192
+ | `region` | `path`(=name), `sequence`(null), `x`(0×s), `y`(0×s), `scaleX`(1), `scaleY`(1), `rotation`(0), **`width`/`height` (no default — `map.width * scale`, `undefined` → `NaN`)**, `color` | `:540-559` | 📘 |
193
+ | `boundingbox` | `vertexCount` (no default), `vertices`, `color` | `:560-567` | 📘 |
194
+ | `mesh` | `path`(=name), `sequence`, `color`, `width`(0), `height`(0), `uvs` (no default — **its length defines `worldVerticesLength`**), `triangles` (no default — `undefined` if missing), `vertices`, `edges`(null), `hull`(0, **stored ×2** as `hullLength`) | `:568-605` | 📘 |
195
+ | `linkedmesh` | same head, then `source` (required to make it linked), `slot`(null), `skin`(null), `timelines`(true). **A map with `type:"mesh"` and a `source` key is also a linked mesh** — the two cases share one branch (`:568-569`) and the `source` check at `:582` is what decides | `:568-605` | 📘 |
196
+ | `path` | `closed`(false), `constantSpeed`(true), `vertexCount` (no default), `vertices`, `lengths` (no default — `map.lengths.length` is dereferenced), `color` | `:606-623` | 📘 |
197
+ | `point` | `x`(0×s), `y`(0×s), `rotation`(0), `color` | `:624-634` | 📘 |
198
+ | `clipping` | `end`(null → slot name), `convex`(false, **4.3**), `inverse`(false, **4.3**), `vertexCount`, `vertices`, `color` | `:635-651` | 📘 (convex/inverse 🔍) |
199
+
200
+ **Any other `type` string returns `null`** (`:653`) — the attachment vanishes with no error.
201
+
202
+ **`sequence`** (`readSequence`, `:656-663`) 🔍 — region and mesh only:
203
+ `count` (0), `start` (1), `digits` (0), `setup` (0). Absent → `new Sequence(1, false)`.
204
+
205
+ **Vertex encoding** (`readVertices`, `:666-693`) — **the highest-risk field in the format.**
206
+ There is no flag. If `vertices.length === verticesLength` (i.e. `uvs.length`, or `vertexCount<<1`)
207
+ it is read as **unweighted** x/y pairs; otherwise as the **weighted** run-length encoding
208
+ `boneCount, (boneIndex, bindX, bindY, weight) × boneCount, …`. A coincidental length match reads
209
+ weight data as coordinates. Editor exports carry both encodings.
210
+
211
+ 🚨 The second risk in the same field is `boneIndex`: it is a position in the emitted bone array,
212
+ so the run means something different the moment the bone list changes, and nothing in the file
213
+ records what it used to mean. A rig spec therefore writes `weights` — the same data with the bones
214
+ **named** — and rigc encodes this run on emit. The raw form stays reachable behind
215
+ `"boneIndexing": "raw"` for transcribing an export verbatim.
216
+
217
+ ### 1.7 Events — `root.events` (object, not array) (`:469-484`)
218
+
219
+ `eventName → { int (0), float (0), string (""), audio (null), volume, balance }`.
220
+ **`volume` and `balance` are only read when `audio` is set** (`:478-481`) — otherwise the setup values
221
+ stand. 📘
222
+
223
+ ### 1.8 Animation timelines — `root.animations[animName]` (`readAnimation`, `:696-1272`)
224
+
225
+ Top-level groups inside one animation: `slots`, `bones`, `ik`, `transform`, `path`, `physics`,
226
+ `slider`, `attachments`, `drawOrder`, `drawOrderFolder`, `events`, plus a nonessential `color`
227
+ (`:1268-1269`). Anything else is ignored.
228
+
229
+ | Group | Timeline | Value fields per key | Curve channels | Line | Doc |
230
+ | --- | --- | --- | --- | --- | --- |
231
+ | `slots.<slot>` | `attachment` | `name` (nullable) | **none** | `:713-721` | 📘 |
232
+ | | `rgba` | `color` (`rrggbbaa`) | 4 | `:722-751` | 📘 |
233
+ | | `rgb` | `color` (`rrggbb`) | 3 | `:752-780` | 🔍 |
234
+ | | `alpha` | `value` | 1 | `:781-784` | 🔍 |
235
+ | | `rgba2` | `light`, `dark` | 7 | `:785-821` | 🔍 |
236
+ | | `rgb2` | `light`, `dark` | 6 | `:822-857` | 🔍 |
237
+ | | *anything else* | — | — | **throws** `Invalid timeline type for a slot` (`:858-859`) | |
238
+ | `bones.<bone>` | `rotate` | `value` | 1 | `:878` | 📘 |
239
+ | | `translate` | `x`, `y` | 2 | `:879` | 📘 |
240
+ | | `translatex` / `translatey` | `value` | 1 | `:880-881` | 🔍 |
241
+ | | `scale` | `x`, `y` | 2 | `:882` | 📘 |
242
+ | | `scalex` / `scaley` | `value` | 1 | `:883-884` | 🔍 |
243
+ | | `shear` | `x`, `y` | 2 | `:885` | 📘 |
244
+ | | `shearx` / `sheary` | `value` | 1 | `:886-887` | 🔍 |
245
+ | | `inherit` | `inherit` (enum string) | **none** | `:888-896` | 🔍 |
246
+ | | *anything else* | — | — | **throws** `Invalid timeline type for a bone` (`:897-898`) | |
247
+ | `ik.<constraint>` | (one array, no sub-name) | `mix`(1), `softness`(0×s), `bendPositive`(true), `compress`(false), `stretch`(false) | 2 (mix, softness) | `:906-945` | 📘 |
248
+ | `transform.<constraint>` | (one array) | `mixRotate`(1), `mixX`(1), `mixY`(=mixX), `mixScaleX`(1), `mixScaleY`(1), `mixShearY`(1) | 6 | `:948-999` | 📘 |
249
+ | `path.<constraint>` | `position` | `value` | 1 | `:1015-1019` | 📘 |
250
+ | | `spacing` | `value` | 1 | `:1020-1024` | 📘 |
251
+ | | `mix` | `mixRotate`, `mixX`, `mixY` | 3 | `:1025-1056` | 📘 |
252
+ | `physics.<constraint>` | `inertia`/`strength`/`damping`/`mass`/`wind`/`gravity` | `value` (default 0) | 1 each | `:1088-1093` | 🔍 |
253
+ | | `mix` | `value` (default **1**) | 1 | `:1094-1098` | 🔍 |
254
+ | | `reset` | *no value* — time only | **none** | `:1080-1086` | 🔍 |
255
+ | | *anything else* | — | — | silently `continue`d (`:1099`) — **no throw** | |
256
+ | `slider.<constraint>` | `time` | `value` (default 1) | 1 | `:1121` | 🔍 |
257
+ | | `mix` | `value` (default 1) | 1 | `:1122` | 🔍 |
258
+ | `attachments.<skin>.<slot>.<attachment>` | `deform` | `offset`(0), `vertices[]` | 1 | `:1149-1187` | 📘 |
259
+ | | `sequence` | `time`, `mode`(`"hold"`), `index`(0), `delay`(inherits previous) | **none** | `:1188-1201` | 🔍 |
260
+ | | *anything else* | — | — | silently ignored | |
261
+ | `drawOrder` | (array of keys) | `time`, `offsets[]` of `{slot, offset}`. **No `offsets` = reset to setup order** (`:1352-1353`) | **none** | `:1209-1217` | 📘 (spelled `draworder`) |
262
+ | `drawOrderFolder` | (array of folders) | `slots[]` (slot names in the folder), `keys[]` of the same `{time, offsets}` shape, resolved *within the folder* | **none** | `:1220-1239` | 🔍 **4.3 only** |
263
+ | `events` | (array) | `name` (required, throws on miss), `time`(0), `int`/`float`/`string` (default = event's setup), `volume`/`balance` **only when the event has an audio path** | **none** | `:1242-1261` | 📘 |
264
+
265
+ Important asymmetries worth writing down:
266
+
267
+ - **The physics group's constraint name may be the empty string** (`:1067`), which yields `index = -1`
268
+ and applies to *all* physics constraints. There is no documentation of this.
269
+ - **A `deform` key with no `vertices`** resets to the setup mesh (weighted → zeros, unweighted → the
270
+ base vertices) — `:1158-1160`.
271
+ - **`drawOrder` produces exactly one timeline for the whole animation**; `drawOrderFolder` produces
272
+ one per folder entry.
273
+ - **Empty timeline arrays**: bones `continue` on `frames === 0` (`:875`); ik/transform/path/physics/
274
+ slider `continue` when `[0]` is missing; **slots do not** — an empty `rgba` array reaches
275
+ `timelineMap[0]` and dereferences `keyMap.color` → `TypeError`.
276
+
277
+ ### 1.9 Curve encoding (`readCurve`, `:1388-1401`; `readTimeline1/2`, `:1296-1346`)
278
+
279
+ Three encodings, on the **key that starts the interval** (`keyMap.curve`, never the destination key):
280
+
281
+ | Encoding | JSON | Meaning |
282
+ | --- | --- | --- |
283
+ | linear | `curve` absent | straight interpolation |
284
+ | stepped | `"curve": "stepped"` | `timeline.setStepped(frame)` (`:1391`) |
285
+ | bezier | `"curve": [ … ]` | array of **exactly 4 numbers per value channel**, concatenated in channel order |
286
+
287
+ Layout, exactly: for channel index `value`, `i = value << 2` and the four numbers are
288
+ `[cx1, cy1, cx2, cy2]` (`:1394-1398`). These are **absolute (time, value) control points**, not
289
+ normalised graph-view handles — `cy1`/`cy2` are multiplied by the timeline's `scale` factor, `cx1`/`cx2`
290
+ are not. So the array length per timeline is `4 × channels` from the table in §1.8:
291
+ rotate 4, translate 8, scale 8, shear 8, rgb 12, rgba 16, rgb2 24, rgba2 28, ik 8, transform 24,
292
+ path mix 12, alpha/scalex/deform/physics/slider 4.
293
+
294
+ **A short array is the format's nastiest silent failure**: `curve[i+3]` is `undefined`, the product
295
+ is `NaN`, and nothing throws. Timelines with no curve at all (`attachment`,
296
+ `inherit`, `sequence`, `drawOrder`, `events`, `physics reset`) ignore a `curve` key entirely.
297
+
298
+ The X axis is **seconds** in 4.x. It was frames in 3.8 — one more reason the 3.8 doc is actively
299
+ dangerous as a spec.
300
+
301
+ ### 1.10 Atlas text format (`TextureAtlas.ts`, S3)
302
+
303
+ Reader rules (`:194-227`): entries are `key: v1, v2, v3, v4` with **at most four values**
304
+ (`:224`); a line with no colon terminates the current block (`:214`). A **blank line closes a page
305
+ block** (`:119-121`). Page names are `line.trim()` (`:123`) but **region names are the raw line**
306
+ (`:131`) — leading whitespace becomes part of the name.
307
+
308
+ **Header entries before the first page are read and silently discarded** (`:106-111` — the comment
309
+ says so literally).
310
+
311
+ | Page field | Parsed into | Doc |
312
+ | --- | --- | --- |
313
+ | `size: w, h` | `page.width/height` (`:43-46`) — **used to compute every UV**, so a wrong value collapses all of them | 📘 |
314
+ | `format: …` | **parsed and thrown away** (`:47-49`, "we don't need format in WebGL") | 📘 |
315
+ | `filter: min, mag` | `minFilter`/`magFilter` (`:50-53`) | 📘 |
316
+ | `repeat: x\|y\|xy\|none` | `uWrap`/`vWrap` (`:54-57`) | 📘 |
317
+ | `pma: true\|false` | `page.pma` (`:58-60`) | 📘 |
318
+ | `scale: n` | 🚨 **emitted by the Spine texture packer, documented nowhere, and silently discarded by the reader.** There is no `pageFields.scale`, so `if (field) field(page)` at `:126-127` is a no-op. It records the export-time downscale factor; the runtime is expected to compensate via `SkeletonJson.scale` or `referenceScale`, not to read this line. | ❌ undocumented |
319
+
320
+ | Region field | Parsed into | Doc |
321
+ | --- | --- | --- |
322
+ | `bounds: x, y, w, h` | 4.x compact form (`:71-76`) | 📘 |
323
+ | `offsets: ox, oy, ow, oh` | 4.x compact form (`:85-90`) | 📘 |
324
+ | `xy: x, y` | **deprecated** alias (`:63-66`) | 📘 |
325
+ | `size: w, h` | **deprecated** alias (`:67-70`) | 📘 |
326
+ | `offset: ox, oy` | **deprecated** (`:77-80`) | 📘 |
327
+ | `orig: w, h` | **deprecated** (`:81-84`) | 📘 |
328
+ | `rotate: true \| false \| <degrees>` | `true` → 90; `false` → 0; anything else `parseInt` (`:91-97`). **90 swaps width/height in the UV computation** (`:161-167`) | 📘 |
329
+ | `index: n` | frame index for sequential regions (`:98-100`) | 📘 |
330
+ | `split: l, r, t, b` | **no dedicated field.** Falls through to the generic bucket → `region.names`/`region.values` (`:139-147`) | 📘 |
331
+ | `pad: l, r, t, b` | same generic bucket | 📘 |
332
+ | *any other key* | same generic bucket, `parseInt`-ed | 🔍 |
333
+
334
+ If `orig`/`offsets` never set an original size, it falls back to the packed size (`:149-152`).
335
+
336
+ ### 1.11 What the binary `.skel` adds or drops relative to JSON (S2)
337
+
338
+ Same feature set — same five constraint types (`SkeletonBinary.ts:1427-1431`), same attachment types,
339
+ same timeline catalogue (it imports the identical list, `:30`). Differences that matter to a JSON-only
340
+ emitter:
341
+
342
+ | Aspect | JSON | Binary |
343
+ | --- | --- | --- |
344
+ | Strings | inline | string table, index-referenced (`:93-100`) |
345
+ | Hash | string field | two int32s, joined as hex (`:76-78`) |
346
+ | Nonessential gate | per-field presence | **one boolean** (`:86`), then `fps`/`images`/`audio` (`:87-90`) |
347
+ | Bone `color`/`icon`/`iconSize`/`iconRotation` | JSON reads all four | nonessential block (`:121-126`) |
348
+ | **Bone `visible`** | ❌ **not readable from JSON** | `:126` |
349
+ | **Skin `color`** | ❌ **not readable from JSON** | `:448` |
350
+ | Slot `visible` | `:138` ✓ | `:145` |
351
+ | Animation `color` | `:1268` ✓ | `:1211` |
352
+ | Attachment colours | always read when present | only when nonessential (`:513`, `:600`, `:617`, `:630`) |
353
+
354
+ ⇒ **Two things are JSON-inexpressible in 4.3 spine-ts: `bone.visible` and `skin.color`.** Both are
355
+ editor-affordance data with zero rendering effect: there is no *rendering-relevant* feature that
356
+ binary can express and JSON cannot.
357
+
package/package.json CHANGED
@@ -1,6 +1,110 @@
1
1
  {
2
2
  "name": "rig-c",
3
- "version": "0.0.0-stage",
4
- "stub": true,
5
- "description": "Temporary package placeholder for staged publishing"
6
- }
3
+ "version": "2.20.4",
4
+ "description": "AI-authored Spine 2D rigging and animation, verified before it is written. rigc compiles a rig spec into Spine 4.3 skeleton data, gates it with named assertions held to a spine-core round trip's verdicts, and emits nothing that fails. Output imports into the Spine editor. Ships as an agent skill.",
5
+ "type": "module",
6
+ "bin": {
7
+ "rigc": "./bin/rigc.cjs"
8
+ },
9
+ "exports": {
10
+ "./plate": "./tools/plate.ts",
11
+ "./font5x7": "./tools/font5x7.ts",
12
+ "./transform": "./src/transform.ts",
13
+ "./render": "./src/render.ts",
14
+ "./png": "./src/png.ts",
15
+ "./compile": "./src/compile.ts",
16
+ "./rig": "./src/rig.ts",
17
+ "./mesh": "./src/mesh.ts",
18
+ "./errors": "./src/errors.ts",
19
+ "./meshcompare": "./src/meshcompare.ts",
20
+ "./cli": "./cli.ts",
21
+ "./package.json": "./package.json",
22
+ "./*.ts": "./*.ts",
23
+ "./*.mjs": "./*.mjs",
24
+ "./*.cjs": "./*.cjs",
25
+ "./*.json": "./*.json",
26
+ "./*.md": "./*.md",
27
+ "./LICENSE": "./LICENSE",
28
+ "./bin/rigc": "./bin/rigc.cjs",
29
+ "./tools/png_probe": "./tools/png_probe.mjs",
30
+ "./*": "./*.ts"
31
+ },
32
+ "engines": {
33
+ "bun": ">=1.2.0"
34
+ },
35
+ "license": "MIT",
36
+ "author": "firejune",
37
+ "repository": {
38
+ "type": "git",
39
+ "url": "https://github.com/firejune/rigc.git"
40
+ },
41
+ "homepage": "https://github.com/firejune/rigc#readme",
42
+ "bugs": "https://github.com/firejune/rigc/issues",
43
+ "keywords": [
44
+ "spine",
45
+ "spine2d",
46
+ "skeletal-animation",
47
+ "rig",
48
+ "compiler",
49
+ "skeleton-json",
50
+ "atlas",
51
+ "validator",
52
+ "codegen",
53
+ "ai-agents",
54
+ "cli",
55
+ "spine-2d",
56
+ "ai",
57
+ "animation",
58
+ "rigging",
59
+ "agent-skill",
60
+ "claude"
61
+ ],
62
+ "files": [
63
+ "bin/rigc.cjs",
64
+ "cli.ts",
65
+ "cli_core.ts",
66
+ "src",
67
+ "tools/plate.ts",
68
+ "tools/font5x7.ts",
69
+ "tools/contact.ts",
70
+ "tools/png_probe.mjs",
71
+ "tools/measure_contact_depth.ts",
72
+ "tools/editor_roundtrip.ts",
73
+ "docs/AUTHORING.md",
74
+ "docs/SPEC_COVERAGE.md",
75
+ "docs/PROMPTING.md",
76
+ "docs/RIGGING.md",
77
+ "docs/MOTION.md",
78
+ "docs/INGEST.md",
79
+ "docs/FACE.md",
80
+ "NOTICE.md",
81
+ ".claude-plugin",
82
+ "skills"
83
+ ],
84
+ "publishConfig": {
85
+ "access": "public"
86
+ },
87
+ "scripts": {
88
+ "build": "bun cli.ts build",
89
+ "validate": "bun cli.ts validate",
90
+ "explain": "bun cli.ts explain",
91
+ "selftest": "bun selftest.ts",
92
+ "smoke": "bun scripts/install_smoke.ts",
93
+ "typecheck": "bunx tsc --noEmit",
94
+ "typecheck:runs": "bunx tsc --noEmit -p tsconfig.runs.json",
95
+ "lint": "bunx eslint .",
96
+ "fetch-examples": "bash scripts/fetch-examples.sh",
97
+ "bench:usage": "bun bench/count_features.ts",
98
+ "viewer": "vite --config viewer/vite.config.ts",
99
+ "prepublishOnly": "bun scripts/prepublish_gate.ts"
100
+ },
101
+ "devDependencies": {
102
+ "@esotericsoftware/spine-core": "4.3.13",
103
+ "@types/bun": "^1.4.0",
104
+ "eslint": "^10.9.0",
105
+ "spine-html": "^0.4.1",
106
+ "typescript": "^5.9.3",
107
+ "typescript-eslint": "^8.67.0",
108
+ "vite": "^8.2.2"
109
+ }
110
+ }
@@ -0,0 +1,133 @@
1
+ ---
2
+ name: rigc
3
+ description: Author, build and validate Spine 4.3 skeleton data (skeleton.json plus its .atlas) from loose part PNGs with rigc, the rig compiler that verifies its own output with named assertions before writing it. Use for any request to make a Spine rig or Spine animation from PNG parts, or where the source is a Live2D, Unity or video model whose pictures you can render, to run or read rigc build, validate, render, preview, check or vote, or to write or fix a *.rig.json or *.motion.json spec; it says which shipped guide to open for the need at hand. Not for Live2D conversion, cutting an illustration into parts, or real-time face tracking.
4
+ license: MIT
5
+ compatibility: Requires Bun 1.2 or later. The tool is the npm package spine-rigc (bunx spine-rigc, or bun add -d spine-rigc); the command it installs is rigc.
6
+ ---
7
+
8
+ # rigc — a rig compiler for agents
9
+
10
+ rigc compiles a rig spec and a motion spec into Spine 4.3 skeleton data and an
11
+ atlas, runs its named assertions — rigc's own validator in the published package,
12
+ held to a `@esotericsoftware/spine-core` round trip's verdicts in its CI, all but
13
+ the parse itself — and writes **only if every one is green**. You cannot see the
14
+ rig you are authoring. The validator's messages and the shipped guides are the
15
+ whole interface, and this skill only says which of them to open.
16
+
17
+ ## Non-negotiables
18
+
19
+ - **Validation is never bypassed.** `build` writes nothing on a red gate; there is
20
+ no `--no-validate`, and none may be added — AUTHORING §0 says so in as many
21
+ words. Correctness is the whole of the reason: the assertions are what make the
22
+ output trustworthy, and they are held to the official parser's verdicts in the
23
+ repository's CI — all but the parse itself, which runs only where the runtime is
24
+ installed and is reported as a SKIP elsewhere.
25
+ - **The compiler never invents a value.** A field the spec leaves out is a
26
+ `CompileError` naming that field. Fill the spec; do not expect a default read off
27
+ the art — AUTHORING §2 and §5.
28
+ - **The validator's messages are the instructions.** Each names the object, the
29
+ value found and the value required, and AUTHORING §5 maps every named failure to
30
+ the file that has to change.
31
+ - **A green gate cannot see a wrong animation.** If you were given pictures,
32
+ `check` is the half of the loop that can; if not, `render` or `preview` and look —
33
+ AUTHORING §0 and §9.
34
+
35
+ ## Install
36
+
37
+ ```shell
38
+ bunx spine-rigc --help # run it without installing
39
+ bun add -d spine-rigc # or pin it in the project; the command is `rigc`
40
+ bun rigc skills install # then link these skills into .agents/skills
41
+ ```
42
+
43
+ Codex, Gemini CLI and Antigravity read skills from `.agents/skills/` in the
44
+ workspace and none of them reads `node_modules`; `rigc skills install` puts every
45
+ skill the package ships there, and `rigc skills --help` says what it refuses.
46
+
47
+ The package links no Spine runtime. `validate`, `preview` and `vote` below run
48
+ through `@esotericsoftware/spine-core`: install it beside the package
49
+ (`bun add -d @esotericsoftware/spine-core`) and the same `rigc` runs them, and
50
+ `build` runs the round trip as well. Without it they are refused by name, and
51
+ `build` writes the same files gated without the parse — `rigc --version` names the
52
+ entry that ran. In the parse's place that `build` runs two rules of its own over
53
+ rigc's model document, `A00_MODEL_READ` and `A00_MODEL_REGIONS_ON_PAGES`; they are
54
+ not registry assertions, so its summary counts two more, and AUTHORING §5.2 has a
55
+ row for each.
56
+
57
+ ## The loop
58
+
59
+ 1. `rigc build --rig <spec> --motion <spec> --images <dir> --out <dir>` compiles,
60
+ gates, and writes only on green.
61
+ 2. Read the report. Every red line names the file to change; fix the spec and
62
+ build again.
63
+ 3. `rigc explain --rig <spec> --motion <spec> --out <dir>` when the red line is
64
+ not enough — the compiled rig as a table: every bone with its resolved parent,
65
+ the slots in draw order, every timeline key by key, and a `DEFORM` block per
66
+ deform key. It takes no `--profile`, it never gates, and it writes nothing, so
67
+ the figures are readable on a build the gate is refusing — AUTHORING §0 and
68
+ §4.11.2.
69
+ 4. `rigc render --candidate <out>` or `rigc preview --candidate <out>` — look at
70
+ it. A rig with its head off its torso passes the gate; looking is what catches it.
71
+ Open one frame at full size, not only `contact.png`: the sheet is for spacing
72
+ across frames, and a defect is read on a frame. Ask it three things — is any
73
+ picture drawn twice, is there a straight edge where the art has none, does a part
74
+ cover a feature the art shows — and answer each with `render --hide <slot>` (or
75
+ `--slot <slot,…>`), which draws the frame again without that part on the same
76
+ grid, so the two frames say which part a pixel is. AUTHORING §0 holds the three.
77
+ 5. `rigc check --candidate <out> --frames <dir>` when you have reference pictures
78
+ (`--out <dir>` writes the picture each of its numbers came from — open the worst).
79
+ Read its output from the top: the framing block says whether the figures under
80
+ it are about placement or motion. A figure is where the loop starts, not where it
81
+ ends — keep the first green build's figure, check every later build against the
82
+ same frames, and stop when the figure stops moving, not when it exists; AUTHORING
83
+ §9.2 says how to read the framing block and the floor to read a figure against.
84
+ `rigc vote --candidate <a> --candidate <b>` when several candidates are green and
85
+ only a person can choose between them.
86
+ 6. `rigc validate <out>` re-gates artifacts already on disk, and
87
+ `rigc <command> --help` is each command's own flag table. `rigc repack <out>
88
+ --out <dir>` packs a build again under `build --pack`'s packing flags when its
89
+ parts were not kept, and writes only after every region and the skeleton are
90
+ shown unchanged (`--accept-skeleton-differences` writes a rebuild whose skeleton
91
+ differs, every difference printed; `--stage-box <slot>` reads a build's stage box)
92
+ — AUTHORING §0.4.
93
+ 7. Every finished unit ends with `rigc preview --candidate <out>`, and the report
94
+ names the `.html` it wrote. The hand-off to a person is part of the work.
95
+
96
+ The loop in full, with `pose` before it and `chainfit` after the first build:
97
+ AUTHORING §0.
98
+
99
+ ## When the source is not loose PNGs
100
+
101
+ A Live2D model, a Unity scene or a video is a player, not a set of parts. Make the
102
+ reference frames first: render the source at the rate you will check at, into one
103
+ directory of `f0000.png`, `f0001.png`…, and `rigc check --frames <that dir> --fps <rate>`
104
+ reads them with no `frames.json`. A port with no reference frames is unmeasured,
105
+ not finished. The parts are the source's own texture cut along its drawables, never
106
+ a screenshot: a screenshot is the composed result, and a part cut from it carries
107
+ every part under it. Where a part goes is its drawable's geometry — its vertices in
108
+ model space, its UVs, its triangles — which is a mesh attachment with those three
109
+ (AUTHORING §3.4, *Mesh attachment*); a region at the drawable's bounding-box centre
110
+ keeps only the box. rigc reads none of those formats — FACE §11, the paragraph
111
+ that opens *No Live2D file is read or written*
112
+ ([FACE.md](https://github.com/firejune/rigc/blob/main/docs/FACE.md#11-non-goals--stated-so-nobody-proposes-them-as-gaps)) —
113
+ only the pictures they produce. The rule, the background those frames need and
114
+ what a wrong one costs: AUTHORING §0, *When the source is a foreign player*.
115
+
116
+ ## Which guide, for which need
117
+
118
+ Read [AUTHORING.md](https://github.com/firejune/rigc/blob/main/docs/AUTHORING.md) first, whatever the need: the two
119
+ spec files field by field, the emission rules, the loop, and the failure map. Then:
120
+
121
+ | The request is… | Open | Skill |
122
+ | --- | --- | --- |
123
+ | a **skeleton** — how many bones, where each pivot sits, what hangs off what | [RIGGING.md](https://github.com/firejune/rigc/blob/main/docs/RIGGING.md) | `rigc-rigging` |
124
+ | a **movement** — an idle, a loop, from this pose to that one | [MOTION.md](https://github.com/firejune/rigc/blob/main/docs/MOTION.md) | `rigc-motion` |
125
+ | a **face** — a blink, a gaze, a head turn a few degrees off axis | [FACE.md](https://github.com/firejune/rigc/blob/main/docs/FACE.md) | `rigc-face` |
126
+ | a **skeleton.json somebody else authored** — read it, repair it, extend it | [INGEST.md](https://github.com/firejune/rigc/blob/main/docs/INGEST.md) | `rigc-ingest` |
127
+ | you are the **person operating** the agent rather than the agent | [PROMPTING.md](https://github.com/firejune/rigc/blob/main/docs/PROMPTING.md) | — |
128
+
129
+ Every guide linked here is in the installed package at `node_modules/spine-rigc/docs/`,
130
+ which is the copy that matches the rigc you run; the links go to the repository's
131
+ `main`. Inside the Claude Code plugin the same files are at `${CLAUDE_PLUGIN_ROOT}/docs/`.
132
+ Formats, the CLI reference and the licence chain:
133
+ [README.md](https://github.com/firejune/rigc/blob/main/README.md), installed at `node_modules/spine-rigc/README.md`.