# ripopt Release Checklist

A reusable checklist for cutting a new ripopt release. Copy this file when
starting a release and check items off as you go. The order matters: bench
first → docs → versions → verify interfaces → manuscript → tag → publish.

Replace `vX.Y.Z` with the new version number throughout (e.g. `v0.6.2`).

---

## 1. Pre-release verification (clean tree)

- [ ] `git status` — working tree understood (no surprise files)
- [ ] On the correct branch (usually `main`)
- [ ] Pulled latest from origin
- [ ] `cargo check --workspace --all-targets` — no errors
- [ ] `cargo check --examples` — every example compiles (catches stale trait sigs)
- [ ] `cargo build --release` — release build clean, no warnings
- [ ] `cargo test --release --no-fail-fast` — **all** test binaries green
  (use `--no-fail-fast` so a single failure doesn't hide later ones)
- [ ] `cargo test --release -p rmumps` — workspace member tests green
- [ ] `cargo doc --no-deps` — rustdoc builds without warnings
- [ ] **Code coverage**: run `cargo llvm-cov test` and update the coverage
  table in `README.md` with current numbers (this is a release gate)
- [ ] (Optional) `cargo clippy --workspace --all-targets -- -D warnings`
- [ ] (Optional) `cargo fmt --check`

---

## 2. Run the full benchmark suite

- [ ] `make benchmark` — runs HS + CUTEst + large-scale + domain + report
  (~2 hours; do this on a quiet machine)
- [ ] Inspect `benchmarks/BENCHMARK_REPORT.md` — sanity-check the headline numbers
- [ ] Note any regressions vs. the previous release; investigate before tagging
- [ ] Verify all sub-suites actually ran (HS, CUTEst, Electrolyte, Grid, CHO,
  large-scale, gas, water) — if compilation errors silently skipped a suite, the
  numbers will be stale. Check `benchmarks/large_scale/large_scale_results.txt`
  is real output, not error spew.
- [ ] `make gas-run` — gas pipeline NLPs (4 AMPL `.nl` problems). Does NOT
  feed into `BENCHMARK_REPORT.md`; inspect the per-problem console output and
  `benchmarks/gas/*.sol` files manually.
- [ ] `make water-run` — water distribution network NLPs (6 AMPL `.nl`
  problems from MINLPLib). Does NOT feed into `BENCHMARK_REPORT.md`; inspect
  the per-problem console output and `benchmarks/water/*.sol` files manually.

### Domain benchmarks individually (if `make benchmark` doesn't include all)

- [ ] `make hs-run` — HS suite
- [ ] `make electrolyte-run` — electrolyte thermodynamics
- [ ] `make grid-run` — Grid (AC OPF)
- [ ] `make cho-run` — CHO parameter estimation (if applicable)
- [ ] `make gas-run` — gas pipeline NLPs
- [ ] `make water-run` — water distribution network NLPs
- [ ] CUTEst full sweep: `RESULTS_FILE=benchmarks/cutest/results.json cargo run --bin cutest_suite --features cutest,ipopt-native --release`

---

## 3. Update documentation with fresh numbers

Headline metrics that propagate everywhere: HS solved/total, CUTEst
solved/total, both-solve count, geo-mean speedup, median speedup, ripopt-only
and Ipopt-only counts, electrolyte/grid results.

- [ ] `README.md`
  - HS suite table
  - CUTEst suite table
  - Domain benchmarks table
  - Large-scale benchmark table
  - "Interpreting the speed numbers" prose
  - Inline `RIPOPT_VERSION` comment near the C API example
- [ ] `docs/src/benchmarks.md` — mirror of README benchmark sections
- [ ] `docs/src/introduction.md` and other `docs/src/*.md` if API/CLI changed
- [ ] `docs/src/SUMMARY.md` — add entries for any new mdbook pages introduced
  this release (the mdbook TOC does not auto-discover)
- [ ] `mdbook build docs` — rebuild the book and confirm no broken links or
  missing-page warnings
- [ ] `benchmarks/hs/PERFORMANCE_COMPARISON.md` — full HS-specific report
- [ ] `RIPOPT_VS_IPOPT.md` — strategic comparison narrative
- [ ] `benchmarks/electrolyte/electrolyte_benchmark_report.md` — if domain-specific numbers changed
- [ ] `benchmarks/grid/grid_benchmark_report.md` — same
- [ ] `MEMORY.md` (in `~/.claude/projects/...ripopt/memory/`) —
  HS Benchmark Status section, CUTEst Benchmark Status section

---

## 4. CHANGELOG

- [ ] Add a new `## [X.Y.Z] - YYYY-MM-DD` section at the top of `CHANGELOG.md`
- [ ] Group changes under `### Added`, `### Changed`, `### Fixed`,
  `### Performance`, `### Notes`
- [ ] Walk `git log <prev-tag>..HEAD --no-merges` to make sure you didn't miss
  anything
- [ ] Mention every test/regression that was fixed during the release prep
- [ ] Include the workspace version bump line (ripopt + rmumps)

---

## 5. Bump version numbers (every place that hard-codes a version)

These must all match. A grep for the **old** version after bumping is the
safest way to confirm nothing was missed:

```
grep -rn '"X\.Y\.Z"' --include='*.toml' --include='*.h' --include='*.rs' \
     --include='*.md' --include='*.py' . | grep -v target/ | grep -v ref/
```

- [ ] `Cargo.toml` — `[package].version`
- [ ] `Cargo.toml` — `rmumps = { version = "...", path = "rmumps", ... }`
  dependency line (must match `rmumps/Cargo.toml`)
- [ ] `rmumps/Cargo.toml` — `[package].version` (bump even for ripopt-only
  changes if rmumps is also being released; otherwise leave it)
- [ ] `ripopt.h` — `RIPOPT_VERSION_MAJOR`, `_MINOR`, `_PATCH`,
  and the `RIPOPT_VERSION "X.Y.Z"` string literal
- [ ] `README.md` — the inline `// "X.Y.Z"` comment in the C API example
- [ ] `pyomo-ripopt/pyproject.toml` — `[project].version`
- [ ] `pyomo-ripopt/pyomo_ripopt.egg-info/PKG-INFO` — auto-regenerates on
  `pip install`, but verify it after the install step below
- [ ] `Ripopt.jl/Project.toml` — **only if** there are changes that affect
  the Julia binding (FFI signature changes, new C API functions, behavior
  changes Ripopt.jl exposes). Otherwise leave Ripopt.jl on its current
  version. The Julia binding has an independent release cadence.
- [ ] Run `cargo check` after bumping to refresh `Cargo.lock`
- [ ] **Reminder for Section 7b:** any version pin in
  `manuscript/supporting-information.org` (ripopt version, rmumps version,
  Ipopt version, rustc version) must also be bumped. The SI version-pin
  check in Section 7b will catch this, but it's worth noting here so the
  bump is on your radar in one place.

---

## 6. Verify each language interface end-to-end

Each interface should be exercised against the freshly built binary, not a
stale install. Best done in order: native Rust → C → AMPL → Pyomo → Julia →
GAMS, since each layer depends on the one below it.

### 6a. Native Rust library

- [ ] `cargo run --release --example hs071` — known-good Rust example
- [ ] `cargo run --release --example rosenbrock` (or equivalent unconstrained)
- [ ] One large-scale example to exercise sparse path

### 6b. C API + shared library

- [ ] `cargo build --release` produces `target/release/libripopt.{dylib,so}`
- [ ] `ripopt.h` `RIPOPT_VERSION` matches `Cargo.toml`
- [ ] `make test-c` — compiles and runs the bundled C clients
  (`examples/c_api_test.c`, `examples/c_rosenbrock.c`, `examples/c_hs035.c`,
  `examples/c_example_with_options.c`) against the freshly built
  `libripopt.{dylib,so}`
- [ ] `make install` then `ripopt --version` reports the new version

### 6c. AMPL/NL solver binary

- [ ] `cargo build --release --bin ripopt` (the AMPL solver binary)
- [ ] `ripopt --version` and `ripopt --help` work
- [ ] Solve at least one `.nl` file (e.g. an HS problem) and verify it
  reports correct status + objective

### 6d. Pyomo solver plugin

- [ ] `pip install -e ./pyomo-ripopt` from a clean Python env
- [ ] `python -c "from pyomo.environ import SolverFactory; s = SolverFactory('ripopt'); print(s.available())"`
- [ ] Run an end-to-end Pyomo model and verify `result.solver.status == 'ok'`
- [ ] Confirm `pyomo_ripopt.egg-info/PKG-INFO` shows the new version
- [ ] If publishing to PyPI: `python -m build` then check the wheel metadata

### 6e. Julia/JuMP interface (`Ripopt.jl`)

Skip this section entirely if the release contains no changes that affect
Ripopt.jl (most patch releases). Otherwise:

- [ ] `RIPOPT_LIBRARY_PATH=target/release julia --project=Ripopt.jl Ripopt.jl/examples/jump_hs071.jl`
- [ ] Run `Ripopt.jl/examples/jump_rosenbrock.jl` and `c_wrapper_hs071.jl`
- [ ] Bump `Ripopt.jl/Project.toml` only if Ripopt.jl itself changed
- [ ] Verify `Ripopt.jl/examples/ripopt_jump_tutorial.ipynb` still runs
- [ ] On Apple Silicon: confirm no closure-cfunction warnings (the
  module-level `@cfunction` rule from 0.6.0 must hold)

### 6f. GAMS solver link

- [ ] `cargo build --release` (gams link links against `libripopt`)
- [ ] `make -C gams` (build the GAMS bridge)
- [ ] `sudo make -C gams install` (only if a GAMS install is present)
- [ ] `sudo make -C gams test` — solves HS071 and checks the result
- [ ] If you don't have GAMS locally, document this as a manual step the
  release manager must run on a GAMS-equipped machine

### 6g. ASL/AMPL solver runtime sanity (Pyomo path)

- [ ] Confirm `~/.cargo/bin/ripopt` is on `$PATH` so `SolverFactory('ripopt')`
  resolves it (this is the default install location after `make install`)

### 6h. Tutorial notebooks

- [ ] Re-run all 15 notebooks in `tutorials/` **in place** against the newly
  built release, top-to-bottom:

  ```bash
  cd tutorials
  for nb in 0*.ipynb 1*.ipynb; do
      jupyter nbconvert --to notebook --execute --inplace "$nb"
  done
  ```

  Any `pyomo-ripopt` API drift or output-format change surfaces as a cell
  failure. Commit the re-executed notebooks so GitHub renders fresh outputs.
- [ ] Spot-check `15_ripopt_in_practice.ipynb` — this notebook exercises
  the most ripopt-specific surface area (solver options, diagnostics,
  architecture), so any behavioral change is most likely to show up here.
- [ ] Verify `tutorials/README.md` still accurately lists every notebook
  and its topic (update if notebooks were added, removed, or retitled)

---

## 7. Manuscript and supporting information

**Important:** `manuscript/ripopt.tex` is a generated artifact — never edit
it by hand. All content lives in `manuscript/ripopt.org`. The `.tex` (and
`.pdf`) are produced by the scimax export workflow described below. The
same rule applies to `manuscript/supporting-information.org` and its
generated `.tex`/`.pdf`.

### 7a. Update `manuscript/ripopt.org`

- [ ] Abstract — re-state the headline benchmark numbers
- [ ] HS suite section
- [ ] CUTEst suite section
- [ ] Domain-specific benchmarks section — add/update entries for any new
  benchmark suites added since the previous release (gas, water, grid,
  electrolyte, CHO, etc.)
- [ ] Failure analysis section — recompute the failure-mode breakdown
  from the fresh CUTEst run
- [ ] Opportunities for improvement section
- [ ] Conclusions section

### 7b. Update `manuscript/supporting-information.org`

The SI is the implementation/reproducibility companion document. It is
the place where every release-blocking detail about *how* the code works
should live. **Walk the SI top-to-bottom against the actual code state
before tagging.**

- [ ] **File path references** — every `src/...`, `rmumps/...`,
  `benchmarks/...` path cited in the SI must still exist. Reorganizations
  break these silently. Grep the SI for any path-shaped strings and
  re-verify each one. (Common reorganizations to watch for: any rename
  inside `benchmarks/`, anything moved between `src/` and `rmumps/`,
  anything moved between `src/` and a new module split.)
- [ ] **Code line-number references** — line numbers shift any time code
  is added or removed above. For each `file.rs:NNN` citation in the SI,
  open the file at that line and confirm it still points to the function,
  block, or comment the SI claims. Update the line number if it has
  drifted; rewrite the surrounding sentence if the code has been
  refactored beyond just shifting.
- [ ] **Excerpted code snippets** — if the SI quotes Rust source verbatim
  (e.g., a function body, a struct definition, an option list), re-pull
  the current source and diff it against the snippet in the SI. Stale
  snippets are worse than no snippets.
- [ ] **New code coverage** — for every algorithmic change in this
  release (new fallback, new option, new oracle, new convergence rule,
  new linear-algebra path, new problem class, etc.) verify the SI either
  documents it or has a stub pointing to where it lives. Walk the
  CHANGELOG `### Added` and `### Changed` sections and tick each item:
  is it in the SI? If no, decide — should it be? Most algorithmic
  changes should at least get a one-paragraph mention.
- [ ] **Command listings** — every shell command in the SI (`cargo run …`,
  `make …`, `ripopt …`, `python …`) must still work as written. If
  paths, target names, CLI flags, or env vars changed in this release,
  the SI command listings need updating. Run a representative subset
  end-to-end if you can.
- [ ] **Version pins** — bump every literal version string. Common ones:
  ripopt version (`0.6.x`), rmumps version (`0.1.x`), Ipopt version
  (`3.14.19`), Rust toolchain. Cross-reference Section 5 (version bump).
- [ ] **Reproducibility metadata** — re-confirm the listed hardware
  (Apple M3? M4? Mac Mini?), the OS (`Darwin 25.x`?), the Ipopt version,
  the rustc version, and the date stamp. The SI numbers were generated
  on a specific machine; be honest about which one.
- [ ] **CLI option listings** — if the SI documents `ripopt --help`
  output, `SolverOptions` defaults, or AMPL keywords, regenerate from
  the actual current binary (`ripopt --help`) and replace the listing.
- [ ] **Cross-references to the manuscript** — any `Section X` or
  `Equation Y` reference that points back to `ripopt.org` must still
  resolve after manuscript edits in 7a.

### 7c. Build the PDFs

- [ ] Build the manuscript PDF. **Must run from inside `manuscript/`**:
  ```bash
  cd manuscript
  scimax export ripopt.org --format pdf
  ```
  This regenerates both `ripopt.tex` and `ripopt.pdf` in one step.
- [ ] Build the supporting-information PDF, also from `manuscript/`:
  ```bash
  cd manuscript
  scimax export supporting-information.org --format pdf
  ```

### 7d. Verify both PDFs render correctly

- [ ] `manuscript/ripopt.pdf` — page count sane, no `??` undefined
  references, bibliography rendered, figures present, all benchmark
  numbers match the prose
- [ ] `manuscript/supporting-information.pdf` — page count sane, no `??`
  undefined references, every code listing renders without minted
  errors, every cross-reference into the main manuscript resolves

### 7e. Clean up intermediate build artifacts

The scimax/LaTeX pipeline leaves these behind, and **none of them should
ever be committed**. All are gitignored already, but cleaning the working
directory keeps `git status` readable and prevents stale artifacts from
confusing the next build:

```bash
cd manuscript
rm -f ripopt.tex ripopt.html ripopt.pyg ripopt.bbl ripopt.blg
rm -f supporting-information.tex supporting-information.html \
      supporting-information.pyg supporting-information.bbl \
      supporting-information.blg
rm -rf _minted
```

After cleanup the only files in `manuscript/` should be `ripopt.org`,
`ripopt.bib`, `ripopt.pdf`, `supporting-information.org`, and
`supporting-information.pdf`.

### 7f. Commit

- [ ] Stage `manuscript/ripopt.org`, `manuscript/supporting-information.org`,
  `manuscript/ripopt.pdf`, and `manuscript/supporting-information.pdf` in
  the same commit so source and PDF artifact stay in sync. The `.tex`
  files are **not** committed (gitignored, regenerated on every build).

---

## 8. Save tagged benchmark artifacts (per CLAUDE.md)

These let us compare per-problem timing across versions later:

- [ ] `cp benchmarks/hs/hs_ripopt_results.json benchmarks/hs/hs_ripopt_results_vX.Y.Z.json`
- [ ] `cp benchmarks/hs/hs_ipopt_native_results.json benchmarks/hs/hs_ipopt_native_results_vX.Y.Z.json`
- [ ] `cp benchmarks/BENCHMARK_REPORT.json benchmarks/BENCHMARK_REPORT_vX.Y.Z.json`
- [ ] (Optional) `cp benchmarks/cutest/results.json benchmarks/cutest/results_vX.Y.Z.json`
- [ ] (Optional) `cp benchmarks/electrolyte/electrolyte_results.json benchmarks/electrolyte/electrolyte_results_vX.Y.Z.json`
- [ ] (Optional) `cp benchmarks/grid/grid_results.json benchmarks/grid/grid_results_vX.Y.Z.json`
- [ ] (Optional) `cp benchmarks/cho/cho_results.json benchmarks/cho/cho_results_vX.Y.Z.json`

---

## 9. Final pre-tag verification

- [ ] `cargo test --release --no-fail-fast` one more time after all edits
- [ ] `cargo check --examples` one more time (catches any drift introduced
  by docs/manuscript edits)
- [ ] `cargo package --allow-dirty --list -p ripopt` — verify the file list
  matches your `exclude = [...]` rules in `Cargo.toml`. Look for files that
  shouldn't ship: `adversary/`, `.crucible/`, `benchmarks/` (HS, CUTEst,
  electrolyte, grid, cho, gas, water, large_scale), `docs/`, `manuscript/`,
  `research/`, `tutorials/`, large PDFs, `.ipynb` notebooks, `pyomo-ripopt/build/`
- [ ] `cargo package --allow-dirty -p rmumps` — same for rmumps
- [ ] `cargo publish --dry-run -p rmumps` (rmumps must publish first since
  ripopt depends on it)
- [ ] `cargo publish --dry-run -p ripopt`
- [ ] Read the diff of `Cargo.lock` and make sure no surprise dep updates
  snuck in

---

## 10. Tag and publish

Order matters: rmumps first (because ripopt depends on it on crates.io),
then ripopt, then language bindings.

- [ ] `git add -A` (review carefully — there will be benchmark JSON, the
  manuscript pdf, and many docs)
- [ ] `git commit -m "release: vX.Y.Z"` with full release notes in body
- [ ] `git tag -a vX.Y.Z -m "ripopt vX.Y.Z"`
- [ ] `git push origin main`
- [ ] `git push origin vX.Y.Z`
- [ ] `cargo publish -p rmumps`
- [ ] Wait for crates.io to index rmumps (~30s)
- [ ] `cargo publish -p ripopt`
- [ ] (Optional) Publish `pyomo-ripopt` to PyPI: `python -m build && twine upload dist/*`
- [ ] (Optional) Register/tag Ripopt.jl release if bumping it

---

## 11. GitHub release

- [ ] `gh release create vX.Y.Z --notes-file <(awk '/^## \[X.Y.Z\]/,/^## \[/{if(/^## \[/ && !/X.Y.Z/) exit; print}' CHANGELOG.md)`
  (or via the GitHub UI with the CHANGELOG entry pasted in)
- [ ] Attach `manuscript/ripopt.pdf` and
  `manuscript/supporting-information.pdf` if you publish them with the release
- [ ] Verify the release page renders correctly on GitHub

---

## 12. Post-release

- [ ] On crates.io, confirm both `ripopt` and `rmumps` show the new version
- [ ] `cargo install ripopt --version X.Y.Z` from a clean directory and run
  `ripopt --version` to confirm the published binary works
- [ ] **Zenodo DOI** — Zenodo is linked to the GitHub repo and auto-archives
  every published release (metadata in `.zenodo.json`). Wait a few minutes
  after the GitHub release in Section 11, then check
  https://zenodo.org/badge/latestdoi/1152248927 resolves to a new
  version-specific DOI for `vX.Y.Z`. The `latestdoi` badge in `README.md`
  updates automatically — no file edits needed.
- [ ] Update `MEMORY.md` HS/CUTEst status sections with the new release
  numbers if you didn't already in step 3
- [ ] Bump `Cargo.toml` to the next `+dev` working version if you use that
  convention (ripopt currently does not — versions remain on the released
  number until the next release)
- [ ] Close any GitHub issues fixed in this release with a comment pointing
  at the release notes
- [ ] Tweet/announce/etc. as appropriate
- [ ] **Draft a brief LinkedIn post** summarizing the release: one-line hook,
  2–4 headline numbers or changes (HS/CUTEst deltas, notable new features),
  a link to the GitHub release page, and relevant tags (`#Rust`,
  `#Optimization`, `#OpenSource`). Keep it under ~150 words so it renders
  without a "see more" fold on desktop.

---

## Things to double-check that are easy to forget

- [ ] **`cargo check --examples`** — example files often use trait
  signatures that drift; the test suite will not catch this
- [ ] **Compiler warnings** — they don't fail the build but accumulate;
  clean them before tagging
- [ ] **No `--no-verify` git operations** (CLAUDE.md global rule)
- [ ] **Crucible/adversary/research directories** stay excluded from the
  crates.io package (verified via `cargo package --list`)
- [ ] **Large PDFs and notebooks** stay excluded from the package
- [ ] **Benchmark JSON regenerated by `make benchmark`** — these should be
  committed (they're the source of truth for the report numbers) but be
  aware they generate a large diff
- [ ] **License files** — confirm `LICENSE` (EPL-2.0 for ripopt) and
  `rmumps/LICENSE` (CECILL-C) are present and current
- [ ] **rustdoc links** — broken intra-doc links will silently fail; check
  `cargo doc` output for warnings
- [ ] **The C header** (`ripopt.h`) is the public ABI. If any C API
  function signature changed, that's a SemVer concern — major-bump or
  carefully document

