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:
2026-08-13 08:52:41 +00:00
parent 33dc12c948
commit 0776beb6f4
7 changed files with 249 additions and 19 deletions

View File

@@ -430,8 +430,10 @@ fn stage_models_quality_audit() {
/// element late still passes every count-based gate but wires arbitrary
/// vertices, which produces triangles with a repeated index. Before the fix
/// **579** decoded runs on the disc carried such triangles (and a runtime
/// capture confirmed 17 of 93 index batches disagreed with the GPU); after it,
/// 16 sub-meshes do — all in resources that are separately known-broken. Locking the
/// capture confirmed 17 of 93 index batches disagreed with the GPU); after it and
/// the two follow-ups (pad scoring in the grouped path, and preferring a
/// degenerate-free candidate over an earlier dirty one), exactly **one** does.
/// Locking the
/// number in, because the defect is invisible to coverage and to the anchor
/// oracle: every count stays correct while the geometry is mis-wired.
#[test]
@@ -466,14 +468,13 @@ fn decoded_index_runs_have_almost_no_degenerate_triangles() {
}
}
}
// The known 16 sub-meshes: the grouped `.dat` break composites in `ptc_pack`
// (`f102`/`f104`/`e107`, whose marker lists are documented not to map onto the
// stored blocks — 5 sub-meshes), plus `e201_bdy_03_m` (2 containers) and
// `_rou_f402_dead` (9). Every one has an independently recorded anchoring
// problem; a NEW name appearing here is a regression.
// The one: `_rou_f402_dead` in `Stage_S09`, whose degenerate-free block is
// claimed by `e_rou_f003_Near` — both 24-vertex bounding boxes, the identity
// class that needs descriptor-level data, not another heuristic. Anything
// else appearing here is a regression.
assert!(
offenders.len() <= 16,
"{} decoded index runs contain degenerate triangles (expected ≤ 16): {:?}",
offenders.len() <= 1,
"{} decoded index runs contain degenerate triangles (expected ≤ 1): {:?}",
offenders.len(),
&offenders[..offenders.len().min(20)]
);