# r02: explicit critic-input failure handling

This revision fixes one technical P2. It does not change the recipe, renderer,
SVG, prepared PNG, source movie, output movies, source review or native evidence.
The original r01 kit and review inputs remain read-only and unchanged.

## Cause and repair

r01 used the truthiness of both file bytes and the parsed JSON to decide whether
to perform the requested research check. Empty bytes and falsey JSON therefore
behaved like an omitted `--critic`. A real `{}` CLI reproduction returned 0,
created a completed report, and performed only the 604 ordinary measurement rows.
The regression test failed on that success exit before the code was changed.

r02 distinguishes **option omission** from **input validity**:

- Omitted `--critic` remains supported. Source comparison is explicitly `not-run`.
- An explicit path must contain nonempty valid JSON with the expected manifest
  object, recipe/source interval and all three required movie records.
- Required movie metadata, nonempty paths, byte counts and SHA-256 bindings are
  checked before preparation work or creation of the output directory.
- All source-check branches and required row counts use option presence after
  successful validation, never the truthiness of the payload.

No request has been added, no source mapping has been changed, and no source or
moving-quality approval is inferred from passing this gate.

## Executed regression evidence

`R02-VERIFICATION.json` contains sanitized command receipts with exit codes,
output-creation checks and actual measurement counts. `TESTS.json` binds the new
test and verifier bytes. Private stdout/stderr and command receipts remain in
the bounded r02 worker.

| Explicit critic input | r02 exit | Output directory / success report |
| --- | --- | --- |
| Zero bytes | Nonzero | Neither created |
| `{}` | Nonzero | Neither created |
| `[]` | Nonzero | Neither created |
| `null` | Nonzero | Neither created |
| `false` | Nonzero | Neither created |
| Malformed JSON | Nonzero | Neither created |
| Truthy object containing only `reviewState` | Nonzero | Neither created |
| Otherwise valid object with empty `movies` | Nonzero | Neither created |
| Otherwise valid object missing adaptation | Nonzero | Neither created |
| Actual frozen r01 critic manifest | 0 | 906 rows; 151 source-comparison frames per identity |
| Option omitted | 0 | 604 rows; source comparison explicitly `not-run` |

All **16 Python tests** passed: nine actual invalid-input CLI cases, two actual
CLI controls, and the five existing verifier tests. The old Node test suite was
not rerun or counted as new passing work. The valid control uses the original
frozen manifest as a technical fixture, not a new submission to the source critic.
Its source comparison still has no moving, native or source-footage approval.

## Recheck without copying the build

Point the verifier at the existing r01 core, SVG, preparations and raw replays.
Keep all inputs read-only. Use a new r02 output directory, outside the r01 worker.
Use the already installed pinned Node, Pillow, FFmpeg and full Chrome environment
from the original kit; no dependency copies, installation or symlinks are needed.

```bash
export WORDMARK_WORK="$R01_WORK"
export WORDMARK_CODE="$R01_CODE"
export WORDMARK_CRITIC="$R01_WORK/critic-inputs.json"
export WORDMARK_TEST_OUTPUT="$R02_WORK/new-test-run"
export WORDMARK_ENCODER_LOCK="$SHARED_ENCODER_LOCK"
unset WORDMARK_VERIFY_SCRIPT
"$PYTHON_WITH_PILLOW" -B "$R02_CODE/test_verify.py" -v
```

For a direct CLI check:

```bash
"$PYTHON_WITH_PILLOW" -B "$R02_CODE/verify.py" \
  --work "$R01_WORK" --code "$R01_CODE" --out "$R02_WORK/new-proof" \
  --browser "$WORDMARK_BROWSER" --encoder-lock "$SHARED_ENCODER_LOCK" \
  --critic "$INPUT_MANIFEST"
```

Run each case with a fresh output path. `--code` selects the unchanged renderer
files whose hashes are bound to the existing movies; it need not be the directory
containing the patched Python verifier. For the full regression suite, supply
the actual frozen manifest and its existing movies. The source-only ZIP does
not include private critic paths or source footage.

## Packaging and limits

The r02 ZIP is rewritten as a stream from the sealed r01 source-only ZIP. Only
the verifier, Python tests, README, test receipt and package index are replaced;
this note and the r02 verification receipt are added. All other member bytes
must match their r01 SHA-256 entries. No SVG tree is extracted or staged.

The small patch and member inventory accompany the ZIP. `PROOF.json` and the
existing source/native findings remain historical r01 evidence, not new r02
rendering claims. The original r01 `seal.py` is also historical build tooling;
the r02 handoff uses the separate streaming package script and seal receipt.

Persistent r02 allocation is bounded to 14 MiB, with a 20 MiB peak ceiling and
the shared storage guard. No new motion renders, SVGs, PNGs or review clips are
created. Independent technical recheck is still required. Only the coordinator
publishes; source-footage, moving-quality and native-Resolve approvals remain 0.
