Skip to content

Make csauto solver-generic and release 0.6.0 - #39

Merged
ulysse-bonneau-simvia merged 21 commits into
mainfrom
refactor/solver-readiness
Oct 8, 2026
Merged

ulysse-bonneau-simvia merged 21 commits into
mainfrom
refactor/solver-readiness

Conversation

@ulysse-bonneau-simvia

Copy link
Copy Markdown
Collaborator

What

Release 0.6.0: csauto no longer assumes code_saturne. One adapter class describes a solver, and the CLI, the dashboard, doctor and Clean read everything solver-specific from it. code_aster now runs on the same launch path as code_saturne and actually works end to end. The full list is in the [0.6.0] section of CHANGELOG.md.

Highlights:

Breaking: csauto control RUNS CASE stop|extend N|checkpoint|flush replaces the --stop/--extend/--checkpoint/--flush flags; /api/probes takes scope=probes instead of monitoring; restart mode aliases are gone; SolverAdapterBase is merged into SolverAdapter.

Closes #26
Closes #27
Closes #28
Closes #29
Closes #30
Closes #31
Closes #38

Two notes on the issues:

Why

The solver boundary only covered the run lifecycle: code_saturne file names, layouts and regexes were spread across generic modules, so a second solver got a code_saturne dashboard and code_aster runs could not succeed. This makes adding a solver a matter of writing one adapter class.

How to test

T=~/.cache/csauto-try; R=<checkout>
mkdir -p $T
rsync -a --exclude RUNS $R/examples/single-case/ $T/cs/
rsync -a --exclude RUNS $R/examples/codeaster-cube/ $T/ca/
cd $T/cs && csauto prepare doe.csv TEMPLATE RUNS && csauto serve RUNS --port 8000
# second terminal
cd ~/.cache/csauto-try/ca && csauto prepare doe.csv TEMPLATE RUNS && csauto serve RUNS --port 8001

The token is your-secret-token. Work outside /tmp (Docker Desktop mounts /tmp folders empty). On code_aster, case0001 is meant to fail and case0002 to end DONE.

Real runs of both examples in their docker images: CSAUTO_DOCKER_TESTS=1 pytest tests/integration/test_docker_solvers.py -q.

Checklist

  • Tests pass locally (pytest -q): 550 passed, 4 skipped; docker e2e 3 passed
  • Frontend builds (cd frontend && pnpm build), pnpm check 0 errors, pnpm format:check clean
  • No linting errors (ruff check . && ruff format --check .)
  • frontend/dist/ is up-to-date (committed after pnpm build)

ulysse-bonneau-simvia and others added 21 commits October 7, 2026 12:47
The Log Tail default and file priority come from the new tail_file_names
adapter attribute, job id discovery scans anomaly_file_names, and
/api/probes takes scope=probes instead of the code_saturne directory name.

Closes #27
…adapter

template.py now only renders templates.

Closes #28
probes.py and residuals.py keep CSV reading and rendering, and take run
directories and subdirectory names from the adapter. The run_solver.log
residual parser moves into the code_saturne adapter.

Closes #29
…lary

logs.py keeps tailing, the incremental anomaly scanner, outcome scanning
and recency checks. Outcome, anomaly, progress and restart patterns, the
RESU layout and the performance.log parser move into the code_saturne
adapter; anomaly patterns become a generic set adapters extend.

Closes #30
The boundary test globs every module outside csauto/solvers/, matches a
wider token list as whole words, and keeps the deliberate residue in a
named allowlist matching docs/architecture.md. The leaks it found are
removed.

Closes #31
…ng code_saturne

- SolverAdapter is now the single adapter base class. detect_outcome is
  optional: runs record their exit status, used when the logs give no
  verdict. New declarations: supported_runtimes, container_setup,
  ControlAction/RestartMode specs, default_residual_columns, logo and icon.
- Every runtime starts the solver inside the case folder; docker uses
  --entrypoint and --rm, so images need no solver ENTRYPOINT.
- A run stays RUNNING until its process or Slurm job is gone; reused PIDs
  are detected from the process start time.
- Clean only deletes the folders list_run_dirs reports and skips running
  cases. Cases are found from the registry, not a case* name prefix.
- Campaigns record their solver in campaign.json; the docker image
  defaults to the solver's own.
- Templates: \{name} keeps a literal {name}; files keep non-UTF-8 bytes;
  errors name the file.
- Kill only searches docker for docker runs, scoped to the campaign.
- Launch errors no longer strand cases in PENDING or stop the batch.
- sbatch --wrap requests --ntasks and --cpus-per-task.
- API: free-form /api/perf records, /api/tail_files, /api/tail_lines,
  solver logo and icon, typed control values and adapter restart modes.
