Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
36 commits
Select commit Hold shift + click to select a range
760e086
feat(demos): B2 wrong-IMF bias curve + robustness grid
drannarosen Jun 11, 2026
3a56ea7
fix(demos): B2d review fixes -- run-record sync, peaked-shape summary…
drannarosen Jun 11, 2026
648329e
feat(demos): B3 halo+core (t, r_a, r_h) recovery
drannarosen Jun 11, 2026
5c71ab0
fix(demos): B3 occupancy STOP now exits immediately (review fix)
drannarosen Jun 11, 2026
15e112e
docs(website): science-demos section (B1-B3) wired into TOC
drannarosen Jun 11, 2026
3689bc6
docs(batch-b): Task 9 close-out — completion doc + STATUS
drannarosen Jun 11, 2026
598bdd9
docs: pre-release adversarial audit (2026-06-11) + audit-fix implemen…
drannarosen Jun 12, 2026
7fe4cec
fix(ci): decouple the lock from private siblings — unbreaks uv sync, …
drannarosen Jun 12, 2026
095e3b2
test: PROGENAX_STRICT_REFS=1 turns missing reference caches into fail…
drannarosen Jun 12, 2026
02689fb
ci: nightly + release-tag lane running the slow-marked headline valid…
drannarosen Jun 12, 2026
0adc74e
fix(imf): MoeDiStefano2017Full F_twin now follows the paper's q>0.3 c…
drannarosen Jun 12, 2026
7f9b9d5
fix(tests): move test_demo_inference to experimental tier — it needs …
drannarosen Jun 12, 2026
d35164e
fix(profiles): sqrt-stretched CDF grids resolve the core at high W0 —…
drannarosen Jun 12, 2026
9085e88
fix(imf): sample_fixed_n raises on unreachable m_total instead of sil…
drannarosen Jun 12, 2026
b98ff0f
fix(imf): finite gradients at alpha=1 exactly — port exp_safe double-…
drannarosen Jun 12, 2026
f57e34d
fix(profiles): extend sqrt-stretched core grid to EFF, consistently (…
drannarosen Jun 12, 2026
20c62ec
fix(builders): stellar mass-radius relation per Demircan & Kahraman (…
drannarosen Jun 12, 2026
2e38ba3
test: portable tolerance for the EFF eddington-table regression (audi…
drannarosen Jun 12, 2026
43d5481
fix(profiles): King r_t boundary-pinning is now flagged (traced) or r…
drannarosen Jun 12, 2026
6d3d7b6
fix(builders): virial_scale refuses T=0 input; dedupe with rescale_ve…
drannarosen Jun 12, 2026
cb1b94b
perf(diagnostics): Python-if dispatch on static N in q_approx — avoid…
drannarosen Jun 12, 2026
3a86878
fix(api): warn on inconsistent KingProfile r_t; drop the dead KingVel…
drannarosen Jun 12, 2026
83948ba
refactor: single source of truth for the VelocityDF protocol (audit A1)
drannarosen Jun 12, 2026
240ac81
test(eff): pin the documented gamma=3 sub-virial offset + constructor…
drannarosen Jun 12, 2026
2bb8f44
ci: shard the unit tier 3 ways to fix the OOM (audit R1 follow-up)
drannarosen Jun 12, 2026
a09e5a8
fix(api+docs): export the documented IMF symbols; kill the phantom IG…
drannarosen Jun 12, 2026
b825b2a
docs: README rewritten against the current API, with an executable-ex…
drannarosen Jun 12, 2026
7ac2547
docs: remove the fabricated 'pip install progenax-legacy' claim (audi…
drannarosen Jun 12, 2026
7946fdf
docs(src): honest equilibrium caveats — rotation overlays, tidal trun…
drannarosen Jun 12, 2026
e0943be
docs: reconcile the stale test-count claims to ~1243 + 'see CI' frami…
drannarosen Jun 12, 2026
a688e53
chore(packaging): LICENSE (Apache-2.0) + author/classifiers/URL in wh…
drannarosen Jun 12, 2026
b140bac
fix(packaging): [diagnostics] extra + lazy numpy/scipy imports with a…
drannarosen Jun 12, 2026
bc98083
ci: wheel-smoke + nightly version-matrix; fix unit-shard OOM at the r…
drannarosen Jun 12, 2026
86339d1
docs: CHANGELOG for the 0.1.0 pre-release audit-fix arc (audit D7)
drannarosen Jun 12, 2026
1b5f150
docs(close-out): completion doc + STATUS for the pre-release audit-fi…
drannarosen Jun 12, 2026
c37061d
docs(builders): note compute_stellar_radii is a temporary stand-in fo…
drannarosen Jun 13, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
83 changes: 83 additions & 0 deletions .github/workflows/physics-validation.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,83 @@
name: physics-validation

# Nightly + release-tag lane for the headline trust anchors that the PR gate skips:
# the @pytest.mark.slow validations (LIMEPY reference parity, multimass equilibrium,
# Engine A-vs-B anchor, Engine B AD-vs-FD). PROGENAX_STRICT_REFS=1 makes a missing
# reference cache a hard failure here (audit R8/T4), so these gates cannot silently
# no-op. Only the PUBLIC jaxstro sibling is needed (the lock is decoupled from the
# private jaxstroviz/fluxax — audit R1/D2); jaxstroviz-dependent experimental SBC
# tests import-skip.

on:
schedule:
- cron: "17 9 * * *" # nightly
workflow_dispatch:
push:
tags: ["v*"]

env:
JAX_ENABLE_X64: "1"
XLA_PYTHON_CLIENT_PREALLOCATE: "false"
XLA_PYTHON_CLIENT_ALLOCATOR: platform
OMP_NUM_THREADS: "1"
PROGENAX_STRICT_REFS: "1"

jobs:
slow-physics:
runs-on: ubuntu-latest
timeout-minutes: 90
steps:
- name: Checkout progenax
uses: actions/checkout@v4
with:
path: progenax
- name: Checkout jaxstro (public path dependency, sibling layout)
uses: actions/checkout@v4
with:
repository: jaxstro/jaxstro
path: jaxstro
- name: Setup uv
uses: astral-sh/setup-uv@v6
with:
python-version: "3.13"
- name: Sync from lockfile (dev + experimental)
working-directory: progenax
run: uv sync --locked --extra dev --extra experimental
- name: Strict reference guard (cache must be present)
working-directory: progenax
run: uv run --no-sync pytest tests/validation/test_strict_refs.py -q
- name: Slow physics-validation suite (the advertised trust anchors)
working-directory: progenax
run: uv run --no-sync pytest tests -m slow -q

# Version matrix (audit T3): requires-python is >=3.10 but only 3.13 is exercised
# on the PR lane. Nightly, run a representative memory-safe core slice (imf +
# profiles — the IMF maths + the diffrax ODE profiles, the most version-sensitive
# numerics) on the floor and ceiling Python. Not the PR lane (keeps PR latency flat).
version-matrix:
strategy:
fail-fast: false
matrix:
python-version: ["3.10", "3.13"]
runs-on: ubuntu-latest
timeout-minutes: 30
steps:
- name: Checkout progenax
uses: actions/checkout@v4
with:
path: progenax
- name: Checkout jaxstro (public path dependency, sibling layout)
uses: actions/checkout@v4
with:
repository: jaxstro/jaxstro
path: jaxstro
- name: Setup uv
uses: astral-sh/setup-uv@v6
with:
python-version: ${{ matrix.python-version }}
- name: Sync (dev) on Python ${{ matrix.python-version }}
working-directory: progenax
run: uv sync --extra dev --python ${{ matrix.python-version }}
- name: Core version-compat slice (imf + profiles)
working-directory: progenax
run: uv run --no-sync pytest tests/unit/imf tests/unit/profiles -m "not slow" -q
64 changes: 55 additions & 9 deletions .github/workflows/tests.yml
Original file line number Diff line number Diff line change
Expand Up @@ -40,14 +40,27 @@ jobs:
working-directory: progenax
run: uv lock --check

# Released-core invariant, SHARDED across the three test tiers. Each shard runs in a
# fresh process on its own runner -> bounded memory (no OOM), parallel (fast wall-clock),
# and isolates which tier fails. fail-fast: false so all shards report.
# Released-core invariant, SHARDED. Each shard runs in a FRESH process on its own
# runner -> bounded memory, parallel wall-clock, isolates which shard fails.
# The ~900-test unit tier is split into 3 sub-shards (~300 each): one process
# accumulating all of it OOM-killed the 2-core/7 GB runner (exit 143, audit R1
# follow-up). unit-c is a CATCH-ALL (tests/unit minus the two heavy groups), so
# a NEW tests/unit subdir is still run without editing this matrix.
# fail-fast: false so all shards report.
released-core:
strategy:
fail-fast: false
matrix:
suite: [unit, integration, validation]
include:
# imf and profiles are the two heaviest modules (profiles compiles the
# diffrax-ODE King/Michie/LIMEPY + Eddington families) -> each gets its
# own shard. unit-c is the catch-all (everything else) so a new subdir is
# still covered. The conftest CI cache-clear bounds per-module memory.
- { name: unit-a, paths: "tests/unit/imf" }
- { name: unit-b, paths: "tests/unit/profiles" }
- { name: unit-c, paths: "tests/unit --ignore=tests/unit/imf --ignore=tests/unit/profiles" }
- { name: integration, paths: "tests/integration" }
- { name: validation, paths: "tests/validation" }
runs-on: ubuntu-latest
timeout-minutes: 30
steps:
Expand All @@ -67,9 +80,9 @@ jobs:
- name: Sync from lockfile (dev extra)
working-directory: progenax
run: uv sync --locked --extra dev
- name: Run tests/${{ matrix.suite }}
- name: Run ${{ matrix.name }}
working-directory: progenax
run: uv run --no-sync pytest tests/${{ matrix.suite }} -m "not slow" -q
run: uv run --no-sync pytest ${{ matrix.paths }} -m "not slow" -q

# Experimental gravoturb_fdf subsystem (repo-only, NOT in the wheel). Needs the
# [experimental] extra (blackjax, optax); runs the non-slow experimental suite.
Expand Down Expand Up @@ -97,22 +110,55 @@ jobs:
working-directory: progenax
run: uv run --no-sync pytest tests/experimental -m "not slow" -q

# Wheel-smoke (audit M6): build the wheel, install it in a CLEAN venv with only
# the public jaxstro sibling, and import the package + the diagnostics subpackage.
# Catches packaging regressions an editable dev install hides — missing runtime
# deps, experimental/gravoturb leakage, and the R9 eager numpy/scipy import (the
# clean venv has no scipy, so progenax.diagnostics must import lazily).
wheel-smoke:
runs-on: ubuntu-latest
timeout-minutes: 15
steps:
- name: Checkout progenax
uses: actions/checkout@v4
with:
path: progenax
- name: Checkout jaxstro (path dependency, sibling layout)
uses: actions/checkout@v4
with:
repository: jaxstro/jaxstro
path: jaxstro
- name: Setup uv
uses: astral-sh/setup-uv@v6
with:
python-version: "3.13"
- name: Build wheel
working-directory: progenax
run: uv build --wheel -o dist/
- name: Install wheel in a clean venv and import
working-directory: progenax
run: |
uv venv /tmp/clean
uv pip install --python /tmp/clean/bin/python dist/*.whl ../jaxstro
/tmp/clean/bin/python -c "import progenax; import progenax.diagnostics; print(progenax.__name__, 'imports clean (no scipy in venv)')"

# Aggregator gate: a single `tests` check that is green iff every required job above
# succeeded — preserves the `tests` branch-protection check name. `if: always()` so a
# failed/cancelled dependency reds it (a skipped dependent job would otherwise let
# failures slip through branch protection).
tests:
if: always()
needs: [lock-check, released-core, experimental]
needs: [lock-check, released-core, experimental, wheel-smoke]
runs-on: ubuntu-latest
timeout-minutes: 5
steps:
- name: All required jobs succeeded
run: |
echo "lock-check=${{ needs.lock-check.result }} released-core=${{ needs.released-core.result }} experimental=${{ needs.experimental.result }}"
echo "lock-check=${{ needs.lock-check.result }} released-core=${{ needs.released-core.result }} experimental=${{ needs.experimental.result }} wheel-smoke=${{ needs.wheel-smoke.result }}"
if [ "${{ needs.lock-check.result }}" = "success" ] \
&& [ "${{ needs.released-core.result }}" = "success" ] \
&& [ "${{ needs.experimental.result }}" = "success" ]; then
&& [ "${{ needs.experimental.result }}" = "success" ] \
&& [ "${{ needs.wheel-smoke.result }}" = "success" ]; then
echo "all required jobs green"
else
echo "::error::a required job did not succeed"
Expand Down
76 changes: 76 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,76 @@
# Changelog

All notable changes to progenax are documented here. This project adheres to
[Semantic Versioning](https://semver.org/).

## 0.1.0 (unreleased)

First public-ready release candidate. This cycle resolves the release blockers from
the 2026-06-11 pre-release adversarial audit
(`docs/reviews/2026-06-11-prerelease-adversarial-audit.md`), moving progenax from Beta
toward Release Candidate. Finding IDs (R*, J*, S*, A*, T*, D*, M*) refer to that report.

### Science fixes

- **Moe & Di Stefano F_twin normalization** (R3): the twin excess was mixed against the
whole q ∈ [0.1, 1] population instead of the paper's q > 0.3 convention (realized
F_twin 0.367 vs Table-13 0.300 at solar logP=1). `MoeDiStefano2017Full` rebuilt as a
jointly-normalized PL + ft/(1−ft)·I_B twin block; pinned at four Table-13 nodes (`0adc74e`).
- **King/Michie/EFF core resolution at high concentration** (R4): the linear 1000-pt
position-CDF grid under-resolved the core (measured +18% at W0=9, +270% at W0=12, 0.3 r_c).
Replaced with a sqrt-stretched `r = r_t·u²` grid + non-uniform trapezoid; EFF's
`compute_profile_potential` made consistent (`d35164e`, `f57e34d`).
- **`sample_fixed_n` silent mass shortfall** (R5): asking 500 M☉ for n=1000 silently
returned 349 M☉. Now raises on an unreachable target; first tests for all four
mass-target sampling modes (`9085e88`).
- **`compute_stellar_radii` mass–radius relation** (R6): exponents were inverted vs MS
homology (10 M☉ → 6.3 R☉). Adopted Demircan & Kahraman (1991) (`20c62ec`).
- **PowerLawIMF α=1 NaN gradient** (R10): ported the `exp_safe` double-where so gradients
are finite at exactly α=1 (`b98ff0f`).
- **EFF γ=3 sub-virial offset** pinned (S2); honest equilibrium caveats for rotation
overlays, tidal truncation, mass segregation, the binary energy budget, and subgroup
virial (S3/S4/S6/S10/S16) (`240ac81`, `7946fdf`).

### JAX / API hardening

- King `r_t` boundary-pinning is now flagged (traced) or refused (eager) instead of
silently wrong (J4) (`43d5481`).
- `virial_scale` refuses T=0 input and is deduped with `rescale_velocities_to_virial` (J5)
(`6d3d7b6`).
- `q_approx` uses Python-`if` dispatch on static N (no double-branch compile) (J6) (`cb1b94b`).
- Inconsistent `KingProfile` r_t now warns; the dead `KingVelocityDF.r_t` field removed
(S1/A3) (`3a86878`). Zero-rotation-axis now raises (S15) (`7946fdf`).
- Single source of truth for the `VelocityDF` protocol (A1) (`83948ba`).

### Documentation

- Exported the 15 documented IMF/binary symbols from `progenax.__init__`; removed the
phantom `IGIMF`/`EnvironmentIMF` class claims (the environment-dependent IMF is the
functional `BirthEnvironment` + `env_to_imf_params` API) (R7) (`a09e5a8`).
- README rewritten against the current API, with an executable-examples smoke test that
runs every code block; installation.md fixed (R7/D3/D4/H2) (`b825b2a`).
- Removed the fabricated `pip install progenax-legacy` claim (D5/H4) (`7ac2547`).
- Reconciled the three conflicting test-count claims (T5/L3/D6) (`e0943be`).

### Packaging & CI

- **CI un-broken** (R1/D2): the lock was decoupled from the private `jaxstroviz` sibling
(CI had been red since 2026-06-10); the unit tier is sharded and the JAX compilation
cache is bounded in CI to fix OOM kills (`7fe4cec`, `2bb8f44`, `bc98083`).
- Nightly + release-tag lane running the slow-marked headline validations, with
`PROGENAX_STRICT_REFS=1` turning a missing reference cache into a failure (R8/T4)
(`02689fb`, `095e3b2`).
- **LICENSE** added (Apache-2.0, © 2026 Anna Rosen) + author/classifiers/URL in the wheel
metadata (D1) (`a688e53`).
- `[diagnostics]` extra + lazy numpy/scipy imports with actionable errors — `import
progenax.diagnostics` no longer crashes in a clean install (R9) (`b140bac`).
- wheel-smoke (clean-venv import) + nightly Python version matrix (3.10/3.13) (M6/T3)
(`bc98083`).

### Known limitations / deferred (require a separate decision or arc)

- **Not yet pip-installable for outsiders** (R2): the runtime depends on the unpublished
sibling `jaxstro`. Resolution (publish `jaxstro` to PyPI, vendor its `units`+`jaxconfig`,
or a git-URL dependency) is a strategy decision tracked separately.
- Units-policy A2 (DF `G=None` defaults vs protocol-wide explicit G) — a breaking sweep,
deferred. A two-sided quantile stretch for `sample_fixed_n` (R5 follow-up) is deferred.
Loading
Loading