{
    "product": "reference_territory_data_spine",
    "status": "spine_ready",
    "generated_at": "2026-07-07T23:15:33+00:00",
    "grain_count": 5,
    "source_mapping_count": 7,
    "aggregation_rule_count": 6,
    "output_table_count": 3,
    "input_template": "sources/reference-territory-data-spine-template.csv",
    "grains": [
        {
            "key": "commune",
            "label": "Commune",
            "canonical_key": "insee_code",
            "use_case": "Maille pivot pour normaliser OpenAgenda, BNLC, Merimee, BPE, Agence Bio, DATAtourisme et adresses.",
            "aggregation_rule": "Sommer les comptes, conserver les dates sources, calculer les scores par famille puis remonter aux mailles superieures.",
            "public_caveat": "La commune est une maille administrative, pas toujours le perimetre percu d une destination vin."
        },
        {
            "key": "destination",
            "label": "Destination / territoire touristique",
            "canonical_key": "destination_key",
            "use_case": "Page territoire publique, comparaison destination, storytelling oenotourisme.",
            "aggregation_rule": "Agreger les communes rattachees, dedupliquer les POI geolocalises et afficher le perimetre retenu.",
            "public_caveat": "Une destination peut croiser plusieurs communes, labels ou offices ; le perimetre doit etre documente."
        },
        {
            "key": "appellation",
            "label": "Appellation / zone vin",
            "canonical_key": "appellation_key",
            "use_case": "Croiser les donnees vin reglementaires, parcelles, communes AOC/AOP et signaux touristiques.",
            "aggregation_rule": "Utiliser le rattachement officiel disponible puis croiser par communes ou geometries quand la source le permet.",
            "public_caveat": "Une appellation ne correspond pas toujours a une destination touristique ni a une commune unique."
        },
        {
            "key": "department",
            "label": "Departement",
            "canonical_key": "department_code",
            "use_case": "Lecture macro, comparaison robuste quand la maille communale est insuffisante.",
            "aggregation_rule": "Remonter depuis les communes ou utiliser directement les sources deja publiees au departement.",
            "public_caveat": "Le departement gomme les differences fortes entre destinations viticoles internes."
        },
        {
            "key": "region",
            "label": "Region",
            "canonical_key": "region_code",
            "use_case": "Suivi source, couverture DATAtourisme, comparaisons nationales et analyses de marche.",
            "aggregation_rule": "Utiliser les fichiers regionaux quand disponibles, sinon remonter par departements/communes.",
            "public_caveat": "La region est utile pour la couverture source, moins pour l experience visiteur locale."
        }
    ],
    "source_map": [
        {
            "source_key": "datatourisme_fidelity_monitor",
            "label": "DATAtourisme",
            "native_keys": [
                "regional_file",
                "poi_identifier",
                "coordinates",
                "address"
            ],
            "preferred_join_key": "insee_code_or_coordinates",
            "fallback_join": "geocode address if needed, then map to commune",
            "kpi_families": [
                "offer_signal",
                "tourism_poi_density",
                "source_freshness"
            ],
            "grain_support": [
                "commune",
                "destination",
                "department",
                "region"
            ],
            "conflict_policy": "Deduplicate POI by stable identifier, URL, title and coordinates before aggregation."
        },
        {
            "source_key": "insee_bpe_equipment",
            "label": "Insee BPE",
            "native_keys": [
                "GEO",
                "GEO_OBJECT",
                "FACILITY_DOM",
                "FACILITY_TYPE",
                "TIME_PERIOD"
            ],
            "preferred_join_key": "GEO + GEO_OBJECT",
            "fallback_join": "use published aggregate grain directly when commune is unavailable",
            "kpi_families": [
                "tourism_service_density",
                "territory_readiness_score",
                "access_equipment_signal"
            ],
            "grain_support": [
                "commune",
                "department",
                "region"
            ],
            "conflict_policy": "Never mix commune and department values in the same comparison column."
        },
        {
            "source_key": "openagenda_wine_events",
            "label": "OpenAgenda wine events",
            "native_keys": [
                "uid",
                "location_insee",
                "location_coordinates",
                "firstdate_begin",
                "originagenda_uid"
            ],
            "preferred_join_key": "location_insee",
            "fallback_join": "coordinates to commune, then event URL/title for deduplication",
            "kpi_families": [
                "wine_event_signal",
                "territory_activation_score",
                "tourism_demand_context"
            ],
            "grain_support": [
                "commune",
                "destination",
                "department",
                "region"
            ],
            "conflict_policy": "Count events by occurrence window and agenda; do not infer attendance."
        },
        {
            "source_key": "bnlc_carpooling_areas",
            "label": "BNLC covoiturage",
            "native_keys": [
                "id_lieu",
                "insee",
                "Xlong",
                "Ylat",
                "date_maj"
            ],
            "preferred_join_key": "insee",
            "fallback_join": "coordinates to commune when INSEE missing",
            "kpi_families": [
                "carpool_access_signal",
                "low_carbon_access_score"
            ],
            "grain_support": [
                "commune",
                "destination",
                "department"
            ],
            "conflict_policy": "Use open places only for strong access signal; keep unknown status as weak context."
        },
        {
            "source_key": "merimee_heritage_monuments",
            "label": "Merimee monuments",
            "native_keys": [
                "Reference",
                "COG_Insee_lors_de_la_protection",
                "coordonnees_au_format_WGS84"
            ],
            "preferred_join_key": "COG_Insee_lors_de_la_protection",
            "fallback_join": "coordinates to commune, then commune label with caution",
            "kpi_families": [
                "heritage_pull_signal",
                "culture_wine_context_score"
            ],
            "grain_support": [
                "commune",
                "destination",
                "department",
                "region"
            ],
            "conflict_policy": "Deduplicate by monument reference; do not merge with BPE culture unless explicitly mapped."
        },
        {
            "source_key": "agencebio_organic_wine",
            "label": "Agence Bio",
            "native_keys": [
                "siret_or_operator_id",
                "commune",
                "insee_code",
                "year"
            ],
            "preferred_join_key": "insee_code + year",
            "fallback_join": "commune label + department only for internal review",
            "kpi_families": [
                "organic_wine_signal",
                "organic_viticulture_surface_signal"
            ],
            "grain_support": [
                "commune",
                "department",
                "region"
            ],
            "conflict_policy": "Keep operators and surfaces separate unless the source explicitly supports the merge."
        },
        {
            "source_key": "insee_tourism_occupancy",
            "label": "Insee tourism occupancy",
            "native_keys": [
                "territory_code",
                "accommodation_type",
                "time_period"
            ],
            "preferred_join_key": "published statistical territory + time_period",
            "fallback_join": "do not downscale to commune without a documented allocation rule",
            "kpi_families": [
                "tourism_demand_signal",
                "demand_offer_balance"
            ],
            "grain_support": [
                "department",
                "region",
                "statistical_zone"
            ],
            "conflict_policy": "Do not compare demand values against commune-level offer without a visible caveat."
        }
    ],
    "aggregation_rules": [
        {
            "key": "counts_sum_scores_recompute",
            "label": "Comptes sommer, scores recalculer",
            "rule": "Sommer les counts sources, mais recalculer les scores composites a la maille cible.",
            "example": "Additionner les evenements OpenAgenda par commune, puis recalculer `wine_event_signal` pour la destination."
        },
        {
            "key": "freshness_minimum_visible",
            "label": "Fraicheur minimale visible",
            "rule": "Le score public d un KPI multi-source doit exposer la source la plus ancienne ou la plus fragile.",
            "example": "Une page territoire avec DATAtourisme frais et BPE annuel garde la date BPE dans la note methode."
        },
        {
            "key": "no_grain_mixing",
            "label": "Pas de melange de mailles",
            "rule": "Ne pas comparer deux territoires si les KPI ne partagent pas une maille ou une regle de remontée compatible.",
            "example": "Ne pas classer une commune contre un departement sur `tourism_demand_signal`."
        },
        {
            "key": "dedupe_before_counting",
            "label": "Dedupliquer avant de compter",
            "rule": "Les POI, monuments, evenements et lieux doivent etre dedupliques avant aggregation.",
            "example": "Un monument Merimee lie a plusieurs notices externes reste un seul monument via `monument_ref`."
        },
        {
            "key": "caveat_union",
            "label": "Union des caveats",
            "rule": "Le caveat public d un KPI composite doit inclure les limites des sources contribuant au score.",
            "example": "Un score acces mentionne infrastructure BNLC et absence de mesure d usage reel."
        },
        {
            "key": "confidence_weighted_by_source",
            "label": "Confiance ponderee par source",
            "rule": "Le score de confiance doit penaliser les jointures faibles, les mailles impropres et les dates absentes.",
            "example": "Une jointure par libelle commune sans code INSEE reste internal_only ou publish_with_caveat."
        }
    ],
    "outputs": [
        {
            "key": "territory_source_fact_table",
            "label": "Table faits source territoire",
            "grain": "territory_key + source_key + time_period",
            "purpose": "Stocker les valeurs normalisees avant scoring composite.",
            "minimum_columns": [
                "territory_key",
                "territory_grain",
                "source_key",
                "kpi_family",
                "value",
                "unit",
                "time_period",
                "source_url",
                "caveat"
            ]
        },
        {
            "key": "territory_kpi_score_table",
            "label": "Table scores KPI territoire",
            "grain": "territory_key + kpi_key + time_period",
            "purpose": "Publier des KPI recalcules, comparables et copiables.",
            "minimum_columns": [
                "territory_key",
                "kpi_key",
                "score",
                "confidence_score",
                "publication_status",
                "method_note",
                "copy_block"
            ]
        },
        {
            "key": "territory_publication_context",
            "label": "Contexte publication territoire",
            "grain": "territory_key",
            "purpose": "Assembler sources, KPI, caveats et prochaines actions pour les pages publiques.",
            "minimum_columns": [
                "territory_key",
                "hero_claim",
                "primary_sources",
                "missing_sources",
                "public_caveat",
                "next_source_action"
            ]
        }
    ],
    "publication_caveat": "La spine definit les regles d assemblage et de publication. Elle ne remplace pas la normalisation effective, la geocodification ou les controles runtime."
}