# flow-state · debug

Everything here answers a question the app itself cannot. The app shows you one video; these show you whether the generator is doing its job across many.

## Looking at output

### gallery

Every visualizer, six times, on six different songs' content — cast, ink, lattice, palette and parameters all varying. Sorted least-varied first, so the scenes that always look the same come to the top.

~3 min · the one to open when a scene feels familiar · `HOWTO-variety.md` for how to fix what it finds

### the app

Drop a track and watch it. `test/songs/*.wav` holds the synthetic bank if you want something with known properties.

build the bank with `npm run build:songs`

## Is it correct?

### phase gates

Every acceptance gate in `PLAN.md`, run for real. This is the one to check before committing.

?slow=1 full suite · ?phase=12 one phase

### single scene

The whole per-scene battery for one visualizer: renders, animates, deterministic, distinct, param sweep, flash rate, and every declared trait and artifact.

`checks.html?scene=Metaballs` — the loop you are in while writing or migrating one

## Is it varied?

These measure whether two videos differ, which no other gate can see — a generator that ignores its input passes determinism and liveness perfectly. Read the caution below before trusting a single run.

### decompose

Identity against container, measured apart: hold the scene fixed and vary the song's content, then the reverse. The most trustworthy number here — it compares renders directly, and its noise floor is exactly zero.

~1 min

### song variety

Twelve songs from the bank, each with its own audio-derived seed. Also reports coupling — whether songs that sound different come out looking different, which is still unsolved.

~2 min · ?variety=1 for the seed version, one song and many seeds

### arms, with error bars

Three pools compared over repeated runs. The template for any A/B here: it prints the difference against its own noise band and says outright when the two are indistinguishable.

~5 min

### pool sweep

How many scenes a track should draw on. Swept rather than guessed — and the answer turned out to be that it does not matter between 4 and 32.

~5 min

## Command line

### migration status

Which scenes draw the song's content, which still draw their own, and what each remaining one needs. Recipe in `MIGRATION.md`.

    node tools/migration-status.js

### cast census

Which visualizers actually get cast, across songs and seeds — and for the ones that do not, which of the four gates killed them.

    node tools/cast-census.js 12 40

### identity census

How far apart the songs' identities are before anything is rendered. No GPU. Run this before blaming the visualizers.

    node tools/identity-census.js 12

### song bank

Rebuild the test songs and verify they still span every feature the generator reads. Fails loudly if an axis has collapsed.

    npm run build:songs
    npm run check:songs

### static gates

Determinism grep, schema/shader agreement both ways, and the backtick check that guards the shader template literals.

    npm run lint:scenes
    npm test

## Reading the variety numbers

This harness has two classes of measurement and only one of them is safe to steer by. Direct render comparisons — the gallery score, `decompose`, the per-scene gate — have a noise floor of zero and mean what they say. Aggregate ratios — chiefly `separation`, which divides one small difference by another — swing wildly between runs: three runs of the same measurement gave 0.31, 0.56 and 0.07. Four conclusions were drawn and withdrawn during this epic for exactly that reason. Anything under about 0.01 of spread needs `&repeats=3` before it is believed, and a difference that changes sign with the sample is not a difference.

## If a page is dead

A module that fails to load takes the whole graph with it: the page renders, every control is present, and nothing is wired to anything. The app carries a boot guard that says so. The usual causes are a content blocker — a file named like tracking will be treated as tracking, which cost this project a session over a file called `clicktrack.js` — or a dev server caught mid-restart.