# 📦 WP-149 — `test(data): frozen expectations and the augmentation-invariants golden`

> Engineering log · [Phase 13 — Contributor admission, augmentation guard and keypoint parity, v0.6.0](https://borda.github.io/lucid-YOLO/engineering/phase-13-contributor-admission/)
>
> 🔗 Depends on [WP-148](https://borda.github.io/lucid-YOLO/work_packages/wp-148/) · Needed by [WP-150](https://borda.github.io/lucid-YOLO/work_packages/wp-150/)
>
> ⬅️ [WP-148](https://borda.github.io/lucid-YOLO/work_packages/wp-148/) · [Roadmap](https://borda.github.io/lucid-YOLO/ROADMAP/) · [WP-150](https://borda.github.io/lucid-YOLO/work_packages/wp-150/) ➡️


## WP-149 — what a frozen value is allowed to mean

<a id="wp-149"></a>

The guard's third tier is the one that pins rather than proves, and the discipline it needs is entirely about honesty. `tests/data/test_aug_frozen.py` holds nine cases, each stating explicit parameters, asserting a literal, and naming in its own docstring why no closed form was available. Where a closed form does exist the case is not here — it is in [WP-148](https://borda.github.io/lucid-YOLO/work_packages/wp-148/)'s contract file, and every case that could be moved there was.

**The tier-A/tier-B boundary is a real line, not a filing preference.** A rotation is a similarity: it maps a rectangle to a rectangle, so a rotated box re-fits exactly and its expectation is derivable, which is why the quarter-turn rbox case sits in tier A. A shear is not: it maps the rectangle to a parallelogram, and the re-fit is then a *choice* of enclosing rectangle rather than a recovery of the original. That choice is the implementation's, which makes the sheared case tier B and the rotated case tier A, from what looks like the same operation on the same modality. The letterbox split is the same shape of argument in a different place: the ratio and the padding total are arithmetic, but a leftover of seven and a half rows resolving to an offset of three rather than three and three quarters is a convention, and conventions are pinned rather than derived.

**A tier-B docstring that cannot name its reason is a tier-A case in the wrong file.** Writing the reasons out caught three candidates and removed them: a mixup blend at a stated factor on constant images is arithmetic, a ring clipped against a canvas edge is a clip, and a mirrored rotated box is a reflection with a negated angle. All three were computed, found derivable, and either moved to tier A or dropped. What survived is a composite affine's boxes and ring, a fused affine-and-letterbox, an off-centre mosaic where the clip meets the instance filter, a hue rotation across an RGB sector boundary, a sheared rbox, keypoints through a flip-pair swap composed with the warp, an odd letterbox split, and a rasterised triangle.

**The triangle case carries its own arithmetic as a witness.** The ring `(2,2)-(10,3)-(6,11)` has a shoelace area of exactly 34 and rasterises to 32 pixels. Neither number derives the other — the gap *is* the boundary rule, resolved one row at a time against the pixel grid — but writing both down means a reader can see the frozen count is close to the area rather than merely take it on trust, and a future failure at, say, 41 is obviously wrong rather than merely different.

**`goldens/aug_invariants.json` takes what a literal would express badly.** Fifteen scalar aggregates over the same stated-parameter runs: surviving instance counts and written pixel counts pinned exactly, image means and polygon areas pinned within a band. The split follows what each store is good at — the golden harness compares `dict[str, float]`, so a box array flattened into `box_0_x1`-style keys would be a worse version of a pytest literal, while an image mean sitting in a test file would be a number with no tolerance and no home. Image expectations stay aggregate on purpose: the upstream engine samples at a different half-pixel convention, may execute through a different backend, and collapses several warps into one resample, so a byte-exact pixel expectation would fail at swap time for a *correct* implementation and the only escape would be a re-freeze under swap pressure.

**Golden discovery was verified positively, not inferred.** [WP-139](https://borda.github.io/lucid-YOLO/work_packages/wp-139/)'s lesson is that the harness globs, so it cannot notice a golden that was never written; a green run therefore proves nothing about a file's presence. `check_goldens.py` was run directly and printed `PASS aug_invariants.json — 15 metric(s)` and `31/31`, up from 30. That is the check, not the green gate around it.

`tests/data/test_aug_seam.py` is deleted here as [WP-147](https://borda.github.io/lucid-YOLO/work_packages/wp-147/) said it would be. It existed to witness that `__call__` equals `sample()` then `apply()` while the frozen tiers did not yet exist; now that tiers A, B and C all call `apply` with stated parameters, the seam is exercised by every case in all three files, and a witness that outlived its gap would just be a slower way of asserting the same thing.

