Add higher-layer rehost trace probes

This commit is contained in:
Jan Petykiewicz 2026-04-18 12:38:05 -07:00
commit 6bfe4d043f
7 changed files with 741 additions and 22 deletions

View file

@ -167,6 +167,12 @@ guessing one more derived leaf field from nearby offsets, and the checked-in
[`docs/rehost-queue.md`](docs/rehost-queue.md) file is now the control surface for that loop: [`docs/rehost-queue.md`](docs/rehost-queue.md) file is now the control surface for that loop:
after each commit, check the queue and continue unless the queue is empty, a real blocker remains, after each commit, check the queue and continue unless the queue is empty, a real blocker remains,
or approval is needed. A checked-in or approval is needed. A checked-in
The same runtime surface now also exposes higher-layer blocker probes:
`runtime inspect-periodic-company-service-trace <save.gms>`,
`runtime inspect-region-service-trace <save.gms>`, and
`runtime inspect-infrastructure-asset-trace <save.gms>`, so the next city-connection /
linked-transit slices can start from explicit owner-seam blockers instead of another generic save
scan. A checked-in
`EventEffects` export now exists too in `EventEffects` export now exists too in
`artifacts/exports/rt3-1.06/event-effects-table.json`, and a checked-in semantic closure layer now `artifacts/exports/rt3-1.06/event-effects-table.json`, and a checked-in semantic closure layer now
exists beside it in `artifacts/exports/rt3-1.06/event-effects-semantic-catalog.json`. Recovered exists beside it in `artifacts/exports/rt3-1.06/event-effects-semantic-catalog.json`. Recovered

View file

