fix(xbg7): degenerate index runs 582 -> 1 (grouped path + prefer a clean candidate)
Two follow-ups to the pad-scoring fix, both driven by the same invariant (a correctly located index run has no degenerate triangles): - anchor_grouped_meshes picked its pad by first-match too; scoring the pivot run the same way cleared every remaining ptc_pack composite (f102/f104/e107). - anchor_pool_mesh now prefers a degenerate-free candidate over an earlier dirty one. examples/better_home.rs showed the last two resources each had exactly one degenerate-free, pool-covering block, sitting later in file order than the lookalike we took. First-match order is kept for every clean hit, and a dirty block is still used if nothing clean exists, so coverage cannot regress. degenerate index runs, disc-wide: 582 -> 11 -> 1 captured index runs identical: 93/93 (unchanged) resources decoded / misses: 6 209 / 85 (unchanged) index runs changed / anchors moved: 590 / 10 (_rou_f402_dead x8, e201_bdy_03_m x2) Cross-container minority decodes 89 -> 96, and that is progress: all seven new rows are _rou_f402_dead, which now has a majority (32x25x8) for the first time, so its seven wrong copies are named instead of hidden behind "no majority". The last dirty run (_rou_f402_dead in Stage_S09) is blocked by distinct assignment — its clean block is claimed by e_rou_f003_Near, both 24-vertex bounding boxes. A winding-floor escalation for that case was written, measured to fire for nothing, and reverted; the reasoning is kept as a comment. Regression threshold tightened to 1. Suite green with --include-ignored apart from the pre-existing known-failing cross-container consistency test. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01NptfmpjdpNCKEez6d2xvA9
This commit is contained in:
@@ -1458,3 +1458,53 @@ panel lines, bridge tower and funnels. Same 31 991 triangles, same 4 placements,
|
||||
same bounds — only the wiring differs. Third time this project has learned it:
|
||||
**render the output**; a metric that cannot see a 1 600-unit slab could not see
|
||||
this either.
|
||||
|
||||
### ✅ Following it through: degenerate index runs 582 → 1 (2026-08-13)
|
||||
|
||||
The pad-scoring fix left 16 dirty sub-meshes. Two follow-ups cleared all but one.
|
||||
|
||||
**1. The grouped path had the same first-match bug.** `anchor_grouped_meshes`
|
||||
picks `ib0 = vb0 − span − pad` the same way, so it got the same scoring (pivot
|
||||
run's degenerate count, then winding). That cleared every remaining `ptc_pack`
|
||||
composite — `f102_break.dat` (161 degenerate triangles), `f104_break.dat` (3
|
||||
sub-meshes) and `e107_break.dat` — 16 → 11.
|
||||
|
||||
**2. Prefer a degenerate-free candidate over an earlier dirty one.** The last two
|
||||
resources were anchored on a *lookalike earlier in file order*:
|
||||
`examples/better_home.rs` scores every candidate `(start, pad)` for a resource and
|
||||
showed **exactly one** degenerate-free, pool-covering block for `e201_bdy_03_m`
|
||||
(`0x316383C`, ours was `0x248D054` with 4 degenerate triangles) and one for
|
||||
`_rou_f402_dead`. So `anchor_pool_mesh` now keeps first-match order for every
|
||||
clean hit and only searches on when the accepted block is provably mis-fitted,
|
||||
falling back to the dirty block if nothing clean exists (coverage can never
|
||||
regress). 11 → 1.
|
||||
|
||||
| | first-match | + pad scoring | + these two |
|
||||
|---|---|---|---|
|
||||
| decoded sub-meshes with a degenerate index run (disc-wide) | 582 | 11 | **1** |
|
||||
| captured index runs identical (`Stage_S02`) | 76/93 | 93/93 | **93/93** |
|
||||
| resources decoded / misses | 6 209 / 85 | 6 209 / 85 | **6 209 / 85** |
|
||||
| sub-meshes whose index run changed | — | 580 | 590 |
|
||||
| of those, vertex anchor moved | — | 0 | **10** (`_rou_f402_dead` ×8, `e201_bdy_03_m` ×2) |
|
||||
|
||||
**The consistency screen went 89 → 96 minority decodes, and that is progress.**
|
||||
All seven new rows are `_rou_f402_dead`: eight containers now agree on a
|
||||
**32 × 25 × 8** box, which gives the resource a *majority for the first time*, so
|
||||
the seven copies that still land elsewhere (`160×160×78`, `519×100000×519`,
|
||||
`856×463×803`, `779×5769×5769`) are finally **named** instead of hiding behind "no
|
||||
majority". Textbook case of consistency being the weaker witness: the number rose
|
||||
because the decoder got better.
|
||||
|
||||
**The one remaining dirty run** is `_rou_f402_dead` in `Stage_S09`. Its
|
||||
degenerate-free block (`0x17AE620`, validates at pad 0) is **claimed by
|
||||
`e_rou_f003_Near`** — so distinct assignment blocks it, not a gate. Both are
|
||||
24-vertex bounding boxes: the identity class that needs descriptor-level data, not
|
||||
another heuristic. A winding-floor escalation for this case was written, measured
|
||||
to fire for **nothing** on the disc, and reverted (the comment survives in
|
||||
`anchor_pool_mesh`).
|
||||
|
||||
**Next target, with the caveat that killed the last attempt:** six containers
|
||||
decode `_rou_f402_dead` to a *clean but wrong* block, so several degenerate-free
|
||||
candidates exist and file order picks badly. The majority span would separate
|
||||
them — but "flag, don't silently rewrite geometry on a vote" still stands, so this
|
||||
needs the box-identity data, or a capture of a stage that draws it.
|
||||
|
||||
Reference in New Issue
Block a user