Modernization roadmap
This page summarizes the modernization design that produced this documentation site and the
run / doctor / tutorial / namelist additions. The full dossier — including the
persona-validated reviews — lives in the repository under docs/modernization/.
The thesis: close the last mile
Section titled “The thesis: close the last mile”nwp-compose is technically mature, but a persistent last-mile gap stands between
“I have a workflow.yaml” and “my forecast is cycling”: the path is multi-step, assumes Cylc
fluency, and several checks fail silently. The project’s own persona reviews surfaced the same five
themes across every phase:
cylc validatepassing ≠ runnable;- warnings that emit but don’t block;
- docs that drift from code;
- the gap between
generateand “ready to run” (with<workflow_id>undefined); - demo/banner copy overstating capability.
A scientist migrating off a personal bash/cron setup hits themes 1, 4, and 5 on their first afternoon. So the design’s spine is one sentence: lower the Cylc-fluency barrier and make the last mile a guided, honest, gated, single-command experience — without elevation, on any shared system.
The five workstreams
Section titled “The five workstreams”Design principles (the discipline)
Section titled “Design principles (the discipline)”- Unprivileged-everywhere — no sudo, no system services, no Docker daemon;
$XDG-aware, redirectable state; rootless containers; user crontab. - Additive only — the v0 schema is frozen; everything is a new command, doc, or optional parameter. The freeze test stays green.
- Honor code-gen resistance — shell out to Cylc, drive the existing namelist/render primitives;
no Python
flow_builder. - Generate docs from code — the CLI reference is introspected from the live Click app, so a drifted doc is a failing build, not a stale page.
- Gate, don’t just warn — every new path that produces a runnable artifact routes through the same validate/unphysical-block gate.
Tech-stack recommendations
Section titled “Tech-stack recommendations”Ranked, each unprivileged-aware (no recommendation requires elevation):
| # | Recommendation | Why | Effort |
|---|---|---|---|
| 1 | Commit a dependency lockfile (uv.lock) | cylc-flow’s 30+ transitive deps are unpinned today — the highest-value supply-chain win. | low |
| 2 | Vite build for gui/app/ | Retires the ~1.5 MB in-browser Babel transpile and the dev-build React; enables tree-shaking and production builds. Runs entirely as the developer’s user. | medium |
| 3 | SRI on every CDN load | The Pyodide and reveal.js loads lack integrity hashes; add them or self-host. | low |
| 4 | CSP at the Caddy edge | Defense-in-depth beyond tag-level SRI for the hosted GUI. | low |
| 5 | Rootless container recipes | Apptainer/Podman defs so the CI harness and any container path run without a Docker daemon on HPC. | medium |
| 6 | XDG-aware state paths | XDG_CACHE_HOME/XDG_DATA_HOME/CYLC_RUN_DIR honored everywhere (see the unprivileged checklist). | low |
Persona validation
Section titled “Persona validation”Following the repository’s sci-plan → sci-review / eng-plan → eng-review convention, the design
was validated by two adversarial personas — a senior scientist/modeler and a senior research
software engineer. Their reviews (in docs/modernization/51-sci-review.md and 52-eng-review.md)
caught real correctness bugs in the draft specs — e.g. that MPAS CFL is the non-blocking W5, not a
blocking code, and that several components declare namelist templates that are not shipped — which
were folded back into these docs. That is the review loop working as intended.