@ -27,10 +27,11 @@ use rrt_runtime::{
SmpRt3105PackedProfileBlock, SmpSaveLoadSummary, WinInspectionReport, execute_step_command, SmpRt3105PackedProfileBlock, SmpSaveLoadSummary, WinInspectionReport, execute_step_command,
extract_pk4_entry_file, inspect_campaign_exe_file, inspect_cargo_economy_sources_with_bindings, extract_pk4_entry_file, inspect_campaign_exe_file, inspect_cargo_economy_sources_with_bindings,
inspect_cargo_skin_pk4, inspect_cargo_types_dir, inspect_pk4_file, inspect_cargo_skin_pk4, inspect_cargo_types_dir, inspect_pk4_file,
inspect_save_company_and_chairman_analysis_file, inspect_save_company_and_chairman_analysis_file, inspect_save_infrastructure_asset_trace_file,
inspect_save_periodic_company_service_trace_file,
inspect_save_placed_structure_dynamic_side_buffer_file, inspect_save_placed_structure_dynamic_side_buffer_file,
inspect_save_region_queued_notice_records_file, inspect_smp_file, inspect_save_region_queued_notice_records_file, inspect_save_region_service_trace_file,
inspect_unclassified_save_collection_headers_file, inspect_win_file, inspect_smp_file, inspect_unclassified_save_collection_headers_file, inspect_win_file,
load_runtime_snapshot_document, load_runtime_state_import, load_save_slice_file, load_runtime_snapshot_document, load_runtime_state_import, load_save_slice_file,
project_save_slice_to_runtime_state_import, save_runtime_overlay_import_document, project_save_slice_to_runtime_state_import, save_runtime_overlay_import_document,
save_runtime_save_slice_document, save_runtime_snapshot_document, save_runtime_save_slice_document, save_runtime_snapshot_document,
@ -133,6 +134,15 @@ enum Command {
RuntimeInspectSaveCompanyChairman { RuntimeInspectSaveCompanyChairman {
smp_path: PathBuf, smp_path: PathBuf,
}, },
RuntimeInspectPeriodicCompanyServiceTrace {
smp_path: PathBuf,
},
RuntimeInspectRegionServiceTrace {
smp_path: PathBuf,
},
RuntimeInspectInfrastructureAssetTrace {
smp_path: PathBuf,
},
RuntimeInspectSaveRegionQueuedNoticeRecords { RuntimeInspectSaveRegionQueuedNoticeRecords {
smp_path: PathBuf, smp_path: PathBuf,
}, },
@ -294,6 +304,24 @@ struct RuntimeSaveCompanyChairmanAnalysisOutput {
analysis: rrt_runtime::SmpSaveCompanyChairmanAnalysisReport, analysis: rrt_runtime::SmpSaveCompanyChairmanAnalysisReport,
} }
#[derive(Debug, Serialize)]
struct RuntimePeriodicCompanyServiceTraceOutput {
path: String,
trace: rrt_runtime::SmpPeriodicCompanyServiceTraceReport,
}
#[derive(Debug, Serialize)]
struct RuntimeRegionServiceTraceOutput {
path: String,
trace: rrt_runtime::SmpRegionServiceTraceReport,
}
#[derive(Debug, Serialize)]
struct RuntimeInfrastructureAssetTraceOutput {
path: String,
trace: rrt_runtime::SmpInfrastructureAssetTraceReport,
}
#[derive(Debug, Serialize)] #[derive(Debug, Serialize)]
struct RuntimeSaveSliceExportOutput { struct RuntimeSaveSliceExportOutput {
path: String, path: String,
@ -865,6 +893,15 @@ fn real_main() -> Result<(), Box<dyn std::error::Error>> {
Command::RuntimeInspectSaveCompanyChairman { smp_path } => { Command::RuntimeInspectSaveCompanyChairman { smp_path } => {
run_runtime_inspect_save_company_chairman(&smp_path)?; run_runtime_inspect_save_company_chairman(&smp_path)?;
} }
Command::RuntimeInspectPeriodicCompanyServiceTrace { smp_path } => {
run_runtime_inspect_periodic_company_service_trace(&smp_path)?;
}
Command::RuntimeInspectRegionServiceTrace { smp_path } => {
run_runtime_inspect_region_service_trace(&smp_path)?;
}
Command::RuntimeInspectInfrastructureAssetTrace { smp_path } => {
run_runtime_inspect_infrastructure_asset_trace(&smp_path)?;
}
Command::RuntimeInspectSaveRegionQueuedNoticeRecords { smp_path } => { Command::RuntimeInspectSaveRegionQueuedNoticeRecords { smp_path } => {
run_runtime_inspect_save_region_queued_notice_records(&smp_path)?; run_runtime_inspect_save_region_queued_notice_records(&smp_path)?;
} }
@ -1077,6 +1114,28 @@ fn parse_command() -> Result<Command, Box<dyn std::error::Error>> {
smp_path: PathBuf::from(path), smp_path: PathBuf::from(path),
}) })
} }
[command, subcommand, path]
if command == "runtime"
&& subcommand == "inspect-periodic-company-service-trace" =>
{
Ok(Command::RuntimeInspectPeriodicCompanyServiceTrace {
smp_path: PathBuf::from(path),
})
}
[command, subcommand, path]
if command == "runtime" && subcommand == "inspect-region-service-trace" =>
{
Ok(Command::RuntimeInspectRegionServiceTrace {
smp_path: PathBuf::from(path),
})
}
[command, subcommand, path]
if command == "runtime" && subcommand == "inspect-infrastructure-asset-trace" =>
{
Ok(Command::RuntimeInspectInfrastructureAssetTrace {
smp_path: PathBuf::from(path),
})
}
[command, subcommand, path] [command, subcommand, path]
if command == "runtime" if command == "runtime"
&& subcommand == "inspect-save-region-queued-notice-records" => && subcommand == "inspect-save-region-queued-notice-records" =>
@ -1303,7 +1362,7 @@ fn parse_command() -> Result<Command, Box<dyn std::error::Error>> {
}) })
} }
_ => Err( _ => Err(
"usage: rrt-cli [validate [repo-root] | finance eval <snapshot.json> | finance diff <left.json> <right.json> | runtime validate-fixture <fixture.json> | runtime summarize-fixture <fixture.json> | runtime export-fixture-state <fixture.json> <snapshot.json> | runtime diff-state <left.json> <right.json> | runtime summarize-state <snapshot.json> | runtime import-state <input.json> <snapshot.json> | runtime inspect-smp <file.smp> | runtime summarize-save-load <file.smp> | runtime load-save-slice <file.smp> | runtime inspect-save-company-chairman <file.smp> | runtime inspect-save-region-queued-notice-records <file.smp> | runtime inspect-placed-structure-dynamic-side-buffer <file.smp> | runtime inspect-unclassified-save-collections <file.smp> | runtime import-save-state <file.smp> <snapshot.json> | runtime export-save-slice <file.smp> <save-slice.json> | runtime export-overlay-import <snapshot.json> <save-slice.json> <overlay-import.json> | runtime inspect-pk4 <file.pk4> | runtime inspect-cargo-types <CargoTypes-dir> | runtime inspect-cargo-skins <Cargo106.PK4> | runtime inspect-cargo-economy-sources <CargoTypes-dir> <Cargo106.PK4> | runtime inspect-cargo-production-selector <CargoTypes-dir> <Cargo106.PK4> | runtime inspect-cargo-price-selector <CargoTypes-dir> <Cargo106.PK4> | runtime inspect-win <file.win> | runtime extract-pk4-entry <file.pk4> <entry-name> <output-path> | runtime inspect-campaign-exe <RT3.exe> | runtime compare-classic-profile <save1.gms> <save2.gms> [saveN.gms...] | runtime compare-105-profile <save1.gms> <save2.gms> [saveN.gms...] | runtime compare-candidate-table <file1> <file2> [fileN...] | runtime compare-recipe-book-lines <file1> <file2> [fileN...] | runtime compare-setup-payload-core <file1> <file2> [fileN...] | runtime compare-setup-launch-payload <file1> <file2> [fileN...] | runtime compare-post-special-conditions-scalars <file1> <file2> [fileN...] | runtime scan-candidate-table-headers <root-dir> | runtime scan-special-conditions <root-dir> | runtime scan-aligned-runtime-rule-band <root-dir> | runtime scan-post-special-conditions-scalars <root-dir> | runtime scan-post-special-conditions-tail <root-dir> | runtime scan-recipe-book-lines <root-dir> | runtime export-profile-block <save.gms> <profile.json>]" "usage: rrt-cli [validate [repo-root] | finance eval <snapshot.json> | finance diff <left.json> <right.json> | runtime validate-fixture <fixture.json> | runtime summarize-fixture <fixture.json> | runtime export-fixture-state <fixture.json> <snapshot.json> | runtime diff-state <left.json> <right.json> | runtime summarize-state <snapshot.json> | runtime import-state <input.json> <snapshot.json> | runtime inspect-smp <file.smp> | runtime summarize-save-load <file.smp> | runtime load-save-slice <file.smp> | runtime inspect-save-company-chairman <file.smp> | runtime inspect-periodic-company-service-trace <file.smp> | runtime inspect-region-service-trace <file.smp> | runtime inspect-infrastructure-asset-trace <file.smp> | runtime inspect-save-region-queued-notice-records <file.smp> | runtime inspect-placed-structure-dynamic-side-buffer <file.smp> | runtime inspect-unclassified-save-collections <file.smp> | runtime import-save-state <file.smp> <snapshot.json> | runtime export-save-slice <file.smp> <save-slice.json> | runtime export-overlay-import <snapshot.json> <save-slice.json> <overlay-import.json> | runtime inspect-pk4 <file.pk4> | runtime inspect-cargo-types <CargoTypes-dir> | runtime inspect-cargo-skins <Cargo106.PK4> | runtime inspect-cargo-economy-sources <CargoTypes-dir> <Cargo106.PK4> | runtime inspect-cargo-production-selector <CargoTypes-dir> <Cargo106.PK4> | runtime inspect-cargo-price-selector <CargoTypes-dir> <Cargo106.PK4> | runtime inspect-win <file.win> | runtime extract-pk4-entry <file.pk4> <entry-name> <output-path> | runtime inspect-campaign-exe <RT3.exe> | runtime compare-classic-profile <save1.gms> <save2.gms> [saveN.gms...] | runtime compare-105-profile <save1.gms> <save2.gms> [saveN.gms...] | runtime compare-candidate-table <file1> <file2> [fileN...] | runtime compare-recipe-book-lines <file1> <file2> [fileN...] | runtime compare-setup-payload-core <file1> <file2> [fileN...] | runtime compare-setup-launch-payload <file1> <file2> [fileN...] | runtime compare-post-special-conditions-scalars <file1> <file2> [fileN...] | runtime scan-candidate-table-headers <root-dir> | runtime scan-special-conditions <root-dir> | runtime scan-aligned-runtime-rule-band <root-dir> | runtime scan-post-special-conditions-scalars <root-dir> | runtime scan-post-special-conditions-tail <root-dir> | runtime scan-recipe-book-lines <root-dir> | runtime export-profile-block <save.gms> <profile.json>]"
.into(), .into(),
), ),
} }
@ -1544,6 +1603,39 @@ fn run_runtime_inspect_save_company_chairman(
Ok(()) Ok(())
} }
fn run_runtime_inspect_periodic_company_service_trace(
smp_path: &Path,
) -> Result<(), Box<dyn std::error::Error>> {
let report = RuntimePeriodicCompanyServiceTraceOutput {
path: smp_path.display().to_string(),
trace: inspect_save_periodic_company_service_trace_file(smp_path)?,
};
println!("{}", serde_json::to_string_pretty(&report)?);
Ok(())
}
fn run_runtime_inspect_region_service_trace(
smp_path: &Path,
) -> Result<(), Box<dyn std::error::Error>> {
let report = RuntimeRegionServiceTraceOutput {
path: smp_path.display().to_string(),
trace: inspect_save_region_service_trace_file(smp_path)?,
};
println!("{}", serde_json::to_string_pretty(&report)?);
Ok(())
}
fn run_runtime_inspect_infrastructure_asset_trace(
smp_path: &Path,
) -> Result<(), Box<dyn std::error::Error>> {
let report = RuntimeInfrastructureAssetTraceOutput {
path: smp_path.display().to_string(),
trace: inspect_save_infrastructure_asset_trace_file(smp_path)?,
};
println!("{}", serde_json::to_string_pretty(&report)?);
Ok(())
}
fn run_runtime_inspect_save_region_queued_notice_records( fn run_runtime_inspect_save_region_queued_notice_records(
smp_path: &Path, smp_path: &Path,
) -> Result<(), Box<dyn std::error::Error>> { ) -> Result<(), Box<dyn std::error::Error>> {

View file

@ -96,7 +96,7 @@ pub use smp::{
SMP_FOUR_SIDECAR_BYTE_PLANES_MIN_BUNDLE_VERSION, SmpAlignedRuntimeRuleBandLane, SMP_FOUR_SIDECAR_BYTE_PLANES_MIN_BUNDLE_VERSION, SmpAlignedRuntimeRuleBandLane,
SmpAlignedRuntimeRuleBandProbe, SmpAsciiPreview, SmpClassicPackedProfileBlock, SmpAlignedRuntimeRuleBandProbe, SmpAsciiPreview, SmpClassicPackedProfileBlock,
SmpClassicRehydrateProfileProbe, SmpContainerProfile, SmpEarlyContentProbe, SmpClassicRehydrateProfileProbe, SmpContainerProfile, SmpEarlyContentProbe,
SmpHeaderVariantProbe, SmpInspectionReport, SmpKnownTagHit, SmpHeaderVariantProbe, SmpInfrastructureAssetTraceReport, SmpInspectionReport, SmpKnownTagHit,
SmpLoadedCandidateAvailabilityTable, SmpLoadedCargoCatalog, SmpLoadedCargoCatalogEntry, SmpLoadedCandidateAvailabilityTable, SmpLoadedCargoCatalog, SmpLoadedCargoCatalogEntry,
SmpLoadedChairmanProfileEntry, SmpLoadedChairmanProfileTable, SmpLoadedCompanyRoster, SmpLoadedChairmanProfileEntry, SmpLoadedChairmanProfileTable, SmpLoadedCompanyRoster,
SmpLoadedCompanyRosterEntry, SmpLoadedEventRuntimeCollectionSummary, SmpLoadedCompanyRosterEntry, SmpLoadedEventRuntimeCollectionSummary,
@ -108,31 +108,33 @@ pub use smp::{
SmpLoadedWorldFinanceNeighborhoodState, SmpLoadedWorldIssue37State, SmpLoadedWorldFinanceNeighborhoodState, SmpLoadedWorldIssue37State,
SmpLocomotivePolicyFieldObservation, SmpLocomotivePolicyFloatAlignmentCandidate, SmpLocomotivePolicyFieldObservation, SmpLocomotivePolicyFloatAlignmentCandidate,
SmpLocomotivePolicyNeighborhoodProbe, SmpPackedProfileWordLane, SmpLocomotivePolicyNeighborhoodProbe, SmpPackedProfileWordLane,
SmpPostSpecialConditionsScalarLane, SmpPostSpecialConditionsScalarProbe, SmpPeriodicCompanyServiceTraceReport, SmpPostSpecialConditionsScalarLane,
SmpPostTextFieldNeighborhoodProbe, SmpPostTextFloatAlignmentCandidate, SmpPostSpecialConditionsScalarProbe, SmpPostTextFieldNeighborhoodProbe,
SmpPostTextGroundedFieldObservation, SmpPreRecipeScalarPlateauLane, SmpPostTextFloatAlignmentCandidate, SmpPostTextGroundedFieldObservation,
SmpPreRecipeScalarPlateauProbe, SmpPreamble, SmpPreambleWord, SmpRecipeBookLineSummary, SmpPreRecipeScalarPlateauLane, SmpPreRecipeScalarPlateauProbe, SmpPreamble, SmpPreambleWord,
SmpRecipeBookSummaryBook, SmpRecipeBookSummaryProbe, SmpRt3105PackedProfileBlock, SmpRecipeBookLineSummary, SmpRecipeBookSummaryBook, SmpRecipeBookSummaryProbe,
SmpRt3105PackedProfileProbe, SmpRt3105PostSpanBridgeProbe, SmpRt3105SaveBridgePayloadProbe, SmpRegionServiceTraceReport, SmpRt3105PackedProfileBlock, SmpRt3105PackedProfileProbe,
SmpRt3105SaveNameTableEntry, SmpRt3105SaveNameTableProbe, SmpRuntimeAnchorCycleBlock, SmpRt3105PostSpanBridgeProbe, SmpRt3105SaveBridgePayloadProbe, SmpRt3105SaveNameTableEntry,
SmpRuntimePostSpanHeaderCandidate, SmpRuntimePostSpanProbe, SmpRuntimeTrailerBlock, SmpRt3105SaveNameTableProbe, SmpRuntimeAnchorCycleBlock, SmpRuntimePostSpanHeaderCandidate,
SmpSaveAnchorRunBlock, SmpSaveBootstrapBlock, SmpSaveChairmanRecordAnalysisEntry, SmpRuntimePostSpanProbe, SmpRuntimeTrailerBlock, SmpSaveAnchorRunBlock, SmpSaveBootstrapBlock,
SmpSaveCompanyChairmanAnalysisReport, SmpSaveCompanyRecordAnalysisEntry, SmpSaveDwordCandidate, SmpSaveChairmanRecordAnalysisEntry, SmpSaveCompanyChairmanAnalysisReport,
SmpSaveLoadCandidateTableSummary, SmpSaveLoadSummary, SmpSaveCompanyRecordAnalysisEntry, SmpSaveDwordCandidate, SmpSaveLoadCandidateTableSummary,
SmpSavePlacedStructureDynamicSideBufferAlignmentProbe, SmpSaveLoadSummary, SmpSavePlacedStructureDynamicSideBufferAlignmentProbe,
SmpSavePlacedStructureDynamicSideBufferNamePairSummary, SmpSavePlacedStructureDynamicSideBufferNamePairSummary,
SmpSavePlacedStructureDynamicSideBufferPrefixPatternSummary, SmpSavePlacedStructureDynamicSideBufferPrefixPatternSummary,
SmpSavePlacedStructureDynamicSideBufferProbe, SmpSaveRegionQueuedNoticeRecordProbe, SmpSavePlacedStructureDynamicSideBufferProbe, SmpSaveRegionQueuedNoticeRecordProbe,
SmpSaveScalarCandidate, SmpSaveTaggedCollectionHeaderProbe, SmpSaveWorldEconomicTuningProbe, SmpSaveScalarCandidate, SmpSaveTaggedCollectionHeaderProbe, SmpSaveWorldEconomicTuningProbe,
SmpSaveWorldFinanceNeighborhoodProbe, SmpSaveWorldIssue37Probe, SmpSaveWorldFinanceNeighborhoodProbe, SmpSaveWorldIssue37Probe,
SmpSaveWorldSelectionRoleAnalysis, SmpSaveWorldSelectionRoleAnalysisEntry, SmpSaveWorldSelectionRoleAnalysis, SmpSaveWorldSelectionRoleAnalysisEntry,
SmpSecondaryVariantProbe, SmpSharedHeader, SmpSpecialConditionEntry, SmpSpecialConditionsProbe, SmpSecondaryVariantProbe, SmpServiceTraceBranchStatus, SmpSharedHeader,
SmpSpecialConditionEntry, SmpSpecialConditionsProbe,
inspect_save_company_and_chairman_analysis_bytes, inspect_save_company_and_chairman_analysis_bytes,
inspect_save_company_and_chairman_analysis_file, inspect_save_company_and_chairman_analysis_file, inspect_save_infrastructure_asset_trace_file,
inspect_save_periodic_company_service_trace_file,
inspect_save_placed_structure_dynamic_side_buffer_file, inspect_save_placed_structure_dynamic_side_buffer_file,
inspect_save_region_queued_notice_records_file, inspect_smp_bytes, inspect_smp_file, inspect_save_region_queued_notice_records_file, inspect_save_region_service_trace_file,
inspect_unclassified_save_collection_headers_file, load_save_slice_file, inspect_smp_bytes, inspect_smp_file, inspect_unclassified_save_collection_headers_file,
load_save_slice_from_report, load_save_slice_file, load_save_slice_from_report,
}; };
pub use step::{BoundaryEvent, ServiceEvent, StepCommand, StepResult, execute_step_command}; pub use step::{BoundaryEvent, ServiceEvent, StepCommand, StepResult, execute_step_command};
pub use summary::RuntimeSummary; pub use summary::RuntimeSummary;

View file

@ -2882,6 +2882,101 @@ pub struct SmpSaveCompanyChairmanAnalysisReport {
pub notes: Vec<String>, pub notes: Vec<String>,
} }
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
pub struct SmpServiceTraceBranchStatus {
pub branch_name: String,
pub status: String,
#[serde(default)]
pub grounded_inputs: Vec<String>,
#[serde(default)]
pub blocking_inputs: Vec<String>,
#[serde(default)]
pub notes: Vec<String>,
}
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
pub struct SmpPeriodicCompanyServiceTraceEntry {
pub company_id: u32,
pub name: String,
pub active: bool,
#[serde(default)]
pub linked_chairman_profile_id: Option<u32>,
pub preferred_locomotive_engine_type_raw_u8: u8,
pub city_connection_latch: bool,
pub linked_transit_latch: bool,
#[serde(default)]
pub branches: Vec<SmpServiceTraceBranchStatus>,
}
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
pub struct SmpPeriodicCompanyServiceTraceReport {
pub profile_family: String,
#[serde(default)]
pub selected_company_id: Option<u32>,
#[serde(default)]
pub world_issue_37_present: bool,
#[serde(default)]
pub world_finance_neighborhood_present: bool,
#[serde(default)]
pub region_record_body_present: bool,
#[serde(default)]
pub placed_structure_record_body_present: bool,
#[serde(default)]
pub infrastructure_asset_side_buffer_present: bool,
#[serde(default)]
pub companies: Vec<SmpPeriodicCompanyServiceTraceEntry>,
#[serde(default)]
pub notes: Vec<String>,
}
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
pub struct SmpRegionServiceTraceEntry {
pub name: String,
#[serde(default)]
pub profile_collection_count: Option<u32>,
pub policy_leading_f32_0_text: String,
pub policy_leading_f32_1_text: String,
pub policy_leading_f32_2_text: String,
#[serde(default)]
pub branches: Vec<SmpServiceTraceBranchStatus>,
}
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
pub struct SmpRegionServiceTraceReport {
pub profile_family: String,
#[serde(default)]
pub region_collection_header_present: bool,
#[serde(default)]
pub region_record_triplet_count: usize,
#[serde(default)]
pub queued_notice_record_count: usize,
#[serde(default)]
pub entries: Vec<SmpRegionServiceTraceEntry>,
#[serde(default)]
pub notes: Vec<String>,
}
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
pub struct SmpInfrastructureAssetTraceReport {
pub profile_family: String,
#[serde(default)]
pub placed_structure_collection_header_present: bool,
#[serde(default)]
pub placed_structure_record_triplet_count: usize,
#[serde(default)]
pub side_buffer_present: bool,
#[serde(default)]
pub side_buffer_decoded_embedded_name_row_count: usize,
#[serde(default)]
pub side_buffer_unique_name_pair_count: usize,
#[serde(default)]
pub triplet_alignment_overlap_count: usize,
#[serde(default)]
pub branches: Vec<SmpServiceTraceBranchStatus>,
#[serde(default)]
pub notes: Vec<String>,
}
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)] #[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
pub struct SmpLoadedSpecialConditionsTable { pub struct SmpLoadedSpecialConditionsTable {
pub source_kind: String, pub source_kind: String,
@ -3315,6 +3410,356 @@ pub fn inspect_save_region_queued_notice_records_file(
)) ))
} }
fn build_service_trace_branch_status(
branch_name: &str,
status: &str,
grounded_inputs: &[&str],
blocking_inputs: &[&str],
notes: &[&str],
) -> SmpServiceTraceBranchStatus {
SmpServiceTraceBranchStatus {
branch_name: branch_name.to_string(),
status: status.to_string(),
grounded_inputs: grounded_inputs
.iter()
.map(|value| value.to_string())
.collect(),
blocking_inputs: blocking_inputs
.iter()
.map(|value| value.to_string())
.collect(),
notes: notes.iter().map(|value| value.to_string()).collect(),
}
}
pub fn inspect_save_periodic_company_service_trace_file(
path: &Path,
) -> Result<SmpPeriodicCompanyServiceTraceReport, Box<dyn std::error::Error>> {
let inspection = inspect_smp_file(path)?;
let analysis = inspect_save_company_and_chairman_analysis_file(path)?;
let profile_family = analysis.profile_family.clone();
let selected_company_id = analysis.selected_company_id;
let region_record_body_present = analysis.region_record_triplets.is_some();
let placed_structure_record_body_present = analysis.placed_structure_record_triplets.is_some();
let infrastructure_asset_side_buffer_present =
analysis.placed_structure_dynamic_side_buffer.is_some();
let world_issue_37_present = analysis.world_issue_37.is_some();
let world_finance_neighborhood_present = analysis.world_finance_neighborhood.is_some();
let companies = analysis
.company_entries
.iter()
.map(|entry| {
let mut branches = Vec::new();
branches.push(build_service_trace_branch_status(
"route_preference_override",
if entry.preferred_locomotive_engine_type_raw_u8 == 2 {
"grounded_electric_override_candidate"
} else {
"grounded_non_electric_or_inactive_override_candidate"
},
&[
"company periodic side-latch trio",
"world route-preference byte",
"preferred locomotive engine-type lane",
],
&[],
&[
"This probe keeps the outer owner at the save-owned input level; the concrete runtime reader/apply/restore seam is already grounded separately.",
],
));
branches.push(build_service_trace_branch_status(
"annual_finance_policy",
"runnable_from_grounded_owner_state",
&[
"company market/cache owner state",
"periodic side-latches",
"world issue/timing owner state",
"derived annual-finance readers",
],
&[],
&[
"The shellless annual-finance helper is already rehosted on top of runtime-owned state.",
],
));
branches.push(build_service_trace_branch_status(
"city_connection_announcement",
"blocked_missing_region_and_infrastructure_asset_owner_seams",
&[
"company periodic side-latches",
"route-preference override seam",
"annual-finance sequencing owner",
],
&[
"region pending/completion/one-shot/severity lanes",
"stable region id or class discriminator",
"placed-structure or infrastructure-asset consumer mapping",
],
&[
"Current atlas evidence places this branch above both the region pending-bonus lane and infrastructure/placed-structure consumers.",
],
));
branches.push(build_service_trace_branch_status(
"linked_transit_roster_maintenance",
"blocked_missing_infrastructure_asset_consumer_mapping",
&[
"company linked-transit latch",
"route-preference override seam",
],
&[
"placed-structure record-body semantics",
"0x38a5 infrastructure-asset consumer mapping",
],
&[
"The save side now grounds the owner seams, but not yet the higher-layer consumer that turns them into roster or route actions.",
],
));
branches.push(build_service_trace_branch_status(
"industry_acquisition_side_branch",
"blocked_missing_near-city_owner_mapping",
&[
"periodic service outer owner",
"annual-finance ordering",
],
&[
"near-city industry candidate owner seam",
"city or region peer linkage",
],
&[
"The outer owner is bounded, but the concrete candidate/peer scan is not yet rehosted.",
],
));
SmpPeriodicCompanyServiceTraceEntry {
company_id: entry.company_id,
name: entry.name.clone(),
active: entry.active,
linked_chairman_profile_id: entry.linked_chairman_profile_id,
preferred_locomotive_engine_type_raw_u8: entry
.preferred_locomotive_engine_type_raw_u8,
city_connection_latch: entry.city_connection_latch,
linked_transit_latch: entry.linked_transit_latch,
branches,
}
})
.collect::<Vec<_>>();
let mut notes = Vec::new();
let _ = inspection;
notes.push(
"Periodic company service trace is intentionally an outer-owner probe: it reports save-owned branch inputs and blocker seams without serializing the full projected runtime reader state.".to_string(),
);
if region_record_body_present || placed_structure_record_body_present {
notes.push(
"The current blockers are no longer collection identity; they are missing higher-layer consumer semantics for the region and infrastructure/placed-structure owner seams.".to_string(),
);
}
Ok(SmpPeriodicCompanyServiceTraceReport {
profile_family,
selected_company_id,
world_issue_37_present,
world_finance_neighborhood_present,
region_record_body_present,
placed_structure_record_body_present,
infrastructure_asset_side_buffer_present,
companies,
notes,
})
}
pub fn inspect_save_region_service_trace_file(
path: &Path,
) -> Result<SmpRegionServiceTraceReport, Box<dyn std::error::Error>> {
let analysis = inspect_save_company_and_chairman_analysis_file(path)?;
Ok(build_region_service_trace_report(&analysis))
}
pub fn inspect_save_infrastructure_asset_trace_file(
path: &Path,
) -> Result<SmpInfrastructureAssetTraceReport, Box<dyn std::error::Error>> {
let analysis = inspect_save_company_and_chairman_analysis_file(path)?;
Ok(build_infrastructure_asset_trace_report(&analysis))
}
fn build_region_service_trace_report(
analysis: &SmpSaveCompanyChairmanAnalysisReport,
) -> SmpRegionServiceTraceReport {
let entries = analysis
.region_record_triplets
.as_ref()
.map(|probe| {
probe.entries
.iter()
.map(|entry| SmpRegionServiceTraceEntry {
name: entry.name.clone(),
profile_collection_count: entry
.profile_collection
.as_ref()
.map(|collection| collection.live_record_count),
policy_leading_f32_0_text: format!("{:.6}", entry.policy_leading_f32_0),
policy_leading_f32_1_text: format!("{:.6}", entry.policy_leading_f32_1),
policy_leading_f32_2_text: format!("{:.6}", entry.policy_leading_f32_2),
branches: vec![
build_service_trace_branch_status(
"pending_bonus_queue_seed",
"blocked_missing_pending_bonus_owner_lane",
&[
"region triplet envelope",
"embedded profile subcollection",
"policy float lanes",
],
&[
"[region+0x276] pending amount lane",
"[region+0x25e] severity/source lane",
],
&["The queued kind-7 notice family is not obviously persisted in ordinary saves, so the pending queue must be treated as transient until a direct owner seam is found."],
),
build_service_trace_branch_status(
"city_connection_completion",
"blocked_missing_completion_and_one_shot_latches",
&[
"region triplet envelope",
"region name stem",
],
&[
"[region+0x302] completion latch",
"[region+0x316] one-shot notice latch",
"stable region id or class discriminator",
],
&["The remaining region blocker is a separate owner seam for the latches the city-connection branch reads and writes."],
),
],
})
.collect::<Vec<_>>()
})
.unwrap_or_default();
let mut notes = Vec::new();
notes.push(
"Region service trace treats the queued kind-7 notice family as transient runtime state until a persisted owner seam is found.".to_string(),
);
notes.push(
"The current region seam is strong enough to prove record-envelope ownership, profile subcollection ownership, and the absence of hidden 0x55f3 tail padding on grounded saves.".to_string(),
);
SmpRegionServiceTraceReport {
profile_family: analysis.profile_family.clone(),
region_collection_header_present: analysis.region_collection_header.is_some(),
region_record_triplet_count: analysis
.region_record_triplets
.as_ref()
.map(|probe| probe.record_count)
.unwrap_or_default(),
queued_notice_record_count: analysis
.region_queued_notice_records
.as_ref()
.map(|probe| probe.entries.len())
.unwrap_or_default(),
entries,
notes,
}
}
fn build_infrastructure_asset_trace_report(
analysis: &SmpSaveCompanyChairmanAnalysisReport,
) -> SmpInfrastructureAssetTraceReport {
let side_buffer = analysis.placed_structure_dynamic_side_buffer.as_ref();
let alignment = analysis
.placed_structure_dynamic_side_buffer_alignment
.as_ref();
let branches = vec![
build_service_trace_branch_status(
"infrastructure_asset_owner_seam",
if side_buffer.is_some() {
"grounded_separate_owner_seam"
} else {
"blocked_missing_side_buffer_owner_seam"
},
&[
"0x38a5/0x38a6/0x38a7 tagged family",
"embedded 0x55f1 dual-name rows",
"compact 6-byte prefix regimes",
],
if side_buffer.is_some() {
&[]
} else {
&["0x38a5 owner seam"]
},
&[
"This seam should be treated as infrastructure-asset state rather than as a compact alias of placed-structure triplets.",
],
),
build_service_trace_branch_status(
"placed_structure_triplet_alias",
if alignment.is_some_and(|probe| probe.overlapping_name_pair_count == 0) {
"disproved_by_grounded_probe"
} else {
"unresolved"
},
&[
"0x36b1 placed-structure triplet corpus",
"0x38a5 side-buffer name-pair corpus",
],
&[],
&[
"Grounded q.gms evidence currently shows zero overlap between the side-buffer name-pair corpus and the placed-structure triplet name-pair corpus.",
],
),
build_service_trace_branch_status(
"city_connection_consumer_mapping",
"blocked_missing_infrastructure_asset_consumer_mapping",
&[
"grounded 0x38a5 owner seam",
"placed-structure triplet seam",
],
&[
"higher-layer consumer dispatch mapping",
"compact prefix regime semantics",
],
&[
"The remaining problem is how higher-layer service code consumes this separate seam, not whether the seam exists.",
],
),
build_service_trace_branch_status(
"linked_transit_consumer_mapping",
"blocked_missing_infrastructure_asset_consumer_mapping",
&["grounded 0x38a5 owner seam", "company linked-transit latch"],
&[
"side-buffer consumer mapping",
"route or roster rebuild owner path",
],
&[
"The next slice should target the consumer path above the side-buffer seam rather than another raw save scan.",
],
),
];
let notes = vec![
"Infrastructure asset trace now makes the side-buffer-versus-triplet split explicit: owner seam identity is grounded, consumer semantics are still blocked.".to_string(),
];
SmpInfrastructureAssetTraceReport {
profile_family: analysis.profile_family.clone(),
placed_structure_collection_header_present: analysis
.placed_structure_collection_header
.is_some(),
placed_structure_record_triplet_count: analysis
.placed_structure_record_triplets
.as_ref()
.map(|probe| probe.record_count)
.unwrap_or_default(),
side_buffer_present: side_buffer.is_some(),
side_buffer_decoded_embedded_name_row_count: side_buffer
.map(|probe| probe.decoded_embedded_name_row_count)
.unwrap_or_default(),
side_buffer_unique_name_pair_count: side_buffer
.map(|probe| probe.unique_embedded_name_pair_count)
.unwrap_or_default(),
triplet_alignment_overlap_count: alignment
.map(|probe| probe.overlapping_name_pair_count)
.unwrap_or_default(),
branches,
notes,
}
}
pub fn inspect_smp_bytes(bytes: &[u8]) -> SmpInspectionReport { pub fn inspect_smp_bytes(bytes: &[u8]) -> SmpInspectionReport {
inspect_bundle_bytes(bytes, None) inspect_bundle_bytes(bytes, None)
} }
@ -21058,4 +21503,149 @@ mod tests {
assert_eq!(alt_profile.profile_family, "rt3-105-alt-map-container-v1"); assert_eq!(alt_profile.profile_family, "rt3-105-alt-map-container-v1");
assert!(alt_profile.is_known_profile); assert!(alt_profile.is_known_profile);
} }
fn empty_analysis_report() -> SmpSaveCompanyChairmanAnalysisReport {
SmpSaveCompanyChairmanAnalysisReport {
profile_family: "rt3-105-scenario-save-container-v1".to_string(),
selected_company_id: None,
selected_chairman_profile_id: None,
world_selection_context: None,
world_issue_37: None,
world_economic_tuning: None,
world_finance_neighborhood: None,
train_collection_header: None,
train_collection_directory: None,
region_collection_header: None,
region_record_triplets: None,
region_queued_notice_records: None,
placed_structure_collection_header: None,
placed_structure_record_triplets: None,
placed_structure_dynamic_side_buffer: None,
placed_structure_dynamic_side_buffer_alignment: None,
unclassified_tagged_collection_headers: Vec::new(),
company_entries: Vec::new(),
chairman_entries: Vec::new(),
notes: Vec::new(),
}
}
#[test]
fn builds_region_service_trace_report_with_explicit_latch_blockers() {
let mut analysis = empty_analysis_report();
analysis.region_record_triplets = Some(SmpSaveRegionRecordTripletProbe {
profile_family: analysis.profile_family.clone(),
source_kind: "save-region-record-triplets".to_string(),
semantic_family: "marker09".to_string(),
records_tag_offset: 0,
close_tag_offset: 0,
record_count: 1,
entries: vec![SmpSaveRegionRecordTripletEntryProbe {
record_index: 0,
name: "Marker09".to_string(),
name_tag_relative_offset: 0,
policy_tag_relative_offset: 0,
profile_tag_relative_offset: 0,
policy_chunk_len: 0,
profile_chunk_len: 0,
policy_leading_f32_0: 368.0,
policy_leading_f32_1: 0.0,
policy_leading_f32_2: 92.0,
policy_reserved_dwords: Vec::new(),
policy_trailing_word: 0,
policy_trailing_word_hex: "0x0000".to_string(),
profile_collection: Some(SmpSaveRegionProfileCollectionProbe {
direct_collection_flag: 1,
entry_stride: 0x22,
live_id_bound: 17,
live_record_count: 17,
entry_start_relative_offset: 0,
trailing_padding_len: 0,
entries: Vec::new(),
}),
}],
evidence: Vec::new(),
});
let trace = build_region_service_trace_report(&analysis);
assert_eq!(trace.region_record_triplet_count, 1);
assert_eq!(trace.queued_notice_record_count, 0);
assert_eq!(trace.entries.len(), 1);
assert_eq!(
trace.entries[0].branches[0].status,
"blocked_missing_pending_bonus_owner_lane"
);
assert_eq!(
trace.entries[0].branches[1].status,
"blocked_missing_completion_and_one_shot_latches"
);
}
#[test]
fn builds_infrastructure_asset_trace_report_with_alias_disproved_status() {
let mut analysis = empty_analysis_report();
analysis.placed_structure_record_triplets =
Some(SmpSavePlacedStructureRecordTripletProbe {
profile_family: analysis.profile_family.clone(),
source_kind: "save-placed-structure-triplets".to_string(),
semantic_family: "placed-structure-triplets".to_string(),
records_tag_offset: 0,
close_tag_offset: 0,
record_count: 2057,
entries: Vec::new(),
evidence: Vec::new(),
});
analysis.placed_structure_dynamic_side_buffer =
Some(SmpSavePlacedStructureDynamicSideBufferProbe {
profile_family: analysis.profile_family.clone(),
source_kind: "save-side-buffer".to_string(),
semantic_family: "infrastructure-asset".to_string(),
metadata_tag_offset: 0,
records_tag_offset: 0,
close_tag_offset: 0,
records_span_len: 0,
direct_record_stride: 6,
direct_record_stride_hex: "0x00000006".to_string(),
live_id_bound: 3865,
live_id_bound_hex: "0x00000f19".to_string(),
live_record_count: 3865,
live_record_count_hex: "0x00000f19".to_string(),
prefix_leading_dword: 0xff000000,
prefix_leading_dword_hex: "0xff000000".to_string(),
prefix_trailing_word: 1,
prefix_trailing_word_hex: "0x0001".to_string(),
prefix_separator_byte: 0xff,
prefix_separator_byte_hex: "0xff".to_string(),
first_embedded_name_tag_relative_offset: 0x20,
embedded_name_tag_count: 138,
decoded_embedded_name_row_count: 138,
unique_compact_prefix_pattern_count: 7,
prefix_leading_dword_matching_embedded_profile_tag_count: 17,
unique_embedded_name_pair_count: 5,
first_embedded_primary_name: Some("TrackCapST_Cap.3dp".to_string()),
first_embedded_secondary_name: Some("Infrastructure".to_string()),
embedded_name_row_samples: Vec::new(),
compact_prefix_pattern_summaries: Vec::new(),
name_pair_summaries: Vec::new(),
evidence: Vec::new(),
});
analysis.placed_structure_dynamic_side_buffer_alignment =
Some(SmpSavePlacedStructureDynamicSideBufferAlignmentProbe {
unique_side_buffer_name_pair_count: 5,
unique_triplet_name_pair_count: 56,
overlapping_name_pair_count: 0,
side_buffer_row_count: 138,
side_buffer_rows_with_matching_triplet_name_pair_count: 0,
side_buffer_rows_without_matching_triplet_name_pair_count: 138,
triplet_name_pairs_without_side_buffer_match_count: 56,
matched_name_pair_samples: Vec::new(),
unmatched_side_buffer_name_pair_samples: Vec::new(),
evidence: Vec::new(),
});
let trace = build_infrastructure_asset_trace_report(&analysis);
assert!(trace.side_buffer_present);
assert_eq!(trace.triplet_alignment_overlap_count, 0);
assert_eq!(trace.branches[0].status, "grounded_separate_owner_seam");
assert_eq!(trace.branches[1].status, "disproved_by_grounded_probe");
}
} }

