From 3b99f6f62683a3c65aea40986dd8883dd875b1ef Mon Sep 17 00:00:00 2001 From: Jan Petykiewicz Date: Fri, 17 Apr 2026 21:31:28 -0700 Subject: [PATCH] Rehost company highest-coupon bond input --- README.md | 7 +-- crates/rrt-fixtures/src/schema.rs | 10 +++++ crates/rrt-runtime/src/import.rs | 3 ++ crates/rrt-runtime/src/runtime.rs | 20 +++++++++ crates/rrt-runtime/src/smp.rs | 71 ++++++++++++++++++++++++++++--- crates/rrt-runtime/src/summary.rs | 8 ++++ docs/README.md | 5 ++- docs/runtime-rehost-plan.md | 7 +-- 8 files changed, 117 insertions(+), 14 deletions(-) diff --git a/README.md b/README.md index 0bc6429..d3367f8 100644 --- a/README.md +++ b/README.md @@ -76,9 +76,10 @@ derives elapsed years since founding, last dividend, and last bankruptcy from th which lines up directly with the grounded annual finance-policy gates in the atlas. Live bond-slot count is now carried through the same owned company market and annual-finance state too, which matches the stock-capital branch gate that requires at least two live bonds. The same grounded -bond table now also contributes the largest live bond principal into owned company market and -annual-finance state, so the stock-capital approval ladder can extend one rehosted owner-state -surface instead of hunting another isolated finance leaf. A checked-in +bond table now also contributes both the largest live bond principal and the chosen +highest-coupon live bond principal into owned company market and annual-finance state, so the +stock-capital approval ladder can extend one rehosted owner-state surface instead of hunting +another isolated finance leaf. A checked-in The working rule on the remaining frontier is explicit now too: when a lane is still ambiguous, we should prefer rehosting the owning source state or the real reader/setter family rather than guessing one more derived leaf field from nearby offsets. A checked-in diff --git a/crates/rrt-fixtures/src/schema.rs b/crates/rrt-fixtures/src/schema.rs index bd16868..d88e4e7 100644 --- a/crates/rrt-fixtures/src/schema.rs +++ b/crates/rrt-fixtures/src/schema.rs @@ -96,6 +96,8 @@ pub struct ExpectedRuntimeSummary { #[serde(default)] pub selected_company_largest_live_bond_principal: Option, #[serde(default)] + pub selected_company_highest_coupon_live_bond_principal: Option, + #[serde(default)] pub selected_company_assigned_share_pool: Option, #[serde(default)] pub selected_company_unassigned_share_pool: Option, @@ -629,6 +631,14 @@ impl ExpectedRuntimeSummary { )); } } + if let Some(value) = self.selected_company_highest_coupon_live_bond_principal { + if actual.selected_company_highest_coupon_live_bond_principal != Some(value) { + mismatches.push(format!( + "selected_company_highest_coupon_live_bond_principal mismatch: expected {value}, got {:?}", + actual.selected_company_highest_coupon_live_bond_principal + )); + } + } if let Some(value) = self.selected_company_assigned_share_pool { if actual.selected_company_assigned_share_pool != Some(value) { mismatches.push(format!( diff --git a/crates/rrt-runtime/src/import.rs b/crates/rrt-runtime/src/import.rs index d7ac8d8..862ae11 100644 --- a/crates/rrt-runtime/src/import.rs +++ b/crates/rrt-runtime/src/import.rs @@ -5043,6 +5043,7 @@ mod tests { outstanding_shares: 20_000, bond_count: 2, largest_live_bond_principal: Some(500_000), + highest_coupon_live_bond_principal: Some(350_000), mutable_support_scalar_raw_u32: 0x3f99999a, young_company_support_scalar_raw_u32: 0x42700000, support_progress_word: 12, @@ -5091,6 +5092,7 @@ mod tests { outstanding_shares: 18_000, bond_count: 1, largest_live_bond_principal: Some(300_000), + highest_coupon_live_bond_principal: Some(300_000), mutable_support_scalar_raw_u32: 0x3f4ccccd, young_company_support_scalar_raw_u32: 0x42580000, support_progress_word: 9, @@ -6382,6 +6384,7 @@ mod tests { outstanding_shares: 30_000, bond_count: 3, largest_live_bond_principal: Some(750_000), + highest_coupon_live_bond_principal: Some(500_000), mutable_support_scalar_raw_u32: 0x3f19999a, young_company_support_scalar_raw_u32: 0x42580000, support_progress_word: 8, diff --git a/crates/rrt-runtime/src/runtime.rs b/crates/rrt-runtime/src/runtime.rs index 28cfb81..7c00b52 100644 --- a/crates/rrt-runtime/src/runtime.rs +++ b/crates/rrt-runtime/src/runtime.rs @@ -57,6 +57,8 @@ pub struct RuntimeCompanyMarketState { #[serde(default)] pub largest_live_bond_principal: Option, #[serde(default)] + pub highest_coupon_live_bond_principal: Option, + #[serde(default)] pub mutable_support_scalar_raw_u32: u32, #[serde(default)] pub young_company_support_scalar_raw_u32: u32, @@ -103,6 +105,8 @@ pub struct RuntimeCompanyAnnualFinanceState { pub bond_count: u8, #[serde(default)] pub largest_live_bond_principal: Option, + #[serde(default)] + pub highest_coupon_live_bond_principal: Option, pub assigned_share_pool: u32, pub unassigned_share_pool: u32, #[serde(default)] @@ -361,6 +365,7 @@ pub enum RuntimeCompanyMarketMetric { OutstandingShares, BondCount, LargestLiveBondPrincipal, + HighestCouponLiveBondPrincipal, AssignedSharePool, UnassignedSharePool, CachedSharePrice, @@ -1935,6 +1940,7 @@ pub fn runtime_company_annual_finance_state( outstanding_shares: market_state.outstanding_shares, bond_count: market_state.bond_count, largest_live_bond_principal: market_state.largest_live_bond_principal, + highest_coupon_live_bond_principal: market_state.highest_coupon_live_bond_principal, assigned_share_pool, unassigned_share_pool, cached_share_price: rounded_cached_share_price_i64(market_state.cached_share_price_raw_u32), @@ -1969,6 +1975,9 @@ pub fn runtime_company_market_value( RuntimeCompanyMarketMetric::LargestLiveBondPrincipal => annual_finance_state .largest_live_bond_principal .map(|value| value as i64), + RuntimeCompanyMarketMetric::HighestCouponLiveBondPrincipal => annual_finance_state + .highest_coupon_live_bond_principal + .map(|value| value as i64), RuntimeCompanyMarketMetric::AssignedSharePool => { Some(annual_finance_state.assigned_share_pool as i64) } @@ -4243,6 +4252,7 @@ mod tests { outstanding_shares: 20_000, bond_count: 3, largest_live_bond_principal: Some(650_000), + highest_coupon_live_bond_principal: Some(500_000), cached_share_price_raw_u32: 0x42200000, chairman_salary_baseline: 24, chairman_salary_current: 30, @@ -4270,6 +4280,7 @@ mod tests { outstanding_shares: 20_000, bond_count: 3, largest_live_bond_principal: Some(650_000), + highest_coupon_live_bond_principal: Some(500_000), assigned_share_pool: 15_500, unassigned_share_pool: 4_500, cached_share_price: Some(40), @@ -4369,6 +4380,7 @@ mod tests { outstanding_shares: 20_000, bond_count: 2, largest_live_bond_principal: Some(500_000), + highest_coupon_live_bond_principal: Some(300_000), cached_share_price_raw_u32: 0x42200000, chairman_salary_baseline: 18, chairman_salary_current: 27, @@ -4402,6 +4414,14 @@ mod tests { ), Some(500_000) ); + assert_eq!( + runtime_company_market_value( + &state, + 7, + RuntimeCompanyMarketMetric::HighestCouponLiveBondPrincipal + ), + Some(300_000) + ); assert_eq!( runtime_company_market_value(&state, 7, RuntimeCompanyMarketMetric::AssignedSharePool), Some(12_000) diff --git a/crates/rrt-runtime/src/smp.rs b/crates/rrt-runtime/src/smp.rs index 65cec99..2b82728 100644 --- a/crates/rrt-runtime/src/smp.rs +++ b/crates/rrt-runtime/src/smp.rs @@ -2331,6 +2331,8 @@ pub struct SmpSaveCompanyRecordAnalysisEntry { #[serde(default)] pub largest_live_bond_principal: Option, #[serde(default)] + pub highest_coupon_live_bond_principal: Option, + #[serde(default)] pub available_track_laying_capacity: Option, pub company_value_scalar_f32: f32, pub cached_share_support_scalar_f32: f32, @@ -3006,6 +3008,8 @@ pub fn inspect_save_company_and_chairman_analysis_bytes( )?; let largest_live_bond_principal = parse_save_company_largest_live_bond_principal(&bytes, record_offset)?; + let highest_coupon_live_bond_principal = + parse_save_company_highest_coupon_live_bond_principal(&bytes, record_offset)?; let available_track_laying_capacity = parse_save_company_available_track_laying_capacity(&bytes, record_offset)?; let company_value_scalar_f32 = read_f32_at( @@ -3106,6 +3110,7 @@ pub fn inspect_save_company_and_chairman_analysis_bytes( debt, bond_count, largest_live_bond_principal, + highest_coupon_live_bond_principal, available_track_laying_capacity, company_value_scalar_f32, cached_share_support_scalar_f32, @@ -3589,6 +3594,8 @@ fn parse_save_company_roster_probe( let bond_count = read_u8_at(bytes, record_offset + SAVE_COMPANY_RECORD_BOND_COUNT_OFFSET)?; let largest_live_bond_principal = parse_save_company_largest_live_bond_principal(bytes, record_offset)?; + let highest_coupon_live_bond_principal = + parse_save_company_highest_coupon_live_bond_principal(bytes, record_offset)?; let available_track_laying_capacity = parse_save_company_available_track_laying_capacity(bytes, record_offset)?; let mutable_support_scalar_raw_u32 = read_u32_at( @@ -3704,6 +3711,7 @@ fn parse_save_company_roster_probe( outstanding_shares, bond_count, largest_live_bond_principal, + highest_coupon_live_bond_principal, mutable_support_scalar_raw_u32, young_company_support_scalar_raw_u32, support_progress_word, @@ -3905,6 +3913,46 @@ fn parse_save_company_largest_live_bond_principal( Some(largest_live_principal) } +fn parse_save_company_highest_coupon_live_bond_principal( + bytes: &[u8], + record_offset: usize, +) -> Option> { + let bond_count = + read_u8_at(bytes, record_offset + SAVE_COMPANY_RECORD_BOND_COUNT_OFFSET)? as usize; + let mut highest_coupon_principal = None; + let mut highest_coupon_rate = None; + for slot_index in 0..bond_count { + let slot_offset = record_offset + .checked_add(SAVE_COMPANY_RECORD_BOND_TABLE_OFFSET)? + .checked_add(slot_index.checked_mul(SAVE_COMPANY_RECORD_BOND_SLOT_STRIDE)?)?; + let principal = read_i32_at(bytes, slot_offset)?; + if principal <= 0 { + continue; + } + let coupon_rate_raw_u32 = read_u32_at(bytes, slot_offset + 8)?; + let coupon_rate = f32::from_bits(coupon_rate_raw_u32); + if !coupon_rate.is_finite() { + continue; + } + let principal = principal as u32; + match highest_coupon_rate { + Some(current_rate) if coupon_rate < current_rate => {} + Some(current_rate) if coupon_rate == current_rate => { + if let Some(current_principal) = highest_coupon_principal { + if principal > current_principal { + highest_coupon_principal = Some(principal); + } + } + } + _ => { + highest_coupon_rate = Some(coupon_rate); + highest_coupon_principal = Some(principal); + } + } + } + Some(highest_coupon_principal) +} + fn parse_save_company_available_track_laying_capacity( bytes: &[u8], record_offset: usize, @@ -15991,15 +16039,18 @@ mod tests { let slot_offset = record_offset + SAVE_COMPANY_RECORD_BOND_TABLE_OFFSET + slot_index * SAVE_COMPANY_RECORD_BOND_SLOT_STRIDE; - let principal = if index == 0 && slot_index == 1 { - 650_000i32 + let (principal, coupon_rate) = if index == 0 && slot_index == 0 { + (900_000i32, 0.08f32) + } else if index == 0 && slot_index == 1 { + (650_000i32, 0.12f32) } else { - 500_000i32 + (500_000i32, 0.10f32) }; bytes[slot_offset..slot_offset + 4].copy_from_slice(&principal.to_le_bytes()); bytes[slot_offset + 4..slot_offset + 8] .copy_from_slice(&(1894u32 + slot_index as u32).to_le_bytes()); - bytes[slot_offset + 8..slot_offset + 12].copy_from_slice(&(0.10f32).to_le_bytes()); + bytes[slot_offset + 8..slot_offset + 12] + .copy_from_slice(&coupon_rate.to_le_bytes()); } bytes[record_offset + SAVE_COMPANY_RECORD_MERGER_COOLDOWN_OFFSET ..record_offset + SAVE_COMPANY_RECORD_MERGER_COOLDOWN_OFFSET + 4] @@ -16097,7 +16148,7 @@ mod tests { assert_eq!(roster.entries.len(), 2); assert_eq!(roster.entries[0].company_id, 1); assert_eq!(roster.entries[0].linked_chairman_profile_id, Some(1)); - assert_eq!(roster.entries[0].debt, 1_150_000); + assert_eq!(roster.entries[0].debt, 1_550_000); assert_eq!(roster.entries[0].available_track_laying_capacity, Some(603)); assert_eq!(roster.entries[0].merger_cooldown_year, Some(1862)); let market_state = roster.entries[0] @@ -16105,7 +16156,11 @@ mod tests { .as_ref() .expect("company market state should load"); assert_eq!(market_state.outstanding_shares, 20_000); - assert_eq!(market_state.largest_live_bond_principal, Some(650_000)); + assert_eq!(market_state.largest_live_bond_principal, Some(900_000)); + assert_eq!( + market_state.highest_coupon_live_bond_principal, + Some(650_000) + ); assert_eq!(market_state.mutable_support_scalar_raw_u32, 0x3f800000); assert_eq!( market_state.young_company_support_scalar_raw_u32, @@ -16158,6 +16213,10 @@ mod tests { second_market_state.largest_live_bond_principal, Some(500_000) ); + assert_eq!( + second_market_state.highest_coupon_live_bond_principal, + Some(500_000) + ); assert_eq!(second_market_state.chairman_bonus_year, 0); assert_eq!(second_market_state.chairman_bonus_amount, 0); assert_eq!(second_market_state.last_dividend_year, 1850); diff --git a/crates/rrt-runtime/src/summary.rs b/crates/rrt-runtime/src/summary.rs index 6b1c7dc..1bbfc5e 100644 --- a/crates/rrt-runtime/src/summary.rs +++ b/crates/rrt-runtime/src/summary.rs @@ -52,6 +52,7 @@ pub struct RuntimeSummary { pub selected_company_outstanding_shares: Option, pub selected_company_bond_count: Option, pub selected_company_largest_live_bond_principal: Option, + pub selected_company_highest_coupon_live_bond_principal: Option, pub selected_company_assigned_share_pool: Option, pub selected_company_unassigned_share_pool: Option, pub selected_company_cached_share_price: Option, @@ -264,6 +265,8 @@ impl RuntimeSummary { .map(|market_state| market_state.bond_count), selected_company_largest_live_bond_principal: selected_company_market_state .and_then(|market_state| market_state.largest_live_bond_principal), + selected_company_highest_coupon_live_bond_principal: selected_company_market_state + .and_then(|market_state| market_state.highest_coupon_live_bond_principal), selected_company_assigned_share_pool: selected_company_annual_finance_state .as_ref() .map(|finance_state| finance_state.assigned_share_pool), @@ -1963,6 +1966,7 @@ mod tests { crate::RuntimeCompanyMarketState { outstanding_shares: 20_000, bond_count: 2, + highest_coupon_live_bond_principal: Some(350_000), largest_live_bond_principal: Some(500_000), mutable_support_scalar_raw_u32: 0x3f800000, young_company_support_scalar_raw_u32: 0x42340000, @@ -2036,6 +2040,10 @@ mod tests { summary.selected_company_largest_live_bond_principal, Some(500_000) ); + assert_eq!( + summary.selected_company_highest_coupon_live_bond_principal, + Some(350_000) + ); assert_eq!(summary.selected_company_assigned_share_pool, Some(5_000)); assert_eq!(summary.selected_company_unassigned_share_pool, Some(15_000)); assert_eq!(summary.selected_company_cached_share_price, Some(40)); diff --git a/docs/README.md b/docs/README.md index 7623ded..9460944 100644 --- a/docs/README.md +++ b/docs/README.md @@ -127,8 +127,9 @@ The highest-value next passes are now: feeds a shared company market reader for stock-capital, salary, bonus, and issue-calendar values, and now derives elapsed years since founding, last dividend, and last bankruptcy for later annual finance-policy rehosting; live bond-slot count now travels through that same owned annual-finance - state for the stock-capital branch gate, and the grounded bond table now also contributes the - largest live bond principal into that same owner-state surface + state for the stock-capital branch gate, and the grounded bond table now also contributes both + the largest live bond principal and the chosen highest-coupon live bond principal into that same + owner-state surface - the project rule on the remaining closure work is now explicit too: when one runtime-facing field is still ambiguous, prefer rehosting the owning source state or real reader/setter family first instead of guessing another derived leaf field from neighboring raw offsets diff --git a/docs/runtime-rehost-plan.md b/docs/runtime-rehost-plan.md index 2e9f913..88faa10 100644 --- a/docs/runtime-rehost-plan.md +++ b/docs/runtime-rehost-plan.md @@ -218,9 +218,10 @@ helpers. That same owned annual-finance state now also derives elapsed years sin dividend, and last bankruptcy from the runtime calendar, which lines up directly with the grounded annual finance-policy gates in the atlas. Live bond-slot count now also flows through that same owned company market and annual-finance state, matching the stock-capital branch gate that needs -at least two live bonds. The same grounded bond table now also contributes the largest live bond -principal into owned company market and annual-finance state, so later stock-capital gates can -extend a rehosted owner-state seam instead of guessing another finance leaf. +at least two live bonds. The same grounded bond table now also contributes both the largest live +bond principal and the chosen highest-coupon live bond principal into owned company market and +annual-finance state, so later stock-capital gates can extend a rehosted owner-state seam instead +of guessing another finance leaf. ## Why This Boundary