Verification gate

The gate (pipeline/gate/verify.js) is the pipeline's definition of done: every produced .glb must pass it, and a failed gate fails the job. It never warns.

Definition

Two layers:

  1. Structural (always): parse the GLB container and check it against thresholds from the pipeline config.
  2. Render smoke (optional, verify.render: true): import the file into an empty Blender scene through the Blender session and render one frame at the scene's own render size — proves Blender can actually open the artifact.

Report shape

verifyAsset(path, config) returns one report and the CLI prints it:

{
  "ok": true,
  "errors": [],
  "stats": {
    "meshes": 38, "primitives": 38, "triangles": 748,
    "materials": 5, "animations": 2, "animationNames": ["flap", "idle"],
    "textures": 0, "nodes": 48,
    "animationChannels": 54, "movingAnimationChannels": 8,
    "staticAnimationChannels": 46
  },
  "thresholds": { "requireMaterials": true, "requireAnimations": false, "...": "..." },
  "path": "assets/models/smok.glb"
}

triangles counts TRIANGLES-mode primitives via their index accessor (or POSITION count when unindexed). movingAnimationChannels counts channels whose float output samples actually differ by more than 0.00001 from the first sample — a clip that exists but never moves is detected.

Thresholds

Glina carries no triangle budget or file-size bound of its own. A threshold the config does not state is not checked, except the material and animation policy shown below.

Threshold When unset Failure sentence
triTarget no budget is checked over triangle budget: N > MAX (target T) — MAX = target × (100 + tolerance)/100
triTolerancePct required once triTarget is set refusal: verify.triTolerancePct is not set in the pipeline config: a stated triTarget needs the tolerance the gate allows over it
requireMaterials true no materials
requireAnimations false no animation clips, and animation clips contain no changing channels: N channel(s), all static
minAnimationClips no minimum too few animation clips: N < M [names]
always — no meshes, no triangles, length mismatch: header says N, file is M

Lifecycle

  • glina verify <file> runs the gate standalone; exit 0 on pass, 1 on fail. Config is optional — without one the gate checks structure, materials and animation presence but no triangle budget.
  • Inside a sculpt job or studio job the gate runs with throwOnFail, so a failing report becomes asset failed verification: <reasons joined with '; '> and fails the job.
  • verify.enabled: false opts a job out of the gate (not recommended); standalone glina verify always runs.

Invariants and refusals

  • A gate failure is a report (ok: false plus reasons), never a throw. Only an unparseable file throws, with one of these exact sentences:
    • file too small to be a GLB
    • not a GLB (bad magic)
    • truncated before first chunk
    • first GLB chunk is not JSON
    • GLB JSON chunk does not parse: <reason>
  • Render smoke failures append render smoke failed: <reason> to the report and set ok: false; the structural verdict is never masked by them.