//! Versioned compatibility profile for the current release line.
//!
//! The profile is intentionally explicit about what the repository ships today
//! versus what remains for later stages. It can be printed by the CLI and used
//! in documentation or release notes so consumers know which built-ins and
//! aliases are actually available.

#![forbid(unsafe_code)]

use core::fmt;
use std::collections::{BTreeMap, BTreeSet};

use pleiades_ayanamsa::{
    baseline_ayanamsas, built_in_ayanamsas, custom_definition_ayanamsa_labels, metadata_coverage,
    release_ayanamsas, resolve_ayanamsa, validated_provenance_summary_for_report,
    AyanamsaDescriptor,
};
use pleiades_houses::{
    baseline_house_systems, built_in_house_systems, house_system_code_aliases,
    release_house_systems, resolve_house_system, HouseCatalogValidationError, HouseFormulaFamily,
    HouseSystemCodeAlias, HouseSystemDescriptor,
};
use pleiades_types::HouseSystem;

/// The current compatibility-profile identifier.
pub const CURRENT_COMPATIBILITY_PROFILE_ID: &str = "pleiades-compatibility-profile/0.6.123";

/// The current compatibility-profile release summary.
pub const CURRENT_COMPATIBILITY_PROFILE_SUMMARY: &str = "Stage 6 release profile: the baseline catalogs remain published as a routine release artifact while the target Swiss-Ephemeris-class compatibility catalog stays explicit, including the release-specific house-system additions across the Carter, Horizon/Azimuth, APC, Krusinski-Pisa-Goelzer, Albategnius, Pullen, including the exact Pullen SD table of houses, Pullen SD (Neo-Porphyry) table of houses, Pullen SD (Neo-Porphyry), Neo-Porphyry, Pullen SD (Sinusoidal Delta), Pullen SR table of houses, Pullen SR (Sinusoidal Ratio) table of houses, and Pullen SR (Sinusoidal Ratio) spellings, Sunshine, including the Bob Makransky, Makransky Sunshine, and Treindl Sunshine source labels, and Gauquelin families, plus the expanded ayanamsa coverage for J2000/J1900/B1950, True Citra and the True Citra Paksha / True Chitra Paksha / True Chitrapaksha interoperability spellings, DeLuce, Yukteshwar including the Sri Yukteshwar / Shri Yukteswar / Shri Yukteshwar transliterations, PVR Pushya-paksha, including the exact PVR Pushya Paksha spelling, Sheoran, and the Sunil Sheoran / Vedic Sheoran / Sheoran ayanamsa source spellings, the true-nakshatra and Suryasiddhanta Revati/Citra reference modes, the Hipparchus/Babylonian/Galactic reference-frame modes, the latest True Pushya, Udayagiri, Lahiri (VP285), Krishnamurti (VP291), Krishnamurti ayanamsa, Djwhal Khul, JN Bhasin, mean-sun, Valens Moon, and the Valens / Moon / Moon sign / Moon sign ayanamsa / Valens Moon ayanamsa source spellings, Dhruva Galactic Center (Middle Mula), Galactic Center (Cochrane/Mardyks) with the David Cochrane source name, Galactic Equator (Mula), the Babylonian house/sissy/true-geoc/true-topc/true-obs/house-obs variants, the backfilled True Sheoran, Galactic Center (Rgilbrand), the Skydram / Skydram/Galactic Alignment / Skydram (Mardyks) source spellings, and Galactic Center (Mula/Wilhelm) zero-point metadata, including the Dhruva/Gal.Center/Mula (Wilhelm), Mula Wilhelm, and Wilhelm source spellings, the additional Galactic Equator/Center variants including Galactic Equator (True) / True galactic equator / Galactic equator true and the `Gal. Center = 0 Sag` and `Gal. Center = 0 Cap` spellings, the exact Swiss Ephemeris source-label aliases for the Babylonian/Kugler family plus the Babylonian Kugler 1/2/3 plain spellings, the Babylonian 1/2/3 shorthand forms, and Babylonian Huber, the galactic-reference, mean-sun, Sassanian/Sasanian/Zij al-Shah, Aryabhata 499/522, and the Surya Siddhanta / Suryasiddhanta 499/499 CE source-form entries, the expanded APC and Horizon/Azimuth interoperability aliases, the Topocentric house-system alias and the exact Polich-Page \"topocentric\" table of houses, Polich/Page, Polich Page, and T Polich/Page (\"topocentric\") source spellings, the baseline Fagan/Bradley and Usha Shashi source-label appendix entries, the Babylonian house-family labels now rendered as explicit custom-definition territory rather than unresolved release gaps, and the `Equal (MC)` / `Equal (1=Aries)` source-label appendix entries for the release-line equal-house variants, including the `Equal from MC`, `Equal (from MC)`, `Equal (from MC) table of houses`, and `Equal/MC = 10th` spellings alongside the `Equal (MC)` table of houses, `Equal Midheaven table of houses`, `Equal (1=Aries)` table of houses, `Equal/1=0 Aries`, and `Equal (cusp 1 = 0° Aries)` spellings, plus the Wang, Aries houses, P.V.R. Narasimha Rao, and True Mula (Chandra Hari) source-label appendix entries for the ascendant-anchored equal-house and true-Mula variants, along with the exact Swiss Ephemeris house-table code spellings surfaced in the source-label appendix and the Equal table of houses, Whole Sign system, and Morinus house system spellings now called out explicitly in the quick-audit text, plus the Nick Anthony Fiorenza source name for Galactic Equator (Fiorenza). Unsupported modes remain explicit: built-in UTC convenience remains out of scope; built-in Delta T remains out of scope; topocentric body positions remain unsupported; apparent-place corrections are rejected unless a backend explicitly advertises support; native sidereal backend output remains unsupported unless a backend explicitly advertises it.";

/// Returns the current compatibility-profile identifier.
pub const fn current_compatibility_profile_id() -> &'static str {
    CURRENT_COMPATIBILITY_PROFILE_ID
}

/// A release-scoped compatibility profile.
#[derive(Clone, Copy, Debug)]
pub struct CompatibilityProfile {
    /// Stable profile identifier.
    pub profile_id: &'static str,
    /// Human-readable summary of the current release posture.
    pub summary: &'static str,
    /// Scope note describing the long-term house-system target.
    pub target_house_scope: &'static [&'static str],
    /// Scope note describing the long-term ayanamsa target.
    pub target_ayanamsa_scope: &'static [&'static str],
    /// Built-in house systems shipped in this release line.
    pub house_systems: &'static [HouseSystemDescriptor],
    /// House systems that belong to the published baseline milestone.
    pub baseline_house_systems: &'static [HouseSystemDescriptor],
    /// Release-specific house-system additions beyond the baseline milestone.
    pub release_house_systems: &'static [HouseSystemDescriptor],
    /// Built-in ayanamsas shipped in this release line.
    pub ayanamsas: &'static [AyanamsaDescriptor],
    /// Built-in ayanamsas that belong to the published baseline milestone.
    pub baseline_ayanamsas: &'static [AyanamsaDescriptor],
    /// Release-specific ayanamsa additions beyond the baseline milestone.
    pub release_ayanamsas: &'static [AyanamsaDescriptor],
    /// Explicitly documented release-specific notes beyond the baseline milestone.
    pub release_notes: &'static [&'static str],
    /// Validation reference points that are intentionally surfaced separately
    /// from unresolved compatibility gaps.
    pub validation_reference_points: &'static [&'static str],
    /// Labels that are intentionally surfaced as custom-definition territory
    /// instead of unresolved compatibility gaps.
    pub custom_definition_labels: &'static [&'static str],
    /// Explicitly documented compatibility caveats and follow-up notes.
    pub known_gaps: &'static [&'static str],
}

/// Typed summary of the built-in Swiss-Ephemeris house-code alias inventory.
#[derive(Clone, Copy, Debug)]
pub struct HouseCodeAliasInventorySummary {
    aliases: &'static [HouseSystemCodeAlias],
}

impl HouseCodeAliasInventorySummary {
    /// Creates a summary for the provided house-code alias inventory.
    pub const fn new(aliases: &'static [HouseSystemCodeAlias]) -> Self {
        Self { aliases }
    }

    /// Returns the number of short-form aliases in the inventory.
    pub fn count(&self) -> usize {
        self.aliases.len()
    }

    /// Returns the compact rendered short-code mapping.
    pub fn summary_line(&self) -> String {
        self.aliases
            .iter()
            .map(HouseSystemCodeAlias::summary_line)
            .collect::<Vec<_>>()
            .join(", ")
    }

    /// Returns the compact rendered short-code mapping after validation.
    pub fn validated_summary_line(&self) -> Result<String, CompatibilityProfileValidationError> {
        self.validate()?;
        Ok(self.summary_line())
    }

    /// Validates the inventory against the release-profile house-code rules.
    pub fn validate(&self) -> Result<usize, CompatibilityProfileValidationError> {
        validate_house_code_aliases(self.aliases)
    }
}

impl fmt::Display for HouseCodeAliasInventorySummary {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        f.write_str(&self.summary_line())
    }
}

impl CompatibilityProfile {
    /// Returns a short release note string.
    pub const fn release_note(&self) -> &'static str {
        self.summary
    }

    /// Returns the release note after validating the profile's release-facing metadata.
    pub fn validated_release_note(
        &self,
    ) -> Result<&'static str, CompatibilityProfileValidationError> {
        self.validate()?;
        Ok(self.summary)
    }

    /// Returns the long-term target house-system scope as a compact summary.
    pub fn target_house_scope_summary_line(&self) -> String {
        self.target_house_scope.join("; ")
    }

    /// Returns the long-term target house-system scope after validating the profile.
    pub fn validated_target_house_scope_summary_line(
        &self,
    ) -> Result<String, CompatibilityProfileValidationError> {
        self.validate()?;
        Ok(self.target_house_scope_summary_line())
    }

    /// Returns the long-term target ayanamsa scope as a compact summary.
    pub fn target_ayanamsa_scope_summary_line(&self) -> String {
        self.target_ayanamsa_scope.join("; ")
    }

    /// Returns the long-term target ayanamsa scope after validating the profile.
    pub fn validated_target_ayanamsa_scope_summary_line(
        &self,
    ) -> Result<String, CompatibilityProfileValidationError> {
        self.validate()?;
        Ok(self.target_ayanamsa_scope_summary_line())
    }

    /// Returns a typed summary of the Swiss-Ephemeris house-code alias inventory.
    pub fn house_code_alias_inventory_summary(&self) -> HouseCodeAliasInventorySummary {
        HouseCodeAliasInventorySummary::new(house_system_code_aliases())
    }

    /// Returns the number of Swiss Ephemeris house-table code aliases.
    pub fn house_code_alias_count(&self) -> usize {
        self.house_code_alias_inventory_summary().count()
    }

    /// Returns the Swiss-Ephemeris house-code alias table as compact mappings.
    ///
    /// # Examples
    ///
    /// ```
    /// use pleiades_core::current_compatibility_profile;
    ///
    /// let summary = current_compatibility_profile().house_code_aliases_summary_line();
    /// assert!(summary.contains("P -> Placidus"));
    /// ```
    pub fn house_code_aliases_summary_line(&self) -> String {
        self.house_code_alias_inventory_summary().summary_line()
    }

    /// Returns the house-code alias inventory after validating the profile.
    pub fn validated_house_code_aliases_summary_line(
        &self,
    ) -> Result<String, CompatibilityProfileValidationError> {
        self.validate()?;
        self.house_code_alias_inventory_summary()
            .validated_summary_line()
    }

    /// Returns the release-specific house-system canonical names as a compact summary.
    pub fn release_house_system_canonical_names_summary_line(&self) -> String {
        format_canonical_name_summary(&self.release_house_system_canonical_names())
    }

    /// Returns the release-specific house-system canonical names after validating the profile.
    pub fn validated_release_house_system_canonical_names_summary_line(
        &self,
    ) -> Result<String, CompatibilityProfileValidationError> {
        self.validate()?;
        let names = self.release_house_system_canonical_names();
        validate_profile_text_section("release-house-system-canonical-name", names.as_slice())?;
        Ok(format_canonical_name_summary(&names))
    }

    /// Returns the release-specific ayanamsa canonical names as a compact summary.
    pub fn release_ayanamsa_canonical_names_summary_line(&self) -> String {
        format_canonical_name_summary(&self.release_ayanamsa_canonical_names())
    }

    /// Returns the release-specific ayanamsa canonical names after validating the profile.
    pub fn validated_release_ayanamsa_canonical_names_summary_line(
        &self,
    ) -> Result<String, CompatibilityProfileValidationError> {
        self.validate()?;
        let names = self.release_ayanamsa_canonical_names();
        validate_profile_text_section("release-ayanamsa-canonical-name", names.as_slice())?;
        Ok(format_canonical_name_summary(&names))
    }

    /// Returns the representative ayanamsa provenance payload surfaced in the compatibility profile.
    pub fn ayanamsa_provenance_summary_line(&self) -> String {
        validated_provenance_summary_for_report()
            .unwrap_or_else(|error| format!("unavailable ({error})"))
    }

    /// Returns the representative ayanamsa provenance payload after validating the profile.
    pub fn validated_ayanamsa_provenance_summary_line(
        &self,
    ) -> Result<String, CompatibilityProfileValidationError> {
        self.validate()?;
        validated_provenance_summary_for_report().map_err(|error| {
            CompatibilityProfileValidationError::AyanamsaProvenanceSummaryValidationFailed {
                error: error.to_string(),
            }
        })
    }

    /// Returns the custom-definition ayanamsa labels surfaced in the compatibility profile.
    pub fn custom_definition_ayanamsa_labels(&self) -> Vec<&'static str> {
        metadata_coverage().custom_definition_only
    }

    /// Returns the custom-definition ayanamsa labels as a compact human-readable line.
    pub fn custom_definition_ayanamsa_labels_summary_line(&self) -> String {
        format_canonical_name_summary(&self.custom_definition_ayanamsa_labels())
    }

    /// Returns the custom-definition ayanamsa labels after validating the profile.
    pub fn validated_custom_definition_ayanamsa_labels_summary_line(
        &self,
    ) -> Result<String, CompatibilityProfileValidationError> {
        self.validate()?;
        let labels = self.custom_definition_ayanamsa_labels();
        validate_profile_text_section("custom-definition-ayanamsa-label", labels.as_slice())?;
        Ok(format_canonical_name_summary(&labels))
    }

    /// Returns the number of latitude-sensitive house-system descriptors.
    pub fn constrained_house_system_count(&self) -> usize {
        self.house_systems
            .iter()
            .filter(|entry| entry.latitude_sensitive)
            .count()
    }

    /// Returns the number of ayanamsa descriptors that still lack sidereal metadata.
    pub fn ayanamsa_descriptor_only_count(&self) -> usize {
        self.ayanamsas
            .iter()
            .filter(|entry| !entry.has_sidereal_metadata())
            .count()
    }

    fn ayanamsa_alias_bearing_entry_count(&self) -> usize {
        self.ayanamsas
            .iter()
            .filter(|entry| !entry.aliases.is_empty())
            .count()
    }

    /// Returns a compact inventory line for the current compatibility catalog.
    pub fn catalog_inventory_summary_line(&self) -> String {
        fn alias_count<T>(entries: &[T], aliases: impl Fn(&T) -> &'static [&'static str]) -> usize {
            entries.iter().map(|entry| aliases(entry).len()).sum()
        }

        let house_alias_count = alias_count(self.house_systems, |entry| entry.aliases);
        let house_code_alias_count = self.house_code_alias_count();
        let ayanamsa_alias_count = alias_count(self.ayanamsas, |entry| entry.aliases);
        let custom_definition_ayanamsa_labels = self.custom_definition_ayanamsa_labels();
        let ayanamsa_metadata_gap_count = metadata_coverage().without_sidereal_metadata.len();
        let ayanamsa_alias_bearing_entry_count = self.ayanamsa_alias_bearing_entry_count();
        let ayanamsa_provenance = self.ayanamsa_provenance_summary_line();
        let constrained_house_system_count = self.constrained_house_system_count();
        let unconstrained_house_system_count = self
            .house_systems
            .len()
            .saturating_sub(constrained_house_system_count);
        let ayanamsa_descriptor_only_count = self.ayanamsa_descriptor_only_count();
        let metadata_bearing_ayanamsa_count = self
            .ayanamsas
            .len()
            .saturating_sub(ayanamsa_descriptor_only_count);

        let mut text = String::from("Compatibility catalog inventory: ");
        text.push_str("house systems=");
        text.push_str(&self.house_systems.len().to_string());
        text.push_str(" (");
        text.push_str(&self.baseline_house_systems.len().to_string());
        text.push_str(" baseline, ");
        text.push_str(&self.release_house_systems.len().to_string());
        text.push_str(" release-specific, ");
        text.push_str(&house_alias_count.to_string());
        text.push_str(" aliases); house formula families=");
        text.push_str(&self.house_formula_families_summary_line());
        text.push_str("; house latitude-sensitive constraints=");
        text.push_str(&self.latitude_sensitive_house_constraints_summary_line());
        text.push_str("; house-code aliases=");
        text.push_str(&house_code_alias_count.to_string());
        text.push_str("; ayanamsas=");
        text.push_str(&self.ayanamsas.len().to_string());
        text.push_str(" (");
        text.push_str(&self.baseline_ayanamsas.len().to_string());
        text.push_str(" baseline, ");
        text.push_str(&self.release_ayanamsas.len().to_string());
        text.push_str(" release-specific, ");
        text.push_str(&ayanamsa_alias_count.to_string());
        text.push_str(" aliases); custom-definition labels=");
        text.push_str(&self.custom_definition_labels.len().to_string());
        text.push_str("; custom-definition ayanamsa labels=");
        text.push_str(&custom_definition_ayanamsa_labels.len().to_string());
        text.push_str(" (");
        text.push_str(&custom_definition_ayanamsa_labels.join(", "));
        text.push_str("); ayanamsa metadata gaps=");
        text.push_str(&ayanamsa_metadata_gap_count.to_string());
        text.push_str("; ayanamsa alias-bearing entries=");
        text.push_str(&ayanamsa_alias_bearing_entry_count.to_string());
        text.push_str("; catalog posture=house systems=");
        text.push_str(&self.house_systems.len().to_string());
        text.push_str(" (");
        text.push_str(&constrained_house_system_count.to_string());
        text.push_str(" constrained, ");
        text.push_str(&unconstrained_house_system_count.to_string());
        text.push_str(" unconstrained); ayanamsas=");
        text.push_str(&self.ayanamsas.len().to_string());
        text.push_str(" (");
        text.push_str(&ayanamsa_descriptor_only_count.to_string());
        text.push_str(" descriptor-only, ");
        text.push_str(&metadata_bearing_ayanamsa_count.to_string());
        text.push_str(" metadata-bearing); custom-only labels=");
        text.push_str(&self.custom_definition_labels.len().to_string());
        text.push_str("; custom-only ayanamsa labels=");
        text.push_str(&custom_definition_ayanamsa_labels.len().to_string());
        text.push_str("; ayanamsa provenance=");
        text.push_str(&ayanamsa_provenance);
        text.push_str("; known gaps=");
        text.push_str(&self.known_gaps.len().to_string());
        text.push_str("; claim audit: baseline catalogs are the published guarantees; release-specific entries are shipped additions; custom-definition labels remain custom-definition territory; descriptor-only ayanamsa entries remain catalog descriptors; constrained house systems stay explicitly flagged; known gaps stay documented");
        text
    }

    /// Returns the catalog inventory after validating the profile.
    pub fn validated_catalog_inventory_summary_line(
        &self,
    ) -> Result<String, CompatibilityProfileValidationError> {
        self.validate()?;
        Ok(self.catalog_inventory_summary_line())
    }

    /// Returns the compact known-gaps line for the current compatibility profile.
    pub fn known_gaps_summary_line(&self) -> String {
        format_canonical_name_summary(self.known_gaps)
    }

    /// Returns the known-gaps line after validating the profile.
    pub fn validated_known_gaps_summary_line(
        &self,
    ) -> Result<String, CompatibilityProfileValidationError> {
        self.validate()?;
        Ok(self.known_gaps_summary_line())
    }

    /// Returns the compact catalog-posture line for the current compatibility profile.
    pub fn catalog_posture_summary_line(&self) -> String {
        let constrained_house_system_count = self.constrained_house_system_count();
        let unconstrained_house_system_count = self
            .house_systems
            .len()
            .saturating_sub(constrained_house_system_count);
        let ayanamsa_descriptor_only_count = self.ayanamsa_descriptor_only_count();
        let metadata_bearing_ayanamsa_count = self
            .ayanamsas
            .len()
            .saturating_sub(ayanamsa_descriptor_only_count);

        format!(
            "Catalog posture: house systems={} descriptors ({} constrained, {} unconstrained); ayanamsas={} descriptors ({} metadata-bearing, {} descriptor-only); ayanamsa alias-bearing entries={}; ayanamsa metadata gaps={}; custom-definition labels={}; custom-definition ayanamsa labels={}; known gaps={}",
            self.house_systems.len(),
            constrained_house_system_count,
            unconstrained_house_system_count,
            self.ayanamsas.len(),
            metadata_bearing_ayanamsa_count,
            ayanamsa_descriptor_only_count,
            self.ayanamsa_alias_bearing_entry_count(),
            ayanamsa_descriptor_only_count,
            self.custom_definition_labels.len(),
            self.custom_definition_ayanamsa_labels().len(),
            self.known_gaps_summary_line()
        )
    }

    /// Returns the catalog-posture line after validating the profile.
    pub fn validated_catalog_posture_summary_line(
        &self,
    ) -> Result<String, CompatibilityProfileValidationError> {
        self.validate()?;
        Ok(self.catalog_posture_summary_line())
    }

    /// Returns the built-in house systems that are latitude-sensitive.
    pub fn latitude_sensitive_house_systems(&self) -> Vec<&'static str> {
        self.house_systems
            .iter()
            .filter(|entry| entry.latitude_sensitive)
            .map(|entry| entry.canonical_name)
            .collect()
    }

    /// Returns the latitude-sensitive house-system coverage as a compact human-readable line.
    pub fn latitude_sensitive_house_systems_summary_line(&self) -> String {
        format_canonical_name_summary(&self.latitude_sensitive_house_systems())
    }

    /// Returns the latitude-sensitive house-system coverage after validating the profile.
    pub fn validated_latitude_sensitive_house_systems_summary_line(
        &self,
    ) -> Result<String, CompatibilityProfileValidationError> {
        self.validate()?;
        Ok(self.latitude_sensitive_house_systems_summary_line())
    }

    /// Returns the release-facing latitude-sensitive constraint notes.
    pub fn latitude_sensitive_house_constraints(&self) -> Vec<String> {
        self.house_systems
            .iter()
            .filter(|entry| entry.latitude_sensitive)
            .map(|entry| format!("{} [{}]", entry.canonical_name, entry.notes))
            .collect()
    }

    /// Returns the latitude-sensitive house constraints as a compact human-readable line.
    pub fn latitude_sensitive_house_constraints_summary_line(&self) -> String {
        format_string_summary(&self.latitude_sensitive_house_constraints())
    }

    /// Returns the latitude-sensitive house constraints after validating the profile.
    pub fn validated_latitude_sensitive_house_constraints_summary_line(
        &self,
    ) -> Result<String, CompatibilityProfileValidationError> {
        self.validate()?;
        Ok(self.latitude_sensitive_house_constraints_summary_line())
    }

    /// Returns the release-facing failure-mode notes for latitude-sensitive house systems.
    pub fn latitude_sensitive_house_failure_modes(&self) -> Vec<String> {
        self.house_systems
            .iter()
            .filter(|entry| entry.latitude_sensitive)
            .map(|entry| format!("{} [{}]", entry.canonical_name, entry.notes))
            .collect()
    }

    /// Returns the latitude-sensitive house failure modes as a compact human-readable line.
    pub fn latitude_sensitive_house_failure_modes_summary_line(&self) -> String {
        format_string_summary(&self.latitude_sensitive_house_failure_modes())
    }

    /// Returns the unique house formula families represented in the profile,
    /// sorted by their release-facing labels.
    pub fn house_formula_family_names(&self) -> Vec<String> {
        self.house_systems
            .iter()
            .map(|entry| entry.formula_family().to_string())
            .collect::<BTreeSet<_>>()
            .into_iter()
            .collect()
    }

    /// Returns the house-formula-family coverage as a compact human-readable line.
    pub fn house_formula_families_summary_line(&self) -> String {
        let names = self.house_formula_family_names();
        match names.as_slice() {
            [] => "0 (none)".to_string(),
            [single] => format!("1 ({single})"),
            _ => format!("{} ({})", names.len(), names.join(", ")),
        }
    }

    /// Returns the house-formula-family coverage after validating the profile.
    pub fn validated_house_formula_families_summary_line(
        &self,
    ) -> Result<String, CompatibilityProfileValidationError> {
        self.validate()?;
        Ok(self.house_formula_families_summary_line())
    }

    /// Returns the canonical names for the built-in house-system baseline.
    pub fn baseline_house_system_canonical_names(&self) -> Vec<&'static str> {
        Self::canonical_names(self.baseline_house_systems, |entry| entry.canonical_name)
    }

    /// Returns the canonical names for the release-specific house-system additions.
    pub fn release_house_system_canonical_names(&self) -> Vec<&'static str> {
        Self::canonical_names(self.release_house_systems, |entry| entry.canonical_name)
    }

    /// Returns the canonical names for the built-in ayanamsa baseline.
    pub fn baseline_ayanamsa_canonical_names(&self) -> Vec<&'static str> {
        Self::canonical_names(self.baseline_ayanamsas, |entry| entry.canonical_name)
    }

    /// Returns the canonical names for the release-specific ayanamsa additions.
    pub fn release_ayanamsa_canonical_names(&self) -> Vec<&'static str> {
        Self::canonical_names(self.release_ayanamsas, |entry| entry.canonical_name)
    }

    fn canonical_names<T>(
        entries: &[T],
        canonical_name: impl Fn(&T) -> &'static str,
    ) -> Vec<&'static str> {
        entries.iter().map(canonical_name).collect()
    }
}