View file

@ -203,6 +203,12 @@ The highest-value next passes are now:
`docs/rehost-queue.md` file is the control surface for that work loop, and after each commit the `docs/rehost-queue.md` file is the control surface for that work loop, and after each commit the
next queue item should run unless the queue is empty, a real blocker remains, or approval is next queue item should run unless the queue is empty, a real blocker remains, or approval is
needed needed
- `rrt-runtime` now also exposes higher-layer probe commands for the current blocked frontier:
`runtime inspect-periodic-company-service-trace <save.gms>`,
`runtime inspect-region-service-trace <save.gms>`, and
`runtime inspect-infrastructure-asset-trace <save.gms>`. These reports make the current
shellless city-connection / linked-transit blockers explicit as missing owner seams rather than
generic “still unresolved” residue.
- a checked-in `EventEffects` export now exists at - a checked-in `EventEffects` export now exists at
`artifacts/exports/rt3-1.06/event-effects-table.json`, and a checked-in semantic closure layer `artifacts/exports/rt3-1.06/event-effects-table.json`, and a checked-in semantic closure layer
now exists at `artifacts/exports/rt3-1.06/event-effects-semantic-catalog.json` now exists at `artifacts/exports/rt3-1.06/event-effects-semantic-catalog.json`

View file

@ -9,6 +9,11 @@ Working rule:
## Next ## Next
- Follow the new higher-layer probe outputs instead of another blind save scan:
`runtime inspect-infrastructure-asset-trace <save.gms>` now shows that the `0x38a5`
infrastructure-asset seam is grounded and the old alias hypothesis is disproved on `q.gms`, so
the next placed-structure slice should target the consumer mapping above that seam rather than
more collection discovery.
- Reconstruct the save-side region record body on top of the newly corrected non-direct tagged - Reconstruct the save-side region record body on top of the newly corrected non-direct tagged
region seam (`0x5209/0x520a/0x520b`, stride hint `0x06`, `Marker09` record stems) now that the region seam (`0x5209/0x520a/0x520b`, stride hint `0x06`, `Marker09` record stems) now that the
`0x55f3` payload is known to be fully consumed by the embedded profile collection on grounded `0x55f3` payload is known to be fully consumed by the embedded profile collection on grounded
@ -65,6 +70,18 @@ Working rule:
## Recently Done ## Recently Done
- `rrt-runtime` now exposes three higher-layer probe surfaces and matching CLI inspectors:
`runtime inspect-periodic-company-service-trace <save.gms>`,
`runtime inspect-region-service-trace <save.gms>`, and
`runtime inspect-infrastructure-asset-trace <save.gms>`. These reports separate grounded outer
owner inputs, runnable shellless branches, and explicit missing owner seams instead of leaving
the current city-connection / linked-transit frontier as an opaque blocker.
- Those same probes now also sharpen the next queue choice on grounded real saves: the periodic
company outer owner shows annual finance and route-preference override as grounded shellless
branches while city-connection and linked-transit stay blocked on region/infrastructure owner
seams; the region trace keeps the queued kind-`7` notice family on the transient side; and the
infrastructure trace now makes the `0x38a5` consumer-mapping blocker first-class after
disproving any alias to the `0x36b1` placed-structure triplet corpus.
- Save inspection now splits the shared `0x5209/0x520a/0x520b` family correctly: the smaller - Save inspection now splits the shared `0x5209/0x520a/0x520b` family correctly: the smaller
direct `0x1d5` collection is the live train family and now exposes a live-entry directory rooted direct `0x1d5` collection is the live train family and now exposes a live-entry directory rooted
at metadata dword `16`, while the actual region family is the larger non-direct `Marker09` at metadata dword `16`, while the actual region family is the larger non-direct `Marker09`

View file

@ -297,6 +297,12 @@ The process rule for the remaining runtime work is explicit too: prefer rehostin
real reader/setter families over guessing leaf fields, and use `docs/rehost-queue.md` as the real reader/setter families over guessing leaf fields, and use `docs/rehost-queue.md` as the
checked-in control surface for the work loop. After each commit, check that queue and continue checked-in control surface for the work loop. After each commit, check that queue and continue
unless the queue is empty, a real blocker remains, or approval is needed. unless the queue is empty, a real blocker remains, or approval is needed.
The same control surface now also has matching runtime probes:
`runtime inspect-periodic-company-service-trace <save.gms>`,
`runtime inspect-region-service-trace <save.gms>`, and
`runtime inspect-infrastructure-asset-trace <save.gms>`. Use those first when the next blocked
frontier is “which higher-layer owner seam is still missing?” rather than “which raw save
collection exists?”.
That same owner seam now also derives live coupon burden totals directly from saved bond slots, That same owner seam now also derives live coupon burden totals directly from saved bond slots,
which gives later finance service work a bounded runtime reader instead of another synthetic which gives later finance service work a bounded runtime reader instead of another synthetic
finance leaf. finance leaf.