Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
182 commits
Select commit Hold shift + click to select a range
15092f1
feat(world): stack uncompressed terrain layers into one array per set…
jonit-dev Oct 4, 2026
ab6b778
feat(world): per-layer ORM on the splat surface (PRD-493 phase 2)
jonit-dev Oct 4, 2026
9507e2d
feat(render): grade and film grain in the starter, on a new stage sea…
jonit-dev Oct 4, 2026
1273f3b
style: biome's own formatting for grade.spec.ts
jonit-dev Oct 4, 2026
1123968
test(world): a 16-layer uncompressed splat package on a GPU, plus its…
jonit-dev Oct 4, 2026
4b20d24
test(native): a lut-grade conformance row, and the desktop scenario t…
jonit-dev Oct 4, 2026
b09762a
docs: path spelled out for the capture dir
jonit-dev Oct 4, 2026
2a51ee1
Merge develop into PRD-492 and reconcile scaffold fingerprints
jonit-dev Oct 4, 2026
e3016e0
test(world): the sixteen-layer splat package on the native desktop host
jonit-dev Oct 4, 2026
721f3bb
Merge develop CI improvements into PR #431
jonit-dev Oct 4, 2026
499506f
feat(world): a conformance row for sixteen terrain layers on native
jonit-dev Oct 4, 2026
2c0d8ea
Merge develop CI improvements into PR #429
jonit-dev Oct 4, 2026
19f7fdd
fix: reconcile merged terrain fixture Three patch hash
jonit-dev Oct 4, 2026
02c2651
Merge current develop worker changes into PR #429
jonit-dev Oct 4, 2026
720f695
Merge hosted template concurrency improvement into PR #429
jonit-dev Oct 4, 2026
5fe0909
fix(render): neutralise the shipped grade and grain default (PRD-492)
jonit-dev Oct 4, 2026
81e47da
Merge origin/develop into feat/prd-492-grading
jonit-dev Oct 4, 2026
fffb534
Merge origin/feat/prd-492-grading (CI improvements + develop) into th…
jonit-dev Oct 4, 2026
cd40982
Merge origin/feat/prd-493-terrain-layers into feat/machinefall-120fps…
jonit-dev Oct 4, 2026
e9b1715
Merge origin/feat/prd-492-grading into feat/machinefall-120fps-runbook
jonit-dev Oct 4, 2026
4c2ee08
test(scaffold): the thirteen tree hashes on the runbook-merged tree
jonit-dev Oct 4, 2026
c1f5e55
docs(runbook): A7 baseline — 8 streamed-batch pipeline creations per …
jonit-dev Oct 5, 2026
2d6b930
fix(core): the prewarm's draw is a submission three admits, not one i…
jonit-dev Oct 5, 2026
1a39310
docs(runbook): A7 attempt 2 — the non-zero prewarm draw is proved, an…
jonit-dev Oct 5, 2026
705051b
revert(core): take the non-zero prewarm draw back out of #437
jonit-dev Oct 5, 2026
e98f8eb
fix(core): a pre-ORM splat table loads instead of hanging the world a…
jonit-dev Oct 5, 2026
558f08c
fix(core): a shadow casts only for what the main pass draws
jonit-dev Oct 5, 2026
f41dd47
docs(runbook): A7 attempt 3 and the walk on this PR
jonit-dev Oct 5, 2026
e422924
docs(runbook): tick A1 and C1; PRD-492 AC-2 passes
jonit-dev Oct 5, 2026
ecfd653
docs(runbook): read the walk the way PRD-478's ACs define it
jonit-dev Oct 5, 2026
555d3b5
fix(vq-assets): read the lifecycle baseline only after the world has …
Oct 5, 2026
754d4f1
docs(prd): VQ-01 is done; tick runbook row B1
jonit-dev Oct 5, 2026
2d5c8f6
fix(core): the world-matrix walk prunes again, and a frozen batch is …
jonit-dev Oct 5, 2026
fd0795e
docs(prd-492): AC-1 passes at 0.0 ms; the identity box needs a float …
jonit-dev Oct 5, 2026
102ca4d
perf(core): a chunk merge reads its own arrays, and the proxy copies …
jonit-dev Oct 5, 2026
7cce782
docs(prd): PRD-484 is done; tick runbook row A0
jonit-dev Oct 5, 2026
447ff65
fix(starter): load the grade table as float, so an identity table is …
jonit-dev Oct 6, 2026
d5931ba
docs(prd): PRD-492 is done; tick runbook row C2
jonit-dev Oct 6, 2026
e680de5
perf(world): frame spans for the main-thread terrain block and seam work
jonit-dev Oct 6, 2026
8d2c57b
fix(profiling): share the span recorder across core's entry copies
jonit-dev Oct 6, 2026
d52ea95
docs(prd-478): Phase 3's worker box passes on the browser walk
jonit-dev Oct 6, 2026
cd9f71c
feat(scripts): the world gate measures run-to-run noise (PRD-477)
jonit-dev Oct 6, 2026
752ee7f
docs(runbook): tick A2; PRD-477 Phase 1 reds on impostors-on and gree…
jonit-dev Oct 6, 2026
81c71bb
test(native): a workerless terrain inline-settlement conformance row …
jonit-dev Oct 6, 2026
be7d96f
docs(runbook): tick A6; PRD-478 Phase 3 holds on web and the native host
jonit-dev Oct 6, 2026
2fff646
docs(prd-478): AC-3 gate evidence; the box stays open on late objects
jonit-dev Oct 6, 2026
0695d03
fix(core): render virtual shadow levels outside a main-pass bundle re…
jonit-dev Oct 6, 2026
7a222d8
fix(core): render the water reflection outside a main-pass bundle record
jonit-dev Oct 6, 2026
b32dc68
perf(core): stabilize adaptive shadow refresh snapping (PRD-478)
jonit-dev Oct 2, 2026
7251bdd
docs(prd-478): Phase 1 snap and first-frame shadow boxes pass on this…
jonit-dev Oct 6, 2026
8416681
docs(runbook): what is left of AC-1, measured, and where the lever is
jonit-dev Oct 6, 2026
97c4813
feat(native): Android V8 ships cooked KTX2 and Meshopt (PRD-485 done)
jonit-dev Oct 6, 2026
f8de311
feat(gate): an intermittent candidate-only event makes the world gate…
jonit-dev Oct 6, 2026
0d446d1
chore(native): regenerate native coverage after the mobile format map…
jonit-dev Oct 6, 2026
5890dc9
fix(vq-assets): a runtime without a startup phase reads the baseline …
jonit-dev Oct 6, 2026
9316429
docs(prd-493): tick the Android row after it passes on the emulator
jonit-dev Oct 6, 2026
2dc92a2
perf(core): materials that sample one shadow share one render-group b…
jonit-dev Oct 6, 2026
afbc6b8
feat(core): the pyramid occlusion test on the CPU reference (PRD-489 …
jonit-dev Oct 6, 2026
7abad7a
feat(core): `?tnOcclusion=measure` counts what the pyramid cull would…
jonit-dev Oct 6, 2026
1390a7b
feat(core): `?tnOcclusion=measure` counts what the pyramid cull would…
jonit-dev Oct 6, 2026
1779c1d
fix(core): the occlusion pyramid reads the real main-pass depth (PRD-…
jonit-dev Oct 6, 2026
1df557a
fix(core): time the occlusion pyramid and keep its share honest (PRD-…
jonit-dev Oct 6, 2026
f26e82a
fix(core): the GPU timestamp sampler picks frames, not calls (PRD-489…
jonit-dev Oct 6, 2026
494d029
docs(runbook): tick A11; the occlusion go/no-go says go, so A12 opens
jonit-dev Oct 6, 2026
30fe9e0
perf(core): a streamed chunk's textures reach the device before its f…
jonit-dev Oct 6, 2026
b062990
fix(core): a terrain block merges once at a time and keeps late marks
jonit-dev Oct 6, 2026
2d864fe
feat(assets): score every compressed texture against a quality floor …
jonit-dev Oct 7, 2026
c811aa1
test(assets): pin the normal-map score; tick PRD-351 phase 1
jonit-dev Oct 7, 2026
d250152
feat(assets): a texture climbs the codec ladder until it meets the fl…
jonit-dev Oct 7, 2026
e6e9eab
docs(prd-351): tick phase 2 with its red-green specs and the terrain-…
jonit-dev Oct 7, 2026
a3271df
fix(core): the occlusion measure reads the right pyramid rows; PRD-48…
jonit-dev Oct 7, 2026
5d5b0f6
feat(assets): maxSize takes a per-slot map (PRD-351 phase 3)
jonit-dev Oct 7, 2026
e13f2e2
test: the web-codec proof is compressible; pure noise stays below the…
jonit-dev Oct 7, 2026
bca4aae
test(assets): a 4096 colour/normal pair compiles to 2048/1024 under a…
jonit-dev Oct 7, 2026
56ac2bb
docs(prd-351): price the resolution ladder on Quarry (phase 3 evidence)
jonit-dev Oct 7, 2026
a8bfe64
fix(assets): the ladder ships the smallest passing candidate; every g…
jonit-dev Oct 7, 2026
5d4e3e5
test(assets): prove per-slot caps on a 512 pair, not 4096
jonit-dev Oct 7, 2026
4ab72f2
fix(assets): capped fallback keeps caps, bounded RDO, pruned scores (…
jonit-dev Oct 7, 2026
10fcd80
merge: origin/develop into feat/machinefall-120fps-runbook (PR-437)
jonit-dev Oct 7, 2026
152df5f
fix(core): measure complete GPU frames and rendered shadow tails
jonit-dev Oct 7, 2026
55fe306
feat(core): join frame-budget windows on the simulation tick
jonit-dev Oct 7, 2026
fc6e259
chore(deps): refresh pnpm-lock.yaml importers
jonit-dev Oct 7, 2026
8d570c6
fix(core,native): clear root integration gates after the develop merge
jonit-dev Oct 7, 2026
0af2a9b
docs: record partial Phase 2 proof and correct walking mask
jonit-dev Oct 7, 2026
4c548a5
docs: preserve invalid timing attempts in Phase 2 proof
jonit-dev Oct 7, 2026
a54e2e6
docs: record complete Phase 2 reviews and pilot limits
jonit-dev Oct 7, 2026
688cb75
docs: record tick fixture checks and interrupted capture
jonit-dev Oct 7, 2026
82714ed
fix(core): keep fallback caster layers until a shadow key mesh exists
jonit-dev Oct 7, 2026
9111a54
fix(core): rebuild the shadow kernel when the GPU scene's buffers grow
jonit-dev Oct 7, 2026
bad3c0a
docs(prd-478): record pop intervals 04/28 on the shadow-kernel fix
jonit-dev Oct 7, 2026
5eb5baa
docs(prd-478): mark Phase 3 done; both its boxes were already ticked
jonit-dev Oct 8, 2026
fc9d56c
fix(core): shadow keys cast at the main pass's level, from the eye
jonit-dev Oct 8, 2026
db50de7
docs(prd-478): record the eye-LOD shadow fix and its blind review
jonit-dev Oct 8, 2026
f9ee2ec
feat(scripts): pair two playtest reports label by label
jonit-dev Oct 8, 2026
819b9d8
docs(prd-478): record the paired-report tool and the same-arm counter…
jonit-dev Oct 8, 2026
cdf18c1
docs(prd-478): same-arm byMove drifts on a quiet machine too
jonit-dev Oct 8, 2026
47b5b86
feat(core): count shadow window travel in pages
jonit-dev Oct 8, 2026
feb26b7
docs(prds): file PRD-537, far hills show the forest
jonit-dev Oct 8, 2026
ce9f7cc
feat(core): ?tnAdaptiveRefresh=0 fixes the shadow refresh step for co…
jonit-dev Oct 8, 2026
de508ac
docs(prd-478): tick the map-walk iteration tooling box
jonit-dev Oct 8, 2026
7523663
docs(prd-478): tick the GPU-key shadow selection box
jonit-dev Oct 8, 2026
7659ad5
docs(prd-478): record the AC-3 rater pass on all eight poses
jonit-dev Oct 8, 2026
9aa9d97
fix(core): shadow keys pick their level at the authored distance
jonit-dev Oct 8, 2026
fe9d054
Revert "fix(core): shadow keys pick their level at the authored dista…
jonit-dev Oct 8, 2026
0b97127
docs(prd-478): record the stock-shadow reference run and what is blocked
jonit-dev Oct 8, 2026
5ffce51
docs(prd-478): record the RTX 2080 walking timing: GPU holds, CPU tai…
jonit-dev Oct 8, 2026
7709c2a
perf(core): bundle-safety walk skips typed arrays
jonit-dev Oct 8, 2026
2c98e5f
docs(prd-478): record the first AC-1 attribution and its fix
jonit-dev Oct 8, 2026
61c21a8
docs(prd-478): tick AC-2, walking GPU p95 at most 6.6 ms on three runs
jonit-dev Oct 8, 2026
0d87125
docs(prd-478): map the walking CPU tail by frame span
jonit-dev Oct 8, 2026
35e5e7f
perf(core): one compute pass for each GPU-scene dispatch
jonit-dev Oct 8, 2026
3aeb34d
docs(prd-478): record the one-pass compute cut on the walk
jonit-dev Oct 8, 2026
0a0b21a
docs(prd-478): record the pop series on this build, 1 one-arm pop vs …
jonit-dev Oct 8, 2026
6592adc
docs(prd-478): reopen AC-2; longer runs exceed the window bound
jonit-dev Oct 8, 2026
949ff51
docs(prd-478): record both walk measurement boxes
jonit-dev Oct 8, 2026
6f74e06
docs(prd-478): mark Phase 2 done
jonit-dev Oct 8, 2026
5c55761
docs(prd-478): near-pooled walk windows: AC-1 and AC-2 fail under load
jonit-dev Oct 8, 2026
1fe8b30
perf(core): merge adjacent GPU-scene buffer update ranges
jonit-dev Oct 8, 2026
449f365
docs(prd-478): record the range-merge cut on the walk
jonit-dev Oct 8, 2026
4fb65b1
docs(prd-478): name the GPU-side next step, a partial shadow-map update
jonit-dev Oct 8, 2026
4a50db8
docs(prds): file PRD-538, shadow maps redraw only what streamed in
jonit-dev Oct 8, 2026
b4abd91
docs(prd-478): AC-2 waits on PRD-538
jonit-dev Oct 8, 2026
7784988
docs(prd-538): design notes from three's shadow pass
jonit-dev Oct 8, 2026
5a6d9d0
perf(core): terrain tile/block swaps no longer redraw the shadow map
jonit-dev Oct 8, 2026
ed75639
perf(core): a non-casting or swap-twin arrival no longer redraws shadows
jonit-dev Oct 8, 2026
b3c95a5
docs(prd-478): record the invalidation cuts, level renders -68 %
jonit-dev Oct 8, 2026
de10063
docs(prd-538): update level renders after PRD-478's invalidation cuts
jonit-dev Oct 8, 2026
d65fa1e
fix(assets): a lone LOD-named mesh gets a generated chain
jonit-dev Oct 8, 2026
bcb3c7a
docs(prd-538): the early-route GPU cost is the main pass, not this map
jonit-dev Oct 8, 2026
bf507af
docs(prd-478): quiet timing and the early-route main-pass attribution
jonit-dev Oct 8, 2026
a410703
revert(assets): move the lone-LOD chain fix to PRD-539
jonit-dev Oct 8, 2026
c9544d7
docs(prd-538): the early-route fix lives in PRD-539
jonit-dev Oct 8, 2026
ecce9b6
docs(prd-478): AC-3 and AC-2 evidence on a build with PRD-539
jonit-dev Oct 8, 2026
ce8c4de
fix(core): a placement past castLevels casts with the coarsest shape …
jonit-dev Oct 8, 2026
3cabfde
feat: absorb PRD-539 (card LOD levels keep the canopy) into PRD-478
jonit-dev Oct 8, 2026
123f9d2
docs(prd-478): AC-3 on the absorbed build, 23/24 and a start-of-walk pop
jonit-dev Oct 8, 2026
52d5e6c
fix(core): the adaptive LOD bias budgets against the game's target frame
jonit-dev Oct 8, 2026
3829321
docs(prd-478): AC-3 on the bias-fix build, 24/24 vs develop; start-of…
jonit-dev Oct 8, 2026
fc51746
docs(prd-478): record what is open at the 2026-10-08 wrap-up
jonit-dev Oct 8, 2026
892f3f0
docs(prd-478): root cause of the start-of-walk forest arrival
jonit-dev Oct 8, 2026
a67f710
docs(prd-537): Phase 1 measurement, hills stay bare and the dark band
jonit-dev Oct 8, 2026
af2567b
feat(core): TN_FRAME_BUDGET counts the frames past the target frame
jonit-dev Oct 8, 2026
57c8ba8
fix(core): the prewarm gate holds while builds are owed
jonit-dev Oct 8, 2026
e29e8f7
docs(prd-478): AC-3 passes on the gate-fix build
jonit-dev Oct 8, 2026
f240fec
docs(prd-478): AC-2 passes on 3 gated quiet runs
jonit-dev Oct 8, 2026
2002139
fix(core): the bundle-safety walk never enumerates a pixel array (PRD…
jonit-dev Oct 9, 2026
b1520c5
feat(core): split the main draw record into eight shards (PRD-494)
jonit-dev Oct 9, 2026
dcb5204
fix(runtime-native): render bundles take indirect draws (PRD-494)
jonit-dev Oct 9, 2026
4037d1d
Merge PR #473 (feat/prd-494-finish) into the PRD-478 runbook branch
jonit-dev Oct 9, 2026
4512328
Merge docs/prd-far-forest (PRD-only) into the one PR, #473
jonit-dev Oct 9, 2026
5cb69cf
Merge docs/prd-shadow-partial-redraw (PRD-only) into the one PR, #473
jonit-dev Oct 9, 2026
9d2dd86
docs(prds): point PRD-538 and PRD-541 at PRD-541 and PR 473
jonit-dev Oct 9, 2026
e1182d4
docs: PRD-489 links follow its move to done/
jonit-dev Oct 9, 2026
fa42155
perf(core): thirty-two main record shards, not eight (PRD-494)
jonit-dev Oct 9, 2026
d2bb5f6
fix(core): reconcile the merged GPU timing and Three patch upgrade chain
jonit-dev Oct 9, 2026
5c09f6f
docs(native): regenerate the coverage report for the bundle indirect …
jonit-dev Oct 9, 2026
55ea68c
docs(prds): file PRD-549, the engine holds 60 fps on a weak GPU
jonit-dev Oct 9, 2026
ddceec6
docs(prd-549): Phase 1 targets the gate that held the scaler, not ste…
jonit-dev Oct 9, 2026
d60d865
docs(prd-549): the stall rule froze the scaler; shadow redraw hitches…
jonit-dev Oct 9, 2026
1d3cf68
test(core): a main shard is re-recorded in the update its membership …
jonit-dev Oct 9, 2026
4da9bdb
fix(core): a stalled window whose ordinary frame is slow can still sc…
jonit-dev Oct 9, 2026
d8e6543
fix(core): re-record a bundle shard once the compile it was recorded …
jonit-dev Oct 9, 2026
748aab4
fix(core): an invalidation redraw waits out its own GPU cost
jonit-dev Oct 9, 2026
f02533e
docs(prd-549): a realistic laptop goal, 30 fps without redraw hitches
jonit-dev Oct 9, 2026
6d88401
fix(core): a stalled window is judged on its GPU, not its present median
jonit-dev Oct 9, 2026
5612119
docs(prd-494): close the PRD and move it to done/
jonit-dev Oct 9, 2026
f238d26
Merge PR 475 (feat/prd-494-main-record) into the one PRD-494 PR, #473
jonit-dev Oct 9, 2026
e5c4e1f
chore(core): name the reason for four casts and drop one stale waiver
jonit-dev Oct 9, 2026
f0cbc40
docs(native): regenerate the coverage report after the PRD-494 merge
jonit-dev Oct 9, 2026
b4aa015
fix(core): a window full of GPU samples is fresh however late the las…
jonit-dev Oct 9, 2026
8d09083
docs(prd-549): the scaler reaches the floor on the Iris Xe
jonit-dev Oct 9, 2026
1d394b0
fix(core): a 3D texture never asks for a render attachment
jonit-dev Oct 9, 2026
539b765
test(core): the capture fixture spec follows the PRD-494 desktop cont…
jonit-dev Oct 9, 2026
41aedd9
test(create-threenative): scaffold hashes follow the 3D-texture patch…
jonit-dev Oct 9, 2026
3d741c6
fix(assets): a large texture ETC1S cannot shrink skips the UASTC rungs
jonit-dev Oct 9, 2026
877d671
test(create-threenative): grade.spec registers its temporary directory
jonit-dev Oct 9, 2026
eb220f1
test(native): lut-grade and terrain-inline rows admit 1-level rounding
jonit-dev Oct 9, 2026
7342658
test(create-threenative): grade.spec removes the table directory it w…
jonit-dev Oct 9, 2026
8ffc111
Merge origin/develop (b12b257f1) into PR 473
jonit-dev Oct 9, 2026
9e6ac72
docs(native): census after the develop merge
jonit-dev Oct 9, 2026
6e8ca01
fix(ci): golden-path waits 90 s for a dev server cooking its assets
jonit-dev Oct 9, 2026
747197f
docs(prds): PRD-478 AC-1 and PRD-541 AC-2 pass on 3 quiet runs
jonit-dev Oct 9, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions biome.json
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,7 @@
"examples/abyss-vanilla/vite.config.js",
"examples/auto-lod/public/**",
"examples/abyss-framework/public/**",
"examples/prd493-terrain-splat/public/**",
"examples/abyss-framework/vq-assets/.generated/**",
"examples/csg-doorway/public/**",
"docs/benchmark/sweeps/**",
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
# PRD-351 — Compression never looks worse than a floor

**Status:** READY FOR EXECUTION
**Status:** PARTIAL — Phases 1 and 2 have recorded results. Phase 3 prices the normal slot. Bark capture and acceptance remain open.
**Priority:** P2 — Open: SSIM and delta-E00 floors validated, alpha coverage preserved, per-slot caps measured.
**Complexity:** 2 (6-10 files) + 2 (new system) + 1 = **5 → MEDIUM mode**
**Batch:** `docs/PRDs/assets/`
Expand Down Expand Up @@ -162,21 +162,21 @@ that downsampled it. `sandbox/quarry` (PRD-349) is the game; it re-imports its 6

**Implementation**

- [ ] Reuse the shipped `basis_transcoder` for the decode — the spike proved it works standalone at
`cTFRGBA32` (`getWidth`/`getHeight`/`transcodeImage`, not `getImageWidth`).
- [ ] Score only mip 0.
- [ ] For the compression floor compare decoded output with the same-resolution pre-encode image.
- [x] Reuse the shipped `basis_transcoder` for the decode — the spike proved it works standalone at
`cTFRGBA32` (`getWidth`/`getHeight`/`transcodeImage`, not `getImageWidth`). **Done 2026-10-06:** `image-quality.ts` decodes mip 0 through the shipped transcoder at RGBA32.
- [x] Score only mip 0. **Done 2026-10-06.**
- [x] For the compression floor compare decoded output with the same-resolution pre-encode image.
Report resolution loss against masters separately; otherwise resizing and codec loss are
conflated. Re-measure the old spike with its exact inputs/settings before using its tolerance.
- [ ] **This phase changes no output bytes.** It only measures. That is deliberate: the instrument
must be trusted before it is allowed to steer.
conflated. Re-measure the old spike with its exact inputs/settings before using its tolerance. **Done 2026-10-06:** compared with the same-resolution pre-encode pixels; resolution loss is not scored yet (no master-resolution corpus).
- [x] **This phase changes no output bytes.** It only measures. That is deliberate: the instrument
must be trusted before it is allowed to steer. **Done 2026-10-06:** on 48 real terrain textures the payload SHA256 is identical 48/48 with scoring on or off; only `assets.manifest.json` gains the quality fields. Scores: 32 pass, 0 below the floor, 16 normal slots unvalidated; build +8.7 % (4.16 → 4.52 s, single cold sample).

**Tests required**

| Test file | Test name | Assertion | Negative control (observed red) |
|---|---|---|---|
| `__tests__/image-quality.spec.ts` | `should score an identical pair at 1.0` | `ssim(a,a) === 1` | compare a to noise → far below 1 |
| `__tests__/image-quality.spec.ts` | `should reproduce the spike's measured scores` | bark_normal scores 0.969 ± 0.005 | change the window size → drifts out of tolerance, proving the number is computed not hardcoded |
| `__tests__/image-quality.spec.ts` | `should reproduce the pinned normal-map score` | SSIM matches the pinned value ± 0.005. Spike inputs were never committed; PRD-351 D31 substitutes committed `examples/prd493-terrain-splat/public/world/terrain/tex/layer-00_nrm.jpg` (128×128), measured and pinned at SSIM 0.9945243217001697. | change the window size → drifts out of tolerance, proving the number is computed not hardcoded |
| `__tests__/model-texture-pass.spec.ts` | `should report a score for every compressed image` | every row carries SSIM | drop the decode → row missing, fails |

**Revert check:** delete the scoring call → the report loses its column and the third test goes red.
Expand All @@ -198,16 +198,16 @@ rung stays there and gets smaller.

**Implementation**

- [ ] Rungs: `etc1s@150` → `uastc+rdo λ3 +zstd` → `uastc+rdo λ1 +zstd` → `uastc` → `none`.
- [x] Rungs: `etc1s@150` → `uastc+rdo λ3 +zstd` → `uastc+rdo λ1 +zstd` → `uastc` → `none`. **Done 2026-10-06 (`d250152ba`):** the first rung that meets the floor wins; red-green `should escalate an image that fails the floor` (floor 0 control stays on rung 1) and `should keep a clean image on the cheapest rung`.
- [x] **RDO ships behind the floor, with a bounded escape hatch. DECIDED.** It crashed the encoder
module during the PRD-349 spike, so Phase 2 reproduces the crash first. Rule: **timebox the
fix to one day.** If it is not reliable by then, the ladder ships as
`etc1s → uastc → none` with the RDO rungs absent and the reason recorded — the floor and the
escalation are the architecture; RDO is one rung on it, not the point of it.
- [ ] Skip rungs a slot forbids: a normal map never tries `etc1s`.
- [ ] Report: `34 etc1s · 5 escalated to uastc · 0 uncompressed`. Order eligible candidates by
- [x] Skip rungs a slot forbids: a normal map never tries `etc1s`. **Done 2026-10-06:** red-green `should never try etc1s for a normal map` (allowing it goes red).
- [x] Report: `34 etc1s · 5 escalated to uastc · 0 uncompressed`. Order eligible candidates by
measured emitted bytes and retain the smallest that passes; codec names alone do not prove
size ordering. Preserve 349's `not-smaller` and automatic `block-size` fallback behaviour.
size ordering. Preserve 349's `not-smaller` and automatic `block-size` fallback behaviour. **Done 2026-10-06:** `should report the rung histogram`. On the 48-texture terrain corpus the build reports `2 etc1s · 0 escalated to uastc · 46 uncompressed`, and the payload is byte-identical to Phase 1. A cold build takes 8.5 s before and 13.9 s after; a warm build takes 0.07 s.

**Tests required**

Expand Down Expand Up @@ -245,11 +245,13 @@ rung stays there and gets smaller.

**Implementation**

- [ ] Re-import `quarry`'s props with `maxTextureSize: 4096` so the compile sees masters.
- [ ] Compile at 1024, 2048 and 4096. Record bytes, GPU bytes, SSIM and a capture for each.
- [ ] Capture the three at matched camera positions, close enough to read bark detail.
- [ ] Compare the game-owned 2048 colour / 1024 mask candidate against the other arms. Adopt it
only if visible detail and measured device memory justify it; keep compiler defaults intact.
- [x] Re-import `quarry`'s props with `maxTextureSize: 4096` so the compile sees masters. **Done 2026-10-06:** the supplied fresh import has six props with 4096² normal inputs. Only `normalTexture` is bound. The base-color mask is unmapped in `textures/`. proof: `/tmp/perslot.md` and the supplied Phase 3 captures.
- [x] Compile at 1024, 2048 and 4096. Record bytes, GPU bytes, SSIM and a capture for each. **Done 2026-10-06:** montage labels report disk/GPU MB and SSIM: 1024 = 2.24/2.67/0.9885, 2048 = 8.92/10.67/0.9920, 4096 = 35.97/42.67/0.9950. Separate distinct-texture and runtime-load totals are not recorded. proof: [resolution record](../../verification/PRD-351-resolution-ladder.md) and `b8res/montage-pose3-rock-near.jpg`.
- [ ] Capture the three at matched camera positions, close enough to read bark detail. proof: supplied `b8res/montage-*.jpg` and `res-<arm>-<pose>.png`.
**Open:** four matched positions show the trail, mid-distance rocks, a close rock surface, and the route. No capture proves bark detail.
- [x] Compare the game-owned 2048 colour / 1024 mask candidate against the other arms. proof: [resolution record](../../verification/PRD-351-resolution-ladder.md), `/tmp/perslot.md`, and the coordinator's rock-near verdict.
Adopt it only if visible detail and measured device memory justify it. Keep compiler defaults intact.
**Done 2026-10-06:** reject this candidate on this pack. It reaches only the normal slot and equals 1024: 2.24 MB disk, 2.67 MB GPU, SSIM 0.9885. Keep compiler defaults and the game's import policy. No phone run proves a safe total.

**Tests required**

Expand Down Expand Up @@ -295,8 +297,35 @@ Consumer-scoped.

- [ ] `chooseCodec`'s slot constraints feed candidate eligibility, with no surviving second selection path
- [ ] Every gate has a negative control observed failing

**Offline findings (2026-10-06):** executable controls now catch scalar caps, upscaled PNGs,
nonminimum selection, missing/empty histogram rows, changed SSIM windows, omitted scores,
4K downsampling, oversize encoder input, disabled Zstd/mips, stale encoder/metric/RDO/slot
identities and floor/cap decisions, a one-byte fallback budget, interrupted publication,
torn final bytes and colliding staging names. Each mutation fails its assertion or operation
inside the spec; no gate was weakened. The byte-minimum assertion first failed (676 B versus
a passing 566 B candidate); the shared ladder now measures all eligible encodings and keeps
the minimum, with a new cache identity. Browser/native, visual/master-corpus and historical
spike-pair gates remain outside this offline proof. The five assets spec files passed across
focused runs after a combined-run worker exit; `pnpm exec tsc --noEmit -p packages/assets`
passed. The separate default-4K compile gate timed out at 180 s both combined and isolated;
its timeout and assertions remain unchanged. Boxes are unchanged.

- [ ] RDO's encoder crash reproduced, and either fixed within the one-day timebox or the rungs
dropped with the reason recorded
dropped with the reason recorded

**Offline findings (2026-10-06):** PRD-349's assumption spike records four 1024² pine/leaf
maps and `enableRDO`/`rdoQualityLevel`, but no numeric RDO settings or crash trace. Searches
of PRD-349/350, `git log --all -S rdo` and `-S RDO` over assets/docs, and named local files
found neither `spike-matrix.mjs` nor the original PNGs; exact historical replay remains
unproven. `node --import tsx /tmp/b8-gates-rdo.mts` completed current-encoder λ3/λ1 runs
on `examples/prd493-terrain-splat/public/world/terrain/tex/layer-00_diff.jpg` (SHA256
`35ec4682e3ffbf4c385da5734d3da9b2692833f1dc8ab88c9799c288d14cd6af`), nearest-resized
to 1024²/4096², with UASTC, mips, perceptual sRGB and Zstd enabled, quality 150. Outputs
were 4397/4391 B and 7005/6894 B; all had the expected dimensions, mip count and Zstd scheme,
with no crash. These are substitute smoke inputs, not masters or a reproduced/fixed crash;
RDO rungs remain, and no box is ticked.

- [ ] Proved on 4096² masters, not on already-downsampled content
- [ ] Real Quarry browser and native desktop scenarios pass on installed packages; mobile compile
still emits no unsupported codec. Retain native runtime proof for every changed mobile path;
Expand All @@ -311,3 +340,7 @@ Consumer-scoped.
| Measuring every rung makes builds slow | Scores cache on the existing content-addressed key; a second build pays nothing. Measure and report the first-build cost. |
| SSIM passes an image a human would reject | The rule stands: pixel metrics never replace semantic review. Every landing carries an eyes-on capture. |
| 2048 blows a phone's memory budget | Phase 3 records GPU resident bytes against the existing `mobile-memory-budget` reference before adopting the game-owned candidate |

## PR #437 scaffold regression repair (2026-10-06)

Complexity: LOW; shared asset cook, no public API change. Preserve quality floors, smallest-passing selection, authored codec opt-outs, and profile caps. Compare the seven requested scaffold specs at f24f6243e and tip; repair capped fallback and remove redundant scoring; rerun asset specs and all seven scaffold specs. No browser or commits in this sandbox. Verified: six asset spec files, 157 tests passed; assets typecheck and focused Biome check passed (warnings only). The seven requested scaffold files finished with 184 passed / 3 failed: two publication fixtures cannot listen on 127.0.0.1 (EPERM), and the ponytail hook yields empty subprocess stdout; all three also fail at f24f6243e. All 15 original reported regressions now pass. Cold minimal cook: merge base 19.60 s, original tip 230.45 s, repaired tip 47.92 s. Caps now survive below-floor fallback in standalone and embedded images; RDO search is bounded for large images, nonwinning scores are pruned, and smallest-passing/floor/alpha checks remain enforced. Scaffold hashes were refreshed only after restoring the previous Three patch reproduced all thirteen prior hashes. Full package build reached successful JS/DTS output but publint packing is blocked by empty pnpm subprocess output; doc links are unverified because the sandbox rejects the git subprocess. Browser/native gates were not run.
Loading
Loading