# catalog/operations/analysis/robustness_map.yaml

id: analysis_robustness_map
name: Robustness Map
description: >
  Compute per-location robustness scores from a multiverse specification sweep.
  For each spatial unit (or H3 cell when sweeping across spatial units), calculates
  the fraction of specifications that agree on the direction or classification of
  the finding. Output is a vector layer with robustness_score, robustness_class,
  and majority_direction per geometry. Part of spatial multiverse analysis (UC-038).
version: 1.0.0
category: analysis
type: table-to-vector
default_implementation: rust-wasm

inputs:
  - name: multiverse_results
    type: table
    format: parquet
    description: >
      Per-geometry, per-specification results from a multiverse sweep.
      Must contain a geometry column and the effect column specified in params.
    required: true

# Structural: multiverse robustness scoring on any tabular sweep result.
# requires: omitted; output is a robustness metric, not a canonical concept.

outputs:
  - name: robustness
    type: vector
    format: geoparquet
    description: >
      One row per spatial unit with robustness_score [0,1], robustness_class
      (robust/moderate/fragile), n_specs, pct_agreement, and majority_direction.

params:
  effect_column:
    type: string
    description: Column name containing the effect value to evaluate
    required: true
  robustness:
    type: object
    description: Robustness scoring configuration
    properties:
      metric:
        type: string
        enum: [sign, threshold, classification]
        default: sign
        description: >
          How to determine "agreement" across specifications.
          sign: effect > 0 vs <= 0.
          threshold: effect > threshold vs <= threshold.
          classification: majority class agreement.
      field:
        type: string
        description: Column to evaluate (defaults to effect_column)
      threshold:
        type: float
        default: 0
        description: Threshold value for sign and threshold metrics
      h3_resolution:
        type: string
        default: auto
        description: >
          H3 hex resolution for the common grid when the multiverse sweeps
          across different spatial units. 'auto' selects resolution matching
          the finest input unit. Explicit values: 7-12.
      levels:
        type: object
        properties:
          robust:
            type: float
            default: 0.95
          moderate:
            type: float
            default: 0.75
          fragile:
            type: float
            default: 0

granularity:
  output_resolution: input_dependent
  requires_continuous: false
  suitable_aoi_km2:
    min: 1
    max: 500000

uncertainty:
  error_model: empirical
  factors:
    - name: specification_coverage
      description: >
        Robustness score quality depends on the diversity and defensibility
        of the specification space. A narrow sweep may show false robustness.
      severity: high
      mitigations:
        - "Include specifications spanning the full range of defensible choices"
        - "Document which choices were included and which were excluded"
    - name: h3_spatial_join
      description: >
        When sweeping across spatial units, results are joined to H3 cells.
        Boundary effects can assign a cell to the wrong polygon.
      severity: low
      mitigations:
        - "Use H3 resolution finer than the finest input unit"
  limitations:
    - "Robustness is relative to the specification space — it cannot detect threats from choices not included"
    - "H3 resampling introduces minor spatial artifacts at polygon boundaries"

execution:
  realtime_max_specs: 10000
  cost_per_spec: 0.0
  time_per_1000_specs_ms: 50
  memory_profile: medium
  profile:
    cpu: medium
    memory: medium
    io: medium
  scaling:
    model: linear
    parallelizable: true

cache_policy:
  ttl_days: 365
  invalidate_on: [source_update]

# backends: audited 2026-08-14 (defect 50). NOTHING dispatches this op: no arm in folia-engine
# `dispatch_op`, no `registerOp`/OP_TABLE entry in packages/compute, no `_BUILTIN_OP_MAP`
# key in folia/compute.py, no backend manifest. Declared EMPTY on purpose — an absent
# block would be indistinguishable from one nobody ever wrote.
backends: {}

display_hints:
  map:
    renderer: maplibre
    type: fill
    style:
      fill-color:
        property: robustness_class
        stops:
          robust: "#2d6a4f"
          moderate: "#e9c46a"
          fragile: "#e76f51"
      fill-opacity: 0.7
  table:
    renderer: tanstack-table
  chart:
    renderer: observable-plot
    chartType: bar
    description: Distribution of robust/moderate/fragile locations

ui:
  icon: shield-check
  color: "#059669"

references:
  - Ilyushchenko, S. (2026). Spatial Multiverse Analysis. AAG 2026.
  - Kedron, P. & Holler, J. (2026). Recent advances in R&R in CaGIS.
  - Openshaw, S. (1984). The Modifiable Areal Unit Problem.
