Carto now requires an API key for its basemap tiles, so the server no longer fetches them lazily: holmes download fetches the fixed Saguenay pyramid (zooms 9–12, 3400 tiles) with the maintainer's CARTO_KEY, the data archive ships the tiles like every other product, and the tile route serves them read-only — the app is fully offline after the startup sync
The map opens one zoom level wider (9 instead of 10) and clamps panning to the pre-downloaded tile rectangle
uvicorn is now installed with its [standard] extras: watchfiles replaces the stat-polling dev reloader, and uvloop/httptools speed up the event loop and HTTP parsing
The client now waits 20 seconds instead of 10 for the websocket connection to open, so a slow first load or a dev-server reload no longer surfaces as a spurious connection timeout
The hydrographs no longer wait for the station list: the persisted stations' streamflow is requested at connect, in parallel with the 8 MB stations reply, which the server now builds and sends as a background task (its watershed-to-GeoJSON pass ran on the event loop) without holding the requests queued behind it; per-message deflate is off since compressing that reply blocked the loop for nothing on localhost
The projection indicators chart drops the "Mean" column and carries its unit in a rotated y-axis title (Indicators (mm/day)) instead of a figure caption, matching the regime chart above it; the exported _indicators.csv loses its mean column with it
The simulation metrics chart restores the parentheticals the old holmes showed, so the plotted quantity is named: "Water balance (Mean bias)" and "Flow variability (Deviation bias)"
Websocket problems were undiagnosable from the silent server console: it now narrates connects and disconnects, logs every reply with its payload size and elapsed time, times each model's calibration, mirrors every error sent to the client, and runs uvicorn at the warning level so dev-reload restarts are visible
The projection step failed on the float32 forcing the products store since 4.5.0 ('ndarray' object cannot be cast as 'ndarray'): the model layer now casts to float64 at the Rust boundary
The projection indicators legend showed its two labels with no line beside either: the hover highlight raised every .series-tick to the SVG root, which caught the legend swatches too and moved them out of the translated group that positions them
The projection indicators chart drew its broken y axis with the same slope on both sides of the break — the low segment carried 2 mm/day over 58 % of the height and the high segment about as much over the rest — so it read as one linear axis and the two minima columns kept no visible spread. The seasonal minima and maxima now get a scale each, separated by a band of whitespace: the low one is floored at 0 and reaches the minima's own maximum, the high one spans the maxima alone, and each column declares which band it belongs to (the two maxima can sit further apart than either sits from the minima, so the split cannot be inferred from the values)
The regime chart's month labels overlapped the 0 of its y axis, which is pinned to the baseline directly under Jan; the month row now sits clear of it
holmes run opens the dashboard in the default browser once the server is up, as it did in 3.x; debug mode still leaves the browser alone so a reload does not reopen a tab, and a machine without a browser only warns and keeps serving
A freshly built data archive is roughly 190 MB instead of 418 MB: the climate projections, which were 83% of it, are stored at the float32 precision their source already carries and rounded to 0.01 mm/°C — finer than any observation — taking each station's product from 45 MB to 16.5 MB
Watershed outlines are stored once, as the WKB the server already parses for its centroids; the GeoJSON the map draws is derived per request (52 ms, once per session) instead of shipping a second copy of every polygon in the archive
holmes download skips the sources it already refetched today unless --force, so a second run on the same day no longer redownloads identical bytes
The websocket API moved from four api_*.py modules to a single holmes.api package (api.py, calibration.py, projection.py, simulation.py, utils.py)
The startup data sync now reports what it is doing: a first run says so explicitly and that the application starts right after, and the download shows a live megabyte counter instead of an unexplained multi-minute silence
Ctrl-C during the startup data sync now stops the application: the sync moved out of the uvicorn app factory, whose signal handler only sets an exit flag and so ignored the interrupt for the whole download; an interrupted download also clears its staging directory instead of stranding a partial archive
holmes package command zipping every built data product into the dated data-YYYY-MM-DD.zip archive published on the repo's rolling data release
Nightly data-refresh GitHub Actions workflow (.github/workflows/data.yml) running holmes download and holmes package and replacing the dated archive on the data release, toggled with the DATA_REFRESH_ENABLED repository variable (manual runs always allowed)
HOLMES_SKIP_DATA_SYNC environment variable to skip the startup data sync
Breaking: the server no longer builds data at runtime: every product is pre-built and served as one dated zip on the data release; at startup the server downloads and extracts a newer archive if one exists (keeping the current data served during the swap) and raises an actionable MissingDataError when no data is available at all — map tiles remain the one lazily fetched exception
Breaking: data now lives in the per-user data directory (~/.local/share/holmes on Linux, ~/Library/Application Support/holmes on macOS, %LOCALAPPDATA%\holmes\holmes on Windows) instead of the working directory's data/, overridable with the HOLMES_DATA_DIR environment variable (a repo checkout uses HOLMES_DATA_DIR=data)
Breaking: holmes download is now the maintainer path building every product incrementally from its true source — daily re-runs only fetch the current year (plus the previous year in January) for ERA5 and the ministry grid, always refresh the streamflow files, recompute the cheap derived products, and skip the static ones — and requires the new download optional extra (pip install 'holmes-hydro[download]'), which carries the heavy geo dependencies (cdsapi, xarray, rioxarray, exactextract, netcdf4, pystac-client) removed from the base install
Breaking: the runtime cache-or-fetch data layer, the committed data files fetched from the repo, and the per-product release assets, all superseded by the dated archive on the data release
Installation no longer fails with uv or pip ≥ 24.1: the abandoned pre-release geopolars dependency (invalid metadata rejected by modern pip) was replaced with the already-required geopandas, also dropping the transitive pyarrow dependency
User guide in the documentation covering every step and feature of the application, with screenshots in both themes (regenerated with make screenshots)
The application is now bilingual (English/French): a language button in the settings menu (hotkey L) switches every label, chart legend, dialog and model description; the choice persists in localStorage and defaults to English
Model and parameter descriptions are served in both languages (model_info/calibration_info payloads carry {en, fr} texts backed by param_descriptions_fr in holmes-rs 0.7.0)
The documentation is bilingual too (mkdocs-static-i18n): every page has a French sibling, a language switcher on every page, and French-UI screenshot pairs (<scene>-fr-{dark,light}.png) captured by the same make screenshots walk
Breaking: complete rewrite of the application.
The UI is now a guided pipeline (stations → weather → model → calibration → simulation → projection) built on an Elm-architecture vanilla-JS frontend with an interactive Leaflet station map, replacing the tabbed catchment interface
Breaking: the data model is station-based — hydrometric stations (DEH) with weather built from ERA5, nearest MELCC stations, or the ministry grid, replacing the shipped per-catchment CSV bundles; data is fetched from its true source at runtime and cached under data/
Breaking: the CLI is now a Typer app with subcommands: holmes run (dashboard), holmes download (rebuild the published datasets), and holmes experiment (run batch experiments); the entry point moved from holmes.app:run_server to holmes.cli:run_cli
Breaking: minimum supported Python is now 3.12
Climate projections are fetched per station from PAVICS (ClimEx and ESPO-G6-R2), with prebuilt products served from the repo's data release instead of shipped projection CSV files
Breaking: shipped catchment datasets and the observations / CemaNeige-info / projections CSV input formats
scripts/download_hydro_data.py, scripts/run_experiments.py, and scripts/convert_projections_format.py, superseded by holmes download and holmes experiment
WAGENINGEN hydrological model (8-parameter HOOPLA HM19 port of the Warmerdam et al. 1997 model with threshold-based soil-moisture production, capillary rise, cosine ET envelope, fast/slow flow dissociation via the x5 threshold, and fractional-delay routing) support in model registry, config, and simulation dispatching
TANK hydrological model (7-parameter Perrin variant of the Sugawara 1979 cascade of four linear reservoirs S→R→T→L with dual-threshold side-outlets on the surface store, geometric drain-time scaling, cascading ET satisfaction, and fractional-delay routing) support in model registry, config, and simulation dispatching
MORDOR hydrological model (6-parameter Garçon 1999 formulation with four cascading reservoirs U→L→Z→N, proportional rainfall partitioning, nonlinear cubic groundwater discharge, and three-component double-sided UH2 routing with exponent 2.5) support in model registry, config, and simulation dispatching
MARTINE hydrological model (7-parameter Perrin variant of the Mazenc et al. 1984 BRGM model with overflow production, quadratic direct routing, dual-pathway intermediate reservoir, and linear groundwater recession) support in model registry, config, and simulation dispatching
CREC hydrological model support in model registry, config, and simulation dispatching
CREC model documentation page with mathematical formulation, 6-parameter description, and model comparison table update
GARDENIA hydrological model support in model registry, config, and simulation dispatching
HYMOD hydrological model support in model registry, config, and simulation dispatching
HBV hydrological model (9-parameter Bergström/Forsman HBV0 variant) support in model registry, config, and simulation dispatching
XINANJIANG hydrological model (8-parameter Perrin variant of the Zhao et al. 1980 saturation-excess model) support in model registry, config, and simulation dispatching
SACRAMENTO hydrological model (9-parameter Perrin variant of the Burnash et al. 1973 NWSRFS model) support in model registry, config, and simulation dispatching
IHACRES hydrological model (7-parameter Perrin variant of the Jakeman et al. 1990 model with PET-modulated drying constant and parallel fast/slow linear routing) support in model registry, config, and simulation dispatching
TOPMODEL hydrological model (7-parameter Perrin variant of the Beven & Kirkby 1979 model with sigmoid recharge/ET partitioning, exponential groundwater store, and quadratic surface routing) support in model registry, config, and simulation dispatching
NAM hydrological model (10-parameter HOOPLA HM12 port of the Nielsen & Hansen 1973 Nedbør-Afstrømnings-Model with seven reservoirs and a fractional-delay unit hydrograph) support in model registry, config, and simulation dispatching
PDM hydrological model (8-parameter HOOPLA HM13 port of the Moore & Clarke 1981 Probability-Distributed Model with Pareto soil store, cubic ground reservoir, two-stage linear cascade, and fractional-delay routing) support in model registry, config, and simulation dispatching
MOHYSE hydrological model (7-parameter HOOPLA HM10 port of the Fortin & Turcotte 2007 MOdèle HYdrologique Simplifié à l'Extrême with capacity-limited infiltration, dual soil/groundwater linear reservoirs, and gamma unit hydrograph routing) support in model registry, config, and simulation dispatching
SMAR hydrological model (8-parameter Perrin variant of the O'Connell et al. 1970 Soil Moisture Accounting and Routing model with 16-layer soil column, exponentially decaying ET, dual linear/quadratic routing, and fractional-delay routing) support in model registry, config, and simulation dispatching
SIMHYD hydrological model (8-parameter HOOPLA HM15 port of the Chiew et al. 2002 SIMple HYDrological model with exponential infiltration, saturation-proportional interflow/recharge, and dual linear routing reservoirs) support in model registry, config, and simulation dispatching
Warmup period filtering for projections: read_projection_data now computes an is_warmup column marking the first 3 years of each member's time series, and both _aggregate_projections and _evaluate_projection exclude warmup data from their calculations
Projection API now sends raw per-member timeseries (projection) alongside the aggregated data (aggregated_projection), enabling CSV export of the full ensemble
Horizon dropdown labels now display the date range (e.g., "H50 (2041-01-01 to 2070-12-31)")
E2E tests verifying exported CSV content for projection data and results (column structure, ensemble member count, indicator values) for both Baskatong and Au Saumon
Simulation and projection APIs now correctly compare snow model against "none" string instead of None, fixing snow model detection after the 3.4.1 sentinel change
Simulation API explicitly casts hydro parameters to float64, preventing dtype errors when JSON round-trip through JavaScript produces integer values
Projection file upload now resets previous results, preventing stale charts from persisting
Projection remove calibration now properly clears config state
Fixed calibration bug where selecting transformation "none" (high flows) would break calibration by being incorrectly converted to null — the snow model's null sentinel was colliding with the transformation's legitimate "none" value
Backend now sends "none" as a string instead of null for the "no snow model" option, normalizing to None only at the API boundary
Parameter slider step precision increased from 0.1 to 0.01 for non-integer parameters
Renamed bucket model parameters from descriptive names (c_soil, alpha, k_r, delta, beta, k_t) to generic names (x1–x6), matching the convention used by GR4J and CEQUEAU
Calibration bar chart x-axis tick labels are now limited to 10 to prevent overlapping when many iterations are displayed
Cleaned up model registry docstrings to avoid hardcoded model lists
Added CEQUEAU model documentation: overview, mathematical formulation from Perrin (2000), 9-parameter description, and differences from original 11-parameter CEQUEAU
Added comprehensive Concepts documentation with mathematical formulations:
GR4J model: production store, unit hydrographs, routing store equations
Bucket model: linear reservoir theory, flow partitioning, comparison with GR4J
Eager file existence checks in data.py for read_catchment_data, read_projection_data, and _get_available_period to properly handle missing files with lazy Polars operations