diff --git a/README.md b/README.md index 4b02195..2627531 100644 --- a/README.md +++ b/README.md @@ -97,6 +97,9 @@ headlessly as a pure runtime reader over owned annual-finance state, support-adj and current world finance policy rather than as a notes-only atlas fragment. The later deep- distress bankruptcy fallback is now rehosted on that same owner surface too, using the save-native cash reader seam plus the first three trailing net-profit years instead of another ad hoc probe. +The annual bond lane now runs on that same owner surface too, using the simulated post-repayment +cash window plus the linked-transit threshold split to stage `500000` principal issue counts as a +pure runtime reader. The same seam now also carries the fixed-world building-density growth setting plus the linked chairman personality byte, which is enough to run the annual stock-repurchase gate as another pure reader over owned save-native state instead of a guessed finance-side approximation. diff --git a/crates/rrt-runtime/src/lib.rs b/crates/rrt-runtime/src/lib.rs index 525ad3b..0a5f201 100644 --- a/crates/rrt-runtime/src/lib.rs +++ b/crates/rrt-runtime/src/lib.rs @@ -52,28 +52,29 @@ pub use runtime::{ RUNTIME_WORLD_ISSUE_PRIME_RATE, RuntimeCargoCatalogEntry, RuntimeCargoClass, RuntimeCargoPriceTarget, RuntimeCargoProductionTarget, RuntimeChairmanMetric, RuntimeChairmanProfile, RuntimeChairmanTarget, RuntimeCompany, - RuntimeCompanyAnnualCreditorPressureState, RuntimeCompanyAnnualDeepDistressState, - RuntimeCompanyAnnualFinanceState, RuntimeCompanyAnnualStockIssueState, - RuntimeCompanyAnnualStockRepurchaseState, RuntimeCompanyBondSlot, - RuntimeCompanyConditionTestScope, RuntimeCompanyControllerKind, RuntimeCompanyMarketMetric, - RuntimeCompanyMarketState, RuntimeCompanyMetric, RuntimeCompanyStatBandCandidate, - RuntimeCompanyStatSelector, RuntimeCompanyTarget, RuntimeCompanyTerritoryAccess, - RuntimeCompanyTerritoryTrackPieceCount, RuntimeCondition, RuntimeConditionComparator, - RuntimeEffect, RuntimeEventRecord, RuntimeEventRecordTemplate, RuntimeLocomotiveCatalogEntry, - RuntimePackedEventCollectionSummary, RuntimePackedEventCompactControlSummary, - RuntimePackedEventConditionRowSummary, RuntimePackedEventGroupedEffectRowSummary, - RuntimePackedEventNegativeSentinelScopeSummary, RuntimePackedEventRecordSummary, - RuntimePackedEventTextBandSummary, RuntimePlayer, RuntimePlayerConditionTestScope, - RuntimePlayerTarget, RuntimeSaveProfileState, RuntimeServiceState, RuntimeState, - RuntimeTerritory, RuntimeTerritoryMetric, RuntimeTerritoryTarget, RuntimeTrackMetric, - RuntimeTrackPieceCounts, RuntimeTrain, RuntimeWorldFinanceNeighborhoodCandidate, - RuntimeWorldIssueState, RuntimeWorldRestoreState, - runtime_company_annual_creditor_pressure_state, runtime_company_annual_deep_distress_state, - runtime_company_annual_finance_state, runtime_company_annual_stock_issue_state, - runtime_company_annual_stock_repurchase_state, runtime_company_assigned_share_pool, - runtime_company_average_live_bond_coupon, runtime_company_book_value_per_share, - runtime_company_credit_rating, runtime_company_investor_confidence, - runtime_company_management_attitude, runtime_company_market_value, runtime_company_prime_rate, + RuntimeCompanyAnnualBondPolicyState, RuntimeCompanyAnnualCreditorPressureState, + RuntimeCompanyAnnualDeepDistressState, RuntimeCompanyAnnualFinanceState, + RuntimeCompanyAnnualStockIssueState, RuntimeCompanyAnnualStockRepurchaseState, + RuntimeCompanyBondSlot, RuntimeCompanyConditionTestScope, RuntimeCompanyControllerKind, + RuntimeCompanyMarketMetric, RuntimeCompanyMarketState, RuntimeCompanyMetric, + RuntimeCompanyStatBandCandidate, RuntimeCompanyStatSelector, RuntimeCompanyTarget, + RuntimeCompanyTerritoryAccess, RuntimeCompanyTerritoryTrackPieceCount, RuntimeCondition, + RuntimeConditionComparator, RuntimeEffect, RuntimeEventRecord, RuntimeEventRecordTemplate, + RuntimeLocomotiveCatalogEntry, RuntimePackedEventCollectionSummary, + RuntimePackedEventCompactControlSummary, RuntimePackedEventConditionRowSummary, + RuntimePackedEventGroupedEffectRowSummary, RuntimePackedEventNegativeSentinelScopeSummary, + RuntimePackedEventRecordSummary, RuntimePackedEventTextBandSummary, RuntimePlayer, + RuntimePlayerConditionTestScope, RuntimePlayerTarget, RuntimeSaveProfileState, + RuntimeServiceState, RuntimeState, RuntimeTerritory, RuntimeTerritoryMetric, + RuntimeTerritoryTarget, RuntimeTrackMetric, RuntimeTrackPieceCounts, RuntimeTrain, + RuntimeWorldFinanceNeighborhoodCandidate, RuntimeWorldIssueState, RuntimeWorldRestoreState, + runtime_company_annual_bond_policy_state, runtime_company_annual_creditor_pressure_state, + runtime_company_annual_deep_distress_state, runtime_company_annual_finance_state, + runtime_company_annual_stock_issue_state, runtime_company_annual_stock_repurchase_state, + runtime_company_assigned_share_pool, runtime_company_average_live_bond_coupon, + runtime_company_book_value_per_share, runtime_company_credit_rating, + runtime_company_investor_confidence, runtime_company_management_attitude, + runtime_company_market_value, runtime_company_prime_rate, runtime_company_recent_per_share_subscore, runtime_company_stat_value, runtime_company_stat_value_f64, runtime_company_unassigned_share_pool, runtime_world_annual_finance_mode_active, runtime_world_bankruptcy_allowed, diff --git a/crates/rrt-runtime/src/runtime.rs b/crates/rrt-runtime/src/runtime.rs index 775718b..95410b6 100644 --- a/crates/rrt-runtime/src/runtime.rs +++ b/crates/rrt-runtime/src/runtime.rs @@ -310,6 +310,35 @@ pub struct RuntimeCompanyAnnualStockIssueState { pub eligible_for_double_tranche_issue: bool, } +#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)] +pub struct RuntimeCompanyAnnualBondPolicyState { + pub company_id: u32, + #[serde(default)] + pub annual_mode_active: Option, + #[serde(default)] + pub bond_issue_and_repayment_allowed: Option, + pub linked_transit_latch: bool, + #[serde(default)] + pub live_bond_count: Option, + #[serde(default)] + pub live_bond_principal_total: Option, + #[serde(default)] + pub current_cash: Option, + #[serde(default)] + pub cash_after_full_repayment: Option, + #[serde(default)] + pub issue_cash_floor: Option, + #[serde(default)] + pub issue_principal_step: Option, + #[serde(default)] + pub proposed_issue_bond_count: Option, + #[serde(default)] + pub proposed_issue_total_principal: Option, + #[serde(default)] + pub proposed_issue_years_to_maturity: Option, + pub eligible_for_bond_issue_branch: bool, +} + #[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize, Default)] pub struct RuntimeTrackPieceCounts { #[serde(default)] @@ -2235,6 +2264,17 @@ fn runtime_company_highest_live_bond_coupon_rate_f64( .max_by(|left, right| left.partial_cmp(right).unwrap_or(std::cmp::Ordering::Equal)) } +fn runtime_company_total_live_bond_principal(state: &RuntimeState, company_id: u32) -> Option { + let market_state = state.service_state.company_market_state.get(&company_id)?; + Some( + market_state + .live_bond_slots + .iter() + .map(|slot| slot.principal) + .sum(), + ) +} + fn runtime_company_support_adjusted_share_price_scalar_with_pressure_f64( state: &RuntimeState, company_id: u32, @@ -3016,6 +3056,65 @@ pub fn runtime_company_annual_stock_repurchase_state( }) } +pub fn runtime_company_annual_bond_policy_state( + state: &RuntimeState, + company_id: u32, +) -> Option { + const STANDARD_CASH_FLOOR: i64 = -250_000; + const LINKED_TRANSIT_CASH_FLOOR: i64 = -30_000; + const ISSUE_PRINCIPAL_STEP: u32 = 500_000; + const ISSUE_YEARS_TO_MATURITY: u32 = 30; + + let annual_finance_state = runtime_company_annual_finance_state(state, company_id)?; + let current_cash = runtime_company_control_transfer_stat_value_f64( + state, + company_id, + RUNTIME_COMPANY_STAT_SLOT_CURRENT_CASH, + ) + .and_then(runtime_round_f64_to_i64); + let live_bond_principal_total = runtime_company_total_live_bond_principal(state, company_id); + let cash_after_full_repayment = current_cash + .zip(live_bond_principal_total) + .map(|(cash, principal)| cash - i64::from(principal)); + let issue_cash_floor = Some(if annual_finance_state.linked_transit_latch { + LINKED_TRANSIT_CASH_FLOOR + } else { + STANDARD_CASH_FLOOR + }); + let proposed_issue_bond_count = cash_after_full_repayment.zip(issue_cash_floor).map( + |(cash_after_repayment, cash_floor)| { + if cash_after_repayment >= cash_floor { + 0 + } else { + let deficit = (cash_floor - cash_after_repayment) as u64; + deficit.div_ceil(u64::from(ISSUE_PRINCIPAL_STEP)) as u32 + } + }, + ); + let proposed_issue_total_principal = + proposed_issue_bond_count.and_then(|count| count.checked_mul(ISSUE_PRINCIPAL_STEP)); + let eligible_for_bond_issue_branch = runtime_world_annual_finance_mode_active(state) + == Some(true) + && runtime_world_bond_issue_and_repayment_allowed(state) == Some(true) + && proposed_issue_bond_count.is_some_and(|count| count > 0); + Some(RuntimeCompanyAnnualBondPolicyState { + company_id, + annual_mode_active: runtime_world_annual_finance_mode_active(state), + bond_issue_and_repayment_allowed: runtime_world_bond_issue_and_repayment_allowed(state), + linked_transit_latch: annual_finance_state.linked_transit_latch, + live_bond_count: Some(annual_finance_state.bond_count), + live_bond_principal_total, + current_cash, + cash_after_full_repayment, + issue_cash_floor, + issue_principal_step: Some(ISSUE_PRINCIPAL_STEP), + proposed_issue_bond_count, + proposed_issue_total_principal, + proposed_issue_years_to_maturity: Some(ISSUE_YEARS_TO_MATURITY), + eligible_for_bond_issue_branch, + }) +} + fn runtime_company_stock_issue_price_to_book_ratio_f64( pressured_support_adjusted_share_price_scalar: f64, book_value_per_share: f64, @@ -3144,6 +3243,8 @@ pub fn runtime_company_annual_stock_issue_state( .eligible_for_bankruptcy_branch && !runtime_company_annual_deep_distress_state(state, company_id)? .eligible_for_bankruptcy_fallback + && !runtime_company_annual_bond_policy_state(state, company_id)? + .eligible_for_bond_issue_branch && !runtime_company_annual_stock_repurchase_state(state, company_id)? .eligible_for_single_batch_repurchase && passes_share_price_floor == Some(true) @@ -7621,6 +7722,121 @@ mod tests { assert!(!repurchase_state.eligible_for_single_batch_repurchase); } + #[test] + fn derives_annual_bond_policy_state_from_rehosted_owner_state() { + let mut year_stat_family_qword_bits = vec![ + 0u64; + ((RUNTIME_COMPANY_STAT_SLOT_COUNT + 2) * RUNTIME_COMPANY_YEAR_STAT_FAMILY_SPAN) + as usize + ]; + let write_current_value = |bits: &mut Vec, slot_id: u32, value: f64| { + let index = (slot_id * RUNTIME_COMPANY_YEAR_STAT_FAMILY_SPAN) as usize; + bits[index] = value.to_bits(); + }; + write_current_value(&mut year_stat_family_qword_bits, 0x0d, -400_000.0); + + let state = RuntimeState { + calendar: CalendarPoint { + year: 1845, + month_slot: 0, + phase_slot: 0, + tick_slot: 0, + }, + world_flags: BTreeMap::new(), + save_profile: RuntimeSaveProfileState::default(), + world_restore: RuntimeWorldRestoreState { + partial_year_progress_raw_u8: Some(0x0c), + bond_issue_and_repayment_policy_raw_u8: Some(0), + bond_issue_and_repayment_allowed: Some(true), + ..RuntimeWorldRestoreState::default() + }, + metadata: BTreeMap::new(), + companies: vec![RuntimeCompany { + company_id: 11, + current_cash: 0, + debt: 0, + credit_rating_score: None, + prime_rate: None, + active: true, + available_track_laying_capacity: None, + controller_kind: RuntimeCompanyControllerKind::Unknown, + linked_chairman_profile_id: None, + book_value_per_share: 0, + investor_confidence: 0, + management_attitude: 0, + takeover_cooldown_year: None, + merger_cooldown_year: None, + track_piece_counts: RuntimeTrackPieceCounts::default(), + }], + selected_company_id: None, + players: Vec::new(), + selected_player_id: None, + chairman_profiles: Vec::new(), + selected_chairman_profile_id: None, + trains: Vec::new(), + locomotive_catalog: Vec::new(), + cargo_catalog: Vec::new(), + territories: Vec::new(), + company_territory_track_piece_counts: Vec::new(), + company_territory_access: Vec::new(), + packed_event_collection: None, + event_runtime_records: Vec::new(), + candidate_availability: BTreeMap::new(), + named_locomotive_availability: BTreeMap::new(), + named_locomotive_cost: BTreeMap::new(), + all_cargo_price_override: None, + named_cargo_price_overrides: BTreeMap::new(), + all_cargo_production_override: None, + factory_cargo_production_override: None, + farm_mine_cargo_production_override: None, + named_cargo_production_overrides: BTreeMap::new(), + cargo_production_overrides: BTreeMap::new(), + world_runtime_variables: BTreeMap::new(), + company_runtime_variables: BTreeMap::new(), + player_runtime_variables: BTreeMap::new(), + territory_runtime_variables: BTreeMap::new(), + world_scalar_overrides: BTreeMap::new(), + special_conditions: BTreeMap::new(), + service_state: RuntimeServiceState { + company_market_state: BTreeMap::from([( + 11, + RuntimeCompanyMarketState { + bond_count: 2, + linked_transit_latch: true, + live_bond_slots: vec![ + RuntimeCompanyBondSlot { + slot_index: 0, + principal: 200_000, + coupon_rate_raw_u32: 0.09f32.to_bits(), + }, + RuntimeCompanyBondSlot { + slot_index: 1, + principal: 150_000, + coupon_rate_raw_u32: 0.08f32.to_bits(), + }, + ], + year_stat_family_qword_bits, + ..RuntimeCompanyMarketState::default() + }, + )]), + ..RuntimeServiceState::default() + }, + }; + + let bond_state = + runtime_company_annual_bond_policy_state(&state, 11).expect("bond policy state"); + assert_eq!(bond_state.live_bond_count, Some(2)); + assert_eq!(bond_state.live_bond_principal_total, Some(350_000)); + assert_eq!(bond_state.current_cash, Some(-400_000)); + assert_eq!(bond_state.cash_after_full_repayment, Some(-750_000)); + assert_eq!(bond_state.issue_cash_floor, Some(-30_000)); + assert_eq!(bond_state.issue_principal_step, Some(500_000)); + assert_eq!(bond_state.proposed_issue_bond_count, Some(2)); + assert_eq!(bond_state.proposed_issue_total_principal, Some(1_000_000)); + assert_eq!(bond_state.proposed_issue_years_to_maturity, Some(30)); + assert!(bond_state.eligible_for_bond_issue_branch); + } + #[test] fn derives_annual_stock_issue_state_from_rehosted_owner_state() { let mut year_stat_family_qword_bits = vec![ diff --git a/crates/rrt-runtime/src/summary.rs b/crates/rrt-runtime/src/summary.rs index 0d4b63f..c4058fa 100644 --- a/crates/rrt-runtime/src/summary.rs +++ b/crates/rrt-runtime/src/summary.rs @@ -1,10 +1,10 @@ use serde::{Deserialize, Serialize}; use crate::{ - CalendarPoint, RuntimeState, runtime_company_annual_creditor_pressure_state, - runtime_company_annual_deep_distress_state, runtime_company_annual_finance_state, - runtime_company_annual_stock_issue_state, runtime_company_annual_stock_repurchase_state, - runtime_company_unassigned_share_pool, + CalendarPoint, RuntimeState, runtime_company_annual_bond_policy_state, + runtime_company_annual_creditor_pressure_state, runtime_company_annual_deep_distress_state, + runtime_company_annual_finance_state, runtime_company_annual_stock_issue_state, + runtime_company_annual_stock_repurchase_state, runtime_company_unassigned_share_pool, }; fn raw_u32_to_f32_text(raw: u32) -> String { @@ -113,6 +113,17 @@ pub struct RuntimeSummary { pub selected_company_deep_distress_cash_floor: Option, pub selected_company_deep_distress_net_profit_floor: Option, pub selected_company_deep_distress_eligible_for_bankruptcy_fallback: Option, + pub selected_company_annual_bond_linked_transit_latch: Option, + pub selected_company_annual_bond_live_bond_count: Option, + pub selected_company_annual_bond_live_bond_principal_total: Option, + pub selected_company_annual_bond_current_cash: Option, + pub selected_company_annual_bond_cash_after_full_repayment: Option, + pub selected_company_annual_bond_issue_cash_floor: Option, + pub selected_company_annual_bond_issue_principal_step: Option, + pub selected_company_annual_bond_proposed_issue_bond_count: Option, + pub selected_company_annual_bond_proposed_issue_total_principal: Option, + pub selected_company_annual_bond_proposed_issue_years_to_maturity: Option, + pub selected_company_annual_bond_eligible_for_issue_branch: Option, pub selected_company_stock_repurchase_city_connection_latch: Option, pub selected_company_stock_repurchase_building_density_growth_setting: Option, pub selected_company_stock_repurchase_linked_chairman_profile_id: Option, @@ -241,6 +252,9 @@ impl RuntimeSummary { let selected_company_deep_distress_state = state .selected_company_id .and_then(|company_id| runtime_company_annual_deep_distress_state(state, company_id)); + let selected_company_annual_bond_state = state + .selected_company_id + .and_then(|company_id| runtime_company_annual_bond_policy_state(state, company_id)); let selected_company_stock_repurchase_state = state.selected_company_id.and_then(|company_id| { runtime_company_annual_stock_repurchase_state(state, company_id) @@ -524,6 +538,45 @@ impl RuntimeSummary { selected_company_deep_distress_state .as_ref() .map(|pressure_state| pressure_state.eligible_for_bankruptcy_fallback), + selected_company_annual_bond_linked_transit_latch: selected_company_annual_bond_state + .as_ref() + .map(|bond_state| bond_state.linked_transit_latch), + selected_company_annual_bond_live_bond_count: selected_company_annual_bond_state + .as_ref() + .and_then(|bond_state| bond_state.live_bond_count), + selected_company_annual_bond_live_bond_principal_total: + selected_company_annual_bond_state + .as_ref() + .and_then(|bond_state| bond_state.live_bond_principal_total), + selected_company_annual_bond_current_cash: selected_company_annual_bond_state + .as_ref() + .and_then(|bond_state| bond_state.current_cash), + selected_company_annual_bond_cash_after_full_repayment: + selected_company_annual_bond_state + .as_ref() + .and_then(|bond_state| bond_state.cash_after_full_repayment), + selected_company_annual_bond_issue_cash_floor: selected_company_annual_bond_state + .as_ref() + .and_then(|bond_state| bond_state.issue_cash_floor), + selected_company_annual_bond_issue_principal_step: selected_company_annual_bond_state + .as_ref() + .and_then(|bond_state| bond_state.issue_principal_step), + selected_company_annual_bond_proposed_issue_bond_count: + selected_company_annual_bond_state + .as_ref() + .and_then(|bond_state| bond_state.proposed_issue_bond_count), + selected_company_annual_bond_proposed_issue_total_principal: + selected_company_annual_bond_state + .as_ref() + .and_then(|bond_state| bond_state.proposed_issue_total_principal), + selected_company_annual_bond_proposed_issue_years_to_maturity: + selected_company_annual_bond_state + .as_ref() + .and_then(|bond_state| bond_state.proposed_issue_years_to_maturity), + selected_company_annual_bond_eligible_for_issue_branch: + selected_company_annual_bond_state + .as_ref() + .map(|bond_state| bond_state.eligible_for_bond_issue_branch), selected_company_stock_repurchase_city_connection_latch: selected_company_stock_repurchase_state .as_ref() @@ -2911,6 +2964,153 @@ mod tests { ); } + #[test] + fn summarizes_selected_company_annual_bond_policy_state() { + let mut year_stat_family_qword_bits = vec![ + 0u64; + ((crate::RUNTIME_COMPANY_STAT_SLOT_COUNT + 2) + * crate::RUNTIME_COMPANY_YEAR_STAT_FAMILY_SPAN) + as usize + ]; + year_stat_family_qword_bits[(crate::RUNTIME_COMPANY_STAT_SLOT_CURRENT_CASH + * crate::RUNTIME_COMPANY_YEAR_STAT_FAMILY_SPAN) + as usize] = (-400_000.0f64).to_bits(); + + let state = RuntimeState { + calendar: CalendarPoint { + year: 1845, + month_slot: 0, + phase_slot: 0, + tick_slot: 0, + }, + world_flags: BTreeMap::new(), + save_profile: RuntimeSaveProfileState::default(), + world_restore: RuntimeWorldRestoreState { + partial_year_progress_raw_u8: Some(0x0c), + bond_issue_and_repayment_policy_raw_u8: Some(0), + bond_issue_and_repayment_allowed: Some(true), + ..RuntimeWorldRestoreState::default() + }, + metadata: BTreeMap::new(), + companies: vec![RuntimeCompany { + company_id: 11, + current_cash: 0, + debt: 0, + credit_rating_score: None, + prime_rate: None, + active: true, + available_track_laying_capacity: None, + controller_kind: crate::RuntimeCompanyControllerKind::Unknown, + linked_chairman_profile_id: None, + book_value_per_share: 0, + investor_confidence: 0, + management_attitude: 0, + takeover_cooldown_year: None, + merger_cooldown_year: None, + track_piece_counts: RuntimeTrackPieceCounts::default(), + }], + selected_company_id: Some(11), + players: Vec::new(), + selected_player_id: None, + chairman_profiles: Vec::new(), + selected_chairman_profile_id: None, + trains: Vec::new(), + locomotive_catalog: Vec::new(), + cargo_catalog: Vec::new(), + territories: Vec::new(), + company_territory_track_piece_counts: Vec::new(), + company_territory_access: Vec::new(), + packed_event_collection: None, + event_runtime_records: Vec::new(), + candidate_availability: BTreeMap::new(), + named_locomotive_availability: BTreeMap::new(), + named_locomotive_cost: Vec::new().into_iter().collect(), + all_cargo_price_override: None, + named_cargo_price_overrides: BTreeMap::new(), + all_cargo_production_override: None, + factory_cargo_production_override: None, + farm_mine_cargo_production_override: None, + named_cargo_production_overrides: BTreeMap::new(), + cargo_production_overrides: BTreeMap::new(), + world_runtime_variables: BTreeMap::new(), + company_runtime_variables: BTreeMap::new(), + player_runtime_variables: BTreeMap::new(), + territory_runtime_variables: BTreeMap::new(), + world_scalar_overrides: BTreeMap::new(), + special_conditions: BTreeMap::new(), + service_state: RuntimeServiceState { + company_market_state: BTreeMap::from([( + 11, + crate::RuntimeCompanyMarketState { + bond_count: 2, + linked_transit_latch: true, + live_bond_slots: vec![ + crate::RuntimeCompanyBondSlot { + slot_index: 0, + principal: 200_000, + coupon_rate_raw_u32: 0.09f32.to_bits(), + }, + crate::RuntimeCompanyBondSlot { + slot_index: 1, + principal: 150_000, + coupon_rate_raw_u32: 0.08f32.to_bits(), + }, + ], + year_stat_family_qword_bits, + ..crate::RuntimeCompanyMarketState::default() + }, + )]), + ..RuntimeServiceState::default() + }, + }; + + let summary = RuntimeSummary::from_state(&state); + assert_eq!( + summary.selected_company_annual_bond_linked_transit_latch, + Some(true) + ); + assert_eq!( + summary.selected_company_annual_bond_live_bond_count, + Some(2) + ); + assert_eq!( + summary.selected_company_annual_bond_live_bond_principal_total, + Some(350_000) + ); + assert_eq!( + summary.selected_company_annual_bond_current_cash, + Some(-400_000) + ); + assert_eq!( + summary.selected_company_annual_bond_cash_after_full_repayment, + Some(-750_000) + ); + assert_eq!( + summary.selected_company_annual_bond_issue_cash_floor, + Some(-30_000) + ); + assert_eq!( + summary.selected_company_annual_bond_issue_principal_step, + Some(500_000) + ); + assert_eq!( + summary.selected_company_annual_bond_proposed_issue_bond_count, + Some(2) + ); + assert_eq!( + summary.selected_company_annual_bond_proposed_issue_total_principal, + Some(1_000_000) + ); + assert_eq!( + summary.selected_company_annual_bond_proposed_issue_years_to_maturity, + Some(30) + ); + assert_eq!( + summary.selected_company_annual_bond_eligible_for_issue_branch, + Some(true) + ); + } + #[test] fn summarizes_selected_company_stock_issue_state() { let mut year_stat_family_qword_bits = vec![ diff --git a/docs/README.md b/docs/README.md index 31abcbc..4901ae2 100644 --- a/docs/README.md +++ b/docs/README.md @@ -139,7 +139,7 @@ The highest-value next passes are now: bankruptcy, and dividend finance-policy bytes, and the first annual creditor-pressure branch now executes as a pure runtime reader over that owner state instead of remaining atlas-only; the later deep-distress bankruptcy fallback now runs on that same save-native cash and trailing- - profit seam; the annual stock-repurchase and stock-capital issue branches now do too + profit seam; the annual bond, stock-repurchase, and stock-capital issue branches now do too net-profit surface too; the same owner seam now also carries the fixed-world building-density growth setting plus the linked chairman personality byte, which is enough to run the annual stock-repurchase gate headlessly as another pure reader diff --git a/docs/runtime-rehost-plan.md b/docs/runtime-rehost-plan.md index 27700dd..d76e4f1 100644 --- a/docs/runtime-rehost-plan.md +++ b/docs/runtime-rehost-plan.md @@ -232,6 +232,8 @@ bankruptcy branch now runs as a pure runtime reader over owned annual-finance st adjusted share price, and those policy bytes rather than staying in atlas prose only. The later deep-distress bankruptcy fallback now rides the same owner-state seam too, using the save-native cash reader plus the first three trailing net-profit years instead of a parallel raw-offset guess. +The annual bond lane now rides it as well, using the simulated post-repayment cash window plus the +linked-transit threshold split to stage `500000` principal issue counts without shell ownership. That same seam now also carries the fixed-world building-density growth setting plus the linked chairman personality byte, which is enough to rehost the annual stock-repurchase gate on owned save/runtime state instead of another threshold-only note. The stock-capital issue branch now