| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| import type { StoneKey, StoneMember } from '$lib/types/card'; |
|
|
| export type RegistryEntry = { |
| |
| id: string; |
| |
| name: string; |
| |
| stepNames: string[]; |
| |
| tier?: StoneMember['tier']; |
| |
| |
| |
| skipReason?: string; |
| }; |
|
|
| export const STONE_REGISTRY: Record<StoneKey, RegistryEntry[]> = { |
| cornerstone: [ |
| { |
| id: 'CORN-001', |
| name: 'sandy_inundation.lookup', |
| stepNames: ['sandy', 'sandy_inundation', 'sandy_nta'], |
| tier: 'empirical', |
| skipReason: 'Sandy 2012 inundation: query outside NYC bounds', |
| }, |
| { |
| id: 'CORN-002', |
| name: 'dep_stormwater.lookup', |
| stepNames: ['dep', 'dep_stormwater', 'dep_extreme_2080_nta', 'dep_moderate_2050_nta', 'dep_moderate_current_nta'], |
| tier: 'modeled', |
| skipReason: 'NYC DEP stormwater scenarios: query outside NYC bounds', |
| }, |
| { |
| id: 'CORN-003', |
| name: 'usgs_hwm.spatial_join', |
| stepNames: ['ida_hwm', 'ida_hwm_2021'], |
| tier: 'empirical', |
| skipReason: 'USGS Ida HWMs: no marks within 800 m of address', |
| }, |
| { |
| id: 'CORN-004', |
| name: 'prithvi_water.lookup', |
| stepNames: ['prithvi', 'prithvi_eo_v2'], |
| tier: 'modeled', |
| skipReason: 'Prithvi-EO Ida polygons: no polygons within 500 m', |
| }, |
| { |
| id: 'CORN-005', |
| name: 'microtopo.dem_hand_twi', |
| stepNames: ['microtopo', 'microtopo_lidar', 'microtopo_nta'], |
| tier: 'proxy', |
| skipReason: 'USGS 3DEP DEM: query outside NYC raster coverage', |
| }, |
| ], |
| keystone: [ |
| { |
| id: 'KEY-001', |
| name: 'mta_entrance_exposure', |
| stepNames: ['mta_entrances', 'mta_entrance_exposure'], |
| tier: 'empirical', |
| skipReason: 'no entrances within radius', |
| }, |
| { |
| id: 'KEY-002', |
| name: 'nycha.development_join', |
| stepNames: ['nycha', 'nycha_development_exposure'], |
| tier: 'empirical', |
| skipReason: 'no NYCHA developments within 1.0 mi', |
| }, |
| { |
| id: 'KEY-003', |
| name: 'doe.school_join', |
| stepNames: ['doe_schools', 'doe_school_exposure'], |
| tier: 'empirical', |
| skipReason: 'no DOE schools within 1.0 mi', |
| }, |
| { |
| id: 'KEY-004', |
| name: 'doh.facility_join', |
| stepNames: ['doh_hospitals', 'doh_hospital_exposure'], |
| tier: 'empirical', |
| skipReason: 'no acute-care hospitals within 1.0 mi', |
| }, |
| { |
| id: 'KEY-005', |
| name: 'pluto.lot_lookup', |
| stepNames: ['pluto_lookup'], |
| tier: 'empirical', |
| skipReason: 'PLUTO join skipped: queried address not in NYC PLUTO dataset', |
| }, |
| { |
| id: 'KEY-006', |
| name: 'terramind.buildings', |
| stepNames: ['terramind_buildings', 'terramind_synthesis'], |
| tier: 'modeled', |
| skipReason: 'TerraMind Buildings: no eo_chip available for this address (recent <30% cloud Sentinel-2 missing) or no high-confidence prediction', |
| }, |
| ], |
| touchstone: [ |
| { |
| id: 'TCH-001', |
| name: 'floodnet.history', |
| stepNames: ['floodnet'], |
| tier: 'empirical', |
| skipReason: 'FloodNet sensor: no deployments within 600 m', |
| }, |
| { |
| id: 'TCH-002', |
| name: 'nyc311.flood_complaints', |
| stepNames: ['nyc311', 'nyc311_nta'], |
| tier: 'proxy', |
| skipReason: 'NYC 311: no flood-relevant complaints within 200 m', |
| }, |
| { |
| id: 'TCH-003', |
| name: 'nws_obs.metar', |
| stepNames: ['nws_obs'], |
| tier: 'empirical', |
| skipReason: 'NWS hourly METAR: nearest ASOS reports no precipitation', |
| }, |
| { |
| id: 'TCH-004', |
| name: 'noaa_coops.recent', |
| stepNames: ['noaa_tides'], |
| tier: 'empirical', |
| skipReason: 'NOAA tide gauge: nearest station >25 km from address', |
| }, |
| { |
| id: 'TCH-005', |
| name: 'prithvi_nyc_pluvial', |
| stepNames: ['prithvi_live', 'prithvi_eo_live'], |
| tier: 'modeled', |
| skipReason: 'Prithvi-NYC-Pluvial: no <30% cloud Sentinel-2 chip available in the last 120 d for this address', |
| }, |
| { |
| id: 'TCH-006', |
| name: 'terramind.lulc', |
| stepNames: ['terramind_lulc'], |
| tier: 'synthetic', |
| skipReason: 'TerraMind LULC: eo_chip fetch returned no Sentinel-2 tile for this address', |
| }, |
| ], |
| lodestone: [ |
| { |
| id: 'LOD-001', |
| name: 'nws_alerts.flood_relevant', |
| stepNames: ['nws_alerts'], |
| tier: 'modeled', |
| skipReason: 'NWS public alerts: no active flood-relevant alerts at this address', |
| }, |
| { |
| id: 'LOD-002', |
| name: 'ttm_battery_surge.zero_shot', |
| stepNames: ['ttm_forecast'], |
| tier: 'modeled', |
| skipReason: 'Granite TTM r2 zero-shot: forecast not interesting (peak |residual| < 0.3 ft)', |
| }, |
| { |
| id: 'LOD-003', |
| name: 'ttm_battery_surge.fine_tune', |
| stepNames: ['ttm_battery_surge'], |
| tier: 'modeled', |
| skipReason: 'Granite TTM Battery fine-tune: forecast not interesting (peak |residual| < 0.3 m)', |
| }, |
| { |
| id: 'LOD-004', |
| name: 'ttm_311_forecast', |
| stepNames: ['ttm_311_forecast'], |
| tier: 'modeled', |
| skipReason: 'NYC 311 weekly forecast: no per-address history to extrapolate', |
| }, |
| { |
| id: 'LOD-005', |
| name: 'floodnet_forecast', |
| stepNames: ['floodnet_forecast'], |
| tier: 'modeled', |
| skipReason: 'FloodNet sensor recurrence: sensor has < silent-floor historical events; forecast omitted', |
| }, |
| { |
| id: 'LOD-006', |
| name: 'npcc4.slr_projection', |
| stepNames: ['npcc4_projection'], |
| tier: 'modeled', |
| skipReason: 'NPCC4 SLR projection: harbor-wide static reference — see Battery tide gauge in Touchstone', |
| }, |
| ], |
| capstone: [ |
| { |
| id: 'CAP-001', |
| name: 'rag.granite_embedding', |
| stepNames: ['rag_granite_embedding'], |
| tier: 'proxy', |
| skipReason: 'Granite Embedding RAG: no policy retrieval (out-of-NYC scope)', |
| }, |
| { |
| id: 'CAP-002', |
| name: 'gliner.typed_extraction', |
| stepNames: ['gliner_extract'], |
| tier: 'proxy', |
| skipReason: 'GLiNER typed extraction: no RAG hits to extract over', |
| }, |
| { |
| id: 'CAP-003', |
| name: 'granite41.compose_briefing', |
| stepNames: ['reconcile_granite41', 'mellea_reconcile_address', 'reconcile_neighborhood', 'reconcile_development', 'reconcile_live_now'], |
| tier: 'modeled', |
| skipReason: 'Reconciler did not run (no grounded data available)', |
| }, |
| { |
| id: 'CAP-004', |
| name: 'mellea.grounding_check', |
| stepNames: ['mellea_grounding'], |
| tier: 'modeled', |
| skipReason: 'Mellea grounding-check: rolled into reconcile step on this run', |
| }, |
| ], |
| }; |
|
|
| |
| |
| |
| |
| export function fillRosterForStone( |
| stone: StoneKey, |
| liveMembers: StoneMember[], |
| ): StoneMember[] { |
| const registry = STONE_REGISTRY[stone] ?? []; |
| |
| const liveByStep = new Map<string, StoneMember>(); |
| for (const m of liveMembers) { |
| liveByStep.set(m.name, m); |
| } |
|
|
| const out: StoneMember[] = []; |
| const used = new Set<string>(); |
|
|
| for (const entry of registry) { |
| let live: StoneMember | undefined; |
| for (const sn of entry.stepNames) { |
| const hit = liveByStep.get(sn); |
| if (hit) { |
| live = hit; |
| used.add(sn); |
| break; |
| } |
| } |
| if (live) { |
| out.push({ |
| ...live, |
| |
| |
| id: entry.id, |
| name: entry.name, |
| tier: live.tier ?? entry.tier ?? null, |
| }); |
| } else { |
| out.push({ |
| id: entry.id, |
| name: entry.name, |
| status: 'not_invoked', |
| tier: entry.tier ?? null, |
| note: entry.skipReason, |
| }); |
| } |
| } |
|
|
| |
| |
| |
| for (const m of liveMembers) { |
| if (!used.has(m.name)) out.push(m); |
| } |
|
|
| return out; |
| } |
|
|