fn format_canonical_name_summary(names: &[&'static str]) -> String {
    match names {
        [] => "0 (none)".to_string(),
        [single] => format!("1 ({single})"),
        _ => format!("{} ({})", names.len(), names.join(", ")),
    }
}

fn format_string_summary(items: &[String]) -> String {
    match items {
        [] => "0 (none)".to_string(),
        [single] => format!("1 ({single})"),
        _ => format!("{} ({})", items.len(), items.join(", ")),
    }
}

impl CompatibilityProfile {
    /// Validates the profile's internal release-facing metadata.
    pub fn validate(&self) -> Result<(), CompatibilityProfileValidationError> {
        validate_profile_identifier(self.profile_id)?;
        validate_profile_summary(self.summary)?;
        validate_profile_text_section("target-house-scope", self.target_house_scope)?;
        validate_profile_text_section("target-ayanamsa-scope", self.target_ayanamsa_scope)?;
        validate_profile_text_section("release-note", self.release_notes)?;
        validate_profile_text_section(
            "validation-reference-point",
            self.validation_reference_points,
        )?;
        validate_custom_definition_labels(self.custom_definition_labels)?;
        validate_custom_definition_ayanamsa_coverage(self.custom_definition_labels)?;
        self.house_code_alias_inventory_summary().validate()?;
        validate_house_descriptor_metadata(self.house_systems)?;
        validate_ayanamsa_descriptor_metadata(self.ayanamsas)?;
        validate_profile_text_section("compatibility-caveat", self.known_gaps)?;
        validate_profile_text_sections_are_disjoint(&[
            ("target-house-scope", self.target_house_scope),
            ("target-ayanamsa-scope", self.target_ayanamsa_scope),
            ("release-note", self.release_notes),
            (
                "validation-reference-point",
                self.validation_reference_points,
            ),
            ("custom-definition", self.custom_definition_labels),
            ("compatibility-caveat", self.known_gaps),
        ])?;
        validate_catalog_label_uniqueness("house-system", self.house_systems)?;
        validate_house_formula_families(self.house_systems)?;
        validate_catalog_label_uniqueness("ayanamsa", self.ayanamsas)?;
        validate_catalogs_are_disjoint(
            "house-system",
            self.house_systems,
            "ayanamsa",
            self.ayanamsas,
        )?;
        validate_catalog_partitions_are_disjoint(
            "house-system",
            self.baseline_house_systems,
            self.release_house_systems,
        )?;
        validate_catalog_partitions_are_disjoint_ayanamsa(
            "ayanamsa",
            self.baseline_ayanamsas,
            self.release_ayanamsas,
        )?;
        validate_catalog_coverage(
            "house-system",
            self.house_systems,
            self.baseline_house_systems,
            self.release_house_systems,
        )?;
        validate_catalog_coverage_labels(
            "house-system",
            self.house_systems,
            self.baseline_house_systems,
            self.release_house_systems,
            |entry| entry.canonical_name,
        )?;
        validate_catalog_coverage_ayanamsa(
            "ayanamsa",
            self.ayanamsas,
            self.baseline_ayanamsas,
            self.release_ayanamsas,
        )?;
        validate_catalog_coverage_labels(
            "ayanamsa",
            self.ayanamsas,
            self.baseline_ayanamsas,
            self.release_ayanamsas,
            |entry| entry.canonical_name,
        )?;
        validated_provenance_summary_for_report().map_err(|error| {
            CompatibilityProfileValidationError::AyanamsaProvenanceSummaryValidationFailed {
                error: error.to_string(),
            }
        })?;
        Ok(())
    }
}

/// A validation error emitted when the compatibility profile's internal
/// release-facing metadata drifts.
#[derive(Clone, Debug, Eq, PartialEq)]
pub enum CompatibilityProfileValidationError {
    /// The profile identifier is blank.
    BlankProfileIdentifier,
    /// The summary is blank.
    BlankSummary,
    /// The summary no longer describes the baseline/release split explicitly.
    SummaryDoesNotDescribeReleaseSplit,
    /// A text section is empty or contains a blank entry.
    BlankTextSectionEntry {
        /// Section that failed validation.
        section_label: &'static str,
    },
    /// A text section contains an entry with surrounding whitespace.
    WhitespaceTextSectionEntry {
        /// Section that failed validation.
        section_label: &'static str,
        /// Entry that contained surrounding whitespace.
        entry: &'static str,
    },
    /// A text section contains a duplicate entry.
    DuplicateTextSectionEntry {
        /// Section that failed validation.
        section_label: &'static str,
        /// Duplicate entry.
        entry: &'static str,
    },
    /// Two different text sections contain the same entry.
    DuplicateTextSectionEntryAcrossSections {
        /// Duplicate entry.
        entry: &'static str,
        /// First section that contained the entry.
        first_section: &'static str,
        /// Second section that contained the entry.
        second_section: &'static str,
    },
    /// A custom-definition label resolves to a built-in house system or ayanamsa.
    CustomDefinitionLabelResolvesToBuiltIn {
        /// Label that should remain unresolved as a custom definition.
        label: &'static str,
    },
    /// A documented custom-definition-only ayanamsa is missing from the profile's custom-definition label list.
    MissingCustomDefinitionAyanamsaCoverageLabel {
        /// Label that should be surfaced as an intentional custom-definition ayanamsa.
        label: &'static str,
    },
    /// A built-in house-system descriptor failed its own normalization or alias checks.
    HouseDescriptorValidationFailed {
        /// Underlying descriptor validation error.
        error: HouseCatalogValidationError,
    },
    /// A built-in ayanamsa descriptor failed its own normalization or alias checks.
    AyanamsaDescriptorValidationFailed {
        /// Underlying descriptor validation error rendered as a stable summary string.
        error: String,
    },
    /// The representative ayanamsa provenance summary drifted from the current posture.
    AyanamsaProvenanceSummaryValidationFailed {
        /// Underlying summary validation error rendered as a stable summary string.
        error: String,
    },
    /// A Swiss-Ephemeris house-code alias does not resolve back to its typed house system.
    HouseCodeAliasDoesNotRoundTrip {
        /// Alias label that failed validation.
        label: &'static str,
        /// Typed system that the alias should resolve to.
        expected_system: HouseSystem,
    },
    /// Baseline and release partitions overlap on a catalog label.
    CatalogPartitionOverlap {
        /// Catalog that drifted.
        catalog_label: &'static str,
        /// Label that appears in both partitions.
        label: &'static str,
    },
    /// The published catalog contains a duplicate canonical name or alias.
    CatalogLabelCollision {
        /// Catalog that drifted.
        catalog_label: &'static str,
        /// Duplicate label that appeared more than once.
        label: &'static str,
    },
    /// A house-system descriptor resolved to an unknown formula family.
    HouseFormulaFamilyUnknown {
        /// House-system label that drifted.
        label: &'static str,
    },
    /// Two published catalogs share a canonical name or alias.
    CrossCatalogLabelCollision {
        /// First catalog that provided the duplicate label.
        first_catalog_label: &'static str,
        /// Second catalog that re-used the duplicate label.
        second_catalog_label: &'static str,
        /// Duplicate label that appeared in both catalogs.
        label: &'static str,
    },
    /// The total catalog coverage does not equal the baseline plus release partitions.
    CatalogCoverageMismatch {
        /// Catalog that drifted.
        catalog_label: &'static str,
        /// Total entries in the catalog.
        total_count: usize,
        /// Entries in the baseline partition.
        baseline_count: usize,
        /// Entries in the release partition.
        release_count: usize,
    },
    /// The total catalog labels do not match the baseline and release partitions exactly.
    CatalogCoverageLabelMismatch {
        /// Catalog that drifted.
        catalog_label: &'static str,
        /// Label that appears in the total catalog but not in the partitions.
        missing_label: &'static str,
        /// Label that appears in the partitions but not in the total catalog.
        unexpected_label: &'static str,
    },
}

impl fmt::Display for CompatibilityProfileValidationError {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        match self {
            Self::BlankProfileIdentifier => {
                f.write_str("compatibility profile identifier is blank")
            }
            Self::BlankSummary => f.write_str("compatibility profile summary is blank"),
            Self::SummaryDoesNotDescribeReleaseSplit => f.write_str(
                "compatibility profile summary no longer describes the baseline/release split explicitly",
            ),
            Self::BlankTextSectionEntry { section_label } => {
                write!(f, "compatibility profile {section_label} entry is blank")
            }
            Self::WhitespaceTextSectionEntry { section_label, entry } => write!(
                f,
                "compatibility profile {section_label} entry '{}' contains surrounding whitespace",
                entry
            ),
            Self::DuplicateTextSectionEntry { section_label, entry } => write!(
                f,
                "compatibility profile {section_label} entries are not unique: duplicate entry '{}'",
                entry
            ),
            Self::DuplicateTextSectionEntryAcrossSections {
                entry,
                first_section,
                second_section,
            } => write!(
                f,
                "compatibility profile text sections are not unique: duplicate entry '{}' appears in both {} and {}",
                entry, first_section, second_section
            ),
            Self::CustomDefinitionLabelResolvesToBuiltIn { label } => write!(
                f,
                "compatibility profile custom-definition label '{}' should remain unresolved as a built-in house system or ayanamsa",
                label
            ),
            Self::MissingCustomDefinitionAyanamsaCoverageLabel { label } => write!(
                f,
                "compatibility profile custom-definition ayanamsa coverage is missing the documented custom-definition-only label '{}'",
                label
            ),
            Self::HouseDescriptorValidationFailed { error } => write!(
                f,
                "compatibility profile house-system descriptor is invalid: {}",
                error
            ),
            Self::AyanamsaDescriptorValidationFailed { error } => write!(
                f,
                "compatibility profile ayanamsa descriptor is invalid: {}",
                error
            ),
            Self::AyanamsaProvenanceSummaryValidationFailed { error } => write!(
                f,
                "compatibility profile ayanamsa provenance summary is invalid: {}",
                error
            ),
            Self::HouseCodeAliasDoesNotRoundTrip {
                label,
                expected_system,
            } => write!(
                f,
                "compatibility profile house-code alias '{}' should resolve to {}",
                label, expected_system
            ),
            Self::CatalogPartitionOverlap { catalog_label, label } => write!(
                f,
                "compatibility profile {catalog_label} baseline and release slices overlap on label '{}'",
                label
            ),
            Self::CatalogLabelCollision { catalog_label, label } => write!(
                f,
                "compatibility profile {catalog_label} catalog contains duplicate label '{}'",
                label
            ),
            Self::HouseFormulaFamilyUnknown { label } => write!(
                f,
                "compatibility profile house-system '{label}' resolves to an unknown formula family"
            ),
            Self::CrossCatalogLabelCollision {
                first_catalog_label,
                second_catalog_label,
                label,
            } => write!(
                f,
                "compatibility profile {first_catalog_label} and {second_catalog_label} catalogs share duplicate label '{}'",
                label
            ),
            Self::CatalogCoverageMismatch {
                catalog_label,
                total_count,
                baseline_count,
                release_count,
            } => write!(
                f,
                "compatibility profile {catalog_label} catalog coverage mismatch: total={}, baseline={}, release={}",
                total_count, baseline_count, release_count
            ),
            Self::CatalogCoverageLabelMismatch {
                catalog_label,
                missing_label,
                unexpected_label,
            } => write!(
                f,
                "compatibility profile {catalog_label} catalog coverage mismatch: missing label '{}', unexpected label '{}'",
                missing_label, unexpected_label
            ),
        }
    }
}

impl std::error::Error for CompatibilityProfileValidationError {}

fn validate_profile_identifier(
    profile_id: &str,
) -> Result<(), CompatibilityProfileValidationError> {
    if profile_id.trim().is_empty() {
        return Err(CompatibilityProfileValidationError::BlankProfileIdentifier);
    }

    Ok(())
}

fn validate_profile_summary(summary: &str) -> Result<(), CompatibilityProfileValidationError> {
    if summary.trim().is_empty() {
        return Err(CompatibilityProfileValidationError::BlankSummary);
    }

    if summary != CURRENT_COMPATIBILITY_PROFILE_SUMMARY {
        return Err(CompatibilityProfileValidationError::SummaryDoesNotDescribeReleaseSplit);
    }

    Ok(())
}

fn has_surrounding_whitespace(value: &str) -> bool {
    !value.is_empty() && value.trim() != value
}

fn normalized_profile_text_entry(value: &str) -> String {
    value.trim().to_ascii_lowercase()
}

