From 886df7ea30070d29b879e8fd6977a7b74b09364b Mon Sep 17 00:00:00 2001 From: TomHortons Date: Thu, 9 Jul 2026 07:14:26 +0000 Subject: [PATCH 1/9] Add MIT LICENSE file MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The README has declared "MIT" since v1.0.0, but no license text existed in the repository — legally leaving the project unlicensed and blocking citation, redistribution, and JOSS/institutional use. Adds the standard MIT text and points the README's License section at it. Copyright holder "Tomoya Imanishi" is taken from the repository's commit author identity — flagged for maintainer review in the PR. Co-Authored-By: Claude Fable 5 --- LICENSE | 21 +++++++++++++++++++++ README.md | 2 +- pyproject.toml | 1 + 3 files changed, 23 insertions(+), 1 deletion(-) create mode 100644 LICENSE diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000..df75e3e --- /dev/null +++ b/LICENSE @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) 2026 Tomoya Imanishi + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/README.md b/README.md index 0bb4a51..cf0d002 100644 --- a/README.md +++ b/README.md @@ -529,4 +529,4 @@ The test suite covers closed-form price validation (Gil-Pélaez inversion for He ## License -MIT +MIT — see [LICENSE](LICENSE) for the full text. diff --git a/pyproject.toml b/pyproject.toml index ba67b9a..19706d6 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -6,6 +6,7 @@ build-backend = "hatchling.build" name = "quantscenariobench" version = "1.2.1" description = "JAX-native framework for generating reproducible market scenarios, benchmarking portfolio strategies against them, and publishing results as versioned datasets with a hosted Leaderboard" +license = {text = "MIT"} requires-python = ">=3.11" dependencies = [ "jax>=0.4.38", From 2082c77219f639dbb9a6559ee98708fc814b693a Mon Sep 17 00:00:00 2001 From: TomHortons Date: Thu, 9 Jul 2026 07:15:16 +0000 Subject: [PATCH 2/9] Add GitHub Actions CI workflow First automated testing for the repository: the full pytest suite runs on every push to main and every pull request, on Python 3.11 (the requires-python floor) and 3.12, CPU-only JAX, no network secrets. Python 3.13 is deliberately left out until the JAX/diffrax stack is verified against it. Adds the CI badge to the README. Co-Authored-By: Claude Fable 5 --- .github/workflows/ci.yml | 41 ++++++++++++++++++++++++++++++++++++++++ README.md | 1 + 2 files changed, 42 insertions(+) create mode 100644 .github/workflows/ci.yml diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml new file mode 100644 index 0000000..8534447 --- /dev/null +++ b/.github/workflows/ci.yml @@ -0,0 +1,41 @@ +# CI — full test suite on every push to main and every pull request. +# +# Design notes: +# - Matrix covers the supported floor (3.11, per requires-python) and the +# development version (3.12). 3.13 is deliberately omitted until the JAX/ +# diffrax stack is verified against it — widen the matrix in its own PR. +# - JAX runs CPU-only on hosted runners; every test is written for that +# (the full suite takes ~8 minutes locally on CPU). +# - No HF_TOKEN or network secrets: Hub-publishing tests run against mocks. +name: CI + +on: + push: + branches: [main] + pull_request: + +concurrency: + group: ci-${{ github.ref }} + cancel-in-progress: true + +jobs: + test: + runs-on: ubuntu-latest + timeout-minutes: 30 + strategy: + fail-fast: false + matrix: + python-version: ["3.11", "3.12"] + steps: + - uses: actions/checkout@v4 + + - uses: actions/setup-python@v5 + with: + python-version: ${{ matrix.python-version }} + cache: pip + + - name: Install package with dev dependencies + run: pip install -e ".[dev]" + + - name: Run test suite + run: pytest -q diff --git a/README.md b/README.md index cf0d002..5fd4ce8 100644 --- a/README.md +++ b/README.md @@ -1,5 +1,6 @@ # QuantScenarioBench +[![CI](https://github.com/tim-nish/QuantScenarioBench/actions/workflows/ci.yml/badge.svg)](https://github.com/tim-nish/QuantScenarioBench/actions/workflows/ci.yml) [![DOI](https://zenodo.org/badge/DOI/10.5281/zenodo.21097248.svg)](https://doi.org/10.5281/zenodo.21097248) A JAX-native Python framework for generating reproducible stochastic market scenarios and benchmarking portfolio strategies against them, with built-in export to Parquet and the Hugging Face Hub. From ba0b1037d8c4c9640883c2e15f2b830efb8fd257 Mon Sep 17 00:00:00 2001 From: TomHortons Date: Thu, 9 Jul 2026 07:15:47 +0000 Subject: [PATCH 3/9] Add CITATION.cff citation metadata MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Makes the project citable in one click: GitHub renders a "Cite this repository" button from this file. Wired to the existing Zenodo concept DOI (10.5281/zenodo.21097248), which always resolves to the latest archived release; version/date-released track the latest published release (v1.2.1) and are bumped per release via docs/RELEASE_CHECKLIST.md. Adds a Citing section to the README. Author identity "Tomoya Imanishi" is taken from the repository's commit author — flagged for maintainer review (ORCID can be added later). Co-Authored-By: Claude Fable 5 --- CITATION.cff | 32 ++++++++++++++++++++++++++++++++ README.md | 11 +++++++++++ 2 files changed, 43 insertions(+) create mode 100644 CITATION.cff diff --git a/CITATION.cff b/CITATION.cff new file mode 100644 index 0000000..a642a21 --- /dev/null +++ b/CITATION.cff @@ -0,0 +1,32 @@ +# Citation metadata (Citation File Format) — renders GitHub's +# "Cite this repository" button. +# +# Maintenance notes: +# - `doi` is the Zenodo *concept* DOI (same as the README badge): it always +# resolves to the latest release, so it does not need updating per release. +# - `version` / `date-released` DO change per release — bumping them is a +# step in docs/RELEASE_CHECKLIST.md ("Release-prep PR"). +cff-version: 1.2.0 +message: "If you use QuantScenarioBench in your research, please cite it as below." +title: "QuantScenarioBench" +type: software +authors: + - family-names: "Imanishi" + given-names: "Tomoya" +version: "1.2.1" +date-released: "2026-07-04" +doi: "10.5281/zenodo.21097248" +repository-code: "https://github.com/tim-nish/QuantScenarioBench" +license: MIT +abstract: >- + A JAX-native Python framework for generating reproducible stochastic + market scenarios and benchmarking portfolio strategies against them, + with built-in export to Parquet and the Hugging Face Hub. +keywords: + - quantitative finance + - benchmark + - stochastic volatility + - scenario generation + - portfolio optimization + - reproducibility + - JAX diff --git a/README.md b/README.md index 5fd4ce8..f22894e 100644 --- a/README.md +++ b/README.md @@ -528,6 +528,17 @@ The test suite covers closed-form price validation (Gil-Pélaez inversion for He --- +## Citing + +If you use QuantScenarioBench in your research, please cite it. Citation +metadata lives in [`CITATION.cff`](CITATION.cff) — use GitHub's +**"Cite this repository"** button for ready-made BibTeX/APA, or cite the +archived release via the Zenodo DOI badge at the top of this README +(the concept DOI `10.5281/zenodo.21097248` always resolves to the latest +release). + +--- + ## License MIT — see [LICENSE](LICENSE) for the full text. From d93eb4503bc3a8a2208b7793fcb2102c7725972f Mon Sep 17 00:00:00 2001 From: TomHortons Date: Thu, 9 Jul 2026 07:16:20 +0000 Subject: [PATCH 4/9] Add release checklist as a permanent project document MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Documents the full release process — reverse-engineered from how v1.0.0–v1.2.1 were actually released (docs/vX.Y.Z-release-notes prep branch → PR → tag → Zenodo DOI) and extended with the Hugging Face steps: dataset publishing with dataset_version bumps, recording Hub revision hashes for downstream pinning, Leaderboard Space redeploy, and an end-to-end EvaluationResult smoke test. Flags that the package is not yet on PyPI despite the README's install instructions, so first PyPI publication (or a README fix) gates the next release. Linked from the README's Development section. Co-Authored-By: Claude Fable 5 --- README.md | 2 ++ docs/RELEASE_CHECKLIST.md | 76 +++++++++++++++++++++++++++++++++++++++ 2 files changed, 78 insertions(+) create mode 100644 docs/RELEASE_CHECKLIST.md diff --git a/README.md b/README.md index f22894e..6488562 100644 --- a/README.md +++ b/README.md @@ -510,6 +510,8 @@ pytest The test suite covers closed-form price validation (Gil-Pélaez inversion for Heston; Black-Scholes formula for GBM), statistical properties (skew monotonicity in H for rBergomi), Parquet round-trips, and dataset card conformance — plus the Benchmark Core (metrics and baselines validated against hand-derived reference values, Portfolio Optimizer conformance suite), the Evaluation Results pipeline (`BenchmarkResult` → `EvaluationResult` transform, local storage, and Leaderboard aggregation), and the Leaderboard Space (`spaces/leaderboard/`: data loading/rendering, sorting, filtering, and deployment configuration). +The same suite runs in CI on every push and pull request (see the badge at the top of this README). Releases follow [`docs/RELEASE_CHECKLIST.md`](docs/RELEASE_CHECKLIST.md). + --- ## Roadmap diff --git a/docs/RELEASE_CHECKLIST.md b/docs/RELEASE_CHECKLIST.md new file mode 100644 index 0000000..fcbf5e1 --- /dev/null +++ b/docs/RELEASE_CHECKLIST.md @@ -0,0 +1,76 @@ +# Release Checklist + +Covers a full QuantScenarioBench release: the Python library (GitHub tag → PyPI), +the Zenodo DOI, and the Hugging Face assets (benchmark datasets, Evaluation +Results repo, Leaderboard Space). The library follows SemVer; `dataset_version` +is versioned independently (see `CHANGELOG.md` header and `export/_publish.py`). + +Release history convention: each release is prepared on a `docs/vX.Y.Z-release-notes` +branch and merged via PR, then tagged (`v1.0.0`–`v1.2.1` all followed this). + +## 1. Pre-flight + +- [ ] All feature branches for this release are merged to `main`; working tree clean. +- [ ] CI is green on `main` (`.github/workflows/ci.yml`). +- [ ] Fresh-environment test run passes: `pip install -e ".[dev]" && pytest`. +- [ ] Model and strategy conformance suites pass (`tests/test_conformance.py`, + `tests/test_benchmark_conformance.py`). +- [ ] Golden fixtures (`tests/fixtures/golden_benchmark_results.json`) unchanged — + or every intentional metric/behavior change is called out in the CHANGELOG. +- [ ] Decide the version bump (SemVer: breaking → major, feature → minor, fix → patch). +- [ ] Triage open bugs: anything release-blocking (e.g. serialization bugs that + corrupt published EvaluationResults) is fixed or explicitly deferred in the notes. + +## 2. Release-prep PR (branch: `docs/vX.Y.Z-release-notes`) + +- [ ] Bump `version` in `pyproject.toml`. +- [ ] Add the `## [X.Y.Z] - YYYY-MM-DD` section to `CHANGELOG.md` + (Keep a Changelog format: Added / Changed / Fixed / Notes). +- [ ] Update the README: Roadmap table statuses, docs for new capabilities, + and any version numbers quoted in examples. +- [ ] Bump the pinned library ref in `spaces/leaderboard/requirements.txt` + (`quantscenariobench @ git+...@vX.Y.Z` — or the PyPI pin once published there). +- [ ] Verify `spaces/leaderboard/README.md` front-matter (`sdk_version`) still + matches what the Space actually runs. +- [ ] Update `CITATION.cff` (version + release date; DOI after step 3 if versioned DOIs are cited). +- [ ] Open PR, review, merge to `main`. + +## 3. Tag, GitHub Release, DOI + +- [ ] `git tag vX.Y.Z && git push origin vX.Y.Z`. +- [ ] Create the GitHub Release with notes (copy from the CHANGELOG section). +- [ ] Verify Zenodo archived the release and minted the version DOI; confirm the + concept-DOI badge in the README still resolves. + +## 4. Package publish (PyPI) + +> ⚠️ As of v1.2.1 the package is **not** on PyPI even though the README says +> `pip install quantscenariobench`. Until first PyPI publication, either publish +> or fix the README install instructions — don't release with a broken install path. + +- [ ] Build from the tag: `python -m build` (hatchling backend); `twine check dist/*`. +- [ ] Upload (`twine upload` — or GitHub Actions trusted publishing once configured). +- [ ] Verify in a clean venv: `pip install quantscenariobench==X.Y.Z`, then import + and run a minimal `simulate()` + `run_benchmark()` smoke test. + +## 5. Hugging Face assets + +- [ ] If generation code, model parameters, or the Parquet schema changed: + regenerate benchmark sample datasets, bump `dataset_version`, publish with + `publish_to_hub()`, and confirm the auto-generated dataset cards are correct. +- [ ] Record the Hub dataset **revision hashes** for the canonical datasets in the + release notes so downstream benchmark users can pin them. +- [ ] Confirm the Evaluation Results repo (`QSB_EVAL_RESULTS_REPO`) is reachable + and its documented default matches reality. +- [ ] Redeploy the Leaderboard Space: push `spaces/leaderboard/` contents to the + Space's git repo; watch the build; confirm the table renders. +- [ ] End-to-end smoke test: run a baseline strategy on a published dataset with + the released version, `publish_evaluation_results()`, and confirm the run + appears on the live Space. + +## 6. Post-release + +- [ ] Delete the merged `docs/vX.Y.Z-release-notes` branch (local + remote). +- [ ] Close the milestone / shipped issues. +- [ ] Announce (HF org page, relevant communities). +- [ ] File follow-up issues for anything deferred during the release. From 1358b26b726ea8b4b30a311dc7633242fe445ad3 Mon Sep 17 00:00:00 2001 From: TomHortons Date: Thu, 9 Jul 2026 07:25:08 +0000 Subject: [PATCH 5/9] Record GitHub alias alongside the academic author name in CITATION.cff Citations must credit the paper-author identity (Tomoya Imanishi) so they consolidate with other publications; the alias field ties in the GitHub maintainer identity (tim-nish). Co-Authored-By: Claude Fable 5 --- CITATION.cff | 1 + 1 file changed, 1 insertion(+) diff --git a/CITATION.cff b/CITATION.cff index a642a21..da9da8c 100644 --- a/CITATION.cff +++ b/CITATION.cff @@ -13,6 +13,7 @@ type: software authors: - family-names: "Imanishi" given-names: "Tomoya" + alias: "tim-nish" version: "1.2.1" date-released: "2026-07-04" doi: "10.5281/zenodo.21097248" From e0c74e4b8ce1d1cce08025662866c89a287d64b2 Mon Sep 17 00:00:00 2001 From: TomHortons Date: Thu, 9 Jul 2026 07:40:04 +0000 Subject: [PATCH 6/9] Add author ORCID to CITATION.cff CITATION.cff stays the single canonical citation-metadata file; Zenodo reads it via the GitHub integration, so the ORCID propagates to the archived-release record on the next release without a .zenodo.json. Co-Authored-By: Claude Fable 5 --- CITATION.cff | 1 + 1 file changed, 1 insertion(+) diff --git a/CITATION.cff b/CITATION.cff index da9da8c..3f56825 100644 --- a/CITATION.cff +++ b/CITATION.cff @@ -14,6 +14,7 @@ authors: - family-names: "Imanishi" given-names: "Tomoya" alias: "tim-nish" + orcid: "https://orcid.org/0009-0007-0920-536X" version: "1.2.1" date-released: "2026-07-04" doi: "10.5281/zenodo.21097248" From 77bde0fdd3ba8d8d14f298bc3f60e661ecec23e0 Mon Sep 17 00:00:00 2001 From: TomHortons Date: Thu, 9 Jul 2026 07:43:25 +0000 Subject: [PATCH 7/9] Declare README as the package long description python -m build succeeded but twine check warned that long_description and long_description_content_type were missing: [project] had no readme key, so the built METADATA carried no Description. readme = "README.md" fills both fields (content type is inferred from the .md suffix), so the PyPI project page will render the README. twine check dist/* now passes for both the sdist and the wheel. Co-Authored-By: Claude Fable 5 --- pyproject.toml | 1 + 1 file changed, 1 insertion(+) diff --git a/pyproject.toml b/pyproject.toml index 19706d6..d55d87f 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -6,6 +6,7 @@ build-backend = "hatchling.build" name = "quantscenariobench" version = "1.2.1" description = "JAX-native framework for generating reproducible market scenarios, benchmarking portfolio strategies against them, and publishing results as versioned datasets with a hosted Leaderboard" +readme = "README.md" license = {text = "MIT"} requires-python = ">=3.11" dependencies = [ From bdd74bb9626506147781716e2c2487e849e96497 Mon Sep 17 00:00:00 2001 From: TomHortons Date: Thu, 9 Jul 2026 08:17:38 +0000 Subject: [PATCH 8/9] =?UTF-8?q?Add=20gradio=20to=20the=20dev=20extras=20?= =?UTF-8?q?=E2=80=94=20first=20CI=20run=20caught=20it=20missing?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The Leaderboard Space tests (23 of them) import gradio via spaces/leaderboard/app.py, but the [dev] extras never declared it, so the documented fresh-clone flow — pip install -e ".[dev]" && pytest — failed in CI on both Python versions (ModuleNotFoundError; every other test passed: 638 passed, 1 skipped). Local dev environments had gradio installed out-of-band, which is why this was never seen before. Pinned >=6.19 to match spaces/leaderboard/requirements.txt. Deliberately NOT installing spaces/leaderboard/requirements.txt in CI instead: it pins quantscenariobench to a released git tag, which would silently replace the editable install under test. Co-Authored-By: Claude Fable 5 --- pyproject.toml | 1 + 1 file changed, 1 insertion(+) diff --git a/pyproject.toml b/pyproject.toml index d55d87f..7f4edd4 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -24,6 +24,7 @@ dev = [ "pytest-cov>=5.0", "pandas>=2.0", "datasets>=2.0", + "gradio>=6.19", ] [tool.hatch.build.targets.wheel] From c3d19d2a294bb0191fc29808bcf76718e385cffe Mon Sep 17 00:00:00 2001 From: TomHortons Date: Thu, 9 Jul 2026 08:19:35 +0000 Subject: [PATCH 9/9] Narrow AD-27's gradio guard to the actual dependency lists MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The guard test enforced "gradio is not a dependency of the installable package" (AD-27) with a whole-file string scan of pyproject.toml, which cannot tell the package's own dependency list apart from dev tooling — so it rejected the previous commit's legitimate [dev]-extra addition. Replaced with a tomllib check: gradio must be absent from [project].dependencies and from every extra except dev, which is the one sanctioned exception because the test suite itself imports spaces/leaderboard/app.py. The architectural invariant — plain `pip install quantscenariobench` never pulls gradio — is enforced unchanged, and now more precisely. Co-Authored-By: Claude Fable 5 --- tests/test_leaderboard_space_app.py | 23 +++++++++++++++++++++-- 1 file changed, 21 insertions(+), 2 deletions(-) diff --git a/tests/test_leaderboard_space_app.py b/tests/test_leaderboard_space_app.py index b89926c..3ec9846 100644 --- a/tests/test_leaderboard_space_app.py +++ b/tests/test_leaderboard_space_app.py @@ -105,8 +105,27 @@ def test_space_requirements_pin_gradio_and_quantscenariobench(): def test_gradio_absent_from_installable_package_dependencies(): - pyproject_text = (_repo_root() / "pyproject.toml").read_text() - assert "gradio" not in pyproject_text.lower() + # AD-27's guard, narrowed (PR #100): gradio must never be a dependency + # of the installable package — neither runtime nor any extra except + # [dev]. The dev extra is the one sanctioned exception: this test suite + # itself imports spaces/leaderboard/app.py (which imports gradio), so + # the documented contributor flow — pip install -e ".[dev]" && pytest — + # must bring gradio with it (the repository's first CI run failed 23 + # Space tests without it). The previous whole-file string scan could + # not distinguish the package's own dependency list from the dev + # tooling's, so it is replaced with a check of the actual lists. + import tomllib + + pyproject = tomllib.loads((_repo_root() / "pyproject.toml").read_text()) + runtime_deps = "\n".join(pyproject["project"]["dependencies"]) + assert "gradio" not in runtime_deps.lower() + extras = pyproject["project"].get("optional-dependencies", {}) + for extra_name, extra_deps in extras.items(): + if extra_name == "dev": + continue + assert "gradio" not in "\n".join(extra_deps).lower(), ( + f"gradio leaked into the {extra_name!r} extra" + ) # ---------------------------------------------------------------------------