diff --git a/artifacts/exports/rt3-1.06/runtime-effect-kind8-tier2-named-availability-note.md b/artifacts/exports/rt3-1.06/runtime-effect-kind8-tier2-named-availability-note.md index c853f08..b889b98 100644 --- a/artifacts/exports/rt3-1.06/runtime-effect-kind8-tier2-named-availability-note.md +++ b/artifacts/exports/rt3-1.06/runtime-effect-kind8-tier2-named-availability-note.md @@ -156,25 +156,26 @@ rebuild a substantial portion of each live candidate record: `[candidate+0x79c/+0x7a0/+0x78c/+0x790/+0x794/+0x7b0]` - reads the fixed per-record stream fields at `[candidate+0x04/+0x22/+0x26/+0x28/+0x2a/+0x2e/+0x32/+0x33]` +- restores the selector-bank bytes + `[candidate+0xb9/+0xba/+0xbb]` - allocates and streams the packed `0xbc` descriptor array into `[candidate+0x37]` -But in the checked `0x004120b0` body there is still **no** write to: - -- `[candidate+0xba]` -- `[candidate+0xbb]` - So the current strongest ownership split is now: - direct named-availability table `[state+0x66b2]` is not the missing differentiator by itself -- per-record stream-load `0x004120b0` is also not where the port-versus-warehouse availability - bytes are authored -- the surviving writer-side frontier is the later template-bank path in `0x00412d70`, where the - imported record is cloned or reused against one bank-qualified live candidate before the runtime - descriptor and membership rebuilds run +- both source-record import `0x00414490` and per-record stream-load `0x004120b0` do carry the + relevant selector-bank bytes from persisted/source state into the live candidate family +- but the stock `Data/BuildingTypes/*.bca` corpus currently keeps `[record+0xb8/+0xb9/+0xba/+0xbb]` + at zero across every observed file, including `Warehouse.bca` and `Port.bca` +- so the surviving frontier is no longer “does the lower loader import `[candidate+0xba/+0xbb]`?” + but rather which later owner or alternate content path makes the live bank-qualified split differ + from that all-zero shipped BCA corpus before `0x00412d70` clones or reuses one bank-qualified + live candidate That makes the next Tier 2 question more concrete still: -- how the bank-qualified template source selected under `[candidate+0xba]` versus `[candidate+0xbb]` - seeds the later `Warehouse%02d` side in `Louisiana.gmp` +- how any nonzero bank-qualified template source under `[candidate+0xba]` versus `[candidate+0xbb]` + is actually seeded above the stock all-zero BCA corpus, and then + drives the later `Warehouse%02d` side in `Louisiana.gmp` - and whether that preserved bank/template state is the real bridge from the minimal recipe cluster to the shipped `5200 :: [7:0]` `Add Building Warehouse05` row diff --git a/crates/rrt-runtime/src/building.rs b/crates/rrt-runtime/src/building.rs index e42cd74..f369735 100644 --- a/crates/rrt-runtime/src/building.rs +++ b/crates/rrt-runtime/src/building.rs @@ -17,6 +17,10 @@ pub struct BuildingTypeSourceFile { pub raw_stem: String, pub canonical_stem: String, pub source_kind: BuildingTypeSourceKind, + #[serde(default)] + pub byte_len: Option, + #[serde(default)] + pub bca_selector_probe: Option, } #[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)] @@ -27,6 +31,29 @@ pub struct BuildingTypeSourceEntry { pub file_names: Vec, } +#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)] +pub struct BuildingTypeBcaSelectorProbe { + pub byte_0xb8: u8, + pub byte_0xb8_hex: String, + pub byte_0xb9: u8, + pub byte_0xb9_hex: String, + pub byte_0xba: u8, + pub byte_0xba_hex: String, + pub byte_0xbb: u8, + pub byte_0xbb_hex: String, +} + +#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)] +pub struct BuildingTypeBcaSelectorPatternSummary { + pub byte_len: usize, + pub byte_0xb8_hex: String, + pub byte_0xb9_hex: String, + pub byte_0xba_hex: String, + pub byte_0xbb_hex: String, + pub file_count: usize, + pub sample_file_names: Vec, +} + #[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)] pub struct BuildingTypeNamedBindingComparison { pub bindings_path: String, @@ -42,9 +69,11 @@ pub struct BuildingTypeSourceReport { pub bca_file_count: usize, pub bty_file_count: usize, pub unique_canonical_stem_count: usize, + pub bca_selector_pattern_count: usize, #[serde(default)] pub named_binding_comparison: Option, pub notes: Vec, + pub bca_selector_patterns: Vec, pub files: Vec, pub entries: Vec, } @@ -78,6 +107,7 @@ pub fn inspect_building_types_dir_with_bindings( "bty" => BuildingTypeSourceKind::Bty, _ => continue, }; + let bytes = fs::read(entry.path())?; let raw_stem = Path::new(&file_name) .file_stem() .and_then(|stem| stem.to_str()) @@ -90,7 +120,12 @@ pub fn inspect_building_types_dir_with_bindings( file_name, canonical_stem: canonicalize_building_stem(&raw_stem), raw_stem, - source_kind, + source_kind: source_kind.clone(), + byte_len: Some(bytes.len()), + bca_selector_probe: match source_kind { + BuildingTypeSourceKind::Bca => Some(probe_bca_selector_bytes(&bytes)), + BuildingTypeSourceKind::Bty => None, + }, }); } @@ -141,10 +176,45 @@ pub fn inspect_building_types_dir_with_bindings( .iter() .filter(|file| matches!(file.source_kind, BuildingTypeSourceKind::Bty)) .count(); + let mut grouped_selector_patterns = + BTreeMap::<(usize, String, String, String, String), Vec>::new(); + for file in &files { + let Some(probe) = &file.bca_selector_probe else { + continue; + }; + grouped_selector_patterns + .entry(( + file.byte_len.unwrap_or_default(), + probe.byte_0xb8_hex.clone(), + probe.byte_0xb9_hex.clone(), + probe.byte_0xba_hex.clone(), + probe.byte_0xbb_hex.clone(), + )) + .or_default() + .push(file.file_name.clone()); + } + let bca_selector_patterns = grouped_selector_patterns + .into_iter() + .map( + |( + (byte_len, byte_0xb8_hex, byte_0xb9_hex, byte_0xba_hex, byte_0xbb_hex), + file_names, + )| BuildingTypeBcaSelectorPatternSummary { + byte_len, + byte_0xb8_hex, + byte_0xb9_hex, + byte_0xba_hex, + byte_0xbb_hex, + file_count: file_names.len(), + sample_file_names: file_names.into_iter().take(12).collect(), + }, + ) + .collect::>(); let notes = vec![ "BuildingTypes sources are grouped by a canonical stem that lowercases and strips spaces, underscores, and hyphens so paired .bca/.bty variants collapse onto one asset token.".to_string(), "This report is an offline asset-pool view only; it does not by itself assign live candidate ids or prove scenario candidate-table availability.".to_string(), + "For .bca files, the report also exposes the narrow selector-byte window at offsets 0xb8..0xbb used by the grounded aux-candidate and live-candidate stream decoders.".to_string(), ]; let named_binding_comparison = if let Some(bindings_path) = bindings_path { @@ -158,13 +228,32 @@ pub fn inspect_building_types_dir_with_bindings( bca_file_count, bty_file_count, unique_canonical_stem_count: entries.len(), + bca_selector_pattern_count: bca_selector_patterns.len(), named_binding_comparison, notes, + bca_selector_patterns, files, entries, }) } +fn probe_bca_selector_bytes(bytes: &[u8]) -> BuildingTypeBcaSelectorProbe { + let byte_0xb8 = bytes.get(0xb8).copied().unwrap_or(0); + let byte_0xb9 = bytes.get(0xb9).copied().unwrap_or(0); + let byte_0xba = bytes.get(0xba).copied().unwrap_or(0); + let byte_0xbb = bytes.get(0xbb).copied().unwrap_or(0); + BuildingTypeBcaSelectorProbe { + byte_0xb8, + byte_0xb8_hex: format!("0x{byte_0xb8:02x}"), + byte_0xb9, + byte_0xb9_hex: format!("0x{byte_0xb9:02x}"), + byte_0xba, + byte_0xba_hex: format!("0x{byte_0xba:02x}"), + byte_0xbb, + byte_0xbb_hex: format!("0x{byte_0xbb:02x}"), + } +} + fn load_named_binding_comparison( bindings_path: &Path, entries: &[BuildingTypeSourceEntry], @@ -214,3 +303,24 @@ struct BuildingBindingRow { #[serde(default)] candidate_name: Option, } + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn probes_bca_selector_bytes_from_fixed_offsets() { + let mut bytes = vec![0u8; 0xbc + 1]; + bytes[0xb8] = 0x12; + bytes[0xb9] = 0x34; + bytes[0xba] = 0x56; + bytes[0xbb] = 0x78; + let probe = probe_bca_selector_bytes(&bytes); + assert_eq!(probe.byte_0xb8, 0x12); + assert_eq!(probe.byte_0xb9, 0x34); + assert_eq!(probe.byte_0xba, 0x56); + assert_eq!(probe.byte_0xbb, 0x78); + assert_eq!(probe.byte_0xb8_hex, "0x12"); + assert_eq!(probe.byte_0xbb_hex, "0x78"); + } +} diff --git a/docs/rehost-queue.md b/docs/rehost-queue.md index 89bd534..a7bc096 100644 --- a/docs/rehost-queue.md +++ b/docs/rehost-queue.md @@ -678,12 +678,15 @@ Working rule: - the writer-side split is narrower now too: direct disassembly of `0x004120b0` shows that the per-record stream-load helper clears `[candidate+0x79c/+0x7a0/+0x78c/+0x790/+0x794/+0x7b0]`, restores the fixed fields through - `[candidate+0x33]`, and streams the packed `0xbc` descriptor array into `[candidate+0x37]`, - but does **not** write `[candidate+0xba/+0xbb]` in the checked body. So the next concrete - recovery target is now the bank-qualified template source in `0x00412d70`, not the lower - tagged-record reader: - recover how preserved `[candidate+0xba/+0xbb]` bank/template state feeds the - `Warehouse%02d` runtime side before `0x00411ee0 / 0x00411ce0 / 0x00412c10` run. + `[candidate+0x33]`, restores `[candidate+0xb9/+0xba/+0xbb]`, and streams the packed `0xbc` + descriptor array into `[candidate+0x37]`. Direct disassembly of upstream source-record import + `0x00414490` also restores `[record+0xb8/+0xb9/+0xba/+0xbb]`. The new checked-in building + source inspector now shows the stock `Data/BuildingTypes/*.bca` corpus keeps those four bytes + zero across every observed file, including `Warehouse.bca` and `Port.bca`. So the next + concrete recovery target is no longer the lower tagged-record reader itself: + recover which later owner or alternate content path makes the live + `[candidate+0xba/+0xbb]` bank/template state diverge from that all-zero shipped BCA corpus + before `0x00411ee0 / 0x00411ce0 / 0x00412c10` run. kinds”; it is the smaller set of scenario-specific records where that sweep explicitly writes `[event+0x7ef]` itself or a still-later owner does. - two explicit trigger-kind materializations are now grounded inside that retagger: