spine-rigc 1.9.4 → 2.0.1
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/plugin.json +1 -1
- package/NOTICE.md +37 -21
- package/README.md +47 -24
- package/bin/rigc.cjs +45 -2
- package/cli_core.ts +46 -0
- package/docs/AUTHORING.md +78 -14
- package/package.json +4 -5
- package/skills/rigc/SKILL.md +15 -5
- package/src/assertions/bodies/a01.ts +23 -0
- package/src/assertions/bodies/a02.ts +21 -0
- package/src/assertions/bodies/a05.ts +56 -0
- package/src/assertions/bodies/a06.ts +8 -0
- package/src/assertions/bodies/a07.ts +68 -0
- package/src/assertions/bodies/a16.ts +24 -0
- package/src/assertions/bodies/a18.ts +62 -0
- package/src/assertions/bodies/a19.ts +79 -30
- package/src/assertions/bodies/a31.ts +61 -0
- package/src/assertions/bodies/a35.ts +160 -0
- package/src/assertions/emitted/index.ts +121 -0
- package/src/assertions/report.ts +64 -0
- package/src/cli/core_commands.ts +83 -7
- package/src/cli/shared.ts +574 -61
- package/src/cli/spine_commands.ts +36 -366
- package/src/compile.ts +63 -0
- package/src/validate.ts +16 -86
package/src/validate.ts
CHANGED
|
@@ -85,6 +85,7 @@ import type { SlotColourFacts, SlotTimelines } from './assertions/facts/slot_col
|
|
|
85
85
|
import { a03RegionWidthHeightFinite } from './assertions/bodies/a03.ts';
|
|
86
86
|
import { a11NoClippingAttachments } from './assertions/bodies/a11.ts';
|
|
87
87
|
import { a17AtlasPageFilesExist } from './assertions/bodies/a17.ts';
|
|
88
|
+
import { a18DeterministicEmit } from './assertions/bodies/a18.ts';
|
|
88
89
|
import { a45SeparableColorTimelinesOwnTheirChannelsAndPoseAsWritten } from './assertions/bodies/a45.ts';
|
|
89
90
|
import { attachmentRegionLookups, type AttachmentRegionJoin } from './assertions/region_lookups.ts';
|
|
90
91
|
import { attachmentRegionJoins } from './region_joins.ts';
|
|
@@ -182,20 +183,9 @@ export type { Failure } from './assertions/harness.ts';
|
|
|
182
183
|
*/
|
|
183
184
|
export type ValidateProfile = AssertionProfile;
|
|
184
185
|
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
* What the CLI uses when `--profile` is absent — and ONLY the CLI. This is not
|
|
189
|
-
* `validate()`'s default; that function has none (above).
|
|
190
|
-
*
|
|
191
|
-
* `spine` since issue #221. The published package's pitch is "the output imports
|
|
192
|
-
* into the Spine editor", which is exactly the question `spine` asks, and a
|
|
193
|
-
* stranger's first build was being judged instead against one renderer's policy
|
|
194
|
-
* and one project's canvas budget — 14 rules they have no stake in, with the
|
|
195
|
-
* escape hatch documented only in prose. Defaults beat prose. `spine-html` is
|
|
196
|
-
* still one flag away, and every report names the profile that judged it.
|
|
197
|
-
*/
|
|
198
|
-
export const CLI_DEFAULT_PROFILE: ValidateProfile = 'spine';
|
|
186
|
+
// The profiles, the CLI's default and the report printer — `./assertions/report.ts` since issue #1060, so the
|
|
187
|
+
// entry that links none of spine-core gates and prints with them; every one is re-exported from here.
|
|
188
|
+
export { CLI_DEFAULT_PROFILE, reportLines, VALIDATE_PROFILES } from './assertions/report.ts';
|
|
199
189
|
|
|
200
190
|
// What kind of rule each assertion is — `./assertions/kinds.ts` since issue
|
|
201
191
|
// #1025, where the model side's harness reads the same table.
|
|
@@ -842,19 +832,6 @@ export function timelineAddCells(data: ReturnType<SkeletonJson['readSkeletonData
|
|
|
842
832
|
return cells;
|
|
843
833
|
}
|
|
844
834
|
|
|
845
|
-
/**
|
|
846
|
-
* Where two texts first differ, as A18 names it: the 1-based line and each
|
|
847
|
-
* side's line, trimmed and cut to 120 characters.
|
|
848
|
-
*/
|
|
849
|
-
function firstDifferingLine(first: string, second: string): string {
|
|
850
|
-
const a = first.split('\n');
|
|
851
|
-
const b = second.split('\n');
|
|
852
|
-
let i = 0;
|
|
853
|
-
while (i < a.length && i < b.length && a[i] === b[i]) i += 1;
|
|
854
|
-
const cut = (line: string | undefined): string => (line === undefined ? '(end of text)' : JSON.stringify(line.trim().slice(0, 120)));
|
|
855
|
-
return `${i + 1}: ${cut(a[i])} first, ${cut(b[i])} second`;
|
|
856
|
-
}
|
|
857
|
-
|
|
858
835
|
/**
|
|
859
836
|
* The runtime's supply of `SkinEntryFacts` (issue #1025): every skin of the
|
|
860
837
|
* loaded skeleton in the loaded order, and each skin's entries as
|
|
@@ -3348,28 +3325,19 @@ export function validate(input: ValidateInput): ValidateReport {
|
|
|
3348
3325
|
check('A30_STROKE_WITHIN_CAP_CONTAINMENT', () => a30StrokeWithinCapContainment(verdicts, rawBoneTimelines(raw), input));
|
|
3349
3326
|
|
|
3350
3327
|
// --- A18: determinism ----------------------------------------------------
|
|
3328
|
+
// The body is `./assertions/bodies/a18.ts` (issue #1060): the model document's clause, which the model side
|
|
3329
|
+
// runs too, and the Spine pair's two clauses handed to it as this side's encoding findings, in their order.
|
|
3330
|
+
// Same vacuous-pass trap as A09: re-gating artifacts already on disk hands this assertion no second compile.
|
|
3351
3331
|
check('A18_DETERMINISTIC_EMIT', () => {
|
|
3352
|
-
|
|
3353
|
-
|
|
3354
|
-
|
|
3355
|
-
|
|
3356
|
-
|
|
3357
|
-
|
|
3358
|
-
|
|
3359
|
-
|
|
3360
|
-
}
|
|
3361
|
-
if (input.reEmit.atlasText !== input.atlasText) {
|
|
3362
|
-
fail('A18_DETERMINISTIC_EMIT', 'recompiling produced a different skeleton.atlas');
|
|
3363
|
-
}
|
|
3364
|
-
// The model document is compared like the Spine pair (issue #922): it is
|
|
3365
|
-
// written beside them, and a map iterated in an order nothing fixed would
|
|
3366
|
-
// reach it before it reached either of them — the document writes every
|
|
3367
|
-
// `Map` of the model as an array in the map's order.
|
|
3368
|
-
if (input.modelText === undefined) {
|
|
3369
|
-
fail('A18_DETERMINISTIC_EMIT', 'a second compile was handed over with no first model document to compare its skeleton.model.json against');
|
|
3370
|
-
} else if (input.reEmit.modelText !== input.modelText) {
|
|
3371
|
-
fail('A18_DETERMINISTIC_EMIT', `recompiling produced a different skeleton.model.json (first differing line ${firstDifferingLine(input.modelText, input.reEmit.modelText)})`);
|
|
3372
|
-
}
|
|
3332
|
+
const again = input.reEmit;
|
|
3333
|
+
const encoding =
|
|
3334
|
+
again === undefined
|
|
3335
|
+
? []
|
|
3336
|
+
: [
|
|
3337
|
+
...(again.skeletonText !== input.skeletonText ? ['recompiling produced a different skeleton.json'] : []),
|
|
3338
|
+
...(again.atlasText !== input.atlasText ? ['recompiling produced a different skeleton.atlas'] : []),
|
|
3339
|
+
];
|
|
3340
|
+
a18DeterministicEmit(verdicts, { again: again !== undefined, encoding, first: input.modelText, second: again?.modelText ?? '' });
|
|
3373
3341
|
});
|
|
3374
3342
|
|
|
3375
3343
|
// --- every assertion leaves a row ----------------------------------------
|
|
@@ -3444,44 +3412,6 @@ function meshWeightsOf(mesh: MeshAttachment): Array<Array<{ bone: number; weight
|
|
|
3444
3412
|
return out;
|
|
3445
3413
|
}
|
|
3446
3414
|
|
|
3447
|
-
export function reportLines(report: ValidateReport): string[] {
|
|
3448
|
-
const lines: string[] = [];
|
|
3449
|
-
// The profile goes FIRST and names what it left out. A report that says
|
|
3450
|
-
// "green" without saying which rulebook produced it is the one thing this
|
|
3451
|
-
// switch could make worse than no switch: `--profile spine` green means
|
|
3452
|
-
// "valid Spine", never "passes the renderer policy".
|
|
3453
|
-
const renderer = report.profileSkipped.filter((p) => p.kind === 'renderer').length;
|
|
3454
|
-
const archetype = report.profileSkipped.filter((p) => p.kind === 'archetype').length;
|
|
3455
|
-
lines.push(
|
|
3456
|
-
report.profileSkipped.length === 0
|
|
3457
|
-
? ` .. profile ${report.profile} — every assertion applies`
|
|
3458
|
-
: ` .. profile ${report.profile} — ${renderer} renderer-policy and ${archetype} archetype assertion(s) do not apply`,
|
|
3459
|
-
);
|
|
3460
|
-
for (const name of report.passed) lines.push(` PASS ${name}`);
|
|
3461
|
-
for (const s of report.skipped) lines.push(` SKIP ${s.assertion}: ${s.reason}`);
|
|
3462
|
-
for (const p of report.profileSkipped) lines.push(` PROF ${p.assertion}: ${p.kind} rule, not in profile "${report.profile}"`);
|
|
3463
|
-
for (const f of report.failures) lines.push(` FAIL ${f.assertion}: ${f.detail}`);
|
|
3464
|
-
// How many of them MEASURED anything, which is the figure the rows above do
|
|
3465
|
-
// not hand a reader (issue #568). Counting `PASS` lines answers a different
|
|
3466
|
-
// question — before the sweep that closed #568 a run could print seven of
|
|
3467
|
-
// them over a candidate on which five rules had not executed at all.
|
|
3468
|
-
//
|
|
3469
|
-
// ⚠️ Every figure here is a count of ASSERTIONS and not of rows, which is why
|
|
3470
|
-
// the failed side is a Set: `fail()` is called once per finding, so one
|
|
3471
|
-
// assertion can print six `FAIL` lines, and a line-count would report 47 of
|
|
3472
|
-
// 42. The four buckets partition `ASSERTION_NAMES`, so the total is derived
|
|
3473
|
-
// by adding them rather than stated — a `42` written here would be the one
|
|
3474
|
-
// number in the report that no run could contradict.
|
|
3475
|
-
const failed = new Set(report.failures.map((f) => f.assertion));
|
|
3476
|
-
const measured = report.passed.length + failed.size;
|
|
3477
|
-
const total = measured + report.skipped.length + report.profileSkipped.length;
|
|
3478
|
-
lines.push(
|
|
3479
|
-
` .. ${total} assertions: ${measured} measured (${report.passed.length} passed, ${failed.size} failed), ` +
|
|
3480
|
-
`${report.skipped.length} skipped, ${report.profileSkipped.length} not in profile "${report.profile}"`,
|
|
3481
|
-
);
|
|
3482
|
-
return lines;
|
|
3483
|
-
}
|
|
3484
|
-
|
|
3485
3415
|
export function atlasDirOf(atlasPath: string): string {
|
|
3486
3416
|
return dirname(resolve(atlasPath));
|
|
3487
3417
|
}
|