fn validate_profile_text_section(
    section_label: &'static str,
    entries: &[&'static str],
) -> Result<(), CompatibilityProfileValidationError> {
    if entries.is_empty() {
        return Err(CompatibilityProfileValidationError::BlankTextSectionEntry { section_label });
    }

    let mut seen_entries = BTreeSet::new();
    for entry in entries {
        if entry.trim().is_empty() {
            return Err(CompatibilityProfileValidationError::BlankTextSectionEntry {
                section_label,
            });
        }

        if has_surrounding_whitespace(entry) {
            return Err(
                CompatibilityProfileValidationError::WhitespaceTextSectionEntry {
                    section_label,
                    entry,
                },
            );
        }

        if !seen_entries.insert(normalized_profile_text_entry(entry)) {
            return Err(
                CompatibilityProfileValidationError::DuplicateTextSectionEntry {
                    section_label,
                    entry,
                },
            );
        }
    }

    Ok(())
}

fn validate_profile_text_sections_are_disjoint(
    sections: &[(&'static str, &'static [&'static str])],
) -> Result<(), CompatibilityProfileValidationError> {
    let mut seen_entries = BTreeMap::<String, &'static str>::new();
    for (section_label, entries) in sections {
        for entry in *entries {
            let normalized_entry = normalized_profile_text_entry(entry);
            if let Some(existing_section) = seen_entries.get(&normalized_entry) {
                if *existing_section != *section_label {
                    return Err(
                        CompatibilityProfileValidationError::DuplicateTextSectionEntryAcrossSections {
                            entry,
                            first_section: existing_section,
                            second_section: section_label,
                        },
                    );
                }
            } else {
                seen_entries.insert(normalized_entry, section_label);
            }
        }
    }

    Ok(())
}

const INTENTIONAL_CUSTOM_DEFINITION_AYANAMSA_HOMOGRAPHS: &[&str] = &[
    "Babylonian (House)",
    "Babylonian (Sissy)",
    "Babylonian (True Geoc)",
    "Babylonian (True Topc)",
    "Babylonian (True Obs)",
    "Babylonian (House Obs)",
];

fn is_intentional_custom_definition_ayanamsa_homograph(label: &str) -> bool {
    INTENTIONAL_CUSTOM_DEFINITION_AYANAMSA_HOMOGRAPHS.contains(&label)
}

pub fn validate_custom_definition_labels(
    labels: &[&'static str],
) -> Result<usize, CompatibilityProfileValidationError> {
    if labels.is_empty() {
        return Err(CompatibilityProfileValidationError::BlankTextSectionEntry {
            section_label: "custom-definition",
        });
    }

    let mut labels_checked = 0usize;
    let mut seen_labels = BTreeSet::new();

    for label in labels {
        labels_checked += 1;

        if label.trim().is_empty() {
            return Err(CompatibilityProfileValidationError::BlankTextSectionEntry {
                section_label: "custom-definition",
            });
        }

        if has_surrounding_whitespace(label) {
            return Err(
                CompatibilityProfileValidationError::WhitespaceTextSectionEntry {
                    section_label: "custom-definition",
                    entry: label,
                },
            );
        }

        let normalized_label = normalized_profile_text_entry(label);
        if !seen_labels.insert(normalized_label) {
            return Err(
                CompatibilityProfileValidationError::DuplicateTextSectionEntry {
                    section_label: "custom-definition",
                    entry: label,
                },
            );
        }

        if resolve_house_system(label).is_some()
            || (resolve_ayanamsa(label).is_some()
                && !is_intentional_custom_definition_ayanamsa_homograph(label))
        {
            return Err(
                CompatibilityProfileValidationError::CustomDefinitionLabelResolvesToBuiltIn {
                    label,
                },
            );
        }
    }

    Ok(labels_checked)
}

fn validate_custom_definition_ayanamsa_coverage(
    labels: &[&'static str],
) -> Result<usize, CompatibilityProfileValidationError> {
    let coverage = metadata_coverage();
    let mut seen_labels = BTreeSet::new();
    for label in labels {
        seen_labels.insert(normalized_profile_text_entry(label));
    }

    let mut checked = 0usize;
    for label in coverage.custom_definition_only {
        checked += 1;
        if !seen_labels.contains(&normalized_profile_text_entry(label)) {
            return Err(
                CompatibilityProfileValidationError::MissingCustomDefinitionAyanamsaCoverageLabel {
                    label,
                },
            );
        }
    }

    Ok(checked)
}

fn validate_house_descriptor_metadata(
    entries: &[HouseSystemDescriptor],
) -> Result<usize, CompatibilityProfileValidationError> {
    let mut checked = 0usize;
    for entry in entries {
        checked += 1;
        entry.validate().map_err(|error| {
            CompatibilityProfileValidationError::HouseDescriptorValidationFailed { error }
        })?;
    }

    Ok(checked)
}

fn validate_ayanamsa_descriptor_metadata(
    entries: &[AyanamsaDescriptor],
) -> Result<usize, CompatibilityProfileValidationError> {
    let mut checked = 0usize;
    for entry in entries {
        checked += 1;
        entry.validate().map_err(|error| {
            CompatibilityProfileValidationError::AyanamsaDescriptorValidationFailed {
                error: error.to_string(),
            }
        })?;
    }

    Ok(checked)
}

fn validate_house_code_aliases(
    aliases: &[HouseSystemCodeAlias],
) -> Result<usize, CompatibilityProfileValidationError> {
    if aliases.is_empty() {
        return Err(CompatibilityProfileValidationError::BlankTextSectionEntry {
            section_label: "house-code-alias",
        });
    }

    let mut aliases_checked = 0usize;
    let mut seen_labels = BTreeSet::new();

    for alias in aliases {
        aliases_checked += 1;

        if alias.label.trim().is_empty() {
            return Err(CompatibilityProfileValidationError::BlankTextSectionEntry {
                section_label: "house-code-alias",
            });
        }

        if has_surrounding_whitespace(alias.label) {
            return Err(
                CompatibilityProfileValidationError::WhitespaceTextSectionEntry {
                    section_label: "house-code-alias",
                    entry: alias.label,
                },
            );
        }

        let normalized_label = normalized_profile_text_entry(alias.label);
        if !seen_labels.insert(normalized_label) {
            return Err(
                CompatibilityProfileValidationError::DuplicateTextSectionEntry {
                    section_label: "house-code-alias",
                    entry: alias.label,
                },
            );
        }

        match resolve_house_system(alias.label) {
            Some(system) if system == alias.system => {}
            _ => {
                return Err(
                    CompatibilityProfileValidationError::HouseCodeAliasDoesNotRoundTrip {
                        label: alias.label,
                        expected_system: alias.system.clone(),
                    },
                );
            }
        }
    }

    Ok(aliases_checked)
}

fn validate_catalog_partitions_are_disjoint(
    catalog_label: &'static str,
    baseline_entries: &[HouseSystemDescriptor],
    release_entries: &[HouseSystemDescriptor],
) -> Result<(), CompatibilityProfileValidationError> {
    let mut baseline_labels = BTreeSet::new();
    for entry in baseline_entries {
        baseline_labels.insert(entry.canonical_name.trim().to_ascii_lowercase());
        for alias in entry.aliases {
            baseline_labels.insert(alias.trim().to_ascii_lowercase());
        }
    }

    for entry in release_entries {
        for label in std::iter::once(entry.canonical_name).chain(entry.aliases.iter().copied()) {
            if baseline_labels.contains(&label.trim().to_ascii_lowercase()) {
                return Err(
                    CompatibilityProfileValidationError::CatalogPartitionOverlap {
                        catalog_label,
                        label,
                    },
                );
            }
        }
    }

    Ok(())
}

fn validate_catalog_partitions_are_disjoint_ayanamsa(
    catalog_label: &'static str,
    baseline_entries: &[AyanamsaDescriptor],
    release_entries: &[AyanamsaDescriptor],
) -> Result<(), CompatibilityProfileValidationError> {
    let mut baseline_labels = BTreeSet::new();
    for entry in baseline_entries {
        baseline_labels.insert(entry.canonical_name.trim().to_ascii_lowercase());
        for alias in entry.aliases {
            baseline_labels.insert(alias.trim().to_ascii_lowercase());
        }
    }

    for entry in release_entries {
        for label in std::iter::once(entry.canonical_name).chain(entry.aliases.iter().copied()) {
            if baseline_labels.contains(&label.trim().to_ascii_lowercase()) {
                return Err(
                    CompatibilityProfileValidationError::CatalogPartitionOverlap {
                        catalog_label,
                        label,
                    },
                );
            }
        }
    }

    Ok(())
}

fn is_case_only_alias_variant(canonical_name: &str, label: &str) -> bool {
    label != canonical_name && label.eq_ignore_ascii_case(canonical_name)
}

fn validate_catalog_label_uniqueness<T: AliasProfileEntry>(
    catalog_label: &'static str,
    entries: &[T],
) -> Result<(), CompatibilityProfileValidationError> {
    let mut seen_labels = BTreeSet::new();

    for entry in entries {
        for label in std::iter::once(entry.canonical_name()).chain(entry.aliases().iter().copied())
        {
            // Some built-in entries intentionally retain case-only alias variants,
            // such as Vehlow Equal / vehlow equal and the galactic-reference
            // labels that keep both title-case and lowercase spellings visible in
            // the release profile. Keep those forms allowed while still rejecting
            // genuine duplicate labels.
            if is_case_only_alias_variant(entry.canonical_name(), label) {
                continue;
            }

            let normalized_label = label.trim().to_ascii_lowercase();
            if !seen_labels.insert(normalized_label) {
                return Err(CompatibilityProfileValidationError::CatalogLabelCollision {
                    catalog_label,
                    label,
                });
            }
        }
    }

    Ok(())
}

fn collect_catalog_labels<T: AliasProfileEntry>(entries: &[T]) -> BTreeSet<String> {
    let mut labels = BTreeSet::new();

    for entry in entries {
        labels.insert(entry.canonical_name().trim().to_ascii_lowercase());
        for alias in entry.aliases() {
            labels.insert(alias.trim().to_ascii_lowercase());
        }
    }

    labels
}

fn validate_catalogs_are_disjoint<T: AliasProfileEntry, U: AliasProfileEntry>(
    first_catalog_label: &'static str,
    first_entries: &[T],
    second_catalog_label: &'static str,
    second_entries: &[U],
) -> Result<(), CompatibilityProfileValidationError> {
    let first_labels = collect_catalog_labels(first_entries);

    for entry in second_entries {
        for label in std::iter::once(entry.canonical_name()).chain(entry.aliases().iter().copied())
        {
            let normalized_label = label.trim().to_ascii_lowercase();
            if first_labels.contains(&normalized_label) {
                return Err(
                    CompatibilityProfileValidationError::CrossCatalogLabelCollision {
                        first_catalog_label,
                        second_catalog_label,
                        label,
                    },
                );
            }
        }
    }

    Ok(())
}

fn validate_catalog_coverage(
    catalog_label: &'static str,
    total_entries: &[HouseSystemDescriptor],
    baseline_entries: &[HouseSystemDescriptor],
    release_entries: &[HouseSystemDescriptor],
) -> Result<(), CompatibilityProfileValidationError> {
    if total_entries.len() != baseline_entries.len() + release_entries.len() {
        return Err(
            CompatibilityProfileValidationError::CatalogCoverageMismatch {
                catalog_label,
                total_count: total_entries.len(),
                baseline_count: baseline_entries.len(),
                release_count: release_entries.len(),
            },
        );
    }

    Ok(())
}

fn validate_house_formula_families(
    entries: &[HouseSystemDescriptor],
) -> Result<usize, CompatibilityProfileValidationError> {
    let mut checked = 0usize;

    for entry in entries {
        checked += 1;
        if entry.formula_family() == HouseFormulaFamily::Unknown {
            return Err(
                CompatibilityProfileValidationError::HouseFormulaFamilyUnknown {
                    label: entry.canonical_name,
                },
            );
        }
    }

    Ok(checked)
}

fn validate_catalog_coverage_ayanamsa(
    catalog_label: &'static str,
    total_entries: &[AyanamsaDescriptor],
    baseline_entries: &[AyanamsaDescriptor],
    release_entries: &[AyanamsaDescriptor],
) -> Result<(), CompatibilityProfileValidationError> {
    if total_entries.len() != baseline_entries.len() + release_entries.len() {
        return Err(
            CompatibilityProfileValidationError::CatalogCoverageMismatch {
                catalog_label,
                total_count: total_entries.len(),
                baseline_count: baseline_entries.len(),
                release_count: release_entries.len(),
            },
        );
    }

    Ok(())
}

fn collect_catalog_names<T>(
    entries: &[T],
    canonical_name: impl Fn(&T) -> &'static str,
) -> BTreeSet<String> {
    let mut names = BTreeSet::new();

    for entry in entries {
        names.insert(canonical_name(entry).trim().to_ascii_lowercase());
    }

    names
}

fn validate_catalog_coverage_labels<T>(
    catalog_label: &'static str,
    total_entries: &[T],
    baseline_entries: &[T],
    release_entries: &[T],
    canonical_name: impl Fn(&T) -> &'static str,
) -> Result<(), CompatibilityProfileValidationError> {
    let total_names = collect_catalog_names(total_entries, &canonical_name);
    let mut partition_names = collect_catalog_names(baseline_entries, &canonical_name);
    partition_names.extend(collect_catalog_names(release_entries, &canonical_name));

    if total_names == partition_names {
        return Ok(());
    }

    let missing_label = total_entries
        .iter()
        .map(&canonical_name)
        .find(|label| !partition_names.contains(&label.trim().to_ascii_lowercase()))
        .unwrap_or_else(|| {
            total_entries
                .first()
                .map_or("", |entry| canonical_name(entry))
        });
    let unexpected_label = baseline_entries
        .iter()
        .chain(release_entries.iter())
        .map(&canonical_name)
        .find(|label| !total_names.contains(&label.trim().to_ascii_lowercase()))
        .unwrap_or_else(|| {
            baseline_entries
                .first()
                .map_or("", |entry| canonical_name(entry))
        });

    Err(
        CompatibilityProfileValidationError::CatalogCoverageLabelMismatch {
            catalog_label,
            missing_label,
            unexpected_label,
        },
    )
}

/// Returns the current compatibility profile.
pub const fn current_compatibility_profile() -> CompatibilityProfile {
    CompatibilityProfile {
        profile_id: CURRENT_COMPATIBILITY_PROFILE_ID,
        summary: CURRENT_COMPATIBILITY_PROFILE_SUMMARY,

        target_house_scope: &[
            "Target house scope: the full Swiss-Ephemeris-class house-system catalog remains the long-term compatibility goal.",
            "Baseline milestone: Placidus, Koch, Porphyry, Regiomontanus, Campanus, Equal, Whole Sign, Alcabitius, Meridian/ARMC/Axial variants, Topocentric, and Morinus are shipped today.",
        ],
        target_ayanamsa_scope: &[
            "Target ayanamsa scope: the full Swiss-Ephemeris-class ayanamsa catalog remains the long-term compatibility goal.",
            "Baseline milestone: Lahiri, Raman, Krishnamurti, Fagan/Bradley, True Chitra, and documented aliases/custom variants are shipped today.",
        ],
        house_systems: built_in_house_systems(),
        baseline_house_systems: baseline_house_systems(),
        release_house_systems: release_house_systems(),
        ayanamsas: built_in_ayanamsas(),
        baseline_ayanamsas: baseline_ayanamsas(),
        release_ayanamsas: release_ayanamsas(),
        release_notes: &[
            "The JPL snapshot backend preserves selected asteroid coverage, including the source-backed custom body asteroid:433-Eros, and the validation report surfaces that subset separately from the planetary comparison corpus.",
            "Release-specific house-system additions now include Equal (MC), Equal (1=Aries), Vehlow Equal, Vehlow house system, Vehlow Equal house system, Sripati, Carter (poli-equatorial), including Carter's poli-equatorial, Horizon/Azimuth, APC, Krusinski-Pisa-Goelzer, Krusinski/Pisa/Goelzer, Albategnius, Pullen SD, Pullen SR, including the exact Pullen SD table of houses, Pullen SD (Neo-Porphyry) table of houses, Pullen SD (Neo-Porphyry), Neo-Porphyry, Pullen SD (Sinusoidal Delta), Pullen SR table of houses, Pullen SR (Sinusoidal Ratio) table of houses, and Pullen SR (Sinusoidal Ratio) spellings, Sunshine, including the Bob Makransky, Makransky Sunshine, and Treindl Sunshine source labels for Sunshine, and Gauquelin sectors, with the Whole Sign (house 1 = Aries) label, the Whole sign houses, 1. house = Aries source spelling, Wang alias, Equal MC / Equal/MC / Equal Midheaven / Equal Midheaven house system aliases, Equal (cusp 1 = Asc) source spelling, Equal (MC) and Equal (1=Aries) source-label appendix entries, including the Equal from MC, Equal (from MC), Equal (from MC) table of houses, and Equal/MC = 10th spellings alongside the Equal (MC) table of houses, Equal (MC) house system, Equal/MC house system, Equal (1=Aries) table of houses, Equal/1=Aries house system spelling, and Equal (1=Aries) house system spellings, plus the exact Equal/1=0 Aries and Equal (cusp 1 = 0° Aries) source-label forms, APC houses / Ascendant Parallel Circle / WvA aliases, Horizon / Horizontal / Azimuthal aliases, the exact Topocentric source labels `Polich-Page \"topocentric\" table of houses`, `Polich/Page`, `Polich Page`, and `T Polich/Page (\"topocentric\")`, the `Horizon/Azimuth house system` and `Horizon/Azimuth table of houses` source labels, the Vehlow-equal source label and the Vehlow house system / Vehlow Equal house system / Vehlow Equal table of houses search forms, the Bob Makransky source label for Sunshine, the Topocentric house system alias, the baseline Placidus and Koch table-of-houses source spellings, the remaining Albategnius / Pullen SD (Sinusoidal Delta) / Pullen SR (Sinusoidal Ratio) / Gauquelin source labels, the Swiss Ephemeris single-letter house-table codes P/K/R/C/O/E/W/N/V/A/H/B/M/S/I/G plus the additional T/U/X/Y interoperability codes resolving to their corresponding built-ins, and the exact Swiss Ephemeris house-table code spellings A equal, D equal / MC, E equal = A, N whole sign houses, 1. house = Aries, S sripati, I sunshine, W equal, whole sign, V equal Vehlow, T topocentric, U Krusinski-Pisa-Goelzer, Zariel, X axial rotation system/ Meridian houses, and Y APC houses, plus the explicit Meridian house system, Horizontal house system, and Azimuth house system spellings.",
            "The compatibility profile now also renders a source-label appendix for the built-in house systems so common Placidus, Koch, Equal, Whole Sign, Topocentric, Vehlow, Meridian, Zariel, ARMC, Sunshine, APC, and Horizon/Azimuth spellings — including the Swiss Ephemeris \"Equal (cusp 1 = Asc)\", \"Whole Sign (house 1 = Aries)\", \"Polich-Page \\\"topocentric\\\" table of houses\", \"T Polich/Page (\\\"topocentric\\\")\", \"Horizon/Azimuth house system\", and \"Horizon/Azimuth table of houses\" forms — are searchable alongside the ayanamsa appendix, and the latest release-specific house-system label batches now also surface the exact Placidus table of houses, Koch table of houses, Koch houses, house system of the birth place, Albategnius, Pullen, Vehlow house system, Vehlow Equal house system, and Gauquelin search forms, plus the exact Equal table of houses, Whole Sign system, and Morinus house system spellings now called out explicitly in the quick-audit text.",
            "The compatibility profile now also surfaces the exact Swiss Ephemeris house-table code spellings A equal, D equal / MC, E equal = A, N whole sign houses, 1. house = Aries, S sripati, I sunshine, W equal, whole sign, V equal Vehlow, T topocentric, U Krusinski-Pisa-Goelzer, Zariel, X axial rotation system/ Meridian houses, and Y APC houses so the code-style interoperability forms remain searchable alongside the canonical house names.",
            "The Equal (MC) and Equal (1=Aries) release-line house entries now also accept the plain Equal (MC) house system, Equal Midheaven table of houses, and Equal (1=Aries) house system spellings, keeping the release-facing alias batch aligned with common source-label wording.",
            "The compatibility profile now also renders source-label appendix entries for Lahiri / Chitrapaksha / Chitra Paksha, True Chitra / Chitra, Krishnamurti Ayanamsha / Krishnamurti Ayanamsa / Krishnamurti ayanamsa / Krishnamurti (Swiss) / Krishnamurti Paddhati / KP ayanamsa, Fagan/Bradley Ayanamsha / Fagan/Bradley / Fagan Bradley / Fagan / Bradley / Fagan-Bradley, Usha Shashi / Usha / Shashi, and the Yukteshwar / Sri Yukteshwar / Shri Yukteshwar transliterations so the baseline sidereal spellings remain searchable alongside the existing Raman appendix entry and the rest of the ayanamsa catalog.",
            "The compatibility profile now also renders source-label appendix entries for P.V.R. Narasimha Rao, Aries houses, and True Mula (Chandra Hari) so the release-facing interoperability labels stay aligned with the documented source spellings for the Pushya-paksha, equal-house, and true-Mula variants.",
            "The compatibility profile now also renders source-label appendix entries for the Galactic equator, IAU 1958, true, Mula, and Fiorenza spellings, including the David Cochrane and Nick Anthony Fiorenza source names for the Cochrane and Fiorenza galactic-reference entries, so the release-facing galactic-reference labels stay aligned with the resolver aliases.",
            "The compatibility profile now also renders a source-label appendix entry for Raman so the B. V. Raman, B.V. Raman, B V Raman, Raman Ayanamsha, and Raman ayanamsa spellings are searchable alongside the other baseline ayanamsa labels.",
            "The True Citra entry now also accepts the True Citra Paksha and True Chitrapaksha spellings, and the release profile summary highlights that alias batch explicitly so the release-facing source-label appendix stays aligned with common interoperability wording.",
            "Release-specific ayanamsa additions now include J2000, J1900, B1950, True Citra, DeLuce, Yukteshwar (including the Sri Yukteshwar / Shri Yukteswar / Shri Yukteshwar transliterations), PVR Pushya-paksha, Sheoran, True Revati, True Mula, Suryasiddhanta (Revati), Suryasiddhanta (Citra), Lahiri (ICRC), Lahiri (1940), Usha Shashi, Suryasiddhanta (499 CE), Aryabhata (499 CE), Sassanian, Hipparchus, Babylonian (Kugler 1), Babylonian (Kugler 2), Babylonian (Kugler 3), Babylonian (Huber), Babylonian (Eta Piscium), Babylonian (Aldebaran) with the Babylonian/Aldebaran = 15 Tau source form, Babylonian (House), Babylonian (Sissy), Babylonian (True Geoc), Babylonian (True Topc), Babylonian (True Obs), Babylonian (House Obs), True Pushya, Udayagiri, Lahiri (VP285), Krishnamurti (VP291) with the Krishnamurti-Senthilathiban source form, Djwhal Khul, JN Bhasin, Suryasiddhanta (Mean Sun), the Surya Siddhanta mean-sun source forms, the Aryabhata mean-sun source forms, Aryabhata (Mean Sun), Babylonian (Britton), Aryabhata (522 CE), True Sheoran, Galactic Center, Galactic Center (Rgilbrand), Galactic Center (Mardyks) with the Skydram / Skydram/Galactic Alignment / Skydram (Mardyks) source spellings, Galactic Center (Mula/Wilhelm), Dhruva Galactic Center (Middle Mula), Galactic Center (Cochrane), Galactic Equator (IAU 1958), Galactic Equator (True), Galactic Equator (Mula), Galactic Equator (Fiorenza), and Valens Moon, with explicit zero-point metadata now published for Hipparchus, Babylonian (Kugler 1), Babylonian (Kugler 2), Babylonian (Kugler 3), Babylonian (Britton), Udayagiri, Lahiri (VP285), Krishnamurti (VP291), True Sheoran, Galactic Center, Galactic Center (Rgilbrand), Galactic Center (Mardyks) with the Skydram / Skydram/Galactic Alignment / Skydram (Mardyks) source spellings, Galactic Center (Mula/Wilhelm) including the Dhruva/Gal.Center/Mula (Wilhelm), Mula Wilhelm, and Wilhelm source spellings, Galactic Center (Cochrane), JN Bhasin, Babylonian (Eta Piscium), Babylonian (Aldebaran), Galactic Equator (Mula), Suryasiddhanta (Mean Sun), the Surya Siddhanta mean-sun source forms, the Aryabhata mean-sun source forms, Aryabhata (Mean Sun), Aryabhata (522 CE), Galactic Equator (True) / True galactic equator / Galactic equator true entries; the Babylonian house-family source labels now resolve as exact aliases too, Galactic Equator (Fiorenza) continues to carry a J2000.0 reference epoch and 25° zero-point offset for the release profile, the Babylonian house-family labels now render in a separate custom-definition section, and the plain Moon alias also resolves to Valens Moon for compatibility with existing label variants, while the Valens Moon source-label appendix now also includes the Valens, Moon, Moon sign, Moon sign ayanamsa, and Valens Moon ayanamsa source spellings, the release profile now surfaces the Aryabhata 499/522 and Surya Siddhanta / Suryasiddhanta 499/499 CE source spellings explicitly, and the release-facing source-label appendix now also calls out the Babylonian 1/2/3 shorthand labels, Babylonian Huber, Aryabhatan Kaliyuga / Aryabhata Kaliyuga spellings, Fagan/Bradley Ayanamsha / Fagan/Bradley spellings, Krishnamurti Ayanamsha / Krishnamurti (Swiss) search forms, the Sunil Sheoran / Vedic Sheoran / Sheoran ayanamsa spellings, and the Usha Shashi search forms explicitly, alongside the new Lahiri / Chitrapaksha and True Chitra / Chitra appendix entries.",
            "Non-standard ayanamsa labels such as True Balarama, Aphoric, and Takra are intentionally treated as custom definitions until a documented source mapping is added.",
            "The compatibility profile is intended to be archived with release validation outputs and release notes.",
        ],
        validation_reference_points: &[
            "The stage-4 validation corpus remains the reference point for tightening house formulas whenever future revisions land.",
        ],
        custom_definition_labels: custom_definition_ayanamsa_labels(),
        known_gaps: &[
            "The newly added historical/reference-frame and formula-variant ayanamsa modes are catalogued and resolvable, and the release line now publishes explicit sidereal metadata for Babylonian (Huber), Babylonian (Britton), Babylonian (Kugler 1), Babylonian (Kugler 2), Babylonian (Kugler 3), Galactic Center (Cochrane), Galactic Center (Mardyks), Galactic Center (Rgilbrand), Galactic Center (Mula/Wilhelm), Galactic Equator (IAU 1958), Galactic Equator (Fiorenza), Suryasiddhanta (Revati), Suryasiddhanta (Citra), True Pushya, True Sheoran, Udayagiri, Lahiri (VP285), Krishnamurti (VP291), Djwhal Khul, Valens Moon, and the remaining historical/reference-frame catalog entries; additional metadata/source mapping work remains scheduled for any unreconciled future breadth batches or custom definitions.",
            "Labels outside the published compatibility profile, including ad hoc names such as True Balarama, Aphoric, and Takra, should be modeled as custom ayanamsa definitions rather than assumed to be built-ins.",
        ],
    }
}

/// Returns the compatibility-profile house formula family summary for report surfaces.
pub fn house_formula_families_summary_for_report() -> String {
    current_compatibility_profile().house_formula_families_summary_line()
}

/// Returns the compatibility-profile house formula family summary after validating the profile.
pub fn validated_house_formula_families_summary_for_report(
) -> Result<String, CompatibilityProfileValidationError> {
    current_compatibility_profile().validated_house_formula_families_summary_line()
}

/// Returns the compatibility-profile latitude-sensitive house-system summary for report surfaces.
pub fn latitude_sensitive_house_systems_summary_for_report() -> String {
    current_compatibility_profile().latitude_sensitive_house_systems_summary_line()
}

/// Returns the compatibility-profile latitude-sensitive house-system summary after validating the profile.
pub fn validated_latitude_sensitive_house_systems_summary_for_report(
) -> Result<String, CompatibilityProfileValidationError> {
    current_compatibility_profile().validated_latitude_sensitive_house_systems_summary_line()
}

/// Returns the compatibility-profile latitude-sensitive house-constraint summary for report surfaces.
pub fn latitude_sensitive_house_constraints_summary_for_report() -> String {
    current_compatibility_profile().latitude_sensitive_house_constraints_summary_line()
}

/// Returns the compatibility-profile latitude-sensitive house-constraint summary after validating the profile.
pub fn validated_latitude_sensitive_house_constraints_summary_for_report(
) -> Result<String, CompatibilityProfileValidationError> {
    current_compatibility_profile().validated_latitude_sensitive_house_constraints_summary_line()
}

/// Returns the compatibility-profile custom-definition ayanamsa summary for report surfaces.
pub fn custom_definition_ayanamsa_labels_summary_for_report() -> String {
    current_compatibility_profile().custom_definition_ayanamsa_labels_summary_line()
}

/// Returns the compatibility-profile custom-definition ayanamsa summary after validating the profile.
pub fn validated_custom_definition_ayanamsa_labels_summary_for_report(
) -> Result<String, CompatibilityProfileValidationError> {
    current_compatibility_profile().validated_custom_definition_ayanamsa_labels_summary_line()
}

/// Returns the compatibility-profile catalog inventory summary for report surfaces.
pub fn catalog_inventory_summary_for_report() -> String {
    current_compatibility_profile().catalog_inventory_summary_line()
}

/// Returns the compatibility-profile catalog-posture summary for report surfaces.
pub fn catalog_posture_summary_for_report() -> String {
    current_compatibility_profile().catalog_posture_summary_line()
}

/// Returns the compatibility-profile catalog-posture summary after validating the profile.
pub fn validated_catalog_posture_summary_for_report(
) -> Result<String, CompatibilityProfileValidationError> {
    current_compatibility_profile().validated_catalog_posture_summary_line()
}

/// Returns the compatibility-profile known-gaps summary for report surfaces.
pub fn known_gaps_summary_for_report() -> String {
    current_compatibility_profile().known_gaps_summary_line()
}

/// Returns the compatibility-profile known-gaps summary after validating the profile.
pub fn validated_known_gaps_summary_for_report(
) -> Result<String, CompatibilityProfileValidationError> {
    current_compatibility_profile().validated_known_gaps_summary_line()
}

/// Returns the compatibility-profile catalog inventory summary after validating the profile.
pub fn validated_catalog_inventory_summary_for_report(
) -> Result<String, CompatibilityProfileValidationError> {
    current_compatibility_profile().validated_catalog_inventory_summary_line()
}

/// Returns the compatibility-profile latitude-sensitive house failure modes summary for report surfaces.
pub fn latitude_sensitive_house_failure_modes_summary_for_report() -> String {
    current_compatibility_profile().latitude_sensitive_house_failure_modes_summary_line()
}

/// Returns the compatibility-profile latitude-sensitive house failure modes summary after validating the profile.
pub fn validated_latitude_sensitive_house_failure_modes_summary_for_report(
) -> Result<String, CompatibilityProfileValidationError> {
    current_compatibility_profile().validate()?;
    Ok(current_compatibility_profile().latitude_sensitive_house_failure_modes_summary_line())
}

/// Returns the compatibility-caveats summary for report surfaces.
pub fn compatibility_caveats_summary_for_report(
    profile: &CompatibilityProfile,
    release_profiles: &crate::release_profiles::ReleaseProfileIdentifiers,
) -> String {
    let mut text = String::new();

    text.push_str("Compatibility caveats summary\n");
    text.push_str("Profile: ");
    text.push_str(release_profiles.compatibility_profile_id);
    text.push('\n');
    text.push_str("Compatibility caveats: ");
    text.push_str(&profile.known_gaps.len().to_string());
    text.push('\n');
    text.push_str("House formula families: ");
    text.push_str(&profile.house_formula_families_summary_line());
    text.push('\n');
    text.push_str("Latitude-sensitive house systems: ");
    text.push_str(&profile.latitude_sensitive_house_systems_summary_line());
    text.push('\n');
    text.push_str("Latitude-sensitive house constraints: ");
    text.push_str(&profile.latitude_sensitive_house_constraints_summary_line());
    text.push('\n');
    text.push_str("Latitude-sensitive house failure modes: ");
    text.push_str(&profile.latitude_sensitive_house_failure_modes_summary_line());
    text.push('\n');
    text.push_str("Descriptor-only ayanamsa labels: ");
    text.push_str(&profile.custom_definition_ayanamsa_labels_summary_line());
    text.push('\n');
    for gap in profile.known_gaps {
        text.push_str("- ");
        text.push_str(gap);
        text.push('\n');
    }

    text
}

/// Returns the house-code alias inventory for report surfaces.
pub fn house_code_aliases_summary_for_report() -> String {
    current_compatibility_profile().house_code_aliases_summary_line()
}

/// Returns the house-code alias inventory after validating the current profile.
pub fn validated_house_code_aliases_summary_for_report(
) -> Result<String, CompatibilityProfileValidationError> {
    current_compatibility_profile().validated_house_code_aliases_summary_line()
}

/// Returns the release-specific house-system canonical names summary for report surfaces.
pub fn release_house_system_canonical_names_summary_for_report() -> String {
    current_compatibility_profile().release_house_system_canonical_names_summary_line()
}

/// Returns the release-specific house-system canonical names summary after validating the profile.
pub fn validated_release_house_system_canonical_names_summary_for_report(
) -> Result<String, CompatibilityProfileValidationError> {
    current_compatibility_profile().validated_release_house_system_canonical_names_summary_line()
}

/// Returns the release-specific ayanamsa canonical names summary for report surfaces.
pub fn release_ayanamsa_canonical_names_summary_for_report() -> String {
    current_compatibility_profile().release_ayanamsa_canonical_names_summary_line()
}

/// Returns the release-specific ayanamsa canonical names summary after validating the profile.
pub fn validated_release_ayanamsa_canonical_names_summary_for_report(
) -> Result<String, CompatibilityProfileValidationError> {
    current_compatibility_profile().validated_release_ayanamsa_canonical_names_summary_line()
}

/// Returns the target house-system scope summary for report surfaces.
pub fn target_house_scope_summary_for_report() -> String {
    current_compatibility_profile().target_house_scope_summary_line()
}

/// Returns the target house-system scope summary after validating the profile.
pub fn validated_target_house_scope_summary_for_report(
) -> Result<String, CompatibilityProfileValidationError> {
    current_compatibility_profile().validated_target_house_scope_summary_line()
}

/// Returns the target ayanamsa scope summary for report surfaces.
pub fn target_ayanamsa_scope_summary_for_report() -> String {
    current_compatibility_profile().target_ayanamsa_scope_summary_line()
}

/// Returns the target ayanamsa scope summary after validating the profile.
pub fn validated_target_ayanamsa_scope_summary_for_report(
) -> Result<String, CompatibilityProfileValidationError> {
    current_compatibility_profile().validated_target_ayanamsa_scope_summary_line()
}

fn write_scope_section(
    f: &mut fmt::Formatter<'_>,
    title: &str,
    lines: &[&'static str],
) -> fmt::Result {
    writeln!(f, "{}", title)?;
    for line in lines {
        writeln!(f, "- {}", line)?;
    }
    Ok(())
}

trait AliasProfileEntry {
    fn canonical_name(&self) -> &'static str;
    fn aliases(&self) -> &'static [&'static str];
}

impl AliasProfileEntry for HouseSystemDescriptor {
    fn canonical_name(&self) -> &'static str {
        self.canonical_name
    }

    fn aliases(&self) -> &'static [&'static str] {
        self.aliases
    }
}

impl AliasProfileEntry for AyanamsaDescriptor {
    fn canonical_name(&self) -> &'static str {
        self.canonical_name
    }

    fn aliases(&self) -> &'static [&'static str] {
        self.aliases
    }
}

fn write_alias_section<T: AliasProfileEntry>(
    f: &mut fmt::Formatter<'_>,
    title: &str,
    entries: &[T],
) -> fmt::Result {
    let mut has_aliases = false;
    for entry in entries {
        if !entry.aliases().is_empty() {
            has_aliases = true;
            break;
        }
    }

    if !has_aliases {
        return Ok(());
    }

    writeln!(f, "{}", title)?;
    for entry in entries {
        if entry.aliases().is_empty() {
            continue;
        }

        writeln!(
            f,
            "- {} -> {}",
            entry.aliases().join(", "),
            entry.canonical_name()
        )?;
    }
    Ok(())
}

fn house_source_label_aliases(canonical_name: &str) -> &'static [&'static str] {
    match canonical_name {
        "Placidus" => &["Placidus house system", "Placidus table of houses"],
        "Koch" => &[
            "Koch houses",
            "Koch house system",
            "house system of the birth place",
            "Koch table of houses",
            "W. Koch",
            "W Koch",
        ],
        "Porphyry" => &[
            "Equal Quadrant",
            "Porphyry house system",
            "Porphyry table of houses",
        ],
        "Regiomontanus" => &[
            "Regiomontanus houses",
            "Regiomontanus house system",
            "Regiomontanus table of houses",
        ],
        "Campanus" => &[
            "Campanus houses",
            "Campanus house system",
            "Campanus table of houses",
        ],
        "Equal" => &[
            "A equal",
            "E equal = A",
            "Equal houses",
            "Equal house system",
            "Equal House",
            "Equal table of houses",
            "Wang",
            "Equal (cusp 1 = Asc)",
        ],
        "Whole Sign" => &[
            "W equal, whole sign",
            "Whole Sign houses",
            "Whole Sign table of houses",
            "Whole-sign",
            "Whole Sign system",
            "Whole Sign house system",
        ],
        "Alcabitius" => &[
            "Alcabitius houses",
            "Alcabitius house system",
            "Alcabitius table of houses",
        ],
        "Meridian" => &[
            "X",
            "Meridian houses",
            "Meridian table of houses",
            "Meridian house system",
            "ARMC",
            "Axial Rotation",
            "Axial rotation system",
            "Zariel",
            "X axial rotation system/ Meridian houses",
        ],
        "Axial" => &["Axial variants", "A"],
        "Topocentric" => &[
            "T",
            "Polich-Page",
            "Polich/Page",
            "Polich Page",
            "Polich-Page \"topocentric\" table of houses",
            "T Polich/Page (\"topocentric\")",
            "T topocentric",
            "Topocentric house system",
            "Topocentric table of houses",
        ],
        "Morinus" => &["M", "Morinus houses", "Morinus house system"],
        "Equal (MC)" => &[
            "D equal / MC",
            "Equal from MC",
            "Equal (from MC)",
            "Equal (from MC) table of houses",
            "Equal (MC) table of houses",
            "Equal/MC table of houses",
            "Equal (MC) house system",
            "Equal/MC house system",
            "Equal MC",
            "Equal/MC",
            "Equal Midheaven",
            "Equal Midheaven house system",
            "Equal Midheaven table of houses",
            "Equal/MC = 10th",
        ],
        "Equal (1=Aries)" => &[
            "N",
            "N whole sign houses, 1. house = Aries",
            "Equal/1=Aries",
            "Equal Aries",
            "Aries houses",
            "Whole Sign (house 1 = Aries)",
            "Whole Sign (house 1 = Aries) table of houses",
            "Equal (1=Aries) table of houses",
            "Equal/1=Aries table of houses",
            "Equal (1=Aries) house system",
            "Equal/1=Aries house system",
            "Whole sign houses, 1. house = Aries",
            "Equal/1=0 Aries",
            "Equal (cusp 1 = 0° Aries)",
        ],
        "Vehlow Equal" => &[
            "V equal Vehlow",
            "Vehlow-equal table of houses",
            "Vehlow Equal table of houses",
            "Vehlow-equal",
            "Vehlow",
        ],
        "Sripati" => &[
            "S",
            "S sripati",
            "Śrīpati",
            "Sripati house system",
            "Sripati table of houses",
        ],
        "Carter (poli-equatorial)" => &[
            "Carter",
            "Carter's poli-equatorial",
            "Carter's poli-equatorial table of houses",
            "Poli-Equatorial",
            "Poli-equatorial",
        ],
        "Horizon/Azimuth" => &[
            "Horizon/Azimuth",
            "Horizon",
            "Azimuth",
            "Horizontal",
            "Azimuthal",
            "Horizon table of houses",
            "Horizontal table of houses",
            "Azimuthal table of houses",
            "Horizon/Azimuth table of houses",
            "Horizon house system",
            "Horizon/Azimuth house system",
            "Horizontal house system",
            "Azimuth house system",
            "Azimuthal house system",
            "horizon/azimut",
            "horizon/azimuth",
        ],
        "APC" => &[
            "Y",
            "APC",
            "Ram school",
            "Ram's school",
            "Ramschool",
            "WvA",
            "Y APC houses",
            "APC houses",
            "APC, also known as “Ram school”, table of houses",
            "APC house system",
            "Ascendant Parallel Circle",
        ],
        "Krusinski-Pisa-Goelzer" => &[
            "U",
            "Krusinski",
            "Krusinski-Pisa",
            "Krusinski Pisa",
            "Krusinski/Pisa/Goelzer",
            "Krusinski-Pisa-Goelzer table of houses",
            "U krusinski-pisa-goelzer",
            "Krusinski/Pisa/Goelzer house system",
            "Pisa-Goelzer",
        ],
        "Albategnius" => &[
            "Albategnius table of houses",
            "Savard-A",
            "Savard A",
            "Savard's Albategnius",
        ],
        "Pullen SD" => &[
            "Pullen SD table of houses",
            "Pullen SD (Neo-Porphyry) table of houses",
            "Pullen SD (Neo-Porphyry)",
            "Neo-Porphyry",
            "Pullen (Sinusoidal Delta)",
            "Pullen SD (Sinusoidal Delta)",
            "Pullen SD (Sinusoidal Delta) table of houses",
            "Pullen sinusoidal delta",
        ],
        "Pullen SR" => &[
            "Pullen SR table of houses",
            "Pullen SR (Sinusoidal Ratio) table of houses",
            "Pullen SR (Sinusoidal Ratio)",
            "Pullen (Sinusoidal Ratio)",
            "Pullen sinusoidal ratio",
        ],
        "Sunshine" => &[
            "I",
            "I sunshine",
            "Sunshine",
            "Sunshine houses",
            "Sunshine house system",
            "Sunshine table of houses",
            "Sunshine table of houses, by Bob Makransky",
            "Makransky Sunshine",
            "Bob Makransky",
            "Treindl Sunshine",
        ],
        "Gauquelin sectors" => &[
            "G",
            "Gauquelin",
            "Gauquelin sector",
            "Gauquelin sectors",
            "Gauquelin table of sectors",
        ],
        _ => &[],
    }
}

fn ayanamsa_source_label_aliases(canonical_name: &str) -> &'static [&'static str] {
    match canonical_name {
        "PVR Pushya-paksha" => &[
            "True Pushya (PVRN Rao)",
            "Pushya-paksha",
            "Pushya Paksha",
            "PVR Pushya Paksha",
            "PVR",
            "P.V.R. Narasimha Rao",
        ],
        "Raman" => &[
            "B. V. Raman",
            "B.V. Raman",
            "B V Raman",
            "Raman Ayanamsha",
            "Raman ayanamsa",
        ],
        "Krishnamurti" => &[
            "Krishnamurti Ayanamsha",
            "Krishnamurti Ayanamsa",
            "Krishnamurti ayanamsa",
            "Krishnamurti (Swiss)",
            "Krishnamurti Paddhati",
            "KP ayanamsa",
        ],
        "Fagan/Bradley" => &[
            "Fagan/Bradley Ayanamsha",
            "Fagan/Bradley",
            "Fagan Bradley",
            "Fagan / Bradley",
            "Fagan-Bradley",
        ],
        "Lahiri" => &[
            "Chitra Paksha",
            "Chitrapaksha",
            "Chitra-paksha",
            "Lahiri Ayanamsha",
            "Lahiri ayanamsa",
        ],
        "True Pushya" => &["True Pushya ayanamsa", "Pushya"],
        "True Chitra" => &["Chitra", "True Chitra ayanamsa"],
        "True Citra" => &[
            "True Citra ayanamsa",
            "True Citra Paksha",
            "True Chitra Paksha",
            "True Chitrapaksha",
        ],
        "True Revati" => &["True Revati ayanamsa"],
        "True Mula" => &[
            "True Mula (Chandra Hari)",
            "True Mula ayanamsa",
            "Chandra Hari",
        ],
        "Udayagiri" => &["Udayagiri ayanamsa"],
        "Usha Shashi" => &[
            "Usha Shashi",
            "Ushashashi",
            "Usha-Shashi",
            "Usha/Shashi",
            "Usha / Shashi",
            "Usha Shashi ayanamsa",
            "Revati",
        ],
        "Lahiri (ICRC)" => &["ICRC Lahiri", "Lahiri ICRC"],
        "Lahiri (1940)" => &["Lahiri original", "Panchanga Darpan Lahiri"],
        "DeLuce" => &["De Luce", "DeLuce ayanamsa"],
        "Yukteshwar" => &[
            "Yukteswar",
            "Sri Yukteswar",
            "Sri Yukteshwar",
            "Shri Yukteswar",
            "Shri Yukteshwar",
            "Yukteshwar ayanamsa",
        ],
        "J2000" => &["J2000.0"],
        "J1900" => &["J1900.0"],
        "B1950" => &["B1950.0"],
        "Sheoran" => &[
            "Sunil Sheoran",
            "Vedic Sheoran",
            "Vedic / Sheoran",
            "Sheoran ayanamsa",
            "Sheoran true",
            "True Sheoran ayanamsa",
            "\"Vedic\"/Sheoran",
        ],
        "Djwhal Khul" => &["Djwhal", "Djwhal Khul ayanamsa"],
        "JN Bhasin" => &["J. N. Bhasin", "J.N. Bhasin", "Bhasin"],
        "Suryasiddhanta (Mean Sun)" => &[
            "Suryasiddhanta, mean Sun",
            "Surya Siddhanta, mean Sun",
            "Suryasiddhanta mean sun",
            "Surya Siddhanta mean sun",
            "Suryasiddhanta MSUN",
            "Surya Siddhanta MSUN",
        ],
        "Suryasiddhanta (499 CE)" => &[
            "Suryasiddhanta",
            "Surya Siddhanta",
            "Suryasiddhanta 499",
            "Surya Siddhanta 499",
            "Suryasiddhanta 499 CE",
            "Surya Siddhanta 499 CE",
        ],
        "Aryabhata (499 CE)" => &[
            "Aryabhata",
            "Aryabhata 499",
            "Aryabhata 499 CE",
            "Aryabhatan Kaliyuga",
            "Aryabhata Kaliyuga",
        ],
        "Aryabhata (Mean Sun)" => &[
            "Aryabhata, mean Sun",
            "Aryabhata mean sun",
            "Aryabhata MSUN",
        ],
        "Aryabhata (522 CE)" => &["Aryabhata 522", "Aryabhata 522 CE"],
        "Babylonian (Kugler 1)" => &["Babylonian/Kugler 1", "Babylonian Kugler 1", "Babylonian 1"],
        "Babylonian (Kugler 2)" => &["Babylonian/Kugler 2", "Babylonian Kugler 2", "Babylonian 2"],
        "Babylonian (Kugler 3)" => &["Babylonian/Kugler 3", "Babylonian Kugler 3", "Babylonian 3"],
        "Babylonian (Britton)" => &["Babylonian/Britton", "Babylonian Britton"],
        "Babylonian (Huber)" => &["Babylonian/Huber", "Babylonian Huber"],
        "Babylonian (Eta Piscium)" => &["Babylonian/Eta Piscium"],
        "Babylonian (Aldebaran)" => &["Babylonian/Aldebaran = 15 Tau"],
        "Lahiri (VP285)" => &["Lahiri VP285", "VP285"],
        "Krishnamurti (VP291)" => &[
            "KP VP291",
            "Krishnamurti VP291",
            "Krishnamurti-Senthilathiban",
            "VP291",
        ],
        "True Sheoran" => &["Sheoran true", "True Sheoran ayanamsa"],
        "Galactic Center (Rgilbrand)" => &[
            "Galactic Center (Gil Brand)",
            "Gil Brand",
            "Rgilbrand",
            "Galactic center Rgilbrand",
        ],
        "Galactic Center (Mardyks)" => &[
            "Skydram",
            "Skydram/Galactic Alignment",
            "Skydram (Mardyks)",
            "Mardyks",
            "Galactic center Mardyks",
        ],
        "Galactic Center (Mula/Wilhelm)" => &[
            "Dhruva/Gal.Center/Mula (Wilhelm)",
            "Mula Wilhelm",
            "Wilhelm",
            "Galactic center Mula/Wilhelm",
        ],
        "Galactic Center (Cochrane)" => &[
            "Cochrane (Gal.Center = 0 Cap)",
            "Gal. Center = 0 Cap",
            "Cochrane",
            "Galactic center Cochrane",
            "David Cochrane",
        ],
        "Galactic Center" => &["Galact. Center = 0 Sag", "Gal. Center = 0 Sag"],
        "Sassanian" => &["Zij al-Shah", "Sasanian"],
        "Galactic Equator (IAU 1958)" => &[
            "Galactic Equator (IAU1958)",
            "IAU 1958",
            "Galactic equator IAU 1958",
        ],
        "Galactic Equator (True)" => &["True galactic equator", "Galactic equator true"],
        "Galactic Equator (Mula)" => &[
            "Galactic Equator mid-Mula",
            "Mula galactic equator",
            "Galactic equator Mula",
        ],
        "Galactic Equator" => &["Galactic equator", "Gal. Eq."],
        "Galactic Equator (Fiorenza)" => &[
            "Fiorenza",
            "Galactic equator Fiorenza",
            "Nick Anthony Fiorenza",
        ],
        "Valens Moon" => &[
            "Vettius Valens",
            "Valens",
            "Moon",
            "Moon sign",
            "Moon sign ayanamsa",
            "Valens Moon ayanamsa",
        ],
        _ => &[],
    }
}

fn write_source_label_section<T, F>(
    f: &mut fmt::Formatter<'_>,
    title: &str,
    entries: &[T],
    source_label_aliases: F,
) -> fmt::Result
where
    T: AliasProfileEntry,
    F: Fn(&str) -> &'static [&'static str],
{
    let mut has_source_labels = false;
    for entry in entries {
        if !source_label_aliases(entry.canonical_name()).is_empty() {
            has_source_labels = true;
            break;
        }
    }

    if !has_source_labels {
        return Ok(());
    }

    writeln!(f, "{}", title)?;
    for entry in entries {
        let source_labels = source_label_aliases(entry.canonical_name());
        if source_labels.is_empty() {
            continue;
        }

        writeln!(
            f,
            "- {} -> {}",
            source_labels.join(", "),
            entry.canonical_name()
        )?;
    }
    Ok(())
}

fn write_custom_definition_section(
    f: &mut fmt::Formatter<'_>,
    labels: &[&'static str],
    descriptors: &[AyanamsaDescriptor],
) -> fmt::Result {
    writeln!(f, "Custom-definition labels:")?;
    for label in labels {
        if let Some(entry) = descriptors
            .iter()
            .find(|entry| entry.canonical_name.eq_ignore_ascii_case(label))
        {
            writeln!(f, "- {}", entry)?;
        } else {
            writeln!(f, "- {}", label)?;
        }
    }
    Ok(())
}

impl fmt::Display for CompatibilityProfile {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        if let Err(error) = self.validate() {
            return write!(f, "Compatibility profile unavailable ({error})");
        }

        writeln!(f, "Compatibility profile: {}", self.profile_id)?;
        writeln!(f, "{}", self.summary)?;
        writeln!(f)?;
        write_scope_section(f, "Target compatibility catalog:", self.target_house_scope)?;
        write_scope_section(f, "Target ayanamsa catalog:", self.target_ayanamsa_scope)?;
        writeln!(f)?;
        writeln!(f, "Baseline compatibility milestone:")?;
        writeln!(f, "House systems:")?;
        for entry in self.baseline_house_systems {
            writeln!(f, "- {}", entry)?;
        }
        writeln!(f, "Ayanamsas:")?;
        for entry in self.baseline_ayanamsas {
            writeln!(f, "- {}", entry)?;
        }
        if !self.release_house_systems.is_empty() || !self.release_ayanamsas.is_empty() {
            writeln!(f)?;
            writeln!(f, "Release-specific coverage beyond baseline:")?;
            if !self.release_house_systems.is_empty() {
                writeln!(f, "House systems:")?;
                for entry in self.release_house_systems {
                    writeln!(f, "- {}", entry)?;
                }
            }
            if !self.release_ayanamsas.is_empty() {
                writeln!(f, "Ayanamsas:")?;
                for entry in self.release_ayanamsas {
                    writeln!(f, "- {}", entry)?;
                }
            }
        }
        if !self.release_notes.is_empty() {
            writeln!(f)?;
            write_scope_section(
                f,
                "Release-specific notes beyond baseline:",
                self.release_notes,
            )?;
        }
        writeln!(f)?;
        let coverage = metadata_coverage();
        writeln!(f, "Coverage summary:")?;
        writeln!(
            f,
            "- house systems: {} total ({} baseline, {} release-specific)",
            self.house_systems.len(),
            self.baseline_house_systems.len(),
            self.release_house_systems.len()
        )?;
        writeln!(
            f,
            "- ayanamsas: {} total ({} baseline, {} release-specific)",
            self.ayanamsas.len(),
            self.baseline_ayanamsas.len(),
            self.release_ayanamsas.len()
        )?;
        writeln!(f, "- {}", coverage.summary_line())?;
        writeln!(
            f,
            "- house formula families: {}",
            self.house_formula_families_summary_line()
        )?;
        writeln!(
            f,
            "- latitude-sensitive house systems: {}",
            self.latitude_sensitive_house_systems_summary_line()
        )?;
        writeln!(
            f,
            "- latitude-sensitive house constraints: {}",
            self.latitude_sensitive_house_constraints_summary_line()
        )?;
        if !coverage.custom_definition_only.is_empty() {
            writeln!(
                f,
                "- custom-definition ayanamsas: {} (tracked without sidereal metadata)",
                coverage.custom_definition_only.join(", ")
            )?;
        }
        if coverage.is_complete() {
            writeln!(f, "- no unexpected sidereal-metadata gaps remain.")?;
        } else {
            writeln!(
                f,
                "- missing metadata: {}",
                coverage.without_sidereal_metadata.join(", ")
            )?;
        }
        writeln!(f, "{}", self.catalog_inventory_summary_line())?;
        if !self.custom_definition_labels.is_empty() {
            writeln!(
                f,
                "- custom-definition labels: {}",
                self.custom_definition_labels.len()
            )?;
            writeln!(f)?;
            write_custom_definition_section(f, self.custom_definition_labels, self.ayanamsas)?;
        }
        writeln!(f)?;
        write_alias_section(
            f,
            "Alias mappings for built-in house systems:",
            self.house_systems,
        )?;
        writeln!(f)?;
        write_source_label_section(
            f,
            "Source-label aliases for built-in house systems:",
            self.house_systems,
            house_source_label_aliases,
        )?;
        writeln!(f)?;
        write_source_label_section(
            f,
            "Source-label aliases for built-in ayanamsas:",
            self.ayanamsas,
            ayanamsa_source_label_aliases,
        )?;
        writeln!(f)?;
        write_alias_section(f, "Alias mappings for built-in ayanamsas:", self.ayanamsas)?;
        writeln!(f)?;
        write_scope_section(
            f,
            "Validation reference points:",
            self.validation_reference_points,
        )?;
        writeln!(f)?;
        write_scope_section(f, "Compatibility caveats:", self.known_gaps)?;
        writeln!(
            f,
            "Unsupported modes: {}",
            pleiades_backend::unsupported_modes_summary_for_report()
        )?;
        Ok(())
    }
}

#[cfg(test)]
mod tests {
    use super::*;
    use pleiades_ayanamsa::AyanamsaDescriptor;
    use pleiades_houses::{HouseCatalogValidationError, HouseSystemDescriptor};
    use pleiades_types::{Ayanamsa, HouseSystem};

    #[test]
    fn profile_includes_baseline_and_release_catalogs() {
        let profile = current_compatibility_profile();
        assert!(profile
            .house_systems
            .iter()
            .any(|entry| entry.canonical_name == "Equal (MC)"));
        assert!(profile
            .baseline_house_systems
            .iter()
            .any(|entry| entry.canonical_name == "Placidus"));
        assert!(profile
            .release_house_systems
            .iter()
            .any(|entry| entry.canonical_name == "Sripati"));
        assert!(profile
            .release_house_systems
            .iter()
            .any(|entry| entry.canonical_name == "Carter (poli-equatorial)"));
        assert!(profile
            .release_house_systems
            .iter()
            .any(|entry| entry.canonical_name == "Horizon/Azimuth"));
        assert!(profile
            .release_house_systems
            .iter()
            .any(|entry| entry.canonical_name == "APC"));
        assert!(profile
            .release_house_systems
            .iter()
            .any(|entry| entry.canonical_name == "Krusinski-Pisa-Goelzer"));
        assert!(profile
            .release_house_systems
            .iter()
            .any(|entry| entry.canonical_name == "Albategnius"));
        assert!(profile
            .release_house_systems
            .iter()
            .any(|entry| entry.canonical_name == "Pullen SD"));
        assert!(profile
            .release_house_systems
            .iter()
            .any(|entry| entry.canonical_name == "Pullen SR"));
        assert!(profile
            .release_house_systems
            .iter()
            .any(|entry| entry.canonical_name == "Sunshine"));
        assert!(profile
            .release_house_systems
            .iter()
            .any(|entry| entry.canonical_name == "Gauquelin sectors"));
        assert!(profile
            .release_house_systems
            .iter()
            .any(|entry| entry.canonical_name == "Equal (1=Aries)"));
        assert!(profile
            .baseline_ayanamsas
            .iter()
            .any(|entry| entry.canonical_name == "Lahiri"));
        assert!(profile
            .release_ayanamsas
            .iter()
            .any(|entry| entry.canonical_name == "J2000"));
        assert!(profile
            .release_ayanamsas
            .iter()
            .any(|entry| entry.canonical_name == "DeLuce"));
        assert!(profile
            .release_ayanamsas
            .iter()
            .any(|entry| entry.canonical_name == "Yukteshwar"));
        assert!(profile
            .release_ayanamsas
            .iter()
            .any(|entry| entry.canonical_name == "PVR Pushya-paksha"));
        assert!(profile
            .release_ayanamsas
            .iter()
            .any(|entry| entry.canonical_name == "Sheoran"));
        assert!(profile
            .release_ayanamsas
            .iter()
            .any(|entry| entry.canonical_name == "True Revati"));
        assert!(profile
            .release_ayanamsas
            .iter()
            .any(|entry| entry.canonical_name == "True Mula"));
        assert!(profile
            .release_ayanamsas
            .iter()
            .any(|entry| entry.canonical_name == "Suryasiddhanta (Revati)"));
        assert!(profile
            .release_ayanamsas
            .iter()
            .any(|entry| entry.canonical_name == "Suryasiddhanta (Citra)"));
        assert!(profile
            .release_ayanamsas
            .iter()
            .any(|entry| entry.canonical_name == "Lahiri (ICRC)"));
        assert!(profile
            .release_ayanamsas
            .iter()
            .any(|entry| entry.canonical_name == "Sassanian"));
        assert!(profile
            .release_ayanamsas
            .iter()
            .any(|entry| entry.canonical_name == "Hipparchus"));
        assert!(profile
            .release_ayanamsas
            .iter()
            .any(|entry| entry.canonical_name == "Babylonian (Kugler 1)"));
        assert!(profile
            .release_ayanamsas
            .iter()
            .any(|entry| entry.canonical_name == "Babylonian (Kugler 2)"));
        assert!(profile
            .release_ayanamsas
            .iter()
            .any(|entry| entry.canonical_name == "Babylonian (Aldebaran)"));
        assert!(profile
            .release_ayanamsas
            .iter()
            .any(|entry| entry.canonical_name == "Babylonian (House)"));
        assert!(profile
            .release_ayanamsas
            .iter()
            .any(|entry| entry.canonical_name == "Babylonian (Sissy)"));
        assert!(profile
            .release_ayanamsas
            .iter()
            .any(|entry| entry.canonical_name == "Babylonian (True Geoc)"));
        assert!(profile
            .release_ayanamsas
            .iter()
            .any(|entry| entry.canonical_name == "Babylonian (True Topc)"));
        assert!(profile
            .release_ayanamsas
            .iter()
            .any(|entry| entry.canonical_name == "Babylonian (True Obs)"));
        assert!(profile
            .release_ayanamsas
            .iter()
            .any(|entry| entry.canonical_name == "Babylonian (House Obs)"));
        assert!(profile
            .release_ayanamsas
            .iter()
            .any(|entry| entry.canonical_name == "True Pushya"));
        assert!(profile
            .release_ayanamsas
            .iter()
            .any(|entry| entry.canonical_name == "Djwhal Khul"));
        assert!(profile
            .release_ayanamsas
            .iter()
            .any(|entry| entry.canonical_name == "JN Bhasin"));
        assert!(profile
            .release_ayanamsas
            .iter()
            .any(|entry| entry.canonical_name == "Suryasiddhanta (Mean Sun)"));
        assert!(profile
            .release_ayanamsas
            .iter()
            .any(|entry| entry.canonical_name == "Aryabhata (Mean Sun)"));
        assert!(profile
            .release_ayanamsas
            .iter()
            .any(|entry| entry.canonical_name == "Babylonian (Britton)"));
        assert!(profile
            .release_ayanamsas
            .iter()
            .any(|entry| entry.canonical_name == "Aryabhata (522 CE)"));
        assert!(profile
            .release_ayanamsas
            .iter()
            .any(|entry| entry.canonical_name == "Lahiri (VP285)"));
        assert!(profile
            .release_ayanamsas
            .iter()
            .any(|entry| entry.canonical_name == "Krishnamurti (VP291)"));
        assert!(profile
            .release_ayanamsas
            .iter()
            .any(|entry| entry.canonical_name == "True Sheoran"));
        assert!(profile
            .release_ayanamsas
            .iter()
            .any(|entry| entry.canonical_name == "Galactic Center"));
        assert!(profile
            .release_ayanamsas
            .iter()
            .any(|entry| entry.canonical_name == "Galactic Center (Rgilbrand)"));
        assert!(profile
            .release_ayanamsas
            .iter()
            .any(|entry| entry.canonical_name == "Galactic Center (Mardyks)"));
        assert!(profile
            .release_ayanamsas
            .iter()
            .any(|entry| entry.canonical_name == "Galactic Center (Mula/Wilhelm)"));
        assert!(profile
            .release_ayanamsas
            .iter()
            .any(|entry| entry.canonical_name == "Dhruva Galactic Center (Middle Mula)"));
        assert!(profile
            .release_ayanamsas
            .iter()
            .any(|entry| entry.canonical_name == "Galactic Center (Cochrane)"));
        assert!(profile
            .release_ayanamsas
            .iter()
            .any(|entry| entry.canonical_name == "Galactic Equator"));
        assert!(profile
            .release_ayanamsas
            .iter()
            .any(|entry| entry.canonical_name == "Galactic Equator (IAU 1958)"));
        assert!(profile
            .release_ayanamsas
            .iter()
            .any(|entry| entry.canonical_name == "Galactic Equator (True)"));
        assert!(profile
            .release_ayanamsas
            .iter()
            .any(|entry| entry.canonical_name == "Galactic Equator (Mula)"));
        assert!(profile
            .release_ayanamsas
            .iter()
            .any(|entry| entry.canonical_name == "Galactic Equator (Fiorenza)"));
        assert!(profile
            .release_ayanamsas
            .iter()
            .any(|entry| entry.canonical_name == "Valens Moon"));
        assert_eq!(
            profile.house_formula_family_names(),
            vec![
                "Equal".to_string(),
                "Equatorial projection".to_string(),
                "Great-circle".to_string(),
                "Quadrant".to_string(),
                "Sector".to_string(),
                "Solar arc".to_string(),
                "Whole Sign".to_string(),
            ]
        );
        assert_eq!(profile.house_systems, built_in_house_systems());
        assert_eq!(profile.baseline_house_systems, baseline_house_systems());
        assert_eq!(profile.release_house_systems, release_house_systems());
        assert_eq!(profile.ayanamsas, built_in_ayanamsas());
        assert_eq!(profile.baseline_ayanamsas, baseline_ayanamsas());
        assert_eq!(profile.release_ayanamsas, release_ayanamsas());
        assert!(profile
            .target_house_scope
            .iter()
            .any(|line| line.contains("Swiss-Ephemeris-class house-system catalog")));
        assert!(profile
            .target_ayanamsa_scope
            .iter()
            .any(|line| line.contains("Swiss-Ephemeris-class ayanamsa catalog")));
        assert!(profile
            .release_notes
            .iter()
            .any(|note| note.contains("Krusinski-Pisa-Goelzer")));
        assert!(profile
            .release_notes
            .iter()
            .any(|note| note.contains("Treindl Sunshine")));
        assert!(profile
            .release_notes
            .iter()
            .any(|note| note.contains("Babylonian/Aldebaran = 15 Tau")));
        assert!(profile
            .release_notes
            .iter()
            .any(|note| note.contains("Krishnamurti-Senthilathiban")));
        assert!(profile
            .release_notes
            .iter()
            .any(|note| note.contains("B. V. Raman")));
        assert!(profile
            .release_notes
            .iter()
            .any(|note| note.contains("Raman Ayanamsha")));
        assert!(profile
            .release_notes
            .iter()
            .any(|note| note.contains("Raman ayanamsa")));
        assert!(profile
            .release_notes
            .iter()
            .any(|note| note.contains("Whole sign houses, 1. house = Aries")));
        assert!(profile
            .validation_reference_points
            .iter()
            .any(|point| point.contains("validation corpus")));
        assert!(profile
            .validation_reference_points
            .iter()
            .any(|point| point.contains("house formulas")));
        assert!(profile
            .known_gaps
            .iter()
            .all(|gap| !gap.contains("validation corpus")));
        assert!(profile
            .known_gaps
            .iter()
            .all(|gap| !gap.contains("house formulas")));
        assert!(profile
            .custom_definition_labels
            .contains(&"Babylonian (House)"));
        assert!(profile
            .custom_definition_labels
            .contains(&"Babylonian (House Obs)"));
        assert!(profile
            .known_gaps
            .iter()
            .all(|gap| !gap.contains("Babylonian (House)")));
        assert!(profile
            .known_gaps
            .iter()
            .all(|gap| !gap.contains("House Obs")));
    }

    #[test]
    fn compatibility_profile_validate_accepts_the_current_profile() {
        current_compatibility_profile()
            .validate()
            .expect("current compatibility profile should validate");
    }

    #[test]
    fn compatibility_profile_target_scope_summary_helpers_render_and_validate() {
        let profile = current_compatibility_profile();

        assert_eq!(
            profile.target_house_scope_summary_line(),
            profile.target_house_scope.join("; ")
        );
        assert_eq!(
            profile
                .validated_target_house_scope_summary_line()
                .expect("target house scope should validate"),
            profile.target_house_scope.join("; ")
        );
        assert_eq!(
            target_house_scope_summary_for_report(),
            profile.target_house_scope_summary_line()
        );
        assert_eq!(
            validated_target_house_scope_summary_for_report(),
            Ok(profile.target_house_scope.join("; "))
        );
        assert_eq!(
            profile.target_ayanamsa_scope_summary_line(),
            profile.target_ayanamsa_scope.join("; ")
        );
        assert_eq!(
            profile
                .validated_target_ayanamsa_scope_summary_line()
                .expect("target ayanamsa scope should validate"),
            profile.target_ayanamsa_scope.join("; ")
        );
        assert_eq!(
            target_ayanamsa_scope_summary_for_report(),
            profile.target_ayanamsa_scope_summary_line()
        );
        assert_eq!(
            validated_target_ayanamsa_scope_summary_for_report(),
            Ok(profile.target_ayanamsa_scope.join("; "))
        );
    }

    #[test]
    fn compatibility_profile_validate_rejects_whitespace_padded_scope_entries() {
        let mut profile = current_compatibility_profile();
        profile.target_house_scope = &["Target house scope: example "];

        let error = profile
            .validate()
            .expect_err("whitespace-padded scope entry should fail validation");

        assert!(matches!(
            error,
            CompatibilityProfileValidationError::WhitespaceTextSectionEntry {
                section_label: "target-house-scope",
                entry: "Target house scope: example "
            }
        ));
    }

    #[test]
    fn compatibility_profile_validate_rejects_case_insensitive_duplicate_within_section() {
        let mut profile = current_compatibility_profile();
        profile.release_notes = &["Release note entry", "release note entry"];

        let error = profile
            .validate()
            .expect_err("case-insensitive duplicates within a section should fail validation");

        assert!(matches!(
            error,
            CompatibilityProfileValidationError::DuplicateTextSectionEntry {
                section_label: "release-note",
                entry: "release note entry"
            }
        ));
    }

    #[test]
    fn compatibility_profile_validate_rejects_case_insensitive_duplicates_across_sections() {
        let mut profile = current_compatibility_profile();
        profile.validation_reference_points = &["Release note entry"];
        profile.known_gaps = &["release note entry"];

        let error = profile
            .validate()
            .expect_err("case-insensitive duplicates across sections should fail validation");

        assert!(matches!(
            error,
            CompatibilityProfileValidationError::DuplicateTextSectionEntryAcrossSections {
                entry: "release note entry",
                first_section: "validation-reference-point",
                second_section: "compatibility-caveat"
            }
        ));
    }

    #[test]
    fn compatibility_profile_validate_rejects_custom_definition_labels_that_resolve_to_house_systems(
    ) {
        let mut profile = current_compatibility_profile();
        profile.custom_definition_labels = &["Placidus"];

        let error = profile
            .validate()
            .expect_err("custom-definition labels should not resolve to built-in house systems");

        assert!(matches!(
            error,
            CompatibilityProfileValidationError::CustomDefinitionLabelResolvesToBuiltIn {
                label: "Placidus"
            }
        ));
    }

    #[test]
    fn compatibility_profile_validate_rejects_custom_definition_labels_that_resolve_to_ayanamsas() {
        let mut profile = current_compatibility_profile();
        profile.custom_definition_labels = &["Lahiri"];

        let error = profile
            .validate()
            .expect_err("custom-definition labels should not resolve to built-in ayanamsas");

        assert!(matches!(
            error,
            CompatibilityProfileValidationError::CustomDefinitionLabelResolvesToBuiltIn {
                label: "Lahiri"
            }
        ));
    }

    #[test]
    fn compatibility_profile_custom_definition_ayanamsas_align_with_metadata_coverage() {
        let profile = current_compatibility_profile();
        let coverage = metadata_coverage();

        assert!(coverage
            .custom_definition_only
            .iter()
            .all(|label| profile.custom_definition_labels.contains(label)));
        assert!(profile.custom_definition_labels.contains(&"True Balarama"));
        assert!(profile.custom_definition_labels.contains(&"Aphoric"));
        assert!(profile.custom_definition_labels.contains(&"Takra"));
        assert!(resolve_ayanamsa("True Balarama").is_none());
        assert!(resolve_ayanamsa("Aphoric").is_none());
        assert!(resolve_ayanamsa("Takra").is_none());
        let mut resolved_custom_definition_labels = profile
            .custom_definition_labels
            .iter()
            .copied()
            .filter(|label| resolve_ayanamsa(label).is_some())
            .collect::<Vec<_>>();
        resolved_custom_definition_labels.sort_unstable();
        assert_eq!(
            resolved_custom_definition_labels,
            vec![
                "Babylonian (House Obs)",
                "Babylonian (House)",
                "Babylonian (Sissy)",
                "Babylonian (True Geoc)",
                "Babylonian (True Obs)",
                "Babylonian (True Topc)",
            ]
        );
        assert!(profile
            .custom_definition_labels
            .iter()
            .filter(|label| resolve_ayanamsa(label).is_none())
            .all(|label| {
                !profile
                    .release_ayanamsas
                    .iter()
                    .any(|entry| entry.canonical_name == *label)
            }));
    }

    #[test]
    fn compatibility_profile_validate_rejects_missing_custom_definition_ayanamsa_coverage_labels() {
        let mut profile = current_compatibility_profile();
        profile.custom_definition_labels = &["True Balarama", "Aphoric", "Takra"];

        let error = profile.validate().expect_err(
            "custom-definition ayanamsa coverage labels should be required in the profile",
        );

        assert!(matches!(
            error,
            CompatibilityProfileValidationError::MissingCustomDefinitionAyanamsaCoverageLabel {
                label: "Babylonian (House)"
            }
        ));
    }

    #[test]
    fn compatibility_profile_validate_rejects_house_descriptor_metadata_drift() {
        let mut profile = current_compatibility_profile();
        const HOUSE_SYSTEMS: &[HouseSystemDescriptor] = &[HouseSystemDescriptor::new(
            HouseSystem::Placidus,
            "Placidus",
            &[],
            "Release note with trailing whitespace ",
            false,
        )];
        profile.house_systems = HOUSE_SYSTEMS;
        profile.baseline_house_systems = HOUSE_SYSTEMS;
        profile.release_house_systems = &[];

        let error = profile
            .validate()
            .expect_err("invalid house descriptors should fail compatibility-profile validation");

        assert!(matches!(
            error,
            CompatibilityProfileValidationError::HouseDescriptorValidationFailed {
                error: HouseCatalogValidationError::DescriptorNotesNotNormalized {
                    label: "Placidus"
                }
            }
        ));
    }

    #[test]
    fn compatibility_profile_validate_rejects_ayanamsa_descriptor_metadata_drift() {
        let mut profile = current_compatibility_profile();
        const AYANAMSA_SYSTEMS: &[AyanamsaDescriptor] = &[AyanamsaDescriptor::new(
            Ayanamsa::Lahiri,
            "Lahiri",
            &[],
            "Release note with trailing whitespace ",
            None,
            None,
        )];
        profile.ayanamsas = AYANAMSA_SYSTEMS;
        profile.baseline_ayanamsas = AYANAMSA_SYSTEMS;
        profile.release_ayanamsas = &[];

        let error = profile.validate().expect_err(
            "invalid ayanamsa descriptors should fail compatibility-profile validation",
        );

        assert!(matches!(
            error,
            CompatibilityProfileValidationError::AyanamsaDescriptorValidationFailed { error }
                if error.contains("descriptor note") && error.contains("Lahiri")
        ));
    }

    #[test]
    fn compatibility_profile_retains_intentional_case_only_alias_variants() {
        let profile = current_compatibility_profile();

        let vehlow_equal = profile
            .release_house_systems
            .iter()
            .find(|entry| entry.canonical_name == "Vehlow Equal")
            .expect("Vehlow Equal should be part of the release catalog");
        assert!(vehlow_equal.aliases.contains(&"Vehlow equal"));

        let galactic_center = profile
            .release_ayanamsas
            .iter()
            .find(|entry| entry.canonical_name == "Galactic Center")
            .expect("Galactic Center should be part of the release catalog");
        assert!(galactic_center.aliases.contains(&"Galactic center"));

        let galactic_equator = profile
            .release_ayanamsas
            .iter()
            .find(|entry| entry.canonical_name == "Galactic Equator")
            .expect("Galactic Equator should be part of the release catalog");
        assert!(galactic_equator.aliases.contains(&"Galactic equator"));

        let galactic_center_cochrane = profile
            .release_ayanamsas
            .iter()
            .find(|entry| entry.canonical_name == "Galactic Center (Cochrane)")
            .expect("Galactic Center (Cochrane) should be part of the release catalog");
        assert!(galactic_center_cochrane
            .aliases
            .contains(&"Galactic center Cochrane"));

        let galactic_equator_true = profile
            .release_ayanamsas
            .iter()
            .find(|entry| entry.canonical_name == "Galactic Equator (True)")
            .expect("Galactic Equator (True) should be part of the release catalog");
        assert!(galactic_equator_true
            .aliases
            .contains(&"Galactic equator true"));

        assert!(profile.validate().is_ok());
    }

    #[test]
    fn compatibility_profile_validate_rejects_case_insensitive_duplicate_house_labels_within_catalog(
    ) {
        let mut profile = current_compatibility_profile();
        const TOTAL_HOUSE_SYSTEMS: &[HouseSystemDescriptor] = &[
            HouseSystemDescriptor::new(
                HouseSystem::Placidus,
                "Total Placidus",
                &["Total Placidus alias"],
                "Total house coverage",
                false,
            ),
            HouseSystemDescriptor::new(
                HouseSystem::Equal,
                "Total Equal",
                &["Total Placidus alias"],
                "Total house coverage",
                false,
            ),
        ];
        const BASELINE_HOUSE_SYSTEMS: &[HouseSystemDescriptor] = &[HouseSystemDescriptor::new(
            HouseSystem::Placidus,
            "Total Placidus",
            &["Total Placidus alias"],
            "Baseline house coverage",
            false,
        )];
        const RELEASE_HOUSE_SYSTEMS: &[HouseSystemDescriptor] = &[HouseSystemDescriptor::new(
            HouseSystem::Equal,
            "Total Equal",
            &["Total Equal alias"],
            "Release house coverage",
            false,
        )];
        profile.house_systems = TOTAL_HOUSE_SYSTEMS;
        profile.baseline_house_systems = BASELINE_HOUSE_SYSTEMS;
        profile.release_house_systems = RELEASE_HOUSE_SYSTEMS;

        let error = profile
            .validate()
            .expect_err("duplicate house labels should fail validation");

        assert!(matches!(
            error,
            CompatibilityProfileValidationError::CatalogLabelCollision {
                catalog_label: "house-system",
                ..
            }
        ));
        assert!(error.to_string().contains("duplicate label"));
    }

    #[test]
    fn compatibility_profile_validate_rejects_case_insensitive_duplicate_ayanamsa_labels_within_catalog(
    ) {
        let mut profile = current_compatibility_profile();
        const TOTAL_AYANAMSAS: &[AyanamsaDescriptor] = &[
            AyanamsaDescriptor::new(
                Ayanamsa::Lahiri,
                "Total Lahiri",
                &["Total Lahiri alias"],
                "Total ayanamsa coverage",
                None,
                None,
            ),
            AyanamsaDescriptor::new(
                Ayanamsa::Krishnamurti,
                "Total Krishnamurti",
                &["Total Lahiri alias"],
                "Total ayanamsa coverage",
                None,
                None,
            ),
        ];
        const BASELINE_AYANAMSAS: &[AyanamsaDescriptor] = &[AyanamsaDescriptor::new(
            Ayanamsa::Lahiri,
            "Total Lahiri",
            &["Total Lahiri alias"],
            "Baseline ayanamsa coverage",
            None,
            None,
        )];
        const RELEASE_AYANAMSAS: &[AyanamsaDescriptor] = &[AyanamsaDescriptor::new(
            Ayanamsa::Krishnamurti,
            "Total Krishnamurti",
            &["Total Krishnamurti alias"],
            "Release ayanamsa coverage",
            None,
            None,
        )];
        profile.ayanamsas = TOTAL_AYANAMSAS;
        profile.baseline_ayanamsas = BASELINE_AYANAMSAS;
        profile.release_ayanamsas = RELEASE_AYANAMSAS;

        let error = profile
            .validate()
            .expect_err("duplicate ayanamsa labels should fail validation");

        assert!(matches!(
            error,
            CompatibilityProfileValidationError::CatalogLabelCollision {
                catalog_label: "ayanamsa",
                ..
            }
        ));
        assert!(error.to_string().contains("duplicate label"));
    }

    #[test]
    fn compatibility_profile_validate_rejects_case_insensitive_duplicate_house_labels_across_entries(
    ) {
        #[derive(Clone, Copy)]
        struct Entry {
            canonical_name: &'static str,
            aliases: &'static [&'static str],
        }

        impl AliasProfileEntry for Entry {
            fn canonical_name(&self) -> &'static str {
                self.canonical_name
            }

