Releases: martinez-hub/mushin
Release list
v0.10.1
Changed
- Repositioned the project description everywhere (README, PyPI, docs site + metadata, citation, paper): mushin is a framework-agnostic sweep engine built on hydra-zen, with first-class PyTorch Lightning integration — the sweep layer never imports Lightning (Lightning ships in the base install for the deep-learning path). (#146)
Fixed
- A second
run()on the same workflow instance no longer reuses the previous sweep's cached overrides, which silently NaN-filled the new grid. (#145) - ±Inf metric values survive the sidecar round-trip with their sign instead of collapsing to NaN. (#145)
load_from_dirnow recovers each cell's config-group choice, so a finished group sweep reloads with real values instead of an all-NaN grid. (#145)diffno longer reports metric deltas from failed/skipped cells, and a failing cell removes the metrics sidecar left by a prior successful attempt. (#145)show/best/diff/export.tablenow scope to the latest sweep's manifest, excluding stale cell dirs left by an earlier sweep in a reusedworking_dir(the mid-sweep live view is preserved). (#145)Studytraining-sweep resume now fingerprints the methods mapping: renaming, reordering, or editing a method (including afunctools.partial's bound arguments) re-runs cells instead of silently reusing checkpoints trained by old code. One-time cost: the first resume of a sweep created before this change re-runs all cells (its cells lack the fingerprint). (#145)- Fail-soft, sampled, and budget-skipped sweeps can now be reloaded from disk: failed/skipped cells NaN-fill instead of
load_metricsraisingFileNotFoundError, and a stale sidecar in a failed cell's dir is never loaded. (#145) compare_methods(allow_incomplete=True)masks constant (zero-variance) methods over the completed seeds and keeps ±Inf cells in the pairing instead of silently excluding them;summary(reference=...)rejects unknown method names. (#147)max_total_secondsruns on a monotonic clock — a wall-clock step (NTP/DST) mid-sweep can no longer stretch or prematurely expire the budget. (#147)show/export.table: a metric sharing a swept param's name renders as<name> (metric)instead of overwriting the param column, and a typo'dmetrics=entry raises instead of silently dropping the column. (#147)- The grid accounting (dry-run preview,
confirm_abovegate,sample=selection) no longer silently excludes user params whose names merely start with "hydra" (e.g.hydraulic=). (#147) dry_runreports the sampled cell count whensample=is set (the cell-count gate still applies to the full grid, since every cell is launched regardless of sampling); a fresh run reusing aworking_dirno longer inherits the prior sweep'snotes/tags, andtags=[]on resume clears them. (#147)- MCP
get_failures/get_provenanceraise for a nonexistent path instead of reporting an empty-but-valid result;MetricsCallbackwrites standalonetrainer.validate()metrics tovalidate_metrics.pt;seed_everything_per_rankfalls back toLOCAL_RANK; provenance recordsdirty: nullwhengit statuscannot be verified. (#147) working_dirpaths containing=or,(e.g. dirs namedlr=0.1) now work; duplicate sweep values via rawoverrides=are rejected instead of silently collapsing to one cell. (#147)
Removed
- Removed the dead pre-Lightning-1.6
HydraDDPcode path (~130 lines) — the dependency floor ispytorch-lightning >= 2.4. (#147)
Misc
- Reframed the workflows guide's hyperparameter-search section: mushin does
grid and random search (often the whole job for a small discrete space, with the
labeled dataset + stats as a bonus); it does not do adaptive/Bayesian search over
large or continuous spaces, which is where a dedicated optimizer like Optuna is
complementary. Replaces the earlier "mushin is not a hyperparameter optimizer"
overstatement. - The README shows a "latest release" badge linking to the newest GitHub release
and its notes, alongside the existing PyPI version badge.
v0.10.0
[0.10.0] - 2026-07-22
Added
- A pre-launch cell-count gate guards against an accidentally huge grid.
run(..., confirm_above=N)refuses to launch a sweep with more thanNcells,
raising aValueErrorthat reports the count and how to proceed (preview with
dry_run=True, or raise the ceiling). TheMUSHIN_MAX_CELLSenvironment
variable supplies a default ceiling when a call doesn't set one — so a shared
cluster or CI profile can cap every sweep — and an explicitconfirm_above=
wins over it. A malformedMUSHIN_MAX_CELLSis ignored, anddry_run=True
bypasses the gate so an over-limit sweep can still be previewed. mushin.best(root, metric)returns the completed cell that optimizes a metric
across a sweep —mode="max"(default) ormode="min". Likemushin.showit
reads the per-cell sidecars directly (offline, no Hydra/xarray). The returned
BestResultcarries the winningcombo(swept params), the optimizedvalue,
the fullmetricsdict, the cellstatus, and its jobdir(for locating
checkpoints/artifacts). Failed/running/skipped cells and non-finite metric
values are ignored; an unknown metric raises aValueErrorthat lists the
available scalar metrics.mushin.diff(a, b)compares two sweep directories. It aligns cells by their
swept-parameter combination and, for each shared cell, reports the delta
b - aof every metric that is a finite scalar in both — printed as aΔmetric
table. Cells present in only one sweep are listed (only_in_a/only_in_b),
and the two runs' environment provenance is diffed field-by-field
(git/packages/python/accelerator), excluding volatile fields like the per-cell
timestamp. Likeshow/bestit reads the per-cell sidecars directly (offline,
no Hydra/xarray); the returnedDiffResultexposes.rows,.only_in_a,
.only_in_b,.provenance, and.table.mushin.export.table(root)exports a sweep as CSV — one row per cell with its
swept parameters,status, and metrics. It reads the per-cell sidecars directly
(offline, no Hydra/xarray) and writes full-precision values so pandas parses
numbers numerically. Returns the CSV string, or writes topath=and returns the
Path;metrics=restricts the metric columns. A durable, spreadsheet-friendly
substrate for researchers who prefer pandas to xarray.mushin.show(root)prints (and returns) a status/metrics table for a sweep
directory. It reads each cell's status and metrics sidecars directly — pure
JSON, so it needs neither Hydra nor xarray and works mid-sweep — making it handy
for watching a live sweep or eyeballing a finished one before the full
to_xarrayload. Each row carries the cell's swept parameters, its status
(completed/running/failed/skipped/…), and its metric values;metrics=
restricts the metric columns andsort=orders the rows. The returned
ShowResultexposes.rows(one dict per cell) and.table.run()now validates up front that every required task (andpre_task)
parameter is satisfied — by the base config, an override, or the raw
overrides=[...]list — and raises a clearValueErrornaming the missing
parameter(s) before launching. Previously a genuinely missing parameter surfaced
as an opaque per-job HydraConfigAttributeErrorafter the sweep had already
started, once per cell. A parameter supplied only via an override is still valid
(the common decorator case, where the base config is empty), so this adds no
false positives.run(..., cache_dir=...)adds a content-addressed cache of completed cells that
is shared acrossworking_dirs. A cell whose resolved config AND task source
match a previously-computed cell — keyed on the same fingerprints asresume—
reuses that result instead of recomputing, so a cell computed in one sweep is
free in another (a coarse grid refined into a finer one, a re-run in a fresh
directory). Newly-computed cells are stored in the cache automatically. It
complementsresume(which reuses only within a singleworking_dir); a changed
non-swept config value or an edited task body is a cache miss. Cache writes are
best-effort and never fail the cell.run(..., dry_run=True)(and@mushin.sweep.run(dry_run=True)) previews a
sweep instead of launching it: it prints the cell count and each swept axis with
its values — so a range typo shows up as an unexpectedly wide axis before any
compute is spent — and returns a summary dict (num_cells,axes,fixed,
working_dir) with no jobs run.run(..., max_total_seconds=T)adds a graceful wall-clock budget. Once the
budget is exhausted the remaining grid cells are skipped — recorded'skipped'
in the manifest and per-cell status, NaN in the dataset, and surfaced in
self.skipped(and amushin_skippeddataset attr) — instead of the sweep
running to the end. The clock starts at the first computed cell, so at least one
cell always runs and resume cache hits don't consume the budget; a cell already
running is never interrupted. Because a skipped cell is notcompleted, a later
resume=Truewith more time finishes exactly the skipped cells. The budget is
measured per launcher process, so it's best paired with the default sequential
launcher.run(..., notes="...", tags=[...])annotates a sweep with free-form lineage. The
note and tags are recorded in the sweep manifest, exposed on the workflow as
wf.notes/wf.tags(and preserved when a sweep is reloaded with
load_from_dir), and carried on the dataset as themushin_notes/
mushin_tagsattrs — so "why did I run this?" travels with the results.tags
must be a list of strings andnotesa string. A resume that does not re-pass
notes/tagspreserves the original run's lineage rather than wiping it.run(..., sample=K)runs only a randomK-cell subset of the grid — the rest
are skipped (NaN, and listed inself.skipped) — for fast exploration of a large
grid without paying for every cell. The subset is chosen deterministically from
the Hydra job indices (seeded bysample_seed, default 0), so it is reproducible
and identical across a resume; resuming withoutsamplefills in the remaining
cells.sample >= n_cellsruns everything. Because selection is by job index it
is launcher- and axis-type-agnostic. Note: the full grid is still composed by
Hydra (only the sampled cells run), and a sampled sweep is intentionally
incomplete (is_completeis False).
Fixed
-
A batch of defensive-handling fixes: corrupt-but-valid-JSON cell-status sidecars now degrade (resume treats a null/
"3"attempt or a wrong-shape record as absent) instead of crashing;load_from_dirraises a realFileNotFoundErrorfor a missing config (not a-O-stripped assert); the resume contextvar no longer leaks when a status write fails;RobustnessCurve.plotno longer leaks a figure when plotting raises; a malformed tuning pin reaches its friendly "delete it or retune" guard;mushin.lightning/benchmark/mcp/testingare reachable as lazy submodules afterimport mushin;compare/compare_llmsreject an unknowncorrection=before any evaluation runs; the MCPget_failures/get_provenancetools tolerate wrong-type sidecar JSON; and avalue_checkmessage typo ("None orof") is fixed. -
A typo'd sweep-axis name is now caught before launch instead of silently
producing a wrong dataset. Passingrun(lrate=multirun(...))when the task
parameter islrpreviously ran to completion and added a phantomlrate
dimension of constant values; it now raises aValueErrorthat names the likely
intended parameter. Only near-misses of a real target are rejected — deliberate
overrides beyond the task's own parameters (values consumed bypre_task,
config groups, interpolations) and tasks declaring**kwargsare unaffected. -
Fixed
trainer.validate()underHydraDDP/HydraFSDP: the child ranks were told to runfit(train) while rank 0 validated — a collective mismatch that hangs or crashes. The launcher now maps Lightning's_validate_implto apl_validatingflag and_pl_maindispatchestrainer.validateaccordingly, alongside the existing test/predict paths. -
Fixed a cluster of silent-wrong-results bugs in the override → grid pipeline: a fixed (non-
multirun) string containing,or=is no longer re-split into an accidental sweep and"true"/"1"stay strings; a duplicated sweep-axis value is rejected instead of silently collapsing two cells into one;combo_keyis now injective (delimiter-containing values can't collide with a different combination); a dotted sweep (model.width) on a config field that already exists no longer crashes; and a batching sweeper's multi-batch job list is flattened instead of failing after every job ran. -
Fixed five defects found by an adversarial review of the exploration features:
cache_diris now resolved to an absolute path (expanduser().resolve())
before launching. A relative or~-prefixedcache_dirwas previously
dereferenced inside each per-cell job dir (Hydra chdirs into it), so
cross-cell/cross-directory reuse silently never hit.resume=Truewithsample=is now rejected with a clear error. Combining
them overwrote already-completed cells (skipped by job index) toskipped/NaN,
silently discarding prior results; resume WITHOUTsamplefills the rest.- The cell count (used by
sample=,confirm_above=/MUSHIN_MAX_CELLS, and
dry_run) now counts sweep axes supplied via the rawoverrides=[...]list,
not onlyparam=multirun(...)kwargs — so those no longer undercount the grid. compare_methods(allow_incomplete=True)now computes each comparison over
only the seeds completed for both methods (dropping NaN cells), fulfilling its
documented promise instead of feeding NaN into the tests ...
v0.9.0
Added
- Provenance records the accelerator identity (CUDA/cuDNN/device name),
seed_everything_per_rankpersists the effective seed to a per-rank JSON in the run dir, and a resume no longer overwrites the environment snapshot recorded for earlier cells. - Study and evaluate_checkpoints now accept predict_fn/metrics/prob_metrics/correction and forward them to compare (custom-output models and custom batteries work through Study), and checkpoints load lazily one at a time instead of holding every method x seed model in RAM.
- Sweep axes got first-class support for nested (dotted) params and Hydra config groups (the dataset coordinate is the chosen option name),
range(...)overrides expand to their values, and unsupported continuous/adaptive sweeps (interval(...)) raise a clear error instead of crashing or yielding an all-NaN grid. - The MCP server gained
get_failures(failed cells + tracebacks from the manifest) andget_provenance(git/package/accelerator records) tools, and now summarizes long coordinate axes and metric curves instead of flooding the agent's context with raw arrays. - The wheel now ships a PEP 561
py.typedmarker, so mypy/pyright/IDEs see mushin's type annotations. MultiRunMetricsWorkflow.to_dataframe()(andexperiment.workflow.to_dataframe()) returns the sweep as a tidy pandas DataFrame in one call; README and quickstart show the pandas path alongside the dataset.compare/compare_methods/compare_llmsacceptcorrection=--holm(default),bonferroni,fdr_bh(Benjamini-Hochberg), ornone; the statistics guide documents the per-metric family scope.
Changed
import mushinis ~5x faster (~0.13s from ~0.61s): torch now loads on first use, not at import time.
Fixed
- HydraDDP/HydraFSDP now track their child rank processes like Lightning's base launcher -- children are reaped if rank 0 dies (no more orphaned GPU processes), signal forwarding works, and per-rank thread pools are capped; checkpoint/experiment loading errors are real exceptions with actionable messages instead of bare asserts.
- HydraDDP/HydraFSDP rank startup is staggered by a deterministic, tunable delay (
MUSHIN_DDP_LAUNCH_DELAY, default 1s) instead of a hard-coded random 1-5s per rank, and the re-launch command builds a Windows-safehydra.run.diroverride. - Paired tests now report the paired effect size (Cohen's d_z) instead of the pooled-variance d;
mushin_failuresis stored as a JSON attr so failed-sweep datasets survive a netCDF round-trip; a custommetrics=battery no longer inherits the task's probability routing silently (passprob_metrics=explicitly, unknown names now raise). - Resilient sweeps got tougher: a corrupt (mid-write-killed) sweep manifest no longer aborts resume, atomic sidecar writes use unique temp names so concurrent writers cannot clobber each other, and
on_error="nan"now writes the full traceback tomushin_error.txtin the failed cell's directory. - Resume now verifies a config fingerprint before reusing a completed cell -- changing a non-swept value and resuming re-runs the affected cells (with a warning) instead of silently mixing two configurations into one dataset; resuming also no longer ships the full manifest to every worker.
- The MCP server now surfaces workflow-sweep metrics from
mushin_metrics.json(previously only MetricsCallback.ptfiles were read), and a single corrupt per-runconfig.yamlno longer makes the whole experiment unqueryable. parse_scoreno longer misreads an incidental leading integer ("0 issues found...") as a 0.0 score -- a bare integer with trailing text now raises; the LLM guide documents cache-invalidation rules and when paired tests are valid.
Misc
- CI now tests macOS (Apple Silicon + the Intel dependency branch), executes notebooks with the netCDF engine they recommend, and the publish workflow smoke-tests the built wheel and SHA-pins the OIDC publish action; the Python 3.14 classifier is withheld until its CI leg is required to pass.
- Docs: eval-extra callouts everywhere they were missing (examples index, notebooks 02/03/05, custom/segmentation guides, example docstrings), a "Versioning & scope" statement in the README, a tracker (W&B/TensorBoard) how-to in the workflows guide,
multirun/hydra_listreference entries, and the decorator quickstart now points at the decorator example.MultiRunMetricsWorkflow.runis fully documented andplotno longer leaks figures. - Notebook 07 demonstrates xarray group-by (split-apply-combine), notebook 05 finally shows its plot on the docs site, and a netCDF round-trip test pins string-coordinate datasets.
v0.8.0
Changed
- The evaluation layer now requires
pip install mushin-py[eval]—compare, the metric batteries, LLM evaluation, andStudy. This keeps the core sweep → dataset install lean (torchmetrics/scipyare no longer base dependencies); accessing the layer without the extra raises a clear install hint. Thedetection/image/audiobattery extras now implyeval. (#106)
Also in this release (docs/CI, no user-facing API change): path-filtered CI behind a single ci-ok gate, a streamlined README, an arXiv paper source, CITATION.cff + Zenodo DOI, and a leaner test suite.
Full changelog: https://github.com/martinez-hub/mushin/blob/main/CHANGELOG.md
v0.7.0
Ships the hardware-validated HPC features, plus docs/CI hardening.
Added
- Multi-node DDP —
submitit_slurm_config+seed_everything_per_rank, a fail-fast world-size guard, rank-0-only metrics (#50) - HydraFSDP — Fully-Sharded Data Parallel training under Hydra
--multirun(#58) pin_gpu_round_robin— pack several small sweep jobs onto each GPU (#59)
All three were validated on real SLURM/GPU hardware (2× L40S single-node, 2× A100 across 2 nodes) before this release.
Fixed
- HydraDDP/HydraFSDP docs: the working launcher-provides-ranks pattern (#95)
- Actionable error when a bare list/tuple is passed as a sweep arg (#93)
Misc
Docs/examples wiring, cluster-validation runbook, least-privilege CI workflow permissions, and CITATION.cff.
Full changelog: https://github.com/martinez-hub/mushin/blob/main/CHANGELOG.md
v0.6.0
Added
- New
@mushin.sweepdecorator: turn a plaintask-style function into a runnable sweep with no subclassing —experiment.run(lr=multirun([...]), seed=multirun([...]))returns the labeledxarray.Datasetdirectly. Drop toexperiment.workflow(the last-runMultiRunMetricsWorkflow) orexperiment.workflow_clsfor power features;mushin_resumeand allrun()resilience options carry through. TheMultiRunMetricsWorkflowsubclass form is unchanged. - Resumable sweeps now survive a hard process kill (OOM, SLURM preemption): each cell records its completion durably from inside its own job, so
resume=Truenever recomputes finished cells. A task may also declare amushin_resumeparameter to receive aResumeContext(the cell's directory,is_resume, and the last checkpoint) and resume its own training mid-run. - Sweeps can now use out-of-process Hydra launchers (e.g.
hydra-joblib-launcher, submitit): per-cell dispatch is stdlib-picklable, sorun(..., launcher="joblib")parallelizes across worker processes. Previously any process-backed launcher failed with aPicklingError. Resilience, resume, and provenance semantics are unchanged in-process and preserved out-of-process.
Changed
- Provenance capture no longer spawns git subprocesses per sweep cell. The sweep-constant part of the record (git state + package versions — three
gitsubprocesses via_git()) is now captured once perrun()and reused for every cell, instead of being recomputed per cell. An N-cell sweep now spawns 3 git subprocesses instead of 3N (e.g. ~30s less git overhead on a 1000-cell sweep). Each cell'smushin_provenance.jsonis byte-for-byte equivalent (onlytimestamp/configvary per cell, as before). import mushinis now ~65% faster (~1.7s → ~0.6s on a cold import): the Lightning integration (HydraDDP,MetricsCallback) and its heavypytorch_lightningdependency (which also transitively pulled in matplotlib and scipy) now load lazily on first attribute access instead of at import time. The sweep →xarraycore no longer pays for Lightning it never uses. All public names resolve unchanged;_tuning/Studystill work (they import pytorch_lightning only inside functions).
Fixed
- Sweeps no longer emit Hydra's "Future Hydra versions will no longer change working directory at job runtime" deprecation warning:
MultiRunMetricsWorkflow.runnow setshydra.job.chdir=Trueexplicitly (the behavior the workflow already relies on).
Misc
- Added six runnable EQUINE-style example notebooks (sweeps, compare + batteries, Study, resilient sweeps, LLM evaluation, scikit-learn) under an "Example notebooks" section of the docs, executed in CI via nbmake so they stay current.
- CI: a batteries-clean-install job builds the wheel, installs it (non-editable) with the detection/image/audio extras into a fresh env, and runs all 7 battery examples against it — catching packaging / optional-extra issues the editable test job would miss.
- Documentation: a Built-in batteries guide covering all 7 benchmark batteries (classification, segmentation, detection, regression, retrieval, image_quality, audio) with real-model recipes and CI-tested runnable toys.
- Documentation: add a top-level Examples page indexing all runnable example scripts (sweep_to_dataset, sklearn_sweep, compare_classifiers, study_mnist, segmentation_demo, compare_llms_demo, batteries), each with a one-line description and a link, plus a pointer to the batteries guide.
- Documentation: add sweep resilience + provenance (0.5.0) to the README "What it provides" list.
- Documentation: the Built-in batteries guide now shows real captured outputs under every battery, plus a flagship notebook-style walkthrough (comparing two classifiers) with the actual summary table, significant p-values, and interpretation.
v0.5.0
[0.5.0] - 2026-07-14
Added
- Sweep resilience and provenance:
run(on_error="nan")records failed grid cells as
NaN and keeps going (default stays"raise");run(working_dir=..., resume=True)
re-runs only the failed/missing cells and fills them in place;compare/Study
refuse statistics on an incomplete sweep (IncompleteSweepError) until you resume;
every run writes per-job provenance (mushin_provenance.json: git SHA, versions,
config) with an opt-incapture_env=Truefull dependency snapshot.Studyaccepts
on_error/resume/capture_envfor training sweeps.
v0.4.1
[0.4.1] - 2026-07-13
Added
- Added a runnable scikit-learn sweep example (
examples/sklearn_sweep.py) demonstrating that the framework-agnosticMultiRunMetricsWorkflowwraps non-Lightning models (hereLogisticRegression) and still returns a labeledxarray.Dataset.scikit-learnis a dev-only dependency for the example and its test; the package itself has no scikit-learn dependency.
Misc
- Documentation: add a "Frameworks: Lightning-first, sweep layer agnostic" section to Core concepts, clarifying that the sweep->dataset workflow wraps any framework (scikit-learn, XGBoost, ...) while compare/auto-tuning/HydraDDP are PyTorch/Lightning-specific.
- Documentation: add a Changelog page to the docs site that embeds CHANGELOG.md (self-maintaining, no duplication).
- Documentation: add an API reference page for the auto-tuning helpers (
tune_batch_size,tune_learning_rate), which had a usage guide but no auto-generated API docs. - Documentation: announce the 0.4.0 release with a "What's new" highlights callout on the docs home page.
- Documentation: refresh the README for 0.4.0 — add auto-tuning and the task API/batteries to the feature list, note the framework-agnostic sweep layer (scikit-learn example), and fix the stale
vizextra description that referenced the now-deprecatedRobustnessCurve.
v0.4.0
[0.4.0] - 2026-07-13
Added
- Four new built-in task batteries —
regression,image_quality,audio, and
retrieval— plus a per-Taskupdate_fnhook for metrics whose update step is
not(preds, target)(used byretrieval). LPIPS and STOI sit behind the
optional[image]and[audio]extras. Each battery is exported frommushin. - Overhauled the documentation: runnable, tested example scripts (MNIST) that the guides embed verbatim, deeper guides with annotated output, and new Tutorial, Core concepts, Custom metrics/predict_fn, and Statistics pages.
- Public task API:
Taskdataclass plusregister_task,get_task, and
list_tasksmake evaluation tasks first-class and reusable.compare(...)and
Study(...)now accept either aTaskobject or a registered task name, and the
built-in batteries (classification_battery,segmentation_battery,
detection_battery) are exported frommushin. compare(task="detection")— compare trained object detectors across seeds over
the fulltorchmetrics.detectionbounding-box family (mean-average-precision plus
the IoU/GIoU/CIoU/DIoU variants), reporting every scalar metric with Holm-corrected
significance. Needs the optionalmushin-py[detection]extra.mushin.llm.compare_llms— compare LLM systems (callables or hydra-zen configs) across reproducible stochastic seeds with a metric (a plain scorer, atorchmetricstext metric, or the newllm_judgehelper), reporting Holm-corrected statistical significance. Includes an on-disk output cache. Provider-agnostic: you bring the systems, data, and judge model.tune_batch_size/tune_learning_rate: opt-in, reproducibility-preserving
auto-tuning. Lightning's batch/LR finder runs once, the result is pinned to a
sidecar YAML, and later runs reuse it.tune_batch_sizepins a hardware-
independent effective batch, choosing the largest device batch that both fits and
divides the per-device target exactly, so the effective batch is identical on any
GPU count with no drift.
Changed
- Modernized the codebase to Python 3.10+ idioms now that 3.9 is no longer
supported:rufftarget-versionispy310, and the pyupgrade auto-fixes
(Optional[X]/Union[X, Y]->X | None/X | Y) plus explicitzip(..., strict=True)
have been applied. No behavior change. import mushinis now lightweight: thebenchmarkandllmsubsystems load on
first use instead of at import time, so a bare import no longer pulls the
battery/eval machinery. Every existing top-level name still resolves. The default
Hydra config/job name is nowmushin_workflow(wasrai_workflow), and the new
mushin.original_cwd()helper anchors relative paths intask()against the
launch directory rather than Hydra's per-job output directory.
Fixed
- Fixed the docs example scripts surfaced in review: the Study example now trains and evaluates on separate MNIST splits and resolves its checkpoint directory to an absolute path (so it works under Hydra's per-job chdir); the custom-metrics guide no longer implies
Studyacceptsmetrics/predict_fn(those arecompare-only). - Hardening from a repo-wide adversarial audit:
load_experimentnow loads DDP/nested-layout configs (was silentlyNone) and labels each job's ownworking_dir; the benchmarkcompare()path masks zero within-group-variance comparisons (no more false-positive significance — now consistent withcompare_llms, both via a singlecompare_methods);MetricsCallbackkeeps every metric series aligned to the epoch axis (NaN-padding missing metrics, reserving theepochkey);Studylabels the seed coordinate with the real seed values and relocates checkpoints across filesystems (shutil.move); multirun overrides are built with Hydra'schoice(...)syntax so comma-bearing and single-element values are no longer mis-split; and several legibility/robustness fixes (to_datasetempty-method error,_to_devicenamedtuple support,'='-bearing override parsing,load_from_dircache reset, dropped the never-working dict-override path).
Removed
- Dropped support for Python 3.9 (end-of-life October 2025);
mushinnow requires
Python >= 3.10. This refreshes the dependency lockfile to patched versions of
pillow, urllib3, aiohttp, filelock, requests, pytest, and pytorch-lightning,
clearing the Dependabot security alerts anchored on the old Python-3.9 dependency
branch. Thescipy(>= 1.13) andmatplotlib(>= 3.9) floors are raised to their
first NumPy-2-compatible releases, and themcpextra no longer needs a Python
version gate.
Deprecated
BaseWorkflowandRobustnessCurveare deprecated at the top level and will be
removed in a future release. Import them frommushin.workflowsinstead;
accessing them asmushin.BaseWorkflow/mushin.RobustnessCurvenow emits a
DeprecationWarning.
Misc
- Added a
@claudemention bot (GitHub Actions): mention@claudein any issue or PR comment to have Claude answer questions or make changes. PR reviews stay with the Codex connector.
v0.3.0
Marks everything landed since 0.2.1.
Added
mushin-mcp— optional read-only MCP server (pip install "mushin-py[mcp]") for analyzing experiments from Claude Code.- Documentation website — https://martinez-hub.github.io/mushin/ (MkDocs Material, guides + auto-generated API reference).
- Windows CI — full Python 3.9–3.14 matrix on windows-latest.
Changed
- Honest dependency floors — raised to CI-verified minimums (torch ≥ 2.4, pytorch-lightning ≥ 2.4, pandas ≥ 2.2.2, xarray ≥ 2024.6, netCDF4 ≥ 1.7, hydra-zen ≥ 0.10), enforced by a new
min-versionsCI job.
Full changelog: https://github.com/martinez-hub/mushin/blob/main/CHANGELOG.md