Aquascope

Latest version: v0.27.0

Safety actively analyzes 1035885 Python packages for vulnerabilities to keep your Python projects secure.

Scan your dependencies

Page 1 of 5

0.27.0

Added
- **Flow vs normal on every big river** (559, part of 566), the NOAA National Water Model map for the whole world: every GEOGLOWS v2 reach of Strahler order 5 and up (842,581) coloured by its forecast flow for the day against its own normal for the month ([docs](docs/explorer.mdflow-vs-normal-on-the-rivers)).
- `aquascope.flowstatus`: the climatology, once (`flow-climatology.yml`, by hand): the 10th, 25th, 75th and 90th percentiles of daily flow by calendar month from GEOGLOWS's `retrospective/fdc.zarr` (checked to be 1950 to 2019 against four reaches' daily records), published under `flowstatus/climatology/`. Then daily: `warnings run --day0` keeps each reach's first-day ensemble-mean flow from the chunks Floods ahead already reads, so nothing is downloaded twice, and `flowstatus daily` classes it in the river status layer's five HydroSOS classes and publishes `flowstatus/latest.bin.gz` (sorted river ids as varint gaps and a class byte each), `latest.parquet` and a manifest, writing only under `flowstatus/`.
- `aquascope flowstatus [--bbox W S E N] [--river-id ID]` and the MCP tool `flow_vs_normal`; every number in the sentence comes from the counts.
- In the Explorer: **Colour by flow against normal** in the legend's Rivers row (off by default, `fn=1` in a link) colours the living rivers by feature-state on `riverId` in the river status colours, with the forecast day and the share above and below normal in the key, and says so before the first issue is published. The lit network now takes off only its own feature-state.
- **Subbasins by monthly water balance, replayable** (557, part of 566), the HEC-HMS and QSWAT basin map for the whole world, keyless ([docs](docs/explorer.mdwater-balance)).
- `aquascope.waterbalance`: for each HydroBASINS level-4 subbasin and month since January 1990, ERA5 precipitation and actual evapotranspiration (ERA5 total evaporation; WaPOR covers Africa and the Near East since 2018 only), area-weighted from the public ARCO-ERA5 Zarr read anonymously; the subbasin's own runoff, the GEOGLOWS v2 monthly flow at its outlet reach(es) (`hydrosos/hybas_linkno_pairs.parquet`) minus the inflow from the subbasins upstream (level-4 routing built from the Archive's BasinATLAS level-12 table), in mm over its area, withheld with the reason where that difference cannot be trusted (an outlet reach carrying less than half the inflow, more than a metre of inflow a month over the subbasin, more runoff than rain over the record, or downstream of such an outlet); the closure error P - ET - Q, kept and shown; and every value's anomaly against the 1991-2020 normal for the calendar month. Months on preliminary ERA5T are flagged and recomputed when final.
- The CI cost, measured first: ERA5 is one whole-globe hour per chunk, so a month is about 1,460 chunks and 3.5 GB, and 1990 to now about 1.56 TB; decoding and summing a chunk takes about 2 ms, so reading is the cost. GitHub runners read 74 to 186 MB/s (the backfill: 2.3 to 5.9 hours, one or two dispatches), a home line 55 to 76 s a month. GEOGLOWS's monthly record for the 943 outlet reaches is about 0.5 GB. Level 4 only: level 5 would read the same ERA5 but has no GEOGLOWS outlet pairing.
- `.github/workflows/water-balance.yml` (workflow_dispatch and the 9th of each month) computes the missing months newest first until a time budget and publishes only under `waterbalance/` in the Archive; a smoke input (and a 240-hour smoke on pull requests that touch it, which measures the runner's read rate) never publishes.
- In the Explorer: a **Water balance** layer, off by default, with one legend row that switches between rain, evaporation, runoff and the closure error, as amounts or against normal, coloured on the level-4 subbasins and replayed by the time bar month by month. A click opens the map card with the subbasin's month in words and its year as bars (rain beside evaporation and runoff, the normal rain as a tick). The map card takes a layer's own chart (`chart`).
- `aquascope waterbalance [--basin ID | --at LAT LON] [--month YYYY-MM] [--bbox W S E N]`, the MCP tool `water_balance`, and "turn on water balance" in Ask the map.
- **SWMM models and results read anywhere, the browser included** (560, part of 566): an assessment and a thin reader ([docs](docs/assessments/swmm.md)).
- Tested in Pyodide 314.0.7 on EPA's examples: `swmm-api` 0.4.74 reads `.inp` and SWMM 5.2 `.out` but refuses SWMM 5.1 results; `swmmio` 0.8.6 needs `swmm-toolkit` (compiled C, no WebAssembly wheel) and has no `.out` reader of its own.
- `aquascope.io.swmm`: `read_swmm_inp` (nodes and links with coordinates, placed in lon/lat with a `crs` or a model in degrees), `read_swmm_out` (SWMM 5.0 to 5.2 results, one variable at a time in blocks, numpy only) and `swmm_summary` (the flooding nodes and the largest flows, every number from the files). Identical to `swmm-toolkit`'s own reader on five results files; a 56.7 MB file is summarised in 21 ms in Pyodide.
- `aquascope swmm model.inp model.out [--crs] [--top] [--element] [--json]` and the MCP tool `swmm_summary`. The map view is a follow-up, planned in the doc.
- **What if, on the map** (565, part of 566): "20% less rain and 2 degrees warmer" painted on the rivers, from the map card's **What if…** ([docs](docs/explorer.mdwhat-if-on-the-map)).
- `aquascope.whatif_map.whatif`: the basin draining to a reach (GEOGLOWS v2 routing tables), GR4J with the snow store where it snows, ERA5 at the basin's centre, calibrated on the gauge or on the reach's GEOGLOWS simulated flow. Returns the change in mean flow, low flow (Q95) and a T-year flood at the outlet, each with a range from the parameter sets that do as well on the validation years, and every reach downstream to the third major confluence with the change scaled by area (the simplification is stated in the answer). Basins over 100,000 km² are refused.
- `aquascope.advanced.catchment_model` gains `flood_rp`, `members` (the behavioural range), a scenario `demand_cms`, `params` (reuse a calibration) and `source="geoglows"` (calibrate on a reach's simulated flow). Without them its answer is unchanged.
- Keyless rules read a what-if line (`parse_scenario`); the on-device model or your own key only fill the four parameters, and a number not in the words, used twice, against the words' direction or out of bounds is refused (`parse_scenario_reply`).
- `aquascope whatif`, the MCP tool `whatif_map`.
- In the Explorer: sliders and one line in a small panel; the basin and the river below painted brown to teal by the change, labels at the outlet and each major confluence, a before/after swipe, a map card per reach, every run in the action log with undo. The Thur at 1,729 km² gives −38.3% mean flow in the browser and in the CLI.
- **Review my model results** (564, part of 566): checks by code on a model run dropped on the globe, each a pin with its place, severity, value and source ([docs](docs/mesh.mdreview-my-model-results)).
- `aquascope.mesh_review`: the deepest places (depth and model time; the largest rise of the water surface for a file without bed levels), negative depths, cells wetted only by a film, isolated wet cells, wet and dry flicker, the volume error from HEC-RAS volume accounting, the modelled largest extent against Sentinel-1 radar floods (Microsoft AI for Good, 2014-10 to 2024-09) and Groundsource news floods from the Archive's context mirror (comparing with every month on record, and saying so, when the run is outside them), and the modelled peak against each Archive gauge inside the mesh (timing only), or a line saying there is none. `aquascope.io.mesh.Mesh.values_block(..., raw=True)` gives the depths as the file holds them, negative ones included.
- A model only orders and words the findings: numbers are placeholders filled by code and Explain's claim lock refuses any other; keyless, the templates say it.
- `aquascope mesh review FILE [--offline] [--json]`, the MCP tool `mesh_review`, the WebMCP tool `aquascope_review_model`, and "review my model" in Ask the map.
- In the Explorer: **Review these results** on the model's legend row. Pins coloured by severity flag the cells they are about; each is in the action log, and undoing it takes its flags away. The legend row lists the nine checks. Checked on the Bald Eagle Creek example the mesh viewer used, and on synthetic runs with planted problems (each check finds its own and nothing else).
- **Groundwater on the map: a head surface from the Archive's wells, and your MODFLOW heads** (558, part of 566), the ModelMuse view, keyless ([docs](docs/explorer.mdgroundwater)).
- `aquascope.groundwater.surface`: `surface_from_readings` and `groundwater_surface(bbox, month, mode, compare)` interpolate the Environment Agency's wells in the Archive (heads in m above Ordnance Datum, so a head surface, not a depth to water) for a month, or the change against a year before, by inverse distance weighting on a grid regular in Web Mercator, drawn only where at least 3 wells lie within 20 km (the density mask: cells with one or two wells near are returned as too few), with contours. A month most wells have not reported in yet falls back to the latest one they have, and says so.
- `read_modflow_heads(hds, grid, origin, rotation, crs, cell_size, start_date, sidecars)`: a pure-numpy reader of the MODFLOW-2005 and MODFLOW 6 binary head file (identical to FloPy's `HeadFile` on FloPy's Freyberg examples), grids from `.dis` and `.disv` text, MODFLOW-2005 `.dis` and MODFLOW 6 `.dis.grb`, placed in lon/lat from the files (FloPy's `.nam` georeference, a `.tdis`) or a typed origin, rotation and CRS; heads per layer and time, big grids in blocks. FloPy 3.11.0 does load in Pyodide 314, but pulls about 10 MB of matplotlib, so the reader is our own ([docs](docs/modflow.md)).
- In the Explorer: a **Groundwater** layer, off by default, in the one legend: the surface (level or change over 12 months, with a diverging ramp for the change), the wells as small dots, the too-few-wells areas hatched grey, GRACE suggested beside it, following the time bar, and a map card on click. Drop a `.hds` with its grid on the map (the same drop zone as the mesh viewer, routed by file type) and the heads are drawn on the model grid, in the model's own length unit, layer and time step selectable and played with the time bar; a small form asks where the model sits when its files do not say. The files stay in the browser.
- `aquascope groundwater surface`, `aquascope modflow <file>`, and the MCP tools `groundwater_surface` and `modflow_heads`. "turn on groundwater" works in Ask the map.
- **Your model results on the globe, the Crayfish view** (555, part of 566): drop a HEC-RAS 2D plan file or a UGRID NetCDF mesh result on the Explorer's map (or **Tools**, **Open model results**) and see it on the globe, read in your browser and never uploaded ([docs](docs/explorer.mdyour-model-results-on-the-globe-the-crayfish-view)).
- `aquascope.io.mesh`: `read_ras_plan(path)` and `read_ugrid(path)` (and `read_mesh`, which picks) return one slim structure: the cells in longitude and latitude (reprojected with pyproj from RAS Mapper's projection or the UGRID grid mapping, or a `crs` you give), the model's own times, the depth (water surface less the bed; the water surface, said plainly, when the file has no bed levels), the velocity per cell (for HEC-RAS rebuilt from the face normal velocities by least squares, turned to east and north), and the largest depth and speed per cell. Reads are lazy: one time step or one cell when asked. HEC-RAS and NetCDF-4 through h5py, NetCDF-3 through scipy.
- `aquascope mesh FILE [--crs EPSG] [--json]` and the MCP tool `mesh_summary`, every number computed from the file ([docs](docs/mesh.md)).
- In the Explorer: the largest depth in blues by default, velocity arrows thinned to the screen, optional particle traces, the time bar on the model's own clock ("Model time: 37 steps, …", with **Back to dates**), one legend row named after the file, and the map card on a cell with its depth, velocity and time series. A file without a coordinate system asks for its EPSG code; an unsupported or too large file (over 2 GB or 250,000 cells) gets one plain sentence. The file is mounted read-only in a worker with its own Python (WORKERFS), not copied into memory: a 66 MB plan opens with the same memory as a 1.4 MB one.
- Checked first in Pyodide 314.0.7 on real files: the rashdf test plan of HEC's Bald Eagle Creek example (MIT) and the xugrid sample ADH San Diego mesh (MIT). rashdf 0.12.0 works in the browser but brings geopandas and pyarrow, so h5py is used directly; netCDF4 1.7.4 in Pyodide 314.0.7 cannot open NetCDF-4 files, so h5py reads those too.
- `aquascope.io` imports its names on first use, so `aquascope.io.mesh` loads without pandas in the browser. Floods ahead and Floods past let a click on your own model answer first (`clickClaimed` in `explorer/src/core.js`).
- **Scout** (563, part of 566): one pill beside **Ask the map** drops up to ten numbered pins on what stands out in the view, each with its reason, its number and its source, all in the action log with undo ([docs](docs/explorer.mdscout)).
- `aquascope.map_scout`: fixed rules over the published layers. The largest connected areas much above or below normal in the month's world river status; the Floods ahead reaches with the highest class on the largest rivers, one per river group; the month's strongest groups of floods in the news and seen by radar; groups of gauges much above or below normal today around the most extreme one; gauges where no scored model beats the gauge's mean flow (a model at a hundredth or tenfold of the gauge's volume is left out as a units or matching problem). Ranked kind by kind with a share each, never two pins closer than a pin's height on screen, place names from Photon reverse geocoding (ODbL).
- Every number is formatted by the package into slots. A model (Chrome's built-in model, or the reader's own key) may order the findings and word them, but wording with a digit, a percent sign, a number word or a ranking word outside the slots is refused and the rules' wording kept (`apply_wording`, the claim lock).
- Daily, after the Floods ahead issue, `.github/workflows/flood-warnings.yml` runs `python -m aquascope.map_scout daily` and publishes `scout/latest.json` and a dated copy, writing only under `scout/` (a failed scout neither turns the day's issue red nor publishes an empty file). Each finding has its lat and lon, title, reason, headline `value` and `unit`, all its numbers and its source. It adds record checks: a named region's share much above or below normal against the same calendar month since 1990, and flood cells against the same month since 2000. The Explorer uses it for the half-globe view at the newest month and scans the view in the browser otherwise.
- `aquascope layers scout [--bbox W S E N] [--month] [--published]`, the MCP tool `map_scout`, and the WebMCP tool `aquascope_scout`. Map pins can carry a rank and a kind (`addPin`), and a pin's row in the action log opens its card. Scout and Explain this view take turns on the map (each clears the other's numbered marks), and the pills above the map no longer squeeze onto two lines.
- **Explain this view** (562, part of 566): "what am I seeing?" answered with three to five short notes pinned on the map, each at the place it describes and each naming its source ([docs](docs/explorer.mdexplain-this-view)).
- `aquascope.map_explain`: `view_facts(view, status, warnings, floods_past, gauges)` reads a view (its box, date and the layers that are on) into facts by code, each with value, unit, place and source: the share of the mapped land in each river status class and the largest areas away from normal, the reaches forecast to pass their return-period flows, the gauges furthest from normal today, and where most floods were reported or seen by radar. `template_notes` words them with no model. `check_model_notes` holds a model's notes to the facts: the model chooses, orders and words; the numbers go in by code from placeholders, and a note with any other number, in digits or in words, is refused (the claim lock).
- In the Explorer: an **Explain this view** button beside Ask the map, and "what am I seeing?" in the box. Notes are numbered dots in the colour of what they describe, with cards that keep off each other, the legend and the time bar; the on-device model or the reader's own key words them where one is ready, the fixed wording otherwise. Every note is in the action log with its undo.
- `aquascope layers explain W S E N [--date] [--layers] [--facts] [--llm]` and the MCP tool `explain_view`.
- `aquascope.map_layers.status_classes` can read just the cells of a view, decompressing only their tiles.
- **The month in one line** (part of 543): `aquascope.map_layers.river_status_summary(month)` reads a month of the world river status map and says where the rivers are low or high: for a few named regions (rough boxes such as the Amazon, the Sahel, South Asia), the share of the mapped area below and above normal, weighted by latitude, and a headline naming the regions where at least half is one or the other ("River status, September 2026: much of the Sahel and the Amazon below normal, much of southern Africa above"). `aquascope layers status [YYYY-MM] --summary`, the MCP tool `river_status_summary`, and the Explorer's caption over the globe, which makes the same line from the file its worker has already decoded (a test keeps the two equal).
- **Ask the map** (561, part of 566): tell the map what to show in plain words, and every change it makes is listed in an action log on the map with an undo for each and one for all ([docs](docs/explorer.mdask-the-map)).
- `aquascope.map_commands`: the map's action schema (`fly_to`, `set_time`, `set_layer`, `focus_status`, `set_basemap`, `highlight_river`, `draw_area`, `add_pin`), the checks every action passes before it runs, and a keyless phrase grammar for the common requests ("trace the Nile to the sea", "September 2023", "play the last 5 years", "turn on floods past", "where are rivers much above normal in South Asia last July"), with place names looked up in the search's gazetteer (Photon, OpenStreetMap data, ODbL). `aquascope map "<request>" [--resolve] [--llm]` and the MCP tool `map_command`.
- In the Explorer: one line at the top of the map (the pill, or **/**), read by the rules first, then by the model on the device where one is ready, then by the reader's own key from Ask ✨; the line under it says which answered. Never a key of ours.
- The world river status can paint only some classes, and its legend says which (**show all** puts the rest back).
- Pins with notes for the AI roles to come: `addPin({lat, lon, title, text, facts, source})` and `pins()` in `explorer/src/map-actions.js`, each pin in the log and opening a map card. An assistant in the browser acts through the WebMCP tool `aquascope_map_actions`, into the same log.
- **Forecast points and the ensemble plume, the FEWS view** (556, part of 566). Floods ahead reaches and the Archive's forecast gauges in threshold colours, and one click for the ensemble hydrograph in the map card, as Delft-FEWS and the GloFAS and EFAS viewers show them ([docs](docs/explorer.mdforecast-points-and-the-plume-the-fews-view)).
- `aquascope.nownext.plume(river_id | lat, lon, thresholds, run)`: the reach's 51 GEOGLOWS members averaged to UTC days, then per day the median, the middle half, the full range and the mean (`ensemble_daily`), each day's class by the Floods ahead rule (`threshold_class`), the peak, the first day at the 2-year flow, and how many members reach each return-period flow. It reads the run the map shows (`run`), and the newest when that one does not answer. `forecast_points(rows)` classes the forecast gauges of an issued file against each gauge's own return-period flows. Plain Python, so a light worker runs them.
- `aquascope.rivers.forecast_ensemble(river_id, run)`: a thin fetch of the members from the GEOGLOWS API (about 70 kB, CORS open, checked 2026-10-10).
- `aquascope now --river-id ID --plume` and the MCP tool `forecast_plume`.
- The Floods ahead issue's `latest.geojson` now carries each reach's 5- to 100-year flows too, for the card's threshold lines. Until the next daily issue, the page reads them from `latest.parquet`.
- The Explorer: a click on a Floods ahead reach opens the map card, replacing the old popup. The card gives the class and the peak, then the plume within a few seconds: bands, median, dashed return-period lines in their class colours, a class strip under the days, the run's first day and the map's date. The forecast gauges get rings in the same colours past zoom 7 (a thin slate ring below the 2-year flow) and a row in "On the map" while they are in view, and their card shows the plume corrected to the gauge with its own record before the run (left out, with the day it stops, when it ends more than a week before the run), and says so when the correction scored worse than the raw model on the hindcast. The colours are checked under simulated protanopia, deuteranopia and tritanopia (`explorer/tests/fews-core.test.mjs`).
- **World river status on the globe** (544, part of 543). The Explorer opens on the state of the world's rivers: GEOGLOWS v2's monthly HydroSOS map, every HydroBASINS level-4 basin coloured from much below to much above normal, drawn under the gauges at the newest month (September 2026 today), with a small legend on the map that names the month. The time bar replays it month by month back to January 1990, preloading the next frame so a step never flashes; a month with no map (March 2026 is missing from the series) draws nothing and says so. Each month's GeoTIFF (about 500 kB, CC BY 4.0) is read in a worker with geotiff.js 3.0.5 and laid out on a Web Mercator image, so it sits right on the globe and on the flat map. It fades as you zoom in, keeps the basemap's coast and labels on top, and has its own row, opacity and info panel in the rail; `ws=0` in a link turns it off.
- The basins use the same five brown-to-teal colours as the gauges' "Today vs normal", so a dot and the basin around it mean the same thing; GEOGLOWS's own red-to-blue HydroSOS colours are noted in the info panel.
- `aquascope.map_layers.river_status_month(month)`: the month's COG URL, the legend with the file's colours and percentile bounds, the months that exist (the bucket listed live, cached six hours), the method and the licence. `aquascope layers status [YYYY-MM]` and the MCP tool `river_status_month`.
- **Gauges coloured "Today vs normal" by default** (544): where the daily status snapshot covers a gauge it takes its class colour, and every other gauge keeps its agency colour, which the legend says.
- **Floods past on the globe** (547, part of 543). Where floods were reported in the news and seen by radar, on the Explorer's globe from the moment it opens, following the time bar ([docs](docs/explorer.mdfloods-past)).
- `aquascope.context.floods_past`: Groundsource news events (CC BY 4.0) and Microsoft Sentinel-1 flood detections (MIT, October 2014 to September 2024) rolled into one row per half-degree cell and month, the two sources in separate columns. Built from the published mirror on 2026-10-10: 1,405,624 rows over 314 months (January 2000 to February 2026), `grid.parquet` 5.6 MB, the 314 gzipped month files 5.5 MB together (71 KB the largest), `index.json` 29 KB.
- `flood_events_month(month | start, end, bbox)`: the news events and radar detections in a month or a range (up to 60 months), worldwide with the hotspots, or in a box; a small box (a clicked cell) also lists its news events with their dates and its radar months. `aquascope context --floods-past [--month | --from --to] [--bbox]` and the MCP tool `flood_events_month`.
- The `mirror-context` workflow builds it after the flood mirrors and publishes it under `context/floods/monthly/` only; its `floods_monthly_only` input rebuilds just the grid from what is already published.
- The Explorer's **Floods past** layer, on by default: a heat map at the world view, news circles and shaded radar cells closer in, the twelve months up to the map date (or the latest twelve on record), one month at a time while playing or stepping by month, a range when one is set. Months crossfade (at once with reduced motion). A legend on the map says what the two marks mean, the months and the counts, and the licences, with a replay button and a folded one-line form on phones. Clicking a cell outlines it and lists its events. Turned off with the rail row, the legend's ×, or `fp=0` in the link.
- Map layers outside the overlay catalogue can now follow the date and take clicks (`datedExtras` and `clickLayers` in `core.js`).
- **Floods ahead** (546, part of 543): the river reaches the GEOGLOWS forecast expects to reach their 2-year flow in the next 15 days, on the globe.
- `aquascope.archive.warnings`: once a day, the whole GEOGLOWS v2 global forecast Zarr (51 members, 15 days) for every reach of Strahler order 5 and up and every reach an Archive gauge sits on, the ensemble-mean daily peak against the reach's own 2- to 100-year flows (GEOGLOWS's retrospective return periods, Gumbel on annual maxima of daily flow). Chunks are streamed and reduced (about 54 GB a day, nothing on disk); reaches with a 2-year flow under 5 m3/s are not classed. `classify` and `summarise` are pure and tested.
- Publishes only `forecasts/warnings/` (`latest.parquet`, `latest.geojson`, a dated copy and `manifest.json` with the issue date, counts by class, the method, the thresholds, the licences and what it is not: model output, not an official warning). `.github/workflows/flood-warnings.yml` runs daily at 14:23 UTC; a `smoke` input reads that many chunks and never publishes.
- `aquascope warnings [--bbox W S E N] [--min-rp T]` and the MCP tool `flood_warnings(bbox)`.
- The Explorer's **Floods ahead** layer, on by default: reaches glow in their class colour on the globe and light up along the river when closer, the gauges on them pulse (still under reduced motion), the map's date shows each day's class and **Play the 15 days** walks through them, a card on a clicked reach gives the peak, its day and the members that agree, and a legend says the issue date and that it is a model forecast. Before the first issue it says so.
- **Living rivers** (545, part of 543). The Explorer's globe now shows the rivers themselves.
- The GEOGLOWS v2 river network is on by default, drawn by Strahler order so the great rivers read on the globe and the small streams appear as you zoom, above the raster overlays and below the gauges, in a blue that follows the basemap. `rivers=0` in a link turns it off. Its TDX-Hydro credit (CC BY-SA 4.0) is in the map's attribution line.
- **Flow direction (animated)**: a light glint moves along every river the way the water goes, 16 steps a second, paused while the tab is hidden or while the map settles (a basemap change, the time bar's play, a GIF frame) and off when the system asks for reduced motion. A toggle in the rail.
- **A click lights the river up**: the snapped reach is ringed, everything that drains to it turns blue and its way to the sea orange (feature-state on `riverId`), with a key on the map that says what the colours mean and how many reaches and km² drain there, and a progress line while the basin's routing tables load. The rest of the network fades back. The trace to the sea is drawn in the same orange.
- `aquascope.rivers.upstream_ids(river_id, max_n)` and `downstream_ids(river_id, max_n)`: the reaches that drain to a reach (past `max_n`, the ones that drain the most, with the cut said) and the reaches to its outlet, from the routing tables `upstream_area` and `trace_downstream` read. The upstream walk goes a whole level of the network at a time in numpy: once the tables are read, the Mississippi at St. Louis (123,896 reaches) takes about 0.05 s on a laptop. `aquascope river upstream|downstream` and the MCP tools `upstream_ids` and `downstream_ids`.
- **Map first: a click answers on the map** (548, part of 543). A click on a gauge, a river or any place opens a small card anchored to it: the name, today against normal in one sentence (from the Archive's daily status snapshot, or ranked from the record, or for a river from its simulated record since 1940), one sparkline (the record's last 12 months, or the next 15 days from GEOGLOWS with the middle half of the ensemble shaded), one number (the newest value, or the forecast's peak) and **Details**, **Trace to sea**, **Watch** and **Study**. A click that misses the river offers the river the snap found in one click. It fills as the answers arrive, never behind a wall of spinners, and every line says where it comes from. The side panel is folded away until **Details** opens it on the right tab, and a link with `tab=` still opens it there, as before. On a phone the card is a short sheet at the bottom; Escape closes it, focus moves into it, and it is labelled for a screen reader. Other map layers open a card with their own content through `openCard()` in `explorer/src/map-card.js`.
- **Flood depth where floods are forecast** (554, part of 566): the RAS Mapper view of the forecast, keyless ([docs](docs/explorer.mdflood-depth-where-floods-are-forecast)).
- Where Floods ahead expects a reach to pass its 10-, 25-, 50- or 100-year flow, the Explorer draws the JRC CEMS-GloFAS flood depth map (v2.1.2, about 90 m) of the nearest return period at or below it (10, 20, 50 or 100 years; there are no 2- or 5-year maps) around the reach, in a blue depth ramp, labelled "may flood in the next 15 days, model estimate". It steps up and down with the time bar through the forecast days, leaves to a larger river the pixels nearer to it, and a click opens the map card with the reach, the return period and the depth there. Its key (the honest label, the ramp, the count, the licence) is a row of the shared "On the map" legend, under Floods ahead; a rail row and a credit line.
- The depth is read in a worker with geotiff.js from the Source Cooperative mirror (byte ranges, a few hundred kB per half-degree cell) and kept, so stepping repaints from memory.
- `aquascope.flood_depth.flood_depth_overlay(river_id | bbox, return_period, day)`: the map chosen, the tiles with their windows and a GDAL command, the extent around the reach and its clip, and the depth at the reach, the deepest pixel and the wet share. `aquascope layers depth` and the MCP tool `flood_depth_overlay`. Ask the map (561) knows the layer too ("show the flood depth").
- The licence is confirmed from JRC's own files on 2026-10-10: the copyright notice licenses the dataset under CC BY 4.0 (credit given, changes indicated), and the README says "no restrictions, free and open Copernicus product". Both are recorded with the layer.

Changed
- **Python in the browser: Pyodide 314** (part of 566, unblocks 555 and 558). The Explorer's workers move from Pyodide 0.28.2 (Python 3.13) to 314.0.7 (Python 3.14, the newest stable on 2026-10-11), whose package set has geopandas, pyproj, pyarrow, h5py and netCDF4 for the readers to come. Pyodide 314 no longer runs in classic web workers, so the main worker and the light workers start as module workers and load the runtime with `import()`. The version still lives in one place, `explorer/config.js`, which the Pyodide smoke workflow reads. Measured in headless Chrome: about 30 MB of Python on a first visit (29.8 MB, was 31.3 MB), ready in 14 to 16 s on a fresh browser (was 13 to 15 s) and about 12 s on a repeat visit (unchanged). The page's "about 15 MB, once" now says about 30 MB, which is what it was already. Every result the walk-through compared (every gauge tab, GR4J, and the study's flood numbers) came out the same.
- **The globe, calmer** (part of 543): the first view shows the rivers, not the furniture ([docs](docs/explorer.mdon-the-map-one-legend-one-line)).
- **One legend, "On the map"**, instead of a card per layer: a row per layer in map order (gauges, Floods ahead, rivers, Floods past, river status), each with a visibility toggle and a key that opens; a layer with nothing to draw is one muted line, and a lit river's key is a row on top. On a phone it starts as one small chip and, opened, stays inside a quarter of the screen.
- **Quiet gauge clusters**: small, light and see-through with a small count, where they were large dark-blue bubbles; the gauges with a status today show as small dots in their today-vs-normal colour while the rest are still clustered.
- **Floods past that inform**: only the cells that stand out from the region on screen are drawn (about the top eighth of the cells with any count there, above a small floor), as a soft heat up to the regional view and small translucent marks from zoom 6, so Bangladesh is a few hotspots rather than a grid of equal dots. Clicks on a cell start at zoom 6.
- **Rivers**: the great rivers are a little wider on the globe, and the flow is a light glint walking down the line, 16 steps a second, rather than a dark dash. Floods ahead's glows are smaller and softer on the globe.
- **The month's one line** sits under the Ask the map pill on the world view only (zoomed in, it gives the map back), and while the time bar replays a past month the Gauges row says its dots still show today.
- The **/** key opens Ask the map; the search is a click away in the header.
- A GIF of the time bar credits the layers on it (GEOGLOWS for the river status), not NASA GIBS alone.
- The layers keep one order (river status, Floods past, rivers, Floods ahead, gauges), and Floods past shows the latest twelve months on record when the river status opens the map on a month after them.
- On a phone the map's attribution starts folded to its (i), so the time bar never sits on it.

Fixed
- **A sign against a placeholder got past the claim lock** (562, 564): a model's "-{f1}", "–{f1}", "minus {f1}" or "{f1}0" changes the number code fills in. `aquascope.map_explain.placeholder_touches` finds it, `claim_lock` lists it, and Explain, Scout (`check_wording`) and the mesh review now share this one check (a hyphen joining a word or two placeholders, "mid-{month}" or "{f1}-{f2}", is still wording).
- **A stripped HEC-RAS plan file ended in a KeyError** (555): a `*.p01.timeseries.hdf` holds the results but not the mesh. The reader now says which part is missing and what to do: open the full plan, or give the geometry file the plan was run with, which it then reads with it (`read_mesh(..., geometry=)`, `aquascope mesh FILE --geometry G01_HDF`, `aquascope mesh review ... --geometry`, the MCP tools' `geometry`, and the two files dropped together in the Explorer, which says the same in plain words when the plan comes alone). Checked on HEC's Bald Eagle Creek example (`BaldEagleDamBrk.p18.timeseries.hdf` with `BaldEagleDamBrk.g11.hdf`). A plan without summary output says its largest is over the saved steps only. A plan file with results but no 2D areas (only 1D results or boundary lines) is now called a HEC-RAS file with no 2D flow areas, not a file with no UGRID mesh.
- **Floods ahead counted only what the browser draws.** When the daily file keeps only the strongest 10,000 reaches, the legend row and its class chips now give the published totals (for example "20,071 reaches, strongest 10,000 drawn") instead of the cut count.
- The docs build again: pymdown-extensions 12.2 (released 2026-10-10) changed `Highlight.__init__` and broke mkdocstrings 0.29, so every docs build failed. The docs extra now asks for `pymdown-extensions<12.2`.
- Record the verified v0.26.0 Zenodo DOI (`10.5281/zenodo.23246553`) in release citations, Explorer BibTeX and generated study reports; retain the concept DOI for the project as a whole.
- The world river status credit linked to `https://geoglows.org`, which does not resolve (no DNS record). It now links to `https://www.geoglows.org`.

0.26.0

Added
- **A monthly state-of-the-rivers bulletin** (523, part of 526), in the style of WMO HydroSOS ([docs/bulletin.md](docs/bulletin.md)).
- `aquascope.bulletin.status_bulletin(month, sources)`: every Archive gauge with a mirrored discharge record covering the month, its monthly mean (25 days or more) placed against the same month in its other years (10 or more, else left out and counted) as a mid-rank percentile and one of the five classes from much below normal to much above normal. Rolled up per country and per BasinATLAS river basin, with the new monthly highs and lows, the gauges furthest from their usual flow, the coverage and a summary paragraph written by rules, not a model. The published bulletin is read first; otherwise it is built from the discharge bundles, and `top_up` asks the agencies for the days the weekly mirror does not have yet.
- Rendered through the Studio's document composer as print-ready HTML and Markdown, with one map of the gauges coloured by class over Natural Earth outlines, one panel per region.
- `aquascope bulletin [YYYY-MM]` (`--out`, `--json`, `--rebuild`, `--top-up`) and the MCP tool `status_bulletin`.
- Share-alike sources (Greece OpenHi.net) are left out of the bulletin and named, since a status derived from them would carry their terms.
- **The monthly bulletin workflow** (523): `.github/workflows/bulletin.yml` runs on the 3rd of every month (or by hand for a `month`) and writes only `bulletins/<YYYY-MM>/` (`bulletin.html`, `bulletin.md`, `bulletin.json`, `map.png`, `status.parquet`) and `bulletins/index.json` in the Archive dataset. A `smoke` run never publishes. `publish_folder` takes `allow_patterns` so it can upload the HTML, Markdown and PNG.
- **Bulletin in the Explorer** (523): **Bulletin** in the Tools menu opens the latest bulletin in a reader with Print or save as PDF, and the gauges can be coloured by **Last month's status**. Before the first bulletin exists, both say so and the gauges keep their agency colours.
- **Dams and countries on the trace to the sea** (follow-up to 516). `trace_downstream` now lists the Global Dam Watch v1.0 dams within 2 km of the path (CC BY 4.0, read from the Archive's cell-sorted mirror), in the order the water reaches them, with name, storage, main use, the km along the path and the degree of regulation where GDW gives it; the countries it crosses, with the km in each (Natural Earth 1:50m admin-0, public domain, read from the `world-atlas` 2.0.2 TopoJSON on jsDelivr); and the dams upstream of the first reach. Each part says so when its data is not there and the rest of the trace stands (`aquascope.river_path`).
- `upstream_dams(river_id)`: is the river regulated upstream of a reach? The GDW dams that drain to it, each matched to its reach in the GEOGLOWS stream tiles and kept when the routing table says it drains there, their storage, and the degree of regulation (storage as a share of a year's mean flow, from GEOGLOWS v2 annual averages, modelled). Approximate, and it says so. `aquascope river dams [--no-flow]`, the `upstream_dams` MCP tool, and `--dam-km` on `aquascope river trace`.
- The Explorer draws the dams as small squares on the traced line and lists them under the gauges, with one line for the countries and one for the dams upstream.
- The Studio's Scout reads the dams upstream for flood and supply questions, as context in the site notes and on the dams row of the inventory; it changes no plan, and the reconnaissance HydroGym scores against is untouched.
- **The evidence ladder** (518): every global model scored against a gauge's own record, graded, and said in words (`aquascope.evidence`, [docs/evidence.md](docs/evidence.md)).
- `model_skill(source, station_id)` (or an observed series with `lat`/`lon`) scores GEOGLOWS v2 (the reach whose upstream area matches the gauge's catchment, the area ratio recorded), GloFAS (the cell picked by mean flow), NWM v3.0 retrospective (US) and Google's GRRR reanalysis over the overlap: KGE with r, alpha and beta, NSE, percent bias, and the relative error at the 2-, 10- and 100-year flows, each from its own GEV fit. Grade A (KGE >= 0.75), B (>= 0.5), C (> -0.41, the mean-flow benchmark of Knoben et al. 2019), else D, one letter lower when the 100-year flow is off by more than 50 %. One sentence says where they disagree ("GEOGLOWS v2's 100-year flow is 73 % below the gauge's").
- `lean_on(lat, lon)`: which model tracked the graded gauges near a site best.
- `aquascope evidence skill|near|build|publish` and the MCP tools `model_skill` and `model_to_lean_on`.
- `aquascope.rivers.reaches_near`: every reach within a distance of a point, nearest first.
- **A monthly skill table in the Archive** (518). `.github/workflows/model-skill.yml` scores GEOGLOWS (its daily Zarr), NWM v3.0 (US gauges, the AWS Zarr, read a reach chunk at a time) and Google GRRR (a bucket with no CORS, so CI only) at the gauges with a mirrored daily discharge record, and publishes `skill/model_skill.parquet` and `skill/manifest.json`, writing nothing outside `skill/`. Each run takes the gauges never scored first and logs what it left for next month; a smoke input scores six gauges and never publishes. GloFAS is scored live in the page instead, since Open-Meteo's free tier counts a long request as several calls.
- **The Evidence tab in the Explorer** (518). On a gauge, the record and the models on one plot (the gauge bold, the models thin), the skill table and the disagreement sentence; a grade badge beside the gauge's dates; and "Best model skill" in the rail's gauge colouring, with a legend, which stays grey and says why until the table is published.
- **The Studio knows which model to lean on** (518). The Scout reads the published skill at the graded gauges near the site, and when a study quotes a modelled number (the GEOGLOWS reach record or the GloFAS cross-check), the Interpreter adds which model tracked the nearby gauges best to the answer's conditions and limitations. The playbooks and HydroGym's tasks are unchanged.
- **Now and next** (517, part of 526). `aquascope.nownext` says where a river is today and what the next 15 days hold, keyless and in the browser too.
- `flow_status(series, date)`: the day's percentile against 7 days either side of the same date in every other year of the record (at least 10 years, or it says how many there are), and the five classes the USGS National Water Dashboard and WMO HydroSOS use, in one sentence: "Flow is normal for 7 October (72nd percentile of 123 years)." `station_status` tops an Archive copy up with the agency's newest days first.
- `forecast(lat, lon | river_id)`: GEOGLOWS v2 (the ECMWF ensemble for the river reach, 15 days: mean, median, the middle half, the full range and the high-resolution run, averaged to days) and GloFAS v4 through Open-Meteo's flood API (daily ensemble statistics), with the reach's 2- to 100-year flows fitted (Log-Pearson III) to its simulated annual maxima since 1940. Both model output, CC BY 4.0, labelled modelled.
- `correct_to_gauge(model_hist, obs, model_fcst)`: flow-duration quantile mapping of the model onto a gauge's record, one curve per calendar month (one for the year when a month is thin), applied to the forecast. `hindcast_skill` fits it on the first 60 % of the shared days and scores raw against corrected on the rest: KGE with r, alpha and beta, percent bias, and the hit rate and false alarms above the gauge's 2-year flow. When the snapped reach's simulated mean flow is more than twice or under half the gauge's, the answer says the gauge may be on another river.
- `aquascope now LAT LON | --station SOURCE/ID | --river-id ID` and the MCP tools `flow_status`, `flow_forecast` and `correct_to_gauge`.
- **The Now tab in the Explorer** (517). On a gauge: today's status in one sentence, and the 15-day forecast plot (the GEOGLOWS ensemble band, the GloFAS line, the return-period lines and the last 30 observed days), corrected to the gauge with its skill beside it ("Corrected forecast: KGE 0.47 on the 1992-2026 hindcast, raw 0.21."). On a clicked point: the reach's simulated status and the raw forecast. The map date moves a marker across the plot.
- **Gauges coloured "Today vs normal"** (517): a new gauge colouring from the daily status snapshot, with a legend that names the sources covered and when the snapshot was made. Before the first snapshot exists the gauges keep their agency colours and the legend says so.
- **An archive of issued forecasts** (517): the daily `forecast-archive` workflow (`python -m aquascope.archive.forecasts`) writes, only under `forecasts/` in the Archive dataset, one Parquet per issue date with each gauge's GEOGLOWS and GloFAS forecast, raw and corrected (with the GEOGLOWS run's own start date, often the day before), for up to 250 Archive gauges with a live record (a mirrored series of 10 years or more, a value from the last 3 days, a snapped reach), plus `forecasts/status/latest.parquet` for the map, the snapped reaches and a manifest that counts what was dropped and why. A `max_items` smoke run never publishes.
- **Watch a river** (521, part of 526). `aquascope.watch.watch_digest(items, last_seen)` says what changed at watched gauges, river reaches and drawn areas since they were last seen: new days of data and the latest value, today's class against normal and the one before, the forecast peak in the next 15 days against a threshold (a value, a return period from the gauge's own record, or the 2-year flow by default; from the forecast archive, corrected to the gauge, else GEOGLOWS asked there and then, always labelled modelled), and flood events in the news nearby that started since (Groundsource, CC BY 4.0). One line per item, a summary, and the state to keep for next time. The same function is `aquascope watch ID... --since DATE [--threshold ID=10y]` and the MCP tool `watch_digest`.
- **Watch in the Explorer** (521). A ☆ Watch on a gauge, on a clicked point's river reach and on a drawn area; the list stays in the browser (every storage read and write is guarded, so the page works with storage blocked). On the next visit a "Since you were here" panel checks each watched place in the worker, one line each, and can be dismissed; "Watched" in the Tools menu opens it again and jumps to each place. A watched gauge has one line to pick its threshold, and a gauge with a live record has "Follow (Atom)" in its ··· menu.
- **Per-gauge Atom feeds** (521). The daily `forecast-archive` workflow now also runs `python -m aquascope.archive.feeds`, which keeps an Atom 1.0 feed for every gauge in the status snapshot (an entry when its class against normal changes, and when the corrected GEOGLOWS forecast passes the gauge's 2-year flow or a rarer one) plus `feeds/index.json`, writing only under `feeds/` in the Archive. Forecast entries say they are model output and not flood warnings.
- **The issued forecasts carry the gauge's own flows** (521): each row of `forecasts/issued/<date>.parquet` now has `gauge_q2` to `gauge_q100`, the gauge's 2- to 100-year flows from its record, which the corrected values are compared with.
- **Time on the map** (522). One date control at the bottom of the Explorer's map drives every dated layer (VIIRS true colour, IMERG rain, SMAP soil moisture, MODIS snow and land temperature, GRACE water storage).
- Step back and forward by a day, a week or a month, and **play** a range in a loop.
- **Click a day on any hydrograph** and the map jumps to it with a short "Map set to" note; rain comes on if no dated layer is.
- **Swipe compare**: a second map over the first, cut by a draggable handle, showing another date or one other dated layer.
- **GIF export** of the range, made in the browser (gifenc 1.0.3, MIT): up to 40 frames at 640 px, each stamped with its date and the NASA credit.
- The date, step, range and compare date are in the link (`d`, `ts`, `r`, `cmp`, `cl`).
- The date is one state field with a subscribe hook (`setTime` and `onTime` in `core.js`), so later features such as the forecast can follow it. In-browser agents get a `set_map_date` WebMCP tool.
- `aquascope.map_time`: the dated layers with their first and last day as GIBS reports them, and the frames of a time-lapse. Exposed as the MCP tools `dated_layers` and `layer_frames` and as `aquascope layers list [--live]` and `aquascope layers frames LAYER --start --end --step`. `--live` reads the exact intervals and gaps from the GIBS WMTS capabilities.
- **Rivers as objects** (516). `aquascope.rivers` snaps a point to its GEOGLOWS v2 river reach and treats the reach as the thing being studied, keyless and in the browser too.
- `snap_to_river(lat, lon)` reads the global stream network (`streams.pmtiles`, 2.4 GB, by byte ranges) and returns the reach, its stream order and the distance, or says there is no stream within the tolerance (1 km) and names the nearest one.
- `reach_record(river_id)` fetches the reach's simulated daily discharge since 1940 and analyses it like a gauge: annual maxima, return periods (GEV and LP3 with 90 % intervals), the flow-duration curve, the monthly regime and the trend. Labelled modelled everywhere, GEOGLOWS v2 output under CC BY 4.0.
- `upstream_area(river_id)` adds up the unit catchments upstream from the model's routing tables.
- `trace_downstream(river_id)` follows the network to the outlet: the reaches, the length, the path geometry (TDX-Hydro, CC BY-SA 4.0, for display) and the catalog gauges within 2 km of it, in the order the water reaches them.
- The same functions are the `aquascope river snap|record|area|trace` command, four MCP tools (`snap_to_river`, `reach_record`, `upstream_area`, `trace_downstream`) and an Analyst tool. `forecast_stats(river_id)` fetches the 15-day forecast for the forecast view to come.
- **The River tab and a rivers layer in the Explorer** (516). A click snaps to its river and says so; a gauge shows the reach it sits on. The tab draws the 86 years of simulated flow, the return-period table, the flow-duration curve and the monthly regime, and "Trace to the sea" draws the path down to the outlet and lists the gauges it passes. A "Rivers (GEOGLOWS)" layer in the rail shows the stream network by stream order.
- **The Studio reads the reach** (516). The Scout lists the GEOGLOWS reach the site snaps to as a modelled dataset, and the regional branches of the ungauged-flow and flood playbooks end with an optional `reach_record` step, quoted as modelled beside the transferred numbers.
- **Place context** (520): what a hydrologist asks first about a point or a box, from open global data, each line with its licence (`aquascope.context`, `aquascope context LAT LON` or `--bbox`, the MCP tools `place_context` and `area_context`, and a **Context** tab on a clicked point and a **Context** button on a drawn box in the Explorer).
- Flood history: flood events in the news (Google Groundsource, CC BY 4.0) and the months Sentinel-1 radar saw flooding from 2014 to 2024 (Microsoft AI for Good, MIT), plus the months flooded at the exact pixel.
- Surface water since 1984 (JRC Global Surface Water v1.5), modelled flood depth at the 10 to 500-year floods (JRC CEMS-GloFAS v2.1.2), dams nearby (Global Dam Watch v1.0, CC BY 4.0), soil texture and plant-available water (SoilGrids 2.0), actual ET (FAO WaPOR v3) and the nearest NOAA GHCN-Daily rain gauge with a summary of its record.
- Rasters are read a pixel at a time by a new pure-Python Cloud-Optimized GeoTIFF reader (`aquascope.utils.cog`: TIFF and BigTIFF, Deflate, LZW, PackBits, predictors 2 and 3, overviews, GDAL nodata and scale), so it runs in the Explorer's worker without GDAL.
- Flood events, dams and the rain-gauge index come from a new Archive mirror under `context/` (cell-sorted Parquet plus small per-cell files), built by `python -m aquascope.archive.context_mirror` and the manual `mirror-context` workflow. Until it is published those lines say so. Global Water Watch is linked only: its data licence is not confirmed.
- A **Surface water since 1984** map overlay, and the flood events of a point or a box drawn on the map.
- The Studio's Scout lists the context layers with the site's data, and for a flood question reads the flood history into the report's site notes; the flood playbook carries a caveat on historical events when the Scout found some (the plans HydroGym scores are unchanged).
- **Engineering exports** (519, part of 526): any gauge record, or a CSV, as ready inputs for the tools practitioners use (`aquascope.io.engineering`).
- HEC-HMS (the gage record as DSS), HEC-RAS (the `Flow Hydrograph=` block of a `.u` file and the DSS record), HEC-SSP (annual peaks for the Data Importer, the DSS record, and the Bulletin 17C settings with AquaScope's own result to compare), HEC-DSS, SWMM (time series file, `[TIMESERIES]` block, `[INFLOWS]` or `[RAINGAGES]` line), MODFLOW 6 (a River or Well package by stress period with its TDIS file, as text or through FloPy), Delft-FEWS (PI-XML) and Raven (an `.rvt` observation block). Each format cites the documentation it was written against, and each folder carries a README on how to load it.
- Real `.dss` files through HEC's own `hecdss` (MIT) where its native library loads (Linux, Windows); elsewhere, and in the browser, the CSV layout `hecdss` reads.
- `aquascope export --to <tool>` (from `--station source/id` or `--file`), the `engineering_export` MCP tool, an **Export for…** menu on the Explorer's station panel (a zip built in the worker), and an `engineering/` folder in every Studio bundle with a gauge record.
- The `engineering` extra (`hecdss`, `flopy`), included in `all`.
- **Bulletin 17C check** (`aquascope.hydrology.b17c_check`, docs page "Engineering exports"): AquaScope's EMA against the published Bulletin 17C examples, which HEC states HEC-SSP reproduces. It matches Example 1 (Moose River, weighted skew) within 1%, and does not match Example 2 (Orestimba Creek, zeros and low outliers): its Multiple Grubbs-Beck test censors 15 peaks where the Bulletin censors 30, so the rare floods come out several times too large. Examples 3 to 7 need flow intervals the EMA does not take yet.

Changed
- The date row moved out of the layer rail into the time bar.
- **USGS now comes entirely from the Water Data OGC API, version 1** (515). USGS retires the legacy NWIS Water Services host in Q1 2027, and when it goes a call left behind would have become a quiet empty frame and a hole in the Archive. The keyless path, which still called that host for daily and instantaneous values and for station names, now asks `api.waterdata.usgs.gov/ogcapi/v1` like the keyed one, with no key at all instead of the shared demo key. The base moves from `ogcapi/v0` to `v1`, the old collection names (`sta`, `discrete`, `dv`, `iv`) map onto their v1 collections, `statCd` reaches the API as `statistic_id`, and a keyed walk puts the key back on each `next` link, which USGS builds without it. The quality mapping from 481 now reads the v1 `approval_status` and qualifier list, and the shared pacing from 459 is unchanged. The Explorer and the harvest fetch a station's daily mean as one slim page (no geometry, only the fields read, up to 50,000 rows), as USGS advises. Station names no longer fall back on the retired NWIS site service: sites the `monitoring-locations` walk misses (it failed, or a capped run without a bbox walked other sites) are asked for by id, 100 per request and at most 2,000 per call, so a small `--max-items` harvest now names its stations. A test fails the build if the legacy host appears anywhere under `aquascope/`.

Fixed
- **A click beside a big river snaps to the river, not the stream next to it** (follow-up to 516, epic 526). On the Jamuna in Bangladesh a click snapped to a tributary of about 2 m³/s, so the Now tab called the river "much below normal (0th percentile)" while GloFAS had the main river near 15,900 m³/s. `snap_to_river` now weighs every reach within the tolerance and takes the main channel (the highest stream order, the nearer on a tie), and says so; at 24.35 N, 89.8 E: "Snapped 959 m to the main channel (order 8); a smaller stream is 925 m away." `prefer="nearest"` keeps the old rule. When a river at least two orders bigger lies beyond the tolerance (a braided river's water can be kilometres from its mapped centreline), the answer names it and the Explorer offers "Use the larger river" rather than taking it. With a gauge's catchment area (`area_km2`), the reach whose upstream area matches it wins, the rule the evidence ladder (518) already used, now one function (`rivers.match_by_area`) for both; it is only read where the reaches in reach differ in stream order. A gauge without a known area takes the nearest line, since it sits on its own river, and so does the daily forecast archive. `aquascope river snap --nearest --area KM2` and the MCP tool take the same choices.
- **Context on gauges** (follow-up to 520). The station panel has the Context tab a clicked point has: flood history, surface water, flood depth, dams, soil, evaporation and the nearest rain gauge at the gauge's position, from the same `aquascope.context` calls and the same card.
- **Faster Now and Context** (follow-up to 517 and 520). Python in the browser reads one URL at a time, and every call queued behind the one before, so the Now tab took about 50 s and Context about 60 s on the live site. Now:
- Up to three light workers (one on a phone) run beside the main one, without pandas or scipy, and take the calls that only read the network: a click's river snap, the quick forecast and the Context layers, quickest first. A browser that cannot start them sends those calls to the main worker as before.
- The forecast comes in two steps: `forecast(..., history=False)` reads only the GEOGLOWS and GloFAS forecasts, so the plot and its sentence go up first, then the thresholds, the status and the correction, which need the reach's simulated record since 1940, follow on the main worker, reusing the GEOGLOWS part (`known_geoglows`) rather than reading it again. `aquascope now --quick` and `flow_forecast(quick=True)` give the quick answer.
- Every Context line appears as it lands, and forecasts and Context lines already read are kept for the session.
- Measured on a local build in the built-in browser, the worker calls a click makes timed from the page once Python had loaded, two places each and a different place per run: the Now tab's first line came after 33.5 s and 38.9 s before (the whole answer at once) and after 5.9 s and 6.2 s now, the whole answer after 27.6 s and 31.7 s; the first Context line after 8.0 s and 10.6 s before and 5.4 s and 5.5 s now, all seven after 65.8 s and 45.1 s before and 24.5 s and 30.5 s now. The live site adds the wait behind the other calls a click makes, which the light workers now skip.
- **Explorer QA after the every-river epic (526).** Found by walking the live site:
- Clicking a peak on a hydrograph now moves the map to that flood. The annual maxima were drawn at 1 July and opted out of the map date; `analyze_series` now gives each year's peak day (`annual_max.date`), and the gauge and River tab plots draw the markers there (522).
- The Now forecast is no longer pressed flat when the lowest return-period line is far above it (the Potomac's 62 m³/s forecast was drawn under a 2,876 m³/s 2-year line). A line more than four times the plot's highest value (the ensemble's top included) is left off; the sentence above still names it.
- "Use the nearest reach" on a click away from a river now asks the forecast again for that reach (GEOGLOWS and the GloFAS cell on the river), and the line under the point's title names the reach instead of still saying there is no stream.
- A gauge's head (the watch row, the export note, the coverage details) no longer sticks to the top of the panel: open, it left the tabs about a hundred pixels on a laptop and less on a phone. It scrolls away now and the tab strip stays.
- The time bar's note sits above its controls, so turning on Rain or Satellite from the note no longer moves the play button out from under the pointer.
- The River tab's snap line and trace, the Now sentence and the coverage details were at the browser's default size, larger than the rest of the panel; the evidence table's r, α and β headers were upper-cased into "R", "A" and "B".
- Five catalogue sources showed their registry keys in the legend and the panel ("bom", "brazil_ana", "poland_imgw", "greece_hydroscope", "greece_openhi"); they read "BOM (AU)", "ANA (BR)", "IMGW-PIB (PL)", "Hydroscope (GR)" and "OpenHi (GR)" now. The River tab says "over 86.7 simulated years" rather than rounding to 87 beside "86 simulated annual maxima", and the coverage line puts a thousands separator in the observation count.
- The Global Dam Watch mirror placed 35,032 of its 41,145 barriers at 0, 0: it read `LAT_DAM`/`LONG_DAM`, the surveyed position GDW fills only for some, instead of `LAT_RIV`/`LONG_RIV`, the location of every point in the layer (it equals the shapefile's own geometry). The mirror now uses the latter and drops any row left at 0, 0, and every dam reader (the place-context dams, the trace, the dams upstream) skips a row at 0, 0 until the `mirror-context` workflow runs once more to republish the dams.
- The water storage layer (GRACE) showed nothing at the default date, because GIBS has no month after July 2022. The time bar now says when a date is outside a layer's range, and each dated layer carries its first and last day.
- GRACE months whose image starts mid-month (February 2004 starts on the 4th, February 2011 on the 8th) drew nothing, because the map asked GIBS for the first of the month. The layer now carries the image list from the GIBS capabilities and asks for the image that covers the day, and the bar says when a month has no image (the GRACE to GRACE-FO gap).
- **A click on a hillside is no longer given the river's whole catchment** (516). The Explorer's catchment card waits for the snap: with no stream within 1 km it says so, instead of quoting the upstream area of the BasinATLAS sub-basin the point happens to sit in.
- `aquascope/io/hec.py` no longer says the DSS format needs a proprietary library: HEC's `hecdss` is MIT and on PyPI.
- **Windows test assumptions:** repair evidence carries POSIX paths, and the workflow shell check skips on Windows (part of 432).
- **A source's health record keeps the error under a wrapped failure** (498). BOM raises one `RuntimeError` once every parameter type has failed, and the harvest recorded only that message, so the 2026-10-05 outage (503s and timeouts) was filed as unclassified and sent to the repair bot as a possible code fault. The catalog error now carries the exceptions it was raised from, so the health issue names the 503.
- **The weekly harvest now reaches the files the 40-year cap truncated** (501). The 0.21.0 change (270) sorted them first among stations due a refresh, but behind every station never harvested; USGS has about 25,000 of those, enough to fill each weekly budget of 150, so its 133 truncated files would have waited about three years. They now go ahead of new stations, and the stations the Explorer's daily live check reads (Fish River, Thames at Kingston, Seine at Paris) go first of all while new, stale or truncated.
- Record the verified v0.25.0 Zenodo DOI (`10.5281/zenodo.23219118`) in release citations, Explorer BibTeX and generated study reports; retain the concept DOI for the project as a whole.
- **Three tests no longer depend on run order.** A test that ran `aquascope studio` left two things behind for the rest of the run: the saved model keys from `~/.config/aquascope/keys.json` in the environment, and the `aquascope` loggers quieted to WARNING or ERROR. After it, `test_resolve_llm_env_and_errors` found a key and the BOM catalog and USGS DEMO_KEY `caplog` tests saw no warning. The test suite now gives every test an empty config directory and restores the environment and logger levels after each one, and the analyst test clears every provider's key variable. Two more were fixed the same way: `test_resolve_llm_nvidia_env` now clears every provider's key too (an `ANTHROPIC_API_KEY` in the shell failed it), and two Studio bundle tests build on a copy of the shared workspace, so they pass in either order.

0.25.0

Added
- **The Study Desk**, where a finished study is revised, checked and signed (`aquascope.studio.desk`, `aquascope desk`, and a panel beside the report in the Explorer).
- The levers are the design return period, the record window, the years whose flood is left out of the fit, and the distribution the answer quotes.
- A change reruns only the flood steps it touches, with their checks, rewrites the documents, and is recorded as a lettered revision with the answer before and after.
- A sensitivity table shows the design value under the reasonable alternatives (other fits, the largest flood left out, the last 50 or 30 years, excluded years put back). It is computed from the stored annual maxima without a fetch.
- Review comments are kept with their responses.
- Signing as prepared, checked or approved fills the document control and moves the status from DRAFT to CHECKED to ISSUED.
- The report gains a revision history, a sensitivity section and a review log.
- `analyze_station` and `flood_frequency` take `exclude_years`: those years' annual maxima leave the fit, its trend and step-change tests and its bootstrap. They are returned as `annual_max_excluded` and shown as crosses on the frequency curve. Steering offers it as a control.

Changed
- **One command for the Studio.** `aquascope studio` given a finished study's bundle folder (or its `workspace.json`) opens the Study Desk, and the Desk's options (`--exclude-years`, `--estimator`, `--years`, `--set`, `--sign`, `--comment`, `--resolve`, `--by`, `--note`) work on it directly. `aquascope desk` stays as the shorter name for the same thing. `--return-period` also sets the design return period of a new study.

Fixed
- Record the verified v0.24.0 Zenodo DOI (`10.5281/zenodo.23214018`) in release citations, Explorer BibTeX and generated study reports; retain the concept DOI for the project as a whole.
- A Study Desk revision refits the record the study already holds, from its record table, instead of fetching it again. Before, a rerun could arrive with a different record from the first run (in the browser the first run can fall back to the archive's 40-year copy), so a revision mixed a change of assumption with a change of data.
- The `NumExpr defaulting to N threads` line no longer opens every command.
- The Author's software citation no longer depends on the deliverables package being importable.

0.24.0

Added
- **The Studio writes documents a hydrologist would sign.** A new `aquascope.studio.document` composes each study into a technical report and a short technical memorandum, rendered to Word, print-ready HTML (A4, which prints to PDF) and Markdown from the same structure.
- The report opens with a cover and a document-control table: prepared, checked and approved lines that stay blank until a person fills them, with a DRAFT stamp.
- The summary leads with the answer in a box: the design value, the interval that belongs to it, the grade and the reason for the grade.
- The results are organised by question (design flood, stationarity, transfer from similar catchments, climate context), not by plan step.
- Figures and tables are numbered and cross-referenced. At most six figures go in the body and the rest in an appendix.
- Each check is a sentence with its verdict, followed by limitations, recommendations and references, with the reproducibility details in an appendix.
- Numbers are given to three significant figures, and units are written as a reader writes them (m³/s, km², °C).
- An agency's details (organisation, project, client, people, logo, accent colour) come from `aquascope studio --style style.yaml` and travel with the workspace.
- A study that established nothing becomes a one-page study note saying what failed in plain words and what to do next, never a report.
- A flood question at an ungauged site is answered as screening. Transferred depths are converted to m³/s with the upstream area, GloFAS is set beside them, and the report names the gauge whose record would answer it.
- **Publication figures** (`aquascope.viz.publication`).
- One serif face (Times New Roman, else the bundled STIX, so the browser matches the desktop), a full frame with inward ticks and the Okabe-Ito colours.
- Figures are sized to the page, with no title inside the frame, legends placed in the emptiest corner, and Unicode units. They are written as PNG at 300 dpi and as SVG.
- The flood frequency curve is drawn on Gumbel probability paper, with the observed annual maxima at their plotting positions and an annual-exceedance-probability axis. Line series break at gaps instead of joining across them.
- The flow-duration curve uses a normal-probability axis, and the transferred signatures are a dot-and-band plot with their leave-one-out skill.
- Every Studio figure maker uses the style.
- **The Explorer's finished study opens the report and the memo in a reader**, with Print or save as PDF and the Word files beside them.
- **A shorter terminal log for `aquascope studio`.** There is one line per stage and one per analysis, with its checks folded in (`aquascope.studio.progress.Narrator`), and the run ends with the answer box and the files to open. `--verbose` keeps the full event stream.

- **`fetch_series` reads BOM and ANA records**, so the Studio's Scout and the Explorer can use an Australian or Brazilian gauge instead of falling back to regionalisation. `bom` reads BOM Water Data Online's quality-checked daily mean for the requested window, discharge first and water level when the gauge has none (A4261794 carries level only). `brazil_ana` reads ANA's conventional network (`HidroSerieHistorica`), which needs no credentials: daily discharge then stage, reviewed months preferred over provisional ones. Each note says the series lags real time: weeks for BOM, months for ANA. A station on ANA's telemetric or water-quality networks only still has no record. `DIRECT_FETCH_SOURCES` lists the sources `fetch_series` reaches directly. BOM refuses cross-origin requests, so it is marked browser-unreachable and the Explorer says so. Nothing from either source is mirrored.

Changed
- The site reconnaissance can fetch the record of a nearby gauge whose span the catalog does not know (`assess_site(..., probe_km=...)`; the Studio's Scout looks within 10 km), instead of dropping a gauge that may sit at the site. A source AquaScope cannot fetch yet is named as such, with the suggestion to attach the agency's record.
- `flood_frequency` keeps the annual maxima it was fitted to, so the frequency curve can show them.
- The snow sensitivity in the site check is worded for each method. For flood frequency it reads "the annual maxima can mix snowmelt and rainfall floods", not the rainfall-runoff model's "a method without a snow store".
- `aquascope.viz` imports its plotting functions lazily, so importing the package no longer imports matplotlib.

Fixed
- Record the verified v0.23.0 Zenodo DOI (`10.5281/zenodo.23200452`) in release citations, Explorer BibTeX and generated study reports; retain the concept DOI for the project as a whole.

0.23.0

Added
- **`aquascope update` upgrades aquascope the way it was installed**, and `aquascope --version` says which version you have. It reads the newest release from PyPI, works out from the running interpreter whether this copy is a uv tool, a pipx app, a conda or virtual environment or a development checkout, and runs the matching upgrade after asking (`--yes` skips the question, `--check` only reports). A uv tool installed with an exact pin, which `uv tool upgrade` never moves, is reinstalled without the pin with the same extras and Python; a development checkout is told to `git pull`. The logic is in `aquascope.updates`.
- **`aquascope eval` scores, traces and compares finished studies** from their bundles, with no model and no network. `eval score` gives one study's scorecard: outcome and grade, steps and gates passed, failed and skipped, the Critic's checks, report quality on the six HydroGym axes (against its reference when the package has one), plan accuracy against a reference case with `--case`, and time, tokens and cost by role. `eval trace` lays out the run: each phase and step with its duration, the gates that did not pass and why, and the model calls per role. `eval stats` groups many studies by playbook, model, date or grade, names the gate checks that fail or skip most often, and writes CSV. The functions are in `aquascope.evaluation`.
- **The Explorer's station panel lets you choose the period** (270): the full record (the default), the last 40 years or the last 20, next to the record card. The choice travels in the link as `&yr=40`, `&yr=20` or `&yr=all`, and the loading line says which one is on its way, since a full USGS record can be a century of daily values.

Fixed
- **Preserve USGS quality qualifiers on the keyless path** (481). Keyless NWIS observations now retain their qualifiers, so approved, provisional and estimated readings are mapped to the correct quality status instead of being reported as unknown. Added regression coverage for the keyless fetch_raw → normalise path.
- The README and the Explorer guide say the record is fetched in full by default and can be cut to 40 or 20 years (270), and the Analyst guide's reconnaissance example no longer says a fetch serves the last 40 years or that CMIP6 needs model output you supply.
- Record the verified v0.22.0 Zenodo DOI (`10.5281/zenodo.23132668`) in release citations, Explorer BibTeX and generated study reports; retain the concept DOI for the project as a whole.

0.22.0

Changed
- **The design-flow study checks the annual maxima for a step change too** (376). The flood fit that `analyze_station` and `flood_frequency` run now carries Pettitt's test on the same maxima (`ffa.amax_change`), next to the Mann-Kendall trend it already had. A significant shift keeps the estimate and grades it indicative, with the year named and a pointer to the flood change study. There is no new plan step, so every recorded study and HydroGym score stays as it was.

Fixed
- Stale counts outside the count guard (453). `docs/api.md` and `docs/faq.md` state the registry's source count and are now guarded; the banner, the architecture guide and the troubleshooting page no longer carry decorative counts; the dashboard's AI page reads the methodology count from the knowledge base; and `.github/copilot-instructions.md` describes the current repo. Counts in `paper.md`, `ROADMAP.md` and past CHANGELOG entries are snapshots and stay as written, with a comment saying so.
- Record the verified v0.21.0 Zenodo DOI (`10.5281/zenodo.23048856`) in release citations, Explorer BibTeX and generated study reports; retain the concept DOI for the project as a whole.
- The showcase workflow opens a pull request for each new recording again; it had mistaken the long-merged 263 for an open one, so recordings since then sat on `showcase/recordings` unseen. It also stops seeding from that branch once its pull request has closed, which had rolled the recorded studies back to their 2026-09-14 copies.

Page 1 of 5

© 2026 Safety CLI Cybersecurity Inc. All Rights Reserved.