            fn aliases(&self) -> &'static [&'static str] {
                self.aliases
            }
        }

        let entries = [
            Entry {
                canonical_name: "Placidus",
                aliases: &["Placidus house system"],
            },
            Entry {
                canonical_name: "Koch",
                aliases: &["placidus"],
            },
        ];

        let error = validate_catalog_label_uniqueness("house-system", &entries)
            .expect_err("cross-entry duplicate house labels should fail validation");
        assert!(matches!(
            error,
            CompatibilityProfileValidationError::CatalogLabelCollision {
                catalog_label: "house-system",
                label: "placidus"
            }
        ));
    }

    #[test]
    fn compatibility_profile_validate_rejects_case_insensitive_duplicate_ayanamsa_labels_across_entries(
    ) {
        #[derive(Clone, Copy)]
        struct Entry {
            canonical_name: &'static str,
            aliases: &'static [&'static str],
        }

        impl AliasProfileEntry for Entry {
            fn canonical_name(&self) -> &'static str {
                self.canonical_name
            }

            fn aliases(&self) -> &'static [&'static str] {
                self.aliases
            }
        }

        let entries = [
            Entry {
                canonical_name: "Lahiri",
                aliases: &["Lahiri ayanamsa"],
            },
            Entry {
                canonical_name: "Raman",
                aliases: &["lahiri"],
            },
        ];

        let error = validate_catalog_label_uniqueness("ayanamsa", &entries)
            .expect_err("cross-entry duplicate ayanamsa labels should fail validation");
        assert!(matches!(
            error,
            CompatibilityProfileValidationError::CatalogLabelCollision {
                catalog_label: "ayanamsa",
                label: "lahiri"
            }
        ));
    }

    #[test]
    fn compatibility_profile_validate_rejects_cross_catalog_house_and_ayanamsa_label_collisions() {
        #[derive(Clone, Copy)]
        struct Entry {
            canonical_name: &'static str,
            aliases: &'static [&'static str],
        }

        impl AliasProfileEntry for Entry {
            fn canonical_name(&self) -> &'static str {
                self.canonical_name
            }

            fn aliases(&self) -> &'static [&'static str] {
                self.aliases
            }
        }

        let houses = [Entry {
            canonical_name: "Placidus",
            aliases: &["Equal house system"],
        }];
        let ayanamsas = [Entry {
            canonical_name: "Raman",
            aliases: &["equal house system"],
        }];

        let error = validate_catalogs_are_disjoint("house-system", &houses, "ayanamsa", &ayanamsas)
            .expect_err("cross-catalog duplicate labels should fail validation");
        assert!(matches!(
            error,
            CompatibilityProfileValidationError::CrossCatalogLabelCollision {
                first_catalog_label: "house-system",
                second_catalog_label: "ayanamsa",
                label: "equal house system"
            }
        ));
    }

    #[test]
    fn compatibility_profile_validate_rejects_overlapping_catalog_partitions() {
        let mut profile = current_compatibility_profile();
        profile.release_house_systems = profile.baseline_house_systems;

        let error = profile
            .validate()
            .expect_err("overlapping catalog partitions should fail validation");

        assert!(matches!(
            error,
            CompatibilityProfileValidationError::CatalogPartitionOverlap {
                catalog_label: "house-system",
                label: _
            }
        ));
    }

    #[test]
    fn compatibility_profile_validate_rejects_inexact_house_catalog_coverage() {
        let mut profile = current_compatibility_profile();
        const TOTAL_HOUSE_SYSTEMS: &[HouseSystemDescriptor] = &[
            HouseSystemDescriptor::new(
                HouseSystem::Placidus,
                "Total Placidus",
                &["Total Placidus alias"],
                "Total house coverage",
                false,
            ),
            HouseSystemDescriptor::new(
                HouseSystem::Equal,
                "Total Equal",
                &[],
                "Total house coverage",
                false,
            ),
        ];
        const BASELINE_HOUSE_SYSTEMS: &[HouseSystemDescriptor] = &[HouseSystemDescriptor::new(
            HouseSystem::Placidus,
            "Baseline Placidus",
            &["Baseline Placidus alias"],
            "Baseline house coverage",
            false,
        )];
        const RELEASE_HOUSE_SYSTEMS: &[HouseSystemDescriptor] = &[HouseSystemDescriptor::new(
            HouseSystem::Equal,
            "Release Equal",
            &["Release Equal alias"],
            "Release house coverage",
            false,
        )];
        profile.house_systems = TOTAL_HOUSE_SYSTEMS;
        profile.baseline_house_systems = BASELINE_HOUSE_SYSTEMS;
        profile.release_house_systems = RELEASE_HOUSE_SYSTEMS;

        let error = profile
            .validate()
            .expect_err("inexact catalog coverage should fail validation");

        assert!(matches!(
            error,
            CompatibilityProfileValidationError::CatalogCoverageLabelMismatch {
                catalog_label: "house-system",
                missing_label: "Total Placidus",
                unexpected_label: "Baseline Placidus"
            }
        ));
    }

    #[test]
    fn compatibility_profile_validate_rejects_inexact_ayanamsa_catalog_coverage() {
        let mut profile = current_compatibility_profile();
        const TOTAL_AYANAMSAS: &[AyanamsaDescriptor] = &[
            AyanamsaDescriptor::new(
                Ayanamsa::Lahiri,
                "Total Lahiri",
                &["Total Lahiri alias"],
                "Total ayanamsa coverage",
                None,
                None,
            ),
            AyanamsaDescriptor::new(
                Ayanamsa::Krishnamurti,
                "Total Krishnamurti",
                &[],
                "Total ayanamsa coverage",
                None,
                None,
            ),
        ];
        const BASELINE_AYANAMSAS: &[AyanamsaDescriptor] = &[AyanamsaDescriptor::new(
            Ayanamsa::Lahiri,
            "Baseline Lahiri",
            &["Baseline Lahiri alias"],
            "Baseline ayanamsa coverage",
            None,
            None,
        )];
        const RELEASE_AYANAMSAS: &[AyanamsaDescriptor] = &[AyanamsaDescriptor::new(
            Ayanamsa::Krishnamurti,
            "Release Krishnamurti",
            &["Release Krishnamurti alias"],
            "Release ayanamsa coverage",
            None,
            None,
        )];
        profile.ayanamsas = TOTAL_AYANAMSAS;
        profile.baseline_ayanamsas = BASELINE_AYANAMSAS;
        profile.release_ayanamsas = RELEASE_AYANAMSAS;

        let error = profile
            .validate()
            .expect_err("inexact catalog coverage should fail validation");

        assert!(matches!(
            error,
            CompatibilityProfileValidationError::CatalogCoverageLabelMismatch {
                catalog_label: "ayanamsa",
                missing_label: "Total Lahiri",
                unexpected_label: "Baseline Lahiri"
            }
        ));
    }

    #[test]
    fn display_lists_release_sections() {
        let profile = current_compatibility_profile();
        assert!(profile.release_note().contains("David Cochrane"));
        assert!(profile.release_note().contains("Nick Anthony Fiorenza"));
        assert!(profile.release_note().contains("True Sheoran"));
        assert!(profile
            .release_note()
            .contains("Dhruva/Gal.Center/Mula (Wilhelm)"));
        assert!(profile.release_note().contains("Mula Wilhelm"));
        assert!(profile.release_note().contains("Wilhelm"));
        assert!(profile
            .release_note()
            .contains("Equal Midheaven table of houses"));
        assert!(profile.release_note().contains("Equal from MC"));
        assert!(profile.release_note().contains("Polich/Page"));
        assert!(profile.release_note().contains("Polich Page"));
        assert!(profile.release_note().contains("Equal/1=0 Aries"));
        assert!(profile.release_note().contains("Equal (cusp 1 = 0° Aries)"));
        assert!(profile.release_note().contains("Makransky Sunshine"));
        assert!(profile.release_note().contains("Pullen SD table of houses"));
        assert!(profile.release_note().contains("PVR Pushya Paksha"));
        assert!(profile
            .release_note()
            .contains("Pullen SD (Neo-Porphyry) table of houses"));
        assert!(profile.release_note().contains("Pullen SD (Neo-Porphyry)"));
        assert!(profile.release_note().contains("Neo-Porphyry"));
        assert!(profile
            .release_note()
            .contains("Pullen SD (Sinusoidal Delta)"));
        assert!(profile.release_note().contains("Pullen SR table of houses"));
        assert!(profile
            .release_note()
            .contains("Pullen SR (Sinusoidal Ratio) table of houses"));
        assert!(profile
            .release_note()
            .contains("Pullen SR (Sinusoidal Ratio)"));
        assert!(profile
            .release_note()
            .contains("Unsupported modes remain explicit"));

        let rendered = profile.to_string();
        assert!(rendered.contains("Target compatibility catalog:"));
        assert!(rendered.contains(
            "the full Swiss-Ephemeris-class house-system catalog remains the long-term compatibility goal."
        ));
        assert!(rendered.contains("Target ayanamsa catalog:"));
        assert!(rendered.contains(
            "the full Swiss-Ephemeris-class ayanamsa catalog remains the long-term compatibility goal."
        ));
        assert!(rendered.contains("Baseline compatibility milestone:"));
        assert!(rendered.contains("Release-specific coverage beyond baseline:"));
        assert!(rendered.contains("Alias mappings for built-in house systems:"));
        assert!(rendered.contains("Source-label aliases for built-in house systems:"));
        assert!(rendered.contains("Source-label aliases for built-in ayanamsas:"));
        assert!(rendered.contains("Alias mappings for built-in ayanamsas:"));
        assert!(rendered.contains("Coverage summary:"));
        assert!(rendered.contains("Unsupported modes: built-in UTC convenience remains out of scope; built-in Delta T remains out of scope; topocentric body positions remain unsupported; apparent-place corrections are rejected unless a backend explicitly advertises support; native sidereal backend output remains unsupported unless a backend explicitly advertises it"));
        assert!(rendered.contains("house systems:"));
        assert!(rendered.contains("ayanamsas:"));
        assert!(rendered.contains(&format!(
            "house formula families: {}",
            profile.house_formula_families_summary_line()
        )));
        assert!(rendered.contains(&format!(
            "latitude-sensitive house systems: {}",
            profile.latitude_sensitive_house_systems_summary_line()
        )));
        assert!(rendered.contains(&format!(
            "latitude-sensitive house constraints: {}",
            profile.latitude_sensitive_house_constraints_summary_line()
        )));
        assert!(rendered.contains("ayanamsa sidereal metadata:"));
        assert!(rendered.contains(&format!(
            "custom-definition ayanamsas: {} (tracked without sidereal metadata)",
            metadata_coverage().custom_definition_only.join(", ")
        )));
        assert!(rendered.contains("no unexpected sidereal-metadata gaps remain."));
        assert!(rendered.contains("custom-definition labels:"));
        assert!(rendered.contains("Validation reference points:"));
        assert!(rendered.contains("The stage-4 validation corpus remains the reference point for tightening house formulas whenever future revisions land."));
        assert!(rendered.contains("Babylonian (House) (aliases: Babylonian House, BABYL_HOUSE)"));
        assert!(rendered.contains("Treindl Sunshine"));
        assert!(rendered.contains("Makransky Sunshine"));
        assert!(rendered.contains("Sunshine table of houses, by Bob Makransky, Makransky Sunshine, Bob Makransky, Treindl Sunshine -> Sunshine"));
        assert!(rendered.contains("Placidus house system, Placidus table of houses -> Placidus"));
        assert!(rendered.contains("Koch houses, Koch house system, house system of the birth place, Koch table of houses, W. Koch, W Koch -> Koch"));
        assert!(rendered.contains("Whole Sign houses, Whole Sign table of houses, Whole-sign, Whole Sign system, Whole Sign house system -> Whole Sign"));
        assert_eq!(
            profile.latitude_sensitive_house_systems(),
            vec![
                "Placidus",
                "Koch",
                "Horizon/Azimuth",
                "APC",
                "Krusinski-Pisa-Goelzer",
                "Topocentric",
                "Sunshine",
                "Gauquelin sectors",
            ]
        );
        assert!(rendered.contains("Pullen SD (Neo-Porphyry) table of houses"));
        assert!(rendered.contains("Pullen SD (Sinusoidal Delta)"));
        assert!(rendered.contains("Pullen SR (Sinusoidal Ratio) table of houses"));
        assert!(rendered.contains("Babylonian/Aldebaran = 15 Tau"));
        assert!(rendered
            .contains("Babylonian (House Obs) (aliases: Babylonian House Obs, BABYL_HOUSE_OBS)"));
        assert!(
            rendered.contains("Babylonian sidereal mode labeled BABYL_HOUSE in Swiss Ephemeris.")
        );
        assert!(rendered
            .contains("Babylonian sidereal mode labeled BABYL_HOUSE_OBS in Swiss Ephemeris."));
        assert!(rendered.contains(
            "Babylonian/Kugler 1, Babylonian Kugler 1, Babylonian 1 -> Babylonian (Kugler 1)"
        ));
        assert!(rendered.contains("D equal / MC, Equal from MC, Equal (from MC), Equal (from MC) table of houses, Equal (MC) table of houses, Equal/MC table of houses, Equal (MC) house system, Equal/MC house system, Equal MC, Equal/MC, Equal Midheaven, Equal Midheaven house system, Equal Midheaven table of houses, Equal/MC = 10th -> Equal (MC)"));
        assert!(rendered.contains("Equal (MC) table of houses"));
        assert!(rendered.contains("Equal Midheaven table of houses"));
        assert!(rendered.contains("Equal (MC) house system"));
        assert!(rendered.contains(
            "Equal (MC) table of houses, Equal/MC table of houses, Equal (MC) house system, Equal/MC house system, Equal MC, Equal/MC, Equal Midheaven, Equal Midheaven house system, Equal Midheaven table of houses, Equal/MC = 10th -> Equal (MC)"
        ));
        assert!(rendered.contains(
            "N, N whole sign houses, 1. house = Aries, Equal/1=Aries, Equal Aries, Aries houses, Whole Sign (house 1 = Aries), Whole Sign (house 1 = Aries) table of houses, Equal (1=Aries) table of houses, Equal/1=Aries table of houses, Equal (1=Aries) house system, Equal/1=Aries house system, Whole sign houses, 1. house = Aries, Equal/1=0 Aries, Equal (cusp 1 = 0° Aries) -> Equal (1=Aries)"
        ));
        assert!(rendered.contains("Equal (1=Aries) house system"));
        assert!(rendered.contains(
            "Galactic Center (Gil Brand), Gil Brand, Rgilbrand, Galactic center Rgilbrand -> Galactic Center (Rgilbrand)"
        ));
        assert!(rendered.contains(
            "Skydram, Skydram/Galactic Alignment, Skydram (Mardyks), Mardyks, Galactic center Mardyks -> Galactic Center (Mardyks)"
        ));
        assert!(rendered.contains("Galact. Center = 0 Sag, Gal. Center = 0 Sag -> Galactic Center"));
        assert!(rendered.contains(
            "Cochrane (Gal.Center = 0 Cap), Gal. Center = 0 Cap, Cochrane, Galactic center Cochrane, David Cochrane -> Galactic Center (Cochrane)"
        ));
        assert!(rendered.contains("Galactic equator, Gal. Eq. -> Galactic Equator"));
        assert!(
            rendered.contains("IAU 1958, Galactic equator IAU 1958 -> Galactic Equator (IAU 1958)")
        );
        assert!(rendered
            .contains("True galactic equator, Galactic equator true -> Galactic Equator (True)"));
        assert!(rendered.contains(
            "Galactic Equator mid-Mula, Mula galactic equator, Galactic equator Mula -> Galactic Equator (Mula)"
        ));
        assert!(
            rendered.contains("Fiorenza, Galactic equator Fiorenza, Nick Anthony Fiorenza -> Galactic Equator (Fiorenza)")
        );
        assert!(rendered.contains("Zij al-Shah, Sasanian -> Sassanian"));
        assert!(
            rendered.contains("Vettius Valens, Valens, Moon, Moon sign, Moon sign ayanamsa, Valens Moon ayanamsa -> Valens Moon")
        );
        assert!(rendered.contains("Suryasiddhanta, mean Sun"));
        assert!(rendered.contains("Surya Siddhanta, mean Sun"));
        assert!(rendered.contains("Surya Siddhanta mean sun"));
        assert!(rendered.contains("Surya Siddhanta mean-sun source forms"));
        assert!(rendered.contains("Aryabhata mean-sun source forms"));
        assert!(rendered.contains("Suryasiddhanta, mean Sun, Surya Siddhanta, mean Sun, Suryasiddhanta mean sun, Surya Siddhanta mean sun, Suryasiddhanta MSUN, Surya Siddhanta MSUN -> Suryasiddhanta (Mean Sun)"));
        assert!(rendered.contains(
            "Aryabhata, mean Sun, Aryabhata mean sun, Aryabhata MSUN -> Aryabhata (Mean Sun)"
        ));
        assert!(rendered.contains(
            "Suryasiddhanta, Surya Siddhanta, Suryasiddhanta 499, Surya Siddhanta 499, Suryasiddhanta 499 CE, Surya Siddhanta 499 CE -> Suryasiddhanta (499 CE)"
        ));
        assert!(rendered.contains(
            "Aryabhata, Aryabhata 499, Aryabhata 499 CE, Aryabhatan Kaliyuga, Aryabhata Kaliyuga -> Aryabhata (499 CE)"
        ));
        assert!(rendered.contains("Aryabhata 522, Aryabhata 522 CE -> Aryabhata (522 CE)"));
        assert!(rendered.contains("J. N. Bhasin, J.N. Bhasin, Bhasin -> JN Bhasin"));
        assert!(rendered.contains("Lahiri VP285, VP285 -> Lahiri (VP285)"));
        assert!(rendered.contains("KP VP291, Krishnamurti VP291, Krishnamurti-Senthilathiban, VP291 -> Krishnamurti (VP291)"));
        assert!(rendered.contains(
            "True Pushya (PVRN Rao), Pushya-paksha, Pushya Paksha, PVR Pushya Paksha, PVR, P.V.R. Narasimha Rao -> PVR Pushya-paksha"
        ));
        assert!(rendered.contains("True Pushya ayanamsa, Pushya -> True Pushya"));
        assert!(rendered
            .contains("True Citra ayanamsa, True Citra Paksha, True Chitra Paksha, True Chitrapaksha -> True Citra"));
        assert!(rendered.contains("Chitra, True Chitra ayanamsa -> True Chitra"));
        assert!(rendered.contains("True Revati ayanamsa -> True Revati"));
        assert!(rendered
            .contains("True Mula (Chandra Hari), True Mula ayanamsa, Chandra Hari -> True Mula"));
        assert!(rendered.contains("Udayagiri ayanamsa -> Udayagiri"));
        assert!(rendered.contains("ICRC Lahiri, Lahiri ICRC -> Lahiri (ICRC)"));
        assert!(rendered.contains("Lahiri original, Panchanga Darpan Lahiri -> Lahiri (1940)"));
        assert!(rendered.contains("De Luce, DeLuce ayanamsa -> DeLuce"));
        assert!(rendered.contains(
            "T, Polich-Page, Polich/Page, Polich Page, Polich-Page \"topocentric\" table of houses, T Polich/Page (\"topocentric\"), T topocentric, Topocentric house system, Topocentric table of houses -> Topocentric"
        ));
        assert!(rendered.contains("Polich-Page \"topocentric\" table of houses"));
        assert!(rendered.contains("T Polich/Page (\"topocentric\")"));
        assert!(rendered.contains(
            "Horizon, Azimuth, Horizontal, Azimuthal, Horizon table of houses, Horizontal table of houses, Azimuthal table of houses, Horizon/Azimuth table of houses, Horizon house system, Horizon/Azimuth house system, Horizontal house system, Azimuth house system, Azimuthal house system, horizon/azimut, horizon/azimuth -> Horizon/Azimuth"
        ));
        assert!(rendered.contains(
            "X, Meridian houses, Meridian table of houses, Meridian house system, ARMC, Axial Rotation, Axial rotation system, Zariel, X axial rotation system/ Meridian houses -> Meridian"
        ));
        assert!(rendered.contains("Axial variants, A -> Axial"));
        assert!(rendered.contains("M, Morinus houses, Morinus house system -> Morinus"));
        assert!(rendered.contains("Whole Sign house system -> Whole Sign"));
        assert!(rendered.contains("Equal table of houses, Whole Sign system, and Morinus house system spellings now called out explicitly in the quick-audit text"));
        assert!(rendered.contains("horizon/azimuth"));
        assert!(rendered.contains("Horizon/Azimuth house system"));
        assert!(rendered.contains("Horizontal house system"));
        assert!(rendered.contains("Horizontal table of houses"));
        assert!(rendered.contains("Azimuth house system"));
        assert!(rendered.contains("Azimuthal table of houses"));
        assert!(rendered.contains("Meridian house system"));
        assert!(rendered.contains("Horizon/Azimuth table of houses"));
        assert!(rendered
            .contains("Y, APC, Ram school, Ram's school, Ramschool, WvA, Y APC houses, APC houses, APC, also known as “Ram school”, table of houses, APC house system, Ascendant Parallel Circle -> APC"));
        assert!(rendered
            .contains("Chitra Paksha, Chitrapaksha, Chitra-paksha, Lahiri Ayanamsha, Lahiri ayanamsa -> Lahiri"));
        assert!(rendered.contains("Usha Shashi, Ushashashi, Usha-Shashi, Usha/Shashi, Usha / Shashi, Usha Shashi ayanamsa, Revati -> Usha Shashi"));
        assert!(rendered.contains("Yukteswar, Sri Yukteswar, Sri Yukteshwar, Shri Yukteswar, Shri Yukteshwar, Yukteshwar ayanamsa -> Yukteshwar"));
        assert!(rendered.contains("source-label appendix entries for Lahiri / Chitrapaksha / Chitra Paksha, True Chitra / Chitra, Krishnamurti Ayanamsha / Krishnamurti Ayanamsa / Krishnamurti ayanamsa / Krishnamurti (Swiss) / Krishnamurti Paddhati / KP ayanamsa, Fagan/Bradley Ayanamsha / Fagan/Bradley / Fagan Bradley / Fagan / Bradley / Fagan-Bradley, Usha Shashi / Usha / Shashi, and the Yukteshwar / Sri Yukteshwar / Shri Yukteshwar transliterations"));
        assert!(rendered.contains("source-label appendix entries for P.V.R. Narasimha Rao, Aries houses, and True Mula (Chandra Hari)"));
        assert!(rendered.contains(
            "B. V. Raman, B.V. Raman, B V Raman, Raman Ayanamsha, Raman ayanamsa -> Raman"
        ));
        assert!(rendered.contains(
            "Krishnamurti Ayanamsha, Krishnamurti Ayanamsa, Krishnamurti ayanamsa, Krishnamurti (Swiss), Krishnamurti Paddhati, KP ayanamsa -> Krishnamurti"
        ));
        assert!(rendered.contains("Krishnamurti (aliases: KP,"));
        assert!(rendered.contains(
            "Fagan/Bradley Ayanamsha, Fagan/Bradley, Fagan Bradley, Fagan / Bradley, Fagan-Bradley -> Fagan/Bradley"
        ));
        assert!(rendered.contains("Whole Sign (house 1 = Aries), Whole Sign (house 1 = Aries) table of houses, Equal (1=Aries) table of houses, Equal/1=Aries table of houses, Equal (1=Aries) house system, Equal/1=Aries house system, N whole sign houses, 1. house = Aries, Whole sign houses, 1. house = Aries, Equal/1=0 Aries, Equal (cusp 1 = 0° Aries) -> Equal (1=Aries)"));
        assert!(rendered.contains("Equal (1=Aries) table of houses"));
        assert!(rendered.contains("Equal from MC"));
        assert!(rendered.contains(
            "A equal, E equal = A, Equal houses, Equal house system, Equal House, Equal table of houses, Wang, Equal (cusp 1 = Asc) -> Equal"
        ));
        let source_label_section = rendered
            .split("Source-label aliases for built-in house systems:")
            .nth(1)
            .expect("source-label house appendix should be present");
        assert!(source_label_section.contains(
            "A equal, E equal = A, Equal houses, Equal house system, Equal House, Equal table of houses, Wang, Equal (cusp 1 = Asc) -> Equal"
        ));
        assert!(source_label_section.contains(
            "D equal / MC, Equal from MC, Equal (from MC), Equal (from MC) table of houses, Equal (MC) table of houses, Equal/MC table of houses, Equal (MC) house system, Equal/MC house system, Equal MC, Equal/MC, Equal Midheaven, Equal Midheaven house system, Equal Midheaven table of houses, Equal/MC = 10th -> Equal (MC)"
        ));
        assert!(source_label_section.contains(
            "Equal (1=Aries) table of houses, Equal/1=Aries table of houses, Equal (1=Aries) house system, Equal/1=Aries house system, Whole sign houses, 1. house = Aries, Equal/1=0 Aries, Equal (cusp 1 = 0° Aries) -> Equal (1=Aries)"
        ));
        assert!(source_label_section.contains(
            "Equal Quadrant, Porphyry house system, Porphyry table of houses -> Porphyry"
        ));
        assert!(source_label_section.contains("Axial variants, A -> Axial"));
        assert!(source_label_section.contains(
            "Regiomontanus houses, Regiomontanus house system, Regiomontanus table of houses -> Regiomontanus"
        ));
        assert!(source_label_section.contains(
            "Campanus houses, Campanus house system, Campanus table of houses -> Campanus"
        ));
        assert!(source_label_section.contains(
            "Alcabitius houses, Alcabitius house system, Alcabitius table of houses -> Alcabitius"
        ));
        assert!(rendered.contains("D equal / MC, Equal from MC, Equal (from MC), Equal (from MC) table of houses, Equal (MC) table of houses, Equal/MC table of houses, Equal (MC) house system, Equal/MC house system, Equal MC, Equal/MC, Equal Midheaven, Equal Midheaven house system, Equal Midheaven table of houses, Equal/MC = 10th -> Equal (MC)"));
        assert!(rendered.contains("Equal (MC) table of houses"));
        assert!(rendered.contains("J2000.0 -> J2000"));
        assert!(rendered.contains("J1900.0 -> J1900"));
        assert!(rendered.contains("B1950.0 -> B1950"));
        assert!(
            rendered.contains("Vettius Valens, Valens, Moon, Moon sign, Moon sign ayanamsa, Valens Moon ayanamsa -> Valens Moon")
        );
        assert!(rendered.contains("Equal (MC)"));
        assert!(rendered.contains("Equal (MC) table of houses"));
        assert!(rendered.contains("Equal (1=Aries)"));
        assert!(rendered.contains("Equal (1=Aries) table of houses"));
        assert!(rendered.contains("N, N whole sign houses, 1. house = Aries, Equal/1=Aries, Equal Aries, Aries houses, Whole Sign (house 1 = Aries), Whole Sign (house 1 = Aries) table of houses, Equal (1=Aries) table of houses, Equal/1=Aries table of houses, Equal (1=Aries) house system, Equal/1=Aries house system, Whole sign houses, 1. house = Aries, Equal/1=0 Aries, Equal (cusp 1 = 0° Aries) -> Equal (1=Aries)"));
        assert!(rendered.contains("Equal (1=Aries) table of houses"));
        assert!(
            rendered.contains("V equal Vehlow, Vehlow, Vehlow equal, Vehlow house system, Vehlow Equal house system, Vehlow-equal, Vehlow-equal table of houses, Vehlow Equal table of houses -> Vehlow Equal")
        );
        assert!(rendered.contains("Vehlow-equal table of houses, Vehlow Equal table of houses"));
        assert!(rendered.contains("Vehlow-equal, Vehlow -> Vehlow Equal"));
        assert!(rendered.contains(
            "S, S sripati, Śrīpati, Sripati house system, Sripati table of houses -> Sripati"
        ));
        assert!(rendered.contains("Carter (poli-equatorial)"));
        assert!(rendered.contains("Carter's poli-equatorial"));
        assert!(rendered.contains("Carter, Carter's poli-equatorial, Carter's poli-equatorial table of houses, Poli-Equatorial, Poli-equatorial -> Carter (poli-equatorial)"));
        assert!(rendered.contains("Horizon/Azimuth"));
        assert!(rendered.contains("APC"));
        assert!(rendered.contains("Krusinski-Pisa-Goelzer"));
        assert!(rendered.contains("U, Krusinski, Krusinski-Pisa, Krusinski Pisa, Krusinski/Pisa/Goelzer, Krusinski-Pisa-Goelzer table of houses, U krusinski-pisa-goelzer, Krusinski/Pisa/Goelzer house system, Pisa-Goelzer -> Krusinski-Pisa-Goelzer"));
        assert!(rendered.contains("Albategnius"));
        assert!(rendered.contains("Savard-A, Savard A, Savard's Albategnius -> Albategnius"));
        assert!(rendered.contains("Pullen SD"));
        assert!(rendered.contains("Pullen SD table of houses, Pullen SD (Neo-Porphyry) table of houses, Pullen SD (Neo-Porphyry), Neo-Porphyry, Pullen (Sinusoidal Delta), Pullen SD (Sinusoidal Delta), Pullen sinusoidal delta -> Pullen SD"));
        assert!(rendered.contains("Pullen SD table of houses, Pullen SD (Neo-Porphyry) table of houses, Pullen SD (Neo-Porphyry), Neo-Porphyry, Pullen (Sinusoidal Delta), Pullen SD (Sinusoidal Delta), Pullen SD (Sinusoidal Delta) table of houses, Pullen sinusoidal delta -> Pullen SD"));
        assert!(rendered.contains("Pullen SR"));
        assert!(rendered.contains(
            "Pullen SR table of houses, Pullen SR (Sinusoidal Ratio) table of houses, Pullen SR (Sinusoidal Ratio), Pullen (Sinusoidal Ratio), Pullen sinusoidal ratio -> Pullen SR"
        ));
        assert!(rendered.contains(
            "Babylonian/Kugler 1, Babylonian Kugler 1, Babylonian 1 -> Babylonian (Kugler 1)"
        ));
        assert!(rendered.contains(
            "Babylonian/Kugler 2, Babylonian Kugler 2, Babylonian 2 -> Babylonian (Kugler 2)"
        ));
        assert!(rendered.contains(
            "Babylonian/Kugler 3, Babylonian Kugler 3, Babylonian 3 -> Babylonian (Kugler 3)"
        ));
        assert!(rendered.contains("Babylonian/Huber, Babylonian Huber -> Babylonian (Huber)"));
        assert!(rendered.contains(
            "Aryabhata, Aryabhata 499, Aryabhata 499 CE, Aryabhatan Kaliyuga, Aryabhata Kaliyuga -> Aryabhata (499 CE)"
        ));
        assert!(rendered.contains(
            "I, I sunshine, Sunshine, Sunshine houses, Sunshine house system, Sunshine table of houses, Sunshine table of houses, by Bob Makransky, Makransky Sunshine, Bob Makransky, Treindl Sunshine -> Sunshine"
        ));
        assert!(rendered.contains("I sunshine"));
        assert!(rendered.contains(
            "S, S sripati, Śrīpati, Sripati house system, Sripati table of houses -> Sripati"
        ));
        assert!(rendered.contains("S sripati"));
        assert!(rendered.contains("P/K/R/C/O/E/W/N/V/A/H/B/M/S/I/G"));
        assert!(rendered.contains("plus the additional T/U/X/Y interoperability codes"));
        assert!(rendered.contains("A equal, E equal = A, Equal houses, Equal house system, Equal House, Equal table of houses, Wang, Equal (cusp 1 = Asc) -> Equal"));
        assert!(rendered.contains(
            "Equal Quadrant, Porphyry house system, Porphyry table of houses -> Porphyry"
        ));
        assert!(rendered.contains("Regiomontanus houses, Regiomontanus house system, Regiomontanus table of houses -> Regiomontanus"));
        assert!(rendered.contains(
            "Campanus houses, Campanus house system, Campanus table of houses -> Campanus"
        ));
        assert!(rendered.contains(
            "Alcabitius houses, Alcabitius house system, Alcabitius table of houses -> Alcabitius"
        ));
        assert!(rendered.contains("D equal / MC, Equal from MC, Equal (from MC), Equal (from MC) table of houses, Equal (MC) table of houses, Equal/MC table of houses, Equal (MC) house system, Equal/MC house system, Equal MC, Equal/MC, Equal Midheaven, Equal Midheaven house system, Equal Midheaven table of houses, Equal/MC = 10th -> Equal (MC)"));
        assert!(rendered.contains("Equal (MC) table of houses"));
        assert!(rendered.contains(
            "W equal, whole sign, Whole Sign houses, Whole Sign table of houses, Whole-sign, Whole Sign system, Whole Sign house system -> Whole Sign"
        ));
        assert!(
            rendered.contains("V equal Vehlow, Vehlow, Vehlow equal, Vehlow house system, Vehlow Equal house system, Vehlow-equal, Vehlow-equal table of houses, Vehlow Equal table of houses -> Vehlow Equal")
        );
        assert!(rendered.contains(
            "X, Meridian houses, Meridian table of houses, Meridian house system, ARMC, Axial Rotation, Axial rotation system, Zariel, X axial rotation system/ Meridian houses -> Meridian"
        ));
        assert!(rendered.contains("Axial variants, A -> Axial"));
        assert!(rendered.contains("Y, APC, Ram school, Ram's school, Ramschool, WvA, Y APC houses, APC houses, APC, also known as “Ram school”, table of houses, APC house system, Ascendant Parallel Circle -> APC"));
        assert!(rendered.contains("T, Polich-Page, Polich/Page, Polich Page, Polich-Page \"topocentric\" table of houses, T Polich/Page (\"topocentric\"), T topocentric, Topocentric house system, Topocentric table of houses -> Topocentric"));
        assert!(rendered.contains("Gauquelin sectors"));
        assert!(rendered
            .contains("G, Gauquelin, Gauquelin sector, Gauquelin sectors, Gauquelin table of sectors -> Gauquelin sectors"));
        assert!(rendered.contains("J2000"));
        assert!(rendered.contains("DeLuce"));
        assert!(rendered.contains("Yukteshwar"));
        assert!(rendered.contains("PVR Pushya-paksha"));
        assert!(rendered.contains(
            "Sunil Sheoran, Vedic Sheoran, Vedic / Sheoran, Sheoran ayanamsa, Sheoran true, True Sheoran ayanamsa, \"Vedic\"/Sheoran -> Sheoran"
        ));
        assert!(rendered.contains("True Revati"));
        assert!(rendered.contains("True Mula"));
        assert!(rendered.contains("Suryasiddhanta (Revati)"));
        assert!(rendered.contains("Suryasiddhanta (Citra)"));
        assert!(rendered.contains("Lahiri (ICRC)"));
        assert!(rendered.contains("Sassanian"));
        assert!(rendered.contains("Hipparchus"));
        assert!(rendered.contains("Babylonian (Kugler 1)"));
        assert!(rendered.contains("Babylonian (Aldebaran)"));
        assert!(rendered.contains("Babylonian (Eta Piscium)"));
        assert!(rendered.contains("Suryasiddhanta 499 CE"));
        assert!(rendered.contains("Surya Siddhanta 499 CE"));
        assert!(rendered.contains("Babylonian (House)"));
        assert!(rendered.contains("Babylonian (Sissy)"));
        assert!(rendered.contains("Babylonian (True Geoc)"));
        assert!(rendered.contains("Babylonian (True Topc)"));
        assert!(rendered.contains("Babylonian (True Obs)"));
        assert!(rendered.contains("Babylonian (House Obs)"));
        assert!(rendered.contains("Galactic Center"));
        assert!(rendered.contains("Dhruva Galactic Center (Middle Mula)"));
        assert!(rendered.contains("Galactic Equator"));
        assert!(rendered.contains("Compatibility caveats:"));
        assert!(rendered.contains("Placidus house system, Placidus table of houses -> Placidus"));
        assert!(rendered.contains("Porphyry house system, Porphyry table of houses -> Porphyry"));
        assert!(rendered.contains(
            "Regiomontanus houses, Regiomontanus house system, Regiomontanus table of houses -> Regiomontanus"
        ));
        assert!(rendered.contains(
            "Campanus houses, Campanus house system, Campanus table of houses -> Campanus"
        ));
        assert!(rendered.contains(
            "Alcabitius houses, Alcabitius house system, Alcabitius table of houses -> Alcabitius"
        ));
        assert!(rendered.contains("Equal (cusp 1 = Asc) -> Equal"));
        assert!(rendered.contains(
            "Koch houses, Koch house system, house system of the birth place, Koch table of houses, W. Koch, W Koch -> Koch"
        ));
        assert!(rendered.contains("Lahiri"));
        assert!(rendered.contains("Custom-definition labels:"));
        assert!(rendered.contains("- True Balarama"));
        assert!(rendered.contains("- Aphoric"));
        assert!(rendered.contains("- Takra"));
        assert!(rendered.contains("custom definitions"));
        let house_alias_count: usize = profile
            .house_systems
            .iter()
            .map(|entry| entry.aliases.len())
            .sum();
        let ayanamsa_alias_count: usize = profile
            .ayanamsas
            .iter()
            .map(|entry| entry.aliases.len())
            .sum();
        let ayanamsa_alias_bearing_entry_count: usize = profile
            .ayanamsas
            .iter()
            .filter(|entry| !entry.aliases.is_empty())
            .count();
        let ayanamsa_provenance = validated_provenance_summary_for_report()
            .expect("ayanamsa provenance summary should validate");
        assert_eq!(
            profile.ayanamsa_provenance_summary_line(),
            ayanamsa_provenance
        );
        assert_eq!(
            profile
                .validated_ayanamsa_provenance_summary_line()
                .expect("ayanamsa provenance summary should validate"),
            ayanamsa_provenance
        );
        assert!(rendered.contains(&format!(
            "Compatibility catalog inventory: house systems={} ({} baseline, {} release-specific, {} aliases); house formula families={}; house latitude-sensitive constraints={}; house-code aliases={}; ayanamsas={} ({} baseline, {} release-specific, {} aliases); custom-definition labels={}; custom-definition ayanamsa labels={} (Babylonian (House), Babylonian (Sissy), Babylonian (True Geoc), Babylonian (True Topc), Babylonian (True Obs), Babylonian (House Obs)); ayanamsa metadata gaps={}; ayanamsa alias-bearing entries={}; catalog posture=house systems={} ({} constrained, {} unconstrained); ayanamsas={} ({} descriptor-only, {} metadata-bearing); custom-only labels={}; custom-only ayanamsa labels={}; ayanamsa provenance={}; known gaps={}; claim audit: baseline catalogs are the published guarantees; release-specific entries are shipped additions; custom-definition labels remain custom-definition territory; descriptor-only ayanamsa entries remain catalog descriptors; constrained house systems stay explicitly flagged; known gaps stay documented",
            profile.house_systems.len(),
            profile.baseline_house_systems.len(),
            profile.release_house_systems.len(),
            house_alias_count,
            profile.house_formula_families_summary_line(),
            profile.latitude_sensitive_house_constraints_summary_line(),
            pleiades_houses::house_system_code_aliases().len(),
            profile.ayanamsas.len(),
            profile.baseline_ayanamsas.len(),
            profile.release_ayanamsas.len(),
            ayanamsa_alias_count,
            profile.custom_definition_labels.len(),
            profile.custom_definition_ayanamsa_labels().len(),
            metadata_coverage().without_sidereal_metadata.len(),
            ayanamsa_alias_bearing_entry_count,
            profile.house_systems.len(),
            profile.constrained_house_system_count(),
            profile
                .house_systems
                .len()
                .saturating_sub(profile.constrained_house_system_count()),
            profile.ayanamsas.len(),
            profile.ayanamsa_descriptor_only_count(),
            profile
                .ayanamsas
                .len()
                .saturating_sub(profile.ayanamsa_descriptor_only_count()),
            profile.custom_definition_labels.len(),
            profile.custom_definition_ayanamsa_labels().len(),
            ayanamsa_provenance,
            profile.known_gaps.len()
        )));
        assert!(rendered.contains("house systems: 25 total"));
        assert!(rendered.contains("ayanamsas: 59 total"));
        assert!(rendered.contains("ayanamsa metadata gaps=0"));
        assert_eq!(
            profile.catalog_posture_summary_line(),
            catalog_posture_summary_for_report()
        );
        assert_eq!(
            profile
                .validated_catalog_posture_summary_line()
                .expect("catalog posture summary should validate"),
            catalog_posture_summary_for_report()
        );
        assert!(rendered.contains("ayanamsa alias-bearing entries="));
    }

    #[test]
    fn canonical_name_helpers_preserve_release_and_baseline_order() {
        let profile = current_compatibility_profile();

        assert_eq!(
            profile.baseline_house_system_canonical_names(),
            profile
                .baseline_house_systems
                .iter()
                .map(|entry| entry.canonical_name)
                .collect::<Vec<_>>()
        );
        assert_eq!(
            profile.release_house_system_canonical_names(),
            profile
                .release_house_systems
                .iter()
                .map(|entry| entry.canonical_name)
                .collect::<Vec<_>>()
        );
        assert_eq!(
            profile.baseline_ayanamsa_canonical_names(),
            profile
                .baseline_ayanamsas
                .iter()
                .map(|entry| entry.canonical_name)
                .collect::<Vec<_>>()
        );
        assert_eq!(
            profile.release_ayanamsa_canonical_names(),
            profile
                .release_ayanamsas
                .iter()
                .map(|entry| entry.canonical_name)
                .collect::<Vec<_>>()
        );
    }

    #[test]
    fn catalog_inventory_summary_line_reports_the_house_code_alias_count() {
        let profile = current_compatibility_profile();
        let rendered = profile.catalog_inventory_summary_line();

        assert!(rendered.contains(&format!(
            "house-code aliases={}",
            profile.house_code_alias_count()
        )));
        assert!(rendered.contains(&format!(
            "custom-definition ayanamsa labels={} (Babylonian (House), Babylonian (Sissy), Babylonian (True Geoc), Babylonian (True Topc), Babylonian (True Obs), Babylonian (House Obs))",
            profile.custom_definition_ayanamsa_labels().len()
        )));
        assert!(rendered.contains("ayanamsa provenance=representative provenance examples:"));
        assert!(rendered.contains(&format!(
            "house latitude-sensitive constraints={}",
            profile.latitude_sensitive_house_constraints_summary_line()
        )));
        assert_eq!(
            profile.custom_definition_ayanamsa_labels(),
            metadata_coverage().custom_definition_only
        );
        assert_eq!(
            profile.custom_definition_ayanamsa_labels_summary_line(),
            "6 (Babylonian (House), Babylonian (Sissy), Babylonian (True Geoc), Babylonian (True Topc), Babylonian (True Obs), Babylonian (House Obs))"
        );
        assert_eq!(
            profile.validated_catalog_inventory_summary_line(),
            Ok(rendered.clone())
        );
        assert_eq!(
            validated_catalog_inventory_summary_for_report(),
            Ok(rendered.clone())
        );
        assert_eq!(
            profile.validated_house_code_aliases_summary_line(),
            Ok(profile.house_code_aliases_summary_line())
        );
    }

    #[test]
    fn catalog_inventory_summary_line_validation_rejects_invalid_profiles() {
        let profile = current_compatibility_profile();
        let invalid_profile = CompatibilityProfile {
            summary: "",
            ..profile
        };

        assert!(invalid_profile
            .validated_catalog_inventory_summary_line()
            .is_err());
        assert!(invalid_profile
            .validated_house_code_aliases_summary_line()
            .is_err());
        assert!(invalid_profile
            .validated_release_house_system_canonical_names_summary_line()
            .is_err());
        assert!(invalid_profile
            .validated_release_ayanamsa_canonical_names_summary_line()
            .is_err());
        assert!(invalid_profile
            .validated_house_formula_families_summary_line()
            .is_err());
        assert!(invalid_profile
            .validated_latitude_sensitive_house_systems_summary_line()
            .is_err());
        assert!(invalid_profile
            .validated_latitude_sensitive_house_constraints_summary_line()
            .is_err());
        assert!(invalid_profile
            .validated_custom_definition_ayanamsa_labels_summary_line()
            .is_err());
        assert_eq!(
            invalid_profile.to_string(),
            "Compatibility profile unavailable (compatibility profile summary is blank)"
        );
    }

    #[test]
    fn compatibility_caveats_summary_for_report_tracks_the_current_profile() {
        let profile = current_compatibility_profile();
        let release_profiles = crate::release_profiles::current_release_profile_identifiers();
        let rendered = compatibility_caveats_summary_for_report(&profile, &release_profiles);

        assert!(rendered.starts_with("Compatibility caveats summary\nProfile: "));
        assert!(rendered.contains(release_profiles.compatibility_profile_id));
        assert!(rendered.contains("House formula families: "));
        assert!(rendered.contains("Latitude-sensitive house systems: "));
        assert!(rendered.contains("Latitude-sensitive house failure modes: "));
        assert!(rendered.contains("Descriptor-only ayanamsa labels: "));
        let expected_prefix = format!(
            "Compatibility caveats summary\nProfile: {}\nCompatibility caveats: {}\nHouse formula families: {}\nLatitude-sensitive house systems: {}\nLatitude-sensitive house constraints: {}\nLatitude-sensitive house failure modes: {}\nDescriptor-only ayanamsa labels: {}\n",
            release_profiles.compatibility_profile_id,
            profile.known_gaps.len(),
            profile.house_formula_families_summary_line(),
            profile.latitude_sensitive_house_systems_summary_line(),
            profile.latitude_sensitive_house_constraints_summary_line(),
            profile.latitude_sensitive_house_failure_modes_summary_line(),
            profile.custom_definition_ayanamsa_labels_summary_line()
        );
        assert!(rendered.starts_with(&expected_prefix));
        for gap in profile.known_gaps {
            assert!(rendered.contains(gap));
        }
    }

    #[test]
    fn house_code_alias_inventory_summary_tracks_the_built_in_table() {
        let profile = current_compatibility_profile();
        let summary = profile.house_code_alias_inventory_summary();

        assert_eq!(summary.count(), house_system_code_aliases().len());
        assert_eq!(
            summary.summary_line(),
            profile.house_code_aliases_summary_line()
        );
        assert_eq!(
            summary.to_string(),
            profile.house_code_aliases_summary_line()
        );
        assert_eq!(
            summary.validated_summary_line(),
            Ok(profile.house_code_aliases_summary_line())
        );
        assert_eq!(
            summary
                .validate()
                .expect("built-in aliases should validate"),
            summary.count()
        );
    }

    #[test]
    fn house_code_alias_inventory_summary_validation_rejects_empty_inventory() {
        let summary = HouseCodeAliasInventorySummary::new(&[]);

        assert!(summary.validated_summary_line().is_err());
    }

    #[test]
    fn compatibility_profile_validation_rejects_release_summary_drift() {
        let mut profile = current_compatibility_profile();
        profile.summary = "Stage 6 release profile: all built-ins are fully complete and the compatibility catalog is already exhaustive";

        let error = profile
            .validate()
            .expect_err("summary overclaim should fail validation");
        assert_eq!(
            error,
            CompatibilityProfileValidationError::SummaryDoesNotDescribeReleaseSplit
        );
        assert!(profile.validated_release_note().is_err());
    }

    #[test]
    fn compatibility_profile_validation_rejects_release_summary_overstatement_even_with_required_phrases(
    ) {
        let mut profile = current_compatibility_profile();
        profile.summary = "Stage 6 release profile: the baseline catalogs remain published as a routine release artifact while the target Swiss-Ephemeris-class compatibility catalog stays explicit, and all built-ins are fully complete and exhaustive";

        let error = profile
            .validate()
            .expect_err("summary overstatement should fail validation");
        assert_eq!(
            error,
            CompatibilityProfileValidationError::SummaryDoesNotDescribeReleaseSplit
        );
    }

    #[test]
    fn compatibility_profile_validated_release_note_tracks_the_built_in_summary() {
        let profile = current_compatibility_profile();

        assert_eq!(profile.validated_release_note(), Ok(profile.release_note()));
    }

    #[test]
    fn house_code_alias_validation_accepts_the_built_in_table() {
        assert_eq!(
            validate_house_code_aliases(house_system_code_aliases())
                .expect("built-in house-code aliases should validate"),
            house_system_code_aliases().len()
        );
    }

    #[test]
    fn release_canonical_name_summaries_track_the_built_in_catalogs() {
        let profile = current_compatibility_profile();
        let house_summary =
            format_canonical_name_summary(&profile.release_house_system_canonical_names());
        let ayanamsa_summary =
            format_canonical_name_summary(&profile.release_ayanamsa_canonical_names());

        assert_eq!(
            profile.release_house_system_canonical_names_summary_line(),
            house_summary
        );
        assert_eq!(
            profile.release_ayanamsa_canonical_names_summary_line(),
            ayanamsa_summary
        );
        assert_eq!(
            profile.validated_release_house_system_canonical_names_summary_line(),
            Ok(house_summary.clone())
        );
        assert_eq!(
            profile.validated_release_ayanamsa_canonical_names_summary_line(),
            Ok(ayanamsa_summary.clone())
        );
        assert_eq!(
            release_house_system_canonical_names_summary_for_report(),
            house_summary
        );
        assert_eq!(
            release_ayanamsa_canonical_names_summary_for_report(),
            ayanamsa_summary
        );
        assert_eq!(
            validated_release_house_system_canonical_names_summary_for_report(),
            Ok(profile.release_house_system_canonical_names_summary_line())
        );
        assert_eq!(
            validated_release_ayanamsa_canonical_names_summary_for_report(),
            Ok(profile.release_ayanamsa_canonical_names_summary_line())
        );
        assert_eq!(
            profile.validated_house_formula_families_summary_line(),
            Ok(profile.house_formula_families_summary_line())
        );
        assert_eq!(
            profile.validated_latitude_sensitive_house_systems_summary_line(),
            Ok(profile.latitude_sensitive_house_systems_summary_line())
        );
        assert_eq!(
            profile.validated_custom_definition_ayanamsa_labels_summary_line(),
            Ok(profile.custom_definition_ayanamsa_labels_summary_line())
        );
    }

    #[test]
    fn house_code_alias_validation_rejects_duplicates_and_round_trip_drift() {
        let duplicate_aliases = [
            HouseSystemCodeAlias {
                label: "P",
                system: HouseSystem::Placidus,
            },
            HouseSystemCodeAlias {
                label: "p",
                system: HouseSystem::Placidus,
            },
        ];
        assert!(matches!(
            validate_house_code_aliases(&duplicate_aliases),
            Err(
                CompatibilityProfileValidationError::DuplicateTextSectionEntry {
                    section_label: "house-code-alias",
                    entry: "p"
                }
            )
        ));

        let drifted_aliases = [HouseSystemCodeAlias {
            label: "P",
            system: HouseSystem::Koch,
        }];
        assert!(matches!(
            validate_house_code_aliases(&drifted_aliases),
            Err(
                CompatibilityProfileValidationError::HouseCodeAliasDoesNotRoundTrip {
                    label: "P",
                    expected_system: HouseSystem::Koch,
                }
            )
        ));
    }
}