- code_aster runs on the generic launch with a per-launch export copy
  carrying n and nt, and reads its verdict from csauto.stdout.
…olvers

Every registered adapter now runs the same contract checks against a
sample finished case. Opt-in docker tests (CSAUTO_DOCKER_TESTS=1) run the
shipped code_saturne and code_aster examples end to end. New tests cover
exit-status outcomes, live runs that print a verdict early, reused PIDs,
refused launches, Clean through list_run_dirs, campaign solver recording,
template escapes and byte preservation, and the adapter-driven API.

Also mount campaigns at /mnt by default (the image user's home received
the solvers' dotfiles), resolve the runs folder before launching (relative
cidfiles broke docker runs from the CLI), and let the code_aster example
reference its mesh as ../MESH like code_saturne cases.
- The control menu lists the adapter's actions and asks for typed values;
  only running cases receive them. The Restart dialog offers the adapter's
  modes and the run to restart from.
- Log Tail lists /api/tail_files and colours lines with the server's
  severities from the solver's patterns; the case selector stays visible
  when a case has no log yet.
- The header logo and favicon come from the adapter (/api/solver_logo,
  /api/solver_icon); solver images left frontend/static.
- Until /api/app_config loads, only the solver-independent panels show and
  no action is offered; the config is fetched again with a growing delay.
- No more code_saturne defaults: residual curves from the adapter, no
  fallback timing columns, generic labels (Run folders, Results (MB)).
- The boundary test now scans the dashboard source too.
… from leaving half-made cases, and show tail -n lines in full

prepare rendered only files holding a placeholder, so a .comm whose only
braces were escaped kept its backslashes. A row with an empty value raised
after its case folder was copied, which then blocked every later prepare.
csauto tail read only the last 4 KB before keeping -n lines.
Rewrite the solver guide around one adapter class, a conformance sample and
recipes for common solver shapes; describe the boundary as it is in
architecture.md; update the CLI, API, configuration, template, lifecycle,
web UI and troubleshooting pages, the README and the examples; record the
changes in the changelog. Remove claims the code does not back.
Run liveness: a PID is identified by the boot id and its start time in
clock ticks, recorded at launch, instead of the wall-clock launch time,
which broke when WSL2 stepped the clock after a sleep. A PID owned by
another user goes through the same check. Slurm jobs and local runs end
with the solver's exit status, and a queued case no longer flips to DONE
or FAILED from its previous run's logs.

Containers mount the campaign at /csauto instead of /mnt, where WSL drives
and data disks live, and a shared folder that would land inside that
mount point is refused before launch.

A campaign recorded for another solver drops the docker_image,
saturne_bin and singularity_image of the csauto.toml in use, an unknown
recorded solver is a clear error, and campaign.json is written before the
first case. Cases written by csauto 0.5 (CRLF, non-UTF-8 bytes) still
match on a new prepare, and files with a NUL byte past the first 8 KiB
are copied verbatim.

Clean leaves alone a case that has none of the folders to keep (the
dashboard now offers folder choices for one case at a time), lists the
running cases it skipped, resets only cases whose runs it deleted, and
csauto cleanup refreshes statuses first.

Every file route goes through find_case_file, which refuses files outside
the case folder; a NUL byte in a name is a 404. Restarting from a run
without checkpoint is refused. The Log Tail offers the current run's logs
only, probe columns read the header only, and the unused info anomaly
label is gone.
The id_docker field now reads solver:runtime (code_aster:docker), so the
existing telemetry server tells solvers apart without a schema change.
…PARED, ignore run_aster's MPI notice, and stay quiet when telemetry is off
… has one

Site images set up the solver's environment (modules, spack, conda, a user
switch) in their entrypoint script. csauto keeps it: an entrypoint that starts
the solver gets the solver's arguments, one that runs the command it is given
gets the full command (told apart by running the image once with true), and
an image without entrypoint still gets the solver through --entrypoint.
The case selector was rendered inside the same condition as the plot, so
selecting a case that had not run hid both and left no way to pick another.
The controls now render whenever the campaign has cases.

The Probes and Profiles tabs had a second cause: the loader overwrote the flag
that enables a tab, which answers whether the campaign has any probe data,
with one scoped to the current selection. That disabled the tab itself.

(cherry picked from commit 4188a61)
…e CI

Switch to @lucide/svelte, whose icons are Svelte 5 components, rename the
residual plot's local state so it no longer reads as the $state rune, declare
the probes tab snippet outside CardShell so it is not also passed as a prop,
and remove the unused DoeColumnSelector, which imported a setter that no
longer exists.
@ulysse-bonneau-simvia ulysse-bonneau-simvia self-assigned this Oct 8, 2026
@ulysse-bonneau-simvia
ulysse-bonneau-simvia merged commit c70629b into main Oct 8, 2026
6 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment