From 2d3880b624ea31d6b565014fc989d0f2180a336c Mon Sep 17 00:00:00 2001 From: ReaperOfVeriod Date: Tue, 13 Oct 2026 08:11:35 +0200 Subject: [PATCH] The class-ID retraction goes further: the claim was wrong before the fix Pinned down the one slot that still cleared 100x after the record-boundary fix, and it is the artifact this section was written to guard against. The discriminator the original version lacked: a slot that really carries moby class IDs must SPAN the ID space, so some of its values land above a level's moby count. The artifact does the opposite. type slot enrichment distinct values ABOVE the moby count 0xd76cc453 6 305x 9 0 0xf2ef4275 34 64x 9 341 0xf2ef4275 7 46x 16 412 0xf2ef4275 14 34x 16 432 0xd76cc453 9 32x 28 702 The 305x slot's nine values are 1, 3, 4, 5, 10, 11, 12, 13, 15 -- all under a level's ~215 moby classes, so every one scores as a hit and 65536/215 = 305x. Nine small numbers is not a class-ID array. Every slot that actually spans the ID space scores 10x to 64x, which is chance. So the original claim's own numbers gave it away and it was not read that way: "5 to 42 distinct values at 153x to 289x" is exactly the artifact's shape -- a handful of small values, none above the count. The record-boundary fix was never what made this claim wrong. It was wrong when written, and the retraction is complete rather than the partial one I committed two turns ago. The test now separates the two quantities: slots clearing 100x (currently 1, expected to remain the artifact) and slots clearing 100x WHILE ALSO having values above the moby count (currently 0, must stay empty). That second number is the one that means something, and a slot appearing in it would be the first genuine evidence of a class-ID array anywhere in the container. Worth noting the per-SLOT bookkeeping: the count of values above the moby count is keyed by (type, slot), not by type. A first attempt keyed it by type and the artifact type passed the check on the strength of its ordinary slots -- 783 of its own values sat above the count at other slots. The check has to be about the individual slot, or one good slot launders a bad one. 245 tests, clippy clean. --- crates/gc-extract/tests/disc.rs | 52 +++++++++++++++++++++++++------ docs/formats/core-data-regions.md | 37 +++++++++++++++++++--- 2 files changed, 74 insertions(+), 15 deletions(-) diff --git a/crates/gc-extract/tests/disc.rs b/crates/gc-extract/tests/disc.rs index 4a69f0a..80a9f8b 100644 --- a/crates/gc-extract/tests/disc.rs +++ b/crates/gc-extract/tests/disc.rs @@ -3232,9 +3232,14 @@ fn chunk_records_start_16_byte_aligned() { /// boundaries measured against each record's successor, and ragged chunks /// skipped, only 2 types show a varying class-ID slot. /// -/// What survives is narrow and is asserted as such: 2 chunk types show a slot -/// that varies in the manner of a class ID, and exactly 1 of those still clears -/// 100x enrichment over chance. The +/// **Nothing survives, and the flaw was present from the start.** The +/// discriminator the original version lacked: a slot that really carries moby +/// class IDs must span the ID space, so some values land ABOVE a level's moby +/// count. The slot that scored 305x has 9 distinct values -- +/// 1, 3, 4, 5, 10, 11, 12, 13, 15 -- and **none** above the count, because all +/// are under ~215. Every slot that does span the ID space scores 10x to 64x. The +/// original "5 to 42 distinct values at 153x to 289x" had exactly the artifact's +/// shape, so the record-boundary fix was never what made it wrong. The /// `distinct >= 4` filter is still load-bearing -- without it the top slots are /// constants that read as 305x because any value below the moby count counts as /// a hit. See `docs/formats/core-data-regions.md`. @@ -3259,6 +3264,10 @@ fn moby_class_ids_sit_in_one_slot_per_chunk_type() { // (type, slot) -> (nonzero, hits, expected, distinct) let mut stat: BTreeMap<(u32, usize), (u64, u64, f64, usize)> = BTreeMap::new(); + // (type, slot) -> how many of that slot's values land above the moby count. + // Keyed per SLOT, not per type: a type with one artifact slot and several + // ordinary slots must not have the ordinary ones vouch for it. + let mut above: BTreeMap<(u32, usize), usize> = BTreeMap::new(); let mut seen: BTreeMap<(u32, usize), BTreeSet> = BTreeMap::new(); for n in 0..27 { @@ -3294,6 +3303,8 @@ fn moby_class_ids_sit_in_one_slot_per_chunk_type() { e.0 += 1; if (v as u64) < nm { e.1 += 1 + } else { + *above.entry((t, k)).or_default() += 1 } } } @@ -3328,16 +3339,37 @@ fn moby_class_ids_sit_in_one_slot_per_chunk_type() { // boundaries fixed, and this test skipping ragged chunks, only 2 types show // a varying class-ID slot -- so "one class-ID slot per chunk type" was a // measurement of the parser's over-read, not of the disc. + // The discriminator the original version lacked. A slot that really carries + // moby class IDs spans the ID space, so some of its values must land ABOVE a + // level's moby count. The artifact does the opposite: every one of its values + // sits below the count, so every value scores as a hit. + // + // type 0xd76cc453 slot 6 305x 9 distinct values 0 above the count + // type 0xf2ef4275 slot 34 64x 9 distinct values 341 above the count + // type 0xf2ef4275 slot 7 46x 16 distinct values 412 above the count + // + // The 305x slot's values are 1, 3, 4, 5, 10, 11, 12, 13, 15 -- all under a + // level's ~215 moby classes, giving 65536/215 = 305x. Nine small numbers is + // not a class-ID array, and every slot that does span the ID space scores in + // single digits to tens. Nothing reaches 100x while also exceeding the count. + let spanning: Vec<_> = best + .iter() + .filter(|(&t, &(k, r, _))| r >= 100.0 && above.get(&(t, k)).copied().unwrap_or(0) > 0) + .collect(); let strong: Vec<_> = best.values().filter(|(_, r, _)| *r >= 100.0).collect(); - assert!( - !strong.is_empty(), - "no type reaches 100x enrichment -- was 20 before the record-boundary fix" + eprintln!( + "chunk class-ID slots: {} types; {} at >=100x, of which {} also have \ + values above the moby count ({} is the small-value-set artifact)", + best.len(), + strong.len(), + spanning.len(), + strong.len() - spanning.len() ); - assert_eq!(strong.len(), 1, "strong-enrichment type count moved"); assert!( - best.len() >= 2, - "only {} chunk types carry a varying class-ID slot (was >= 20)", - best.len() + spanning.is_empty(), + "{} slots clear 100x while spanning the ID space -- there may be a real \ + class-ID array after all", + spanning.len() ); assert!( best.len() < 20, diff --git a/docs/formats/core-data-regions.md b/docs/formats/core-data-regions.md index 80aa61e..51f5656 100644 --- a/docs/formats/core-data-regions.md +++ b/docs/formats/core-data-regions.md @@ -986,13 +986,40 @@ With record boundaries measured against each record's successor, and ragged chunks skipped, what survives is: chunk types with a varying class-ID-like slot 2 (was >= 20) - of those, clearing 100x enrichment 1 (was >= 20) + of those, ALSO spanning the ID space 0 So the general claim -- one class-ID slot per chunk type, across the container -- -does not hold and is withdrawn. One chunk type still shows a slot that varies in -the manner of a class ID at 100x enrichment over chance. That is asserted, and -the test also asserts the count has *not* returned to 20, so the retraction -cannot quietly lapse back into the claim. +does not hold and is withdrawn. + +### And the flaw was there from the start + +The discriminator the original version lacked is simple: **a slot that really +carries moby class IDs must span the ID space**, so some of its values land above +a level's moby count. The artifact does the opposite. + + type slot enrichment distinct values ABOVE the moby count + 0xd76cc453 6 305x 9 0 + 0xf2ef4275 34 64x 9 341 + 0xf2ef4275 7 46x 16 412 + 0xf2ef4275 14 34x 16 432 + 0xd76cc453 9 32x 28 702 + +The 305x slot's nine values are `1, 3, 4, 5, 10, 11, 12, 13, 15` -- all under a +level's ~215 moby classes, so every one scores as a hit and 65536/215 = 305x. +Every slot that actually spans the ID space scores 10x to 64x, which is chance. + +So the original claim's own numbers gave it away and it was not read that way: +**"5 to 42 distinct values at 153x to 289x"** is exactly the artifact's shape -- +a handful of small values, none above the count. The record-boundary fix was +never what made this claim wrong. It was wrong when written, and the retraction +is complete rather than partial. + +The test now separates the two: it counts slots clearing 100x (currently 1) and +slots that clear 100x *while also* having values above the moby count (currently +0). The second must stay empty; the first is expected to be the artifact. The +count above the moby count is tracked per **slot**, not per type, because a type +with one artifact slot and several ordinary slots must not have the ordinary +ones vouch for it. The `distinct >= 4` filter this section introduced is still load-bearing and is unaffected: without it the top slots are constants that read as 305x, because any