# Weather & Property Lab

Dependency-free static prototypes at `index.html#property`, `index.html#portfolio` and `index.html#evidence`. There is no build step or package installation. Serve this directory using any static HTTP server; ES modules should not be opened via `file://`.

```powershell
# From this weather directory:
python -m http.server 8091 --bind 127.0.0.1
# Open http://127.0.0.1:8091/
node --test domain.test.mjs
```

## Three distinct workflows

1. **Property brief** asks for one location and retrieves public agency facts on demand. Independent source failures stay visible. Flood map attributes, river stage, station observations and forecast/alerts remain distinct. JSON export includes raw responses, source queries and SHA-256 hashes where supported.
2. **Portfolio scenario** imports local CSV or six clearly synthetic properties. A user-selected center/radius, uniform damage assumption, retention and per-location limit produce deterministic stress-test arithmetic. Great-circle distance selects properties. This is not a probability, actuarial expected-loss model, policy interpretation or coverage quote. Imported values are not uploaded.
3. **Evidence timeline** searches USGS earthquake records by radius/date and OpenFEMA incident windows by state/date. Users can copy current NWS evidence or add separately labeled analyst notes. Exports include query scope, time, provenance, failures and capped query status. Source hashes do not make this a certified evidence package.

No cloud storage, accounts, analytics, API keys or scheduled retrieval are implemented. All state is in memory; export before leaving. Government providers receive query coordinates/dates and network metadata when requested. Portfolio CSV never leaves the browser. Location names and analyst notes are not sent to agencies. Browser geolocation is requested only after a click.

## Integrating with existing SkyNow

Existing reviewed source: `Codex/2026-09-26/i-want-to-start-a-weather/skynow`. The actual weather app is React/TypeScript, uses NWS provenance/freshness semantics, and is hosted by Cloudflare Workers with a D1 community database. This workbench does not edit it or apply a migration.

- Link each workflow independently first, preserving the distinction between live official records and assumptions. The parent lab can link directly to the hash routes.
- Reuse SkyNow's `Place` (`latitude`, `longitude`, `name`) and selected point. An integration adapter should map it to this prototype's `{lat,lon}`; do not confuse coordinate ordering.
- Use `src/lib/weather.ts` and `docs/NWS-GRID-LAYERS.md` as the contract for current forecasts. Existing values retain null, native units, actual grid and source timestamps; do not replace them with missing-value fallbacks.
- Share cached NWS point/grid/station calls when ported. The prototype uses separate calls because it is standalone. Never add independent loops per policy/property. The future backend should use the existing Cloudflare boundary with strict fixed provider hosts and bounded requests, not an open proxy.
- `API_CATALOG.md` documents incremental fields and new hazard providers. `api-registry.json` has 17 source entries with status, auth/cost, cadence, identity keys, geographic/time caveats and proposed P&C use. Hourly forecast and station discovery have distinct registry identities.
- `schema.sql` is a **proposal**, deliberately not applied to SkyNow's existing D1 database. Keep community reports separate and preserve current tables/ownership. Future exposure and evidence tables require authentication before a hosted API. Raw hazard responses belong in object storage, referenced by hash.
- Future portfolio footprint overlay needs a real geographic hazard footprint and explicit vulnerability method. The current circular scenario is intentionally user-assumed. Do not relabel it as a storm prediction.

## Validation and limits

Seven focused Node tests passed on 2026-10-10, covering malformed CSV, duplicate IDs, blank numeric fields, zero values, great-circle distances, scenario arithmetic, date bounds and spreadsheet-formula export handling. JavaScript syntax checks passed. The proposed schema parsed in an in-memory SQLite database (11 tables); the registry parsed with 15 unique source IDs.

Bounded live server requests on 2026-10-10 confirmed NFHL point queries, OpenFEMA date-overlap declarations, USGS Water v1 stage and USGS earthquake queries. SWDI hail archive and source metadata were also inspected for the catalog. A `latest-continuous` response included a discontinued 2013 gauge; the UI therefore labels source age explicitly. NFHL reports native NAD83, query input is explicit WGS84. Exact-source URLs and fetched times are recorded in exported packets.

Requests with the browser's Accept header and Origin `https://insurance.levitizedlife.com` returned HTTP 200 and permissive CORS headers for NWS alerts, OpenFEMA declarations, USGS Water and NFHL. Browser source access still depends on availability; terminal success alone does not prove browser interaction. Hosted Chrome smoke on 2026-10-10 passed property fetching (7/7 source requests), portfolio calculation and zero-damage behavior, NWS-to-timeline copy, dated evidence fetching and an unverified analyst note. A discovered unknown-end-date matching issue was fixed: old declarations with a missing end are no longer assumed ongoing. The corrected bounded query returns 27 current-window area records instead of the capped 100 containing old records. The workbench has not been tested on a physical phone. No historical alert archive, paid certification, continuous monitoring, live portfolio overlay, automated claims decisions or guaranteed data completeness is claimed. NWS feeds cover U.S. service areas; unsupported coordinates can still return other providers while NWS fails.

## Manual smoke test

Fetch the default brief once; confirm labels and independent provider status. Run the synthetic scenario, set damage to zero and confirm no assumed damage. Import malformed CSV and confirm an error. Fetch an evidence window, add one analyst note and export JSON/CSV. Confirm an empty query is retained in the source log. No need for repeated wide API sweeps or cosmetic testing.

Hosted smoke evidence: the default 80 km / 10% synthetic scenario selected three properties, $1,555,000 in replacement value, $155,500 gross assumed damage and $140,500 after assumed terms. At 0% both damage totals were $0. The raw-source property JSON downloaded successfully, parsed, and all seven recorded SHA-256 hashes matched independent recalculation of their original response text. Browser download-event automation timed out even though the file was successfully saved; filesystem inspection established the result.

## Offline database handoff

`import-bundle.mjs` is a dependency-free Node CLI/module for the **JSON** exports from the property or evidence workflow. It prepares a new local directory; it makes no requests, opens no database, and uploads nothing. Use Node 20 or newer. The output parent directory must already exist, and the output directory must not exist.

```powershell
# From this weather directory, after exporting in the browser:
node import-bundle.mjs "$env:USERPROFILE/Downloads/property-brief.json" ./property-import-001
# Or use event-evidence.json and a different NEW directory.
node --test import-bundle.test.mjs
```

Review `manifest.json` first. It lists each request, source status, cap/error reasons, raw hash, skipped repeats and artifact hashes. `raw/<sha256>.json` retains exact UTF-8 response text. `statements.json` contains fixed SQL statements with separate bound parameters for a future adapter. `import.sql` is the equivalent reviewable SQLite script: text values are UTF-8 hex casts, so URLs and error messages cannot become SQL syntax. No strings from the export are used as table names or column names.

The script only inserts `source_registry` and `ingest_run` rows into the existing proposal in `schema.sql`. It produces no normalized hazards, measurements, analyst notes, portfolio/exposure rows or evidence-packet rows. Property names, location objects, timeline items and search metadata are omitted; the original provider request URLs still contain query coordinates and dates. Keep your bundle local and review before sharing it.

For a future **separate local SQLite database**, apply `schema.sql` first, then the generated `import.sql`, or bind each statement from `statements.json` in one transaction with foreign keys enabled. The CLI does not execute that step. Source entries begin disabled (`enabled=0`); existing registry settings are preserved. Run IDs are deterministic SHA-256 identifiers over canonical source ID, original request URL, exported request time and raw hash. Identical copied sources collapse within a packet and repeated imports skip existing primary keys. Different retrieval times remain separate runs even when response bytes match. Conflicting repeated metadata within a packet is rejected; conflicts with an existing database cannot be inspected offline and existing rows win.

| Export ID | Registry ID | Required HTTPS host |
| --- | --- | --- |
| `nws-point` | `nws-points` | `api.weather.gov` |
| `nws-alerts` | `nws-alerts` | `api.weather.gov` |
| `nws-hourly` | `nws-hourly` | `api.weather.gov` |
| `nws-stations` | `nws-stations` | `api.weather.gov` |
| `nws-observation` | `nws-observations` | `api.weather.gov` |
| `fema-nfhl` | `fema-nfhl` | `hazards.fema.gov` |
| `usgs-water` | `usgs-water` | `api.waterdata.usgs.gov` |
| `usgs-earthquakes` | `usgs-earthquakes` | `earthquake.usgs.gov` |
| `openfema-declarations` | `openfema-declarations` | `www.fema.gov` |

All input sources are validated before the output directory is created. A missing or mismatched hash, mirrored-data mismatch, unsupported ID/status, non-HTTPS or unexpected host, credentials, custom port or URL fragment rejects the packet. Successful entries require original raw text; unsuccessful entries without a response are retained without inventing one. Empty, unavailable, invalid and partial states remain distinct. Pagination, provider transfer-limit flags and reached request caps are recorded without fetching another page. A hash verifies internal consistency, not agency authenticity or complete hazard coverage.

Limits: 32 MB UTF-8 input, 128 source entries, 6 MB per raw response, 24 MB combined raw responses (including repeats), and 40 MB output. These are importer limits, not provider quotas. Browser exports without SHA-256 cannot be imported as successful evidence; re-export from a secure browser context. The browser recorded `fetchedAt` **before** its request, so this importer stores it as `started_at`; actual completion time, HTTP status, headers and cursors remain NULL. Raw provider pagination signals remain in the response and manifest. Source timestamps and hazard semantics are not normalized. Output reservation and exclusive writes prevent overwrite; a local write failure attempts cleanup of only files created by that invocation.

Importer validation on 2026-10-10: four focused Node tests passed, including a tampered last source leaving no output, repeated-source deduplication and refusal to overwrite, partial/failure retention, and host/hash/size guards. The actual seven-source browser property export passed all checks. In-memory SQLite verification applied generated SQL twice and retained exactly seven runs; separately bound statements produced identical rows. Every generated artifact hash matched, registry entries remained disabled, and no hazard/exposure records were created. Temporary test bundles were removed. The registry now has 17 entries; the importer accepts only the nine export IDs listed above.
