fix floors measuring single lib instead all libs
All checks were successful
CI / lint (push) Successful in 15s
CI / fuzz (push) Successful in 2m3s
CI / test (push) Successful in 23s

This commit is contained in:
Kamal Tufekcic 2026-08-04 02:50:33 +03:00
commit a9665e55f9
2 changed files with 58 additions and 14 deletions

View file

@ -2124,17 +2124,30 @@ fn validate_live_cmd(
fn verify_live_cmd(prof: &GameProfile, pid: u32, dir: &Path, lib: &str) -> Result<OracleCounts> {
let img = load_lib(dir, lib)?;
let classes = schema::enumerate_schema(&img);
// The SAME floor `produce` applies, not merely non-empty: this half compares an offline read against a
// live one through the same `CI_*` constants on the same bytes, so a reshape's survivors agree with
// themselves at ~1.0 and a handful of classes looks like a clean run. See
// `GameProfile::min_schema_classes`.
ensure!(
classes.len() >= prof.min_schema_classes,
"offline schema derivation found {} classes in {lib} (floor {}) — refusing to verify a schema \
whose class table collapsed",
classes.len(),
prof.min_schema_classes
);
// A floor, not merely non-empty: this half compares an offline read against a live one through the same
// `CI_*` constants on the same bytes, so a reshape's survivors agree with themselves at ~1.0 and a
// handful of classes looks like a clean run.
//
// It is `min_schema_classes_LIB`, because this enumerates ONE library while `produce`'s floor counts the
// union across all of them — see `GameProfile::min_schema_classes_lib`. Only `server_lib` has a
// calibrated count, so any other library is enumerated and reported rather than judged against a number
// that does not describe it.
if lib == prof.server_lib {
ensure!(
classes.len() >= prof.min_schema_classes_lib,
"offline schema derivation found {} classes in {lib} (floor {}) — refusing to verify a schema \
whose class table collapsed",
classes.len(),
prof.min_schema_classes_lib
);
} else {
eprintln!(
"NOTE: {lib} is not {}, which is the only library with a calibrated class floor — \
enumerated {} classes, collapse check SKIPPED",
prof.server_lib,
classes.len()
);
}
let live = live::LiveProcess::attach(pid)?;
let base = live

View file

@ -154,6 +154,16 @@ pub struct GameProfile {
/// offline/live layout comparison reads the same bytes through the same `CI_*` constants, so whatever
/// survives a reshape agrees with itself.
pub min_schema_classes: usize,
/// Collapse floor for the schema CLASS table read from a SINGLE library — the live oracle's population.
///
/// Distinct from [`min_schema_classes`](Self::min_schema_classes), and the two may never be shared: that
/// one counts the union across every mapped library, this one counts `server_lib` alone, and the union is
/// roughly twice as large. A floor calibrated on the union rejects every healthy build when applied here,
/// because the honest single-library count sits below it by construction.
///
/// Calibrated the same way as its sibling — well under the observed count, a collapse detector rather
/// than a tight bound — and it only applies to `server_lib`, the one library whose count is calibrated.
pub min_schema_classes_lib: usize,
/// Collapse floor for the DERIVED function tiers — `core + high_confidence`.
///
/// Every table read out of the binary has one of these; the tool's headline product did not, and the
@ -252,9 +262,11 @@ pub const CS2: GameProfile = GameProfile {
// observed live: 271 of 300 bindings attributed across 24 classes
min_vscript_classed: 150,
min_schema_enums: 250,
// CS2 recovers 1,899. A floor at 1,200 is well clear of build-to-build drift and nowhere near
// the range a `SchemaClassInfoData_t` reshape would leave.
// CS2 recovers 1,899 across every mapped library. A floor at 1,200 is well clear of build-to-build
// drift and nowhere near the range a `SchemaClassInfoData_t` reshape would leave.
min_schema_classes: 1_200,
// libserver.so alone holds 852 of those; the live oracle reads that library only.
min_schema_classes_lib: 550,
// CS2 ships 1,086 core + 2,899 high-confidence = 3,985.
min_core_functions: 2_500,
game_key: "csgo",
@ -365,8 +377,10 @@ pub const DOTA: GameProfile = GameProfile {
// observed live: 1,638 of 1,841 bindings attributed across 63 classes
min_vscript_classed: 900,
min_schema_enums: 350,
// Dota recovers 2,962.
// Dota recovers 2,962 across every mapped library.
min_schema_classes: 2_000,
// libserver.so alone holds 1,916 of those; the live oracle reads that library only.
min_schema_classes_lib: 1_250,
// Dota ships 1,096 + 4,047 = 5,143.
min_core_functions: 3_000,
game_key: "dota",
@ -450,6 +464,23 @@ pub const DOTA: GameProfile = GameProfile {
mod tests {
use super::*;
/// The two class floors count DIFFERENT populations — the all-library union and `server_lib` alone —
/// so a profile that gives them the same value has calibrated one of them against the other's
/// population, which rejects every healthy build on whichever site got the larger number.
#[test]
fn the_single_library_class_floor_is_strictly_below_the_all_library_one() {
for prof in [&CS2, &DOTA] {
assert!(
prof.min_schema_classes_lib < prof.min_schema_classes,
"{}: single-library floor {} must sit below the all-library floor {} — one library \
cannot hold more classes than every library",
prof.token,
prof.min_schema_classes_lib,
prof.min_schema_classes
);
}
}
#[test]
fn cs2_launch_args_are_byte_identical_to_the_old_hand_synced_vec() {
// The exact arg vec the live launch requires for map="de_dust2", bots=9 — pins the LaunchSpec