spine-rigc 0.31.0 → 0.33.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -516,7 +516,7 @@ commands take it and what its default is.
516
516
  | `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` and `--padding` tune it |
517
517
  | `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 |
518
518
  | `validate <dir>` | re-gates artifacts already on disk |
519
- | `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. `--stage x,y,w,h` supplies the stage for a skeleton that declares none — 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 |
519
+ | `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 |
520
520
  | `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 |
521
521
  | `render --candidate <dir>` | PNG frames plus a contact sheet, in `render/` |
522
522
  | `preview --candidate <dir>` | one self-contained `.html` that plays it |
@@ -533,9 +533,9 @@ first three work on any reference you have, and `bench` is a repository workflow
533
533
  and `bun run fetch-examples`. The reasoning behind them is in
534
534
  [the benchmark dossier](https://github.com/firejune/rigc/blob/main/docs/BENCHMARK.md).
535
535
 
536
- `build` and `validate` both default to `--profile spine` — the 31 validity rules, which
536
+ `build` and `validate` both default to `--profile spine` — the 32 validity rules, which
537
537
  ask *is this valid Spine 4.3 that any runtime plays correctly?* `--profile spine-html`
538
- adds all 46: the other 15 are one renderer's policy and one canvas budget's, and they
538
+ adds all 47: the other 15 are one renderer's policy and one canvas budget's, and they
539
539
  fire on perfectly correct editor-produced Spine data, which is why they are opt-in.
540
540
  ⇒ **That reason is about foreign data and does not carry to a rig you are authoring
541
541
  yourself: author under `--profile spine-html` and read the extra 15 as findings, and
@@ -595,10 +595,12 @@ One thing it drops on purpose and says so: a path attachment's `lengths`, which
595
595
 
596
596
  ⚠️ **Two values are not in a skeleton at all.**
597
597
 
598
- - **The stage.** `skeleton.width`/`height`: rigc always writes one, and a skeleton that
599
- carries none is refused by name unless `--stage x,y,w,h` supplies it. It is not
600
- derivable — posing the rig gives the *animated* extent, which is a different number
601
- from the setup box. ⛔ **And `--stage` beside a box the file already states is refused
598
+ - **The stage.** `skeleton.width`/`height`. A skeleton that declares none is carried as
599
+ declaring none — the rig spec states `"width": null, "height": null` and the rebuild
600
+ carries no box either, byte for byte
601
+ ([#714](https://github.com/firejune/rigc/issues/714)) — and `--stage x,y,w,h` is how a
602
+ caller *adds* one, recorded as a judgement. It is not derivable — posing the rig gives
603
+ the *animated* extent, which is a different number from the setup box. ⛔ **And `--stage` beside a box the file already states is refused
602
604
  too**, for the opposite reason: two sources for one value, where the file is the record
603
605
  of what was measured. It used to be read after the box and therefore never
604
606
  ([#626](https://github.com/firejune/rigc/issues/626)). ⚠️ This said *"an editor export carries none"* until #594 measured
@@ -681,7 +683,7 @@ letting `A17` blame the editor for the harness's own doing.
681
683
  | 📥 **[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 |
682
684
  | 🤖 **[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 |
683
685
  | 🔬 **[docs/SPEC_COVERAGE.md](docs/SPEC_COVERAGE.md)** | Spine 4.3's full export surface against what rigc emits and what the official examples measurably use, with the ordered gap list |
684
- | 🎓 **[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 46 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 |
686
+ | 🎓 **[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 47 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 |
685
687
  | 📋 [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 |
686
688
  | 🗺️ [ROADMAP.md](https://github.com/firejune/rigc/blob/main/ROADMAP.md) | where this is going, and where it has been. What 1.0 has to mean before the number is claimed — conditions rather than a feature list, because direction here comes from what users hit |
687
689
  | 📐 [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 |
@@ -738,7 +740,7 @@ quality."* All six, with their verdicts, are in
738
740
  [docs/LADDER.md](https://github.com/firejune/rigc/blob/main/docs/LADDER.md).
739
741
 
740
742
  The whole dossier — the yardstick, `diff` and `check` and what neither of them can
741
- see, every rung, the run viewer, the 46 assertions and the selftest behind them — is
743
+ see, every rung, the run viewer, the 47 assertions and the selftest behind them — is
742
744
  [docs/BENCHMARK.md](https://github.com/firejune/rigc/blob/main/docs/BENCHMARK.md).
743
745
  Live rung status is
744
746
  [docs/LADDER.md](https://github.com/firejune/rigc/blob/main/docs/LADDER.md).
package/cli.ts CHANGED
@@ -1746,6 +1746,46 @@ function readPositiveNumber(flags: Record<string, string>, key: string, fallback
1746
1746
  return value;
1747
1747
  }
1748
1748
 
1749
+ /**
1750
+ * Does this skeleton declare a setup stage — a numeric width AND height?
1751
+ *
1752
+ * Read off the loaded `SkeletonData` for `render` and off the file's header for
1753
+ * `preview`, which never loads one; both are the same two fields, because
1754
+ * `SkeletonJson` copies them across unconditionally (`SkeletonJson.js:70-73`),
1755
+ * so a header that omits them leaves `undefined` on a field typed `number`.
1756
+ */
1757
+ function declaresSetupStage(header: { width?: unknown; height?: unknown }): boolean {
1758
+ return typeof header.width === 'number' && typeof header.height === 'number';
1759
+ }
1760
+
1761
+ /** The `skeleton` block of a skeleton file's text, or an empty one where it has none. */
1762
+ function skeletonHeaderOf(skeletonText: string): { width?: unknown; height?: unknown } {
1763
+ const root = JSON.parse(skeletonText) as { skeleton?: { width?: unknown; height?: unknown } };
1764
+ return root.skeleton ?? {};
1765
+ }
1766
+
1767
+ /**
1768
+ * What `render` and `preview` say of their framing on a skeleton that declares
1769
+ * no stage (issue #714).
1770
+ *
1771
+ * ⚠️ **Neither frames to a stage on ANY skeleton**, so this is not a fallback
1772
+ * being announced: `framingViewport` is the union of every animation's posed
1773
+ * bounds, and the Spine Web Player's `calculateAnimationViewport` samples the
1774
+ * playing animation's bounds whenever its config states no viewport box, which
1775
+ * `buildPreview` never does. The line is printed only where a stage is absent
1776
+ * because that is the one case where a reader can ask *what box stood in for
1777
+ * it* — and the answer has to be "none", said, rather than a rectangle that looks
1778
+ * like a default. On a staged skeleton the output is the bytes it always was.
1779
+ */
1780
+ const STAGELESS_FRAMING = {
1781
+ render:
1782
+ 'framing the posed extent of every animation, padded — this skeleton declares no stage, and nothing stands ' +
1783
+ 'in for one: render frames to the posed extent whether or not a stage is declared',
1784
+ preview:
1785
+ "framing the Spine Web Player's own: the posed extent of the animation it plays — this skeleton declares no " +
1786
+ 'stage, and nothing stands in for one: the player frames that way whether or not a stage is declared',
1787
+ } as const;
1788
+
1749
1789
  /**
1750
1790
  * render — the frame series, drawn by the same rasteriser `check` measures with.
1751
1791
  *
@@ -1788,6 +1828,7 @@ function cmdRender(flags: Record<string, string>): void {
1788
1828
  const sampled: Map<string, Frame[]> =
1789
1829
  only === undefined ? sampleAll(data, fps, pose) : new Map([[only, sampleAnimation(data, only, fps, pose)]]);
1790
1830
  console.log(` .. ${viewport.width}x${viewport.height}px at ${fps} fps, ${sampled.size} set(s) -> ${outRoot}`);
1831
+ if (!declaresSetupStage(data)) console.log(` .. ${STAGELESS_FRAMING.render}`);
1791
1832
 
1792
1833
  mkdirSync(outRoot, { recursive: true });
1793
1834
  const sets: FrameSet[] = [];
@@ -1903,6 +1944,7 @@ function cmdPreview(flags: Record<string, string>): void {
1903
1944
  for (const page of pages) {
1904
1945
  console.log(` .. page ${page.name.padEnd(28)} ${(page.bytes.length / 1024).toFixed(1)} KiB`);
1905
1946
  }
1947
+ if (!declaresSetupStage(skeletonHeaderOf(skeletonText))) console.log(` .. ${STAGELESS_FRAMING.preview}`);
1906
1948
 
1907
1949
  const html = buildPreview({
1908
1950
  skeletonText,
@@ -3263,9 +3305,10 @@ const FLAG_MEANINGS: Record<string, string> = {
3263
3305
  '--images <dir>` on every rebuild), `none` states width/height only for `build --atlas-in <pack>` to ' +
3264
3306
  'resolve (default: loose)',
3265
3307
  stage:
3266
- 'the setup bounding box — `skeleton.x,y,width,height` — for a skeleton that declares none. It cannot be ' +
3308
+ 'the setup bounding box — `skeleton.x,y,width,height` — to ADD to a skeleton that declares none. It cannot be ' +
3267
3309
  'derived: posing the rig gives the ANIMATED extent, which is a different number from the setup box, so this ' +
3268
- "is the caller's value, and without it the missing stage is reported as a blocker. ⚠️ An editor export MAY " +
3310
+ "is the caller's value, and without it the absence is carried: the spec states `\"width\": null, \"height\": " +
3311
+ 'null` and the rebuild declares no stage either. ⚠️ An editor export MAY ' +
3269
3312
  'carry none; every editor export measured for this project carries one and ingest reads it straight through, ' +
3270
3313
  'so the flag is for a file that really has none rather than for editor exports as a class. ⛔ Beside a ' +
3271
3314
  'skeleton that already declares a box it is REFUSED rather than ignored: two sources for one value, and the ' +
@@ -3677,12 +3720,11 @@ const USAGE = [
3677
3720
  ' rigc ingest hero.json --out specs/ --images parts/ rig.json + motion.json',
3678
3721
  'The contract is an equality, not a rulebook: build(ingest(x)) is x, byte for byte.',
3679
3722
  'It reads the skeleton and nothing else — no .spine project, no binary .skel, no',
3680
- 'atlas — so two things are the caller\'s and are refused rather than guessed: the',
3681
- 'setup stage (--stage, only when the skeleton itself declares none — beside a box the',
3682
- 'file states, the flag is refused rather than ignored) and how the spec',
3683
- 'reaches the art (--art). --images <dir> is the third and the only optional one: it',
3684
- 'WRITES the rig spec\'s own images directory, relative to --out, so the rebuild needs',
3685
- 'no flag.',
3723
+ 'atlas — so how the spec reaches the art is the caller\'s (--art) and is not guessed.',
3724
+ 'A setup stage the skeleton states is read; one it does not state is carried as',
3725
+ 'absent, and --stage is how a caller adds a box to such a file — beside a box the',
3726
+ 'file states, the flag is refused rather than ignored. --images <dir> WRITES the rig',
3727
+ 'spec\'s own images directory, relative to --out, so the rebuild needs no flag.',
3686
3728
  'Everything the spec format cannot hold is printed as a named finding and',
3687
3729
  'exits non-zero, with both files still written, because a spec plus a list of what',
3688
3730
  'is missing from it beats no spec at all.',