…activations
A sixth Netlify dashboard ("Pattern B"). Answers "a wildfire just activated —
which products should I deploy?" without reading nine algorithm_config.yaml
files, and turns an activation request into a prefilled PR.
Three tabs: Submit (the form, landing tab), Events (event catalog = published
activations + submitted requests), Algorithms (filter by hazard / sensor /
product / modality / date / event, via two entry modes that share one filter
state).
Catalog data is hand-curated from disasters-product-algorithms@dev: 8
algorithms, 33 products, 3 seed activations, 10 hazards. Products carry both
`hazards` (broad — drives discovery) and `primaryHazards` (sparse — drives the
form's auto-selection, so picking Fire proposes 7 products, not 21).
Standards enforcement is the point of the app. Upstream, hazard is a free-text
token in slot 2 of an activation_event string and nothing ever checked it,
which is why Fire/Wildfire and Quake/Earthquake both exist in the wild. Here
the rules live once in src/rules.ts, are mirrored 1:1 in
scripts/validate_data.py, kept honest by scripts/rules_parity_test.py, and
gated in CI. They are enforced live in the form (submit disabled while any
error stands), over the committed data, and on every PR.
The STAC event-name rule is deliberately STRICTER than upstream: it requires
exactly two underscores. Upstream's regex ends in `.+`, so the location slot
swallows extras and 202501_Tropical_Cyclone_CA passes while silently parsing
as hazard="Tropical". This also rejects 202501_Flood_CA_extra, an explicit
upstream pass case. Diverging this way is safe — every name we accept, DPS
accepts too.
- Vite React SPA, zero new runtime deps (React + ReactDOM only)
- HDS (NASA Horizon Design System) over USWDS, light-only
- Thumbnails are public-domain NASA Worldview/GIBS + JPL imagery, bundled
- .gitignore gets `!` negations rather than dse-hub's `git add -f` route, so
a plain `git add algorithm-catalog/` picks the JSON up
Verified: validate_data.py and rules_parity_test.py exit 0, typecheck and
build clean, and a 16-check Playwright pass against the dev server covering
the tab order, live filtering, thumbnail loading, and five negative
event-name cases that must each block submission.
A sixth Netlify dashboard ("Pattern B"). It answers "a wildfire just activated — which products should I deploy?" without reading nine
algorithm_config.yamlfiles, and turns an activation request into a prefilled PR.Three tabs
run_command, noram_min, no secrets.events.jsonplus submitted requests, badgedpublished/submitted. Search + hazard + date filters.Data
Hand-curated from
Disasters-Learning-Portal/disasters-product-algorithms@dev: 8 algorithms, 33 products, 3 seed activations, 10 hazards.Products carry two hazard lists on purpose:
hazards— broad, drives discovery (a false negative is worse than a false positive when you're searching)primaryHazards— sparse, drives the form's auto-selectionThat split is why picking
Firenow proposes 7 products instead of 21.Standards enforcement — the point of the app
Upstream, hazard is a free-text token in slot 2 of an
activation_eventstring and nothing ever checked it. That is whyFire/WildfireandQuake/Earthquakeboth exist in the wild today.Here the rules live once in
src/rules.ts, are mirrored 1:1 inscripts/validate_data.py, kept honest byscripts/rules_parity_test.py, and gated in CI. Three layers:validate_data.pyalso checks referential integrity (every event layer resolves,primaryHazards ⊆ hazards, every thumbnail exists on disk).algorithm-catalog-validate.ymlfails the PR, so hand-written files can't route around the form.One deliberate divergence from upstream
The STAC event-name rule requires exactly two underscores. Upstream's regex ends in
.+, so the location slot swallows extras —202501_Tropical_Cyclone_CApasses there while silently parsing ashazard="Tropical"and writing that as the GeoTIFFHAZARDtag. This also rejects202501_Flood_CA_extra, an explicit upstream pass case (tests/integration/test_dps_validate.sh:41-48).Diverging this way is the safe direction: every name we accept, DPS accepts too. We only refuse names that mis-slot the hazard. Cost: multi-word locations need CamelCase too (
202512_Hurricane_GulfOfMexico), and the error message quotes back what the name would otherwise have been read as.Conventions followed
dse-hub; zero new runtime deps (React + ReactDOM only — no router, no date lib, no icon lib)public/thumbs/CREDITS.md.gitignoregets!negation lines rather than dse-hub'sgit add -froute, so a plaingit add algorithm-catalog/picks the JSON upVerification
validate_data.py→ exit 0, 0 errors, 0 warningsrules_parity_test.py→ exit 0 (constants character-identical + 51 behavioural fixtures)npm run typecheck/npm run build→ cleanNetlify (manual, after merge)
Create a new site from this repo with base directory =
algorithm-catalog. Deploy after this merges — a base dir that doesn't yet exist on the built branch fails with "base directory not found".