Skip to content

Pre-release audit fixes (R1–R10 + S/J/A/T/D): Beta → RC - #6

Merged
drannarosen merged 36 commits into
mainfrom
fix/prerelease-audit
Jun 13, 2026
Merged

drannarosen merged 36 commits into
mainfrom
fix/prerelease-audit

Conversation

@drannarosen

Copy link
Copy Markdown
Member

Pre-release audit fixes — Beta → Release Candidate

Resolves every release blocker from the 2026-06-11 pre-release adversarial audit
(docs/reviews/2026-06-11-prerelease-adversarial-audit.md) via strict RED→GREEN TDD.
Plan: docs/plans/2026-06-12-prerelease-audit-fixes.md. Completion evidence:
.claude-work/PRERELEASE_AUDIT_FIXES_COMPLETE.md.

Scope note: this branch was cut from a local main that was ahead of origin/main,
so it also carries 6 previously-unpushed Batch B science-demo commits (760e086..3689bc6)
in addition to the 28 audit-fix commits. Per the "one final PR" workflow this pushes all the
accumulated local-main work at once.

Highlights (audit findings R1–R10 + S/J/A/T/D)

CI un-broken and hardened (R1/D2/R8/T4/M6/T3)

  • Decoupled the lock from the private jaxstroviz sibling (CI had been red since 2026-06-10).
  • Fixed the unit-tier OOM at the root: CI-gated jax.clear_caches() between modules + unit sharding.
  • New lanes: nightly/release slow-physics + strict-refs, wheel-smoke (clean-venv import), version matrix (3.10/3.13).

Science fixes

  • R3 Moe & Di Stefano F_twin → paper's q>0.3 convention (0.367 → 0.300).
  • R4 King/Michie/EFF core-CDF grid: sqrt-stretched (+270% @w0=12 → <0.1%).
  • R5 sample_fixed_n raises on unreachable target (was a silent 30% shortfall).
  • R6 compute_stellar_radii → Demircan & Kahraman (1991).
  • R10 PowerLawIMF finite gradient at α=1.
  • S2 EFF γ=3 sub-virial band pinned; honest equilibrium caveats (S3/S4/S6/S10/S15/S16).

JAX/API: J4 King r_t pinning flagged/refused; J5 virial_scale T=0 guard + dedupe; J6 q_approx dispatch; S1/A3 King r_t warn + dead-field removal; A1 protocol dedupe.

Docs/packaging: R7 exported 15 documented symbols + killed phantom IGIMF/EnvironmentIMF; README rewrite with an executable-examples smoke test; D5 removed the fabricated progenax-legacy; Apache-2.0 LICENSE + wheel metadata; R9 [diagnostics] extra + lazy numpy/scipy; CHANGELOG.

Verification

  • CI: green (lock-check, unit-a/b/c, integration, validation, experimental, wheel-smoke).
  • FULL gate (incl. slow trust anchors, PROGENAX_STRICT_REFS=1): 1244 passed, exit 0.
  • FAST gate: 1191 passed, exit 0. myst build: 151 pages, 0 warnings.

Deferred (separate decisions/arcs)

  • R2 pip-installability → publish jaxstro to PyPI (chosen; follow-up, mostly in the jaxstro repo).
  • A2 units-policy sweep; sample_fixed_n two-sided stretch; suppress/assert the new S1 warning in ~6 deliberately-inconsistent King-IC tests.

🤖 Generated with Claude Code

drannarosen and others added 30 commits June 11, 2026 13:13
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…, alpha=2.1 valley diagnostic

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Engine B prescribed-density halo+core demo: Plummer halo (Osipkov-Merritt
anisotropic) + EFF(gamma=5) core, sampled in shared-potential equilibrium,
recovering (t, r_a, r_h) by joint MLE + Gauss-Newton Fisher from two binned
observables: per-component sigma_1d(r) (binned EXPECTATION, amendment 4) and
the halo OM beta(r)=r^2/(r^2+r_a^2) closed form (amendment 3). Velocity scale
is the engine's own kappa=G M_sampled/(4 pi mu) convention.

Run (N=3e4, exit 0, ALL PASS): t=0.602+/-0.008 (pull 0.22), r_a=3.05+/-0.05
(0.96), r_h=2.02+/-0.03 (0.51); all 3 dispersed inits -> identical loss
19.1479 (robust optimum); realizability min(f_min_j)=1.16e-4; self-consistency
max|dev/se|=2.34; Fisher PD cond 13.4, near-zero param correlations.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
The occupancy gate printed "STOP: ... a component is starved" but fell
through to make_figure() before the deferred nonzero exit -- unlike the
self-consistency (exit 1) and budget (exit 2) early gates. A "STOP" that
doesn't stop would save a misleading figure on a genuine occupancy failure.
Add sys.exit(4) so a starved fit stops before the figure. (Untaken in the
passing run -- 16/16 bins both components -- so the headline result stands.)

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
New 60-science-demos/ section, four pages + six embedded figures, after the
Validation group. Pedagogical prose with governing equations and honest
clean-mock caveats; every number traces to a freshly-run printed table.

- index.md         -- the shared physics-direct differentiable inference method
                      (binned kinematic summaries -> Gaussian likelihood vs
                      analytic predictions; gradients through the analytic side;
                      MLE + Gauss-Newton Fisher + NUTS) and honest-scope warning.
- cross-engine.md  -- B1: King built by Engine A (lowered-isothermal DF + ODE)
                      vs Engine B (density-defined Eddington); KS 1.5e-4,
                      sigma-dev 3.5e-4, Q_j 0.50019/0.50000 (re-run, ALL PASS).
                      DF eq verified vs GZ15 eq 2 (index g; g=1 -> e^x-1 King).
- imf-equipartition.md -- B2: one alpha drives masses AND equipartition groups;
                      joint (alpha,delta,W0) recovery 2.293/0.397/4.990 (all 3s);
                      mass channel pins alpha 4.7x (Fisher rho -0.265->-0.059);
                      NUTS 0-div post-mean ~MLE; wrong-IMF bias + robustness grid.
- halo-core.md     -- B3: Plummer halo (OM) + EFF core; (t,r_a,r_h) recovery
                      0.602/3.051/2.015 (all <1s); realizability + OM beta.

myst build: exit 0, 0 warnings, 151 pages (the plan's 148 assumed a 144
baseline; the merged LIMEPY arc grew it to 147, + these 4 = 151).
50-validation/index.md gains a one-line cross-link to the new section.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
FULL released-core gate 1192 passed (9:07), exit 0. Demos B1/B3/B2d exit 0
ALL PASS fresh; B2 recovery + all gates PASS fresh (bit-identical to the gold
reference; clean exit 0 in ccf6ed1). myst build 151 pages, 0 warnings. Batch B
science demos (Tasks 1-9) complete on feat/batch-b-demos; merge/push awaits
Anna's go.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…tation plan

Source-of-truth references for the prerelease-audit-fixes arc. STATUS.md
test-count tweak carried along.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…CI red since 2026-06-10 (audit R1/D2)

CI died at `uv lock --check`/`uv sync --locked` with 'Distribution not found
at ../jaxstroviz': the lock pinned an editable path to the PRIVATE jaxstroviz
repo (dragging private fluxax + gravax transitively), which the default
GITHUB_TOKEN cannot check out cross-repo.

Rather than introduce a cross-repo PAT on the PR gate, drop jaxstroviz from the
declared deps entirely (Anna's call). It is only used by the experimental SBC
figure gallery + integer-aware chi^2 helpers (AC18), which already
pytest.importorskip('jaxstroviz') and are xfail. The lock now resolves with
only the PUBLIC jaxstro path source (99 pkgs, no private repos).

Verified in a throwaway venv synced from the new lock with NO jaxstroviz:
  - uv sync --locked --extra dev --extra experimental: exit 0
  - tests/experimental -m 'not slow': 13 passed, 0 errors
  - test_ac18_sbc_rank_uniformity: SKIPPED ('could not import jaxstroviz')
Local figure work: `uv pip install -e ../jaxstroviz`.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…ures (audit T4)

Adds a directory-level strict guard (test_strict_refs.py) and converts the
silent pytest.skip in the LIMEPY parity loader to pytest.fail under strict
mode, so a checkout/packaging mistake can no longer silently disable the
strongest external-reference gate. Verified: strict+absent -> FAIL,
non-strict+absent -> SKIP, present -> PASS, no-env -> SKIP.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…ations (audit R8)

The trust anchors (LIMEPY reference parity, multimass equilibrium, Engine
A-vs-B, Engine B AD-vs-FD) are @pytest.mark.slow and the PR gate runs
-m 'not slow', so they ran nowhere but Anna's laptop. New lane runs
pytest tests -m slow nightly + on v* tags, with PROGENAX_STRICT_REFS=1 so a
missing reference cache fails (T4). Only the public jaxstro sibling is
checked out (lock decoupled from private jaxstroviz/fluxax).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…onvention (audit R3)

Twin excess was mixed against the full q in [0.1,1] population; MD17 p.5/Fig.2
define F_twin relative to q>0.3. Realized F_twin at (M1=1, logP=1) was 0.367
for a Table-13 value of 0.300 (+22% twin overweight). Mixture rebuilt as
PL + ft/(1-ft)*I_B twin block, jointly normalized; the q>0.3 excess is now
ft exactly. Pinned at four Table-13 nodes; sample P(q>0.95) re-derived from
pdf quadrature (was a stale magic constant encoding the overweight).

RED->GREEN: realized 0.3672 != 0.3000 (buggy) -> all 21 pass; downstream
MoeJointOrbit/MoeCompanions/gradients 405 passed.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…optax (audit R1 follow-up)

CI (finally running after the lock decouple) exposed a latent bug red CI had
hidden: this released-core UNIT test imports scripts/_demo_inference.py, which
imports optax/blackjax (the [experimental] extra). The released-core unit shard
syncs --extra dev only -> ModuleNotFoundError: optax fails the whole shard.
Local gates passed only because the dev venv also had experimental installed.

Moved tests/unit -> tests/experimental/unit (runs in the experimental CI lane
where optax is synced); fixed the repo-root parents[2]->[3] path depth.
Verified: 17 passed from new location; tests/unit collects 907 clean in an
optax-free venv.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
… King+Michie (audit R4)

Linear 1000-pt grids left <10 points in the core at W0>=9: measured core-mass
errors +18% (W0=9) and +270% (W0=12) vs a dense ODE reference. r = r_t*u^2
grid + non-uniform trapezoid (per-interval diff(r_grid) weights).

King + Michie fixed and verified:
  RED  W0=12 @ 0.3 r_c: +270.3% (matches audit +264%)
  GREEN all R4 tests pass; W0=7 quadrature gates unchanged; 251 profile tests pass
  AD-vs-FD W0 gradient ratio 0.999805 (pre-fix 0.999869) — differentiability preserved

EFF DELIBERATELY left on the linear grid: it has NO measured core-resolution
bug at typical r_t/a, and compute_profile_potential (profiles/api.py) reads the
same _r_grid with a uniform-dr enclosed-mass integral, so a non-uniform EFF grid
would silently break the EFF potential. Making EFF non-uniform-consistent
(eff.py CDF + api.py potential + its mirror test) is a clean follow-up flagged
for CHECKPOINT 1 — Anna's call, since EFF has no measured bug.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…ently undershooting (audit R5)

Measured: target 500 Msun, n=1000 returned 349 Msun with no warning (the
one-sided quantile stretch q*<=1 caps the total at sum(ppf(u_base)), and the
clipped Newton solver converged to the boundary silently). Adds the
stratified-ceiling guard (eager inputs raise ValueError; traced m_total skips
the check so jit/grad still work — verified grad ~0.9999), an honest docstring,
and the first-ever tests for the mass-target sampling-mode family (sample_m_total
prefix-cut overshoot, _packed fractional-boundary exactness, fixed_n reachable).

RED: test_unreachable_target_raises DID NOT RAISE -> GREEN 6 pass; imf 272 pass.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…where to PowerLawIMF (audit R10)

Four sites (segment_integral, _cdf_unnorm, ppf incl. **(1/e), mean_mass
first_moment) used the bare where(|e|<eps, log, pow/e) pattern; the untaken
branch computed 0/0 and the where-VJP backprops 0*NaN -> grad(mean_mass)(1.0)
and grad(ppf)(1.0) were nan. Ported the exp_safe double-where (e_safe in EVERY
power/division of the regular branch) from imf/differentiable.py.

Per Anna's call (keep exp_safe; honest test): the fix gives FINITE grads
everywhere + FD-exact grads in a neighborhood of a=1; AT exactly a=1 the
double-where selects the alpha-independent log branch (grad branch-limited at a
measure-zero point — same property as the reference differentiable.py). Tests
pin the achievable guarantee, asserting MORE than the prior convention: finite
AT a=1, FD-exact at a=1+/-1e-3, kink-free forward value through a=1.

RED: grad nan at a=1 -> GREEN 25 pass; imf physics 46 pass.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…audit R4)

Per Anna's call: make EFF non-uniform-consistent so it resolves the core at
large r_t/a like King/Michie. Three coupled changes kept in lockstep:
  - eff.py: sqrt-stretched _r_grid (r = r_t*u^2) + non-uniform trapezoid CDF
  - profiles/api.py: compute_profile_potential's EFF enclosed-mass/outer-shell
    integrals (I2, J_outer) now weight each trapezoid by diff(rgrid) — required
    because they read the same _r_grid; a uniform dr would silently mis-integrate
    the core and break M_enc_frac == profile CDF.
  - test_profile_potential.py mirror: recompute with diff(rgrid) (invariant
    unchanged: potential enclosed mass == profile CDF).

Measured (r_t/a=100, dense-quadrature reference, n=4e6):
  0.3a core mass error +4.2% (linear) -> +0.02% (stretched), within shot.
Pinned by a new slow TestEFFHighRtOverACoreResolution. EFF AD-vs-FD r_t
gradient ratio 1.000097. Regression: eff physics + profile potential 31 pass;
eddington + profiles 192 pass.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…1991) (audit R6)

Previous exponents were inverted vs MS homology (uncited): 10 Msun -> 6.3 Rsun
instead of ~4.8, 0.2 Msun -> 0.40 instead of ~0.23, and a factor-2.4 jump at
the hydrogen-burning limit (audit F7). Now D&K91 Eqs. 5-6:
  R = 1.06 M^0.945 (0.08<=M<1.66), R = 1.33 M^0.555 (M>=1.66), BD plateau <0.08.
Low-mass branch meets the BD plateau near-continuously (0.0975 vs 0.1).
Tests anchored to the published fit + observed radii (Anna's call: D&K91 over
Tout+1996). RED 4 fail -> GREEN 11 pass; downstream ICResult consistency 36 pass.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…t T8/R1 follow-up)

CI (running after the lock decouple) exposed test_eff_table_bit_identical_after_refactor
failing on Linux by ~1 ulp: it pinned full-precision float literals captured on
macOS-ARM and compared with ==. Cross-platform FP makes bit-exact == non-portable.
Relaxed to pytest.approx(rel=1e-9) (Anna's call) — ~6 orders above the ulp noise
floor, still catches real refactoring drift; renamed *_bit_identical_* ->
*_stable_*. Local: 2 pass.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…efused (eager) (audit J4)

_auto_ode_domain falls back to the fixed (300, 2000) domain under tracing; for
W0 >~ 10 the ODE never crosses psi=0 there and _find_tidal_radius silently
pinned xi_t to the boundary -- a wrong r_t exactly where the user can't see it.
Adds r_t_is_pinned (Bool, = no psi crossing in the solved domain). from_W0_rc
now REFUSES eagerly for concrete W0 when pinned (ValueError naming xi_max), and
under tracing stores the flag (a traced bool can't gate a raise) -- the Engine-B
concrete/traced pattern. Concrete high-W0 auto-sizes fine (no raise).

RED 4 (no r_t_is_pinned attr) -> GREEN 4; King regression 53 pass.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…locities_to_virial (audit J5)

virial_scale divided by Q_current with no guard -> cold input (v=0) yielded
all-NaN velocities silently. Rescaling from T=0 is mathematically impossible
(0/0). Now ONE implementation in dynamics.virial.rescale_velocities_to_virial
(virial_scale delegates): concrete T=0 raises ValueError('zero kinetic'),
traced T=0 yields NaN via a double-where (honest sentinel; the old +1e-10 hack
silently mapped cold input to ~0). RED 2 -> GREEN; builders+dynamics 42 pass.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…s compiling both branches (audit J6)

Two sites used jax.lax.cond on a STATIC Python int (q_approx auto-dispatch on N,
and _compute_s_bar_subsampled on n_pairs<=max_pairs), so XLA compiled BOTH
branches every shape (~0.4 s wasted) and traced the O(N^2) exact() path even
when subsampling. Replaced with Python if — identical semantics, half the
compile, and no (N,N) allocation for large N. Equivalence pinned: auto==naive
(small N), auto==fast (large N). substructure+diagnostics 60 pass.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…ocityDF.r_t field (audit S1/A3/M4)

S1: KingProfile's direct constructor accepted an arbitrary r_t inconsistent
with c(W0) (the README's own KingProfile(W0=7,r_c=1,r_t=10) builds a
non-self-consistent, non-equilibrium model). For concrete, non-pinned inputs it
now warns (>5% deviation from r_c*xi_t); from_W0_rc derives r_t so never trips.
A3: KingVelocityDF.r_t was stored but never used (sigma^2 = GM/(9 r_c mu);
truncation comes from psi(xi)). Removed the field + param (no compat shim) and
updated every callsite (~18). end_to_end King IC made self-consistent.

Design (flagged for CHECKPOINT 2): WARN (not raise) on inconsistent r_t.
NOTE: ~6 tests deliberately construct inconsistent King ICs (e.g. the r_t-
truncation test) and now emit the warning (non-failing) -- follow-up to
suppress/assert. RED -> GREEN; affected suites 170 pass.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
protocols.py:54 and kinematics/api.py:53 were two structurally identical
runtime_checkable VelocityDF Protocols — a drift hazard. kinematics.api now
imports + re-exports the canonical progenax.protocols.VelocityDF (no importer
referenced the api.py copy). Pinned: api.VelocityDF IS protocols.VelocityDF.
protocols + api + rotation 34 pass.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…-level caveat (audit S2)

The default EFF DF (gamma=3, sharp truncation) is knowingly ~8% sub-virial
(Eddington inversion of a truncated rho with rho(r_t)>0 — disclosed in the
module docstring, but the DEFAULT config is the broken case and nothing pinned
it, so a regression to -15% would have passed). Measured-first: Q=0.458+/-0.0004
(N=20000, seeds 0/1/2). Frozen band 0.45<Q<0.47 pins the ~8% offset and catches
both a -15% regression (Q<=0.425) and a vanished offset (Q>=0.49). Also surfaced
the caveat at the EFFVelocityDF constructor docstring (was module-level only).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
The unit tier (~900 tests) ran in ONE process and OOM-killed the 2-core/7 GB
runner (exit 143, tests passing then SIGTERM at ~52%). Split into unit-a (imf),
unit-b (profiles+binaries), unit-c (catch-all: tests/unit minus those) -- ~300
each in a fresh process, so cumulative JAX memory resets. unit-c uses --ignore
so a new tests/unit subdir is still covered without editing the matrix.
Partition verified locally: 281+318+305 = 904 = whole unit (not-slow).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…IMF/EnvironmentIMF claims (audit R7)

15 documented symbols (PowerLawIMF..MassDependentBinaryFraction +
UniformSphereProfile) were promised by the docs but not exported from
progenax.__init__. Added them + __all__ entries. IGIMF/EnvironmentIMF NEVER
existed as classes (the environment-dependent IMF is the functional
BirthEnvironment + env_to_imf_params API, Marks+2012/Jerabkova+2018 alpha3
relations, NOT galaxy-wide IGIMF integration). Purged the phantom CLASS mentions
from CLAUDE.md (module table, test desc, exports list) and cluster/__init__.py;
legitimate Jerabkova-2018 IGIMF-relation citations in science pages kept.
RED 15 -> GREEN; documented-api + public-api + protocols pass.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…amples smoke test (audit R7/D3/D4/H2)

README taught a DELETED API (TwoComponentConfig/generate_two_component_cluster),
advertised phantom IGIMF/EnvironmentIMF classes + apply_osipkov_merritt(), had
wrong kwargs (two_body_kepler m1/m2, KeplerElements.to_state['position']),
reused PRNG keys, conda install, and stale LOC/test counts. Rewritten against
reality: MultiComponentCluster + Michie + LIMEPY in the feature tables,
anisotropy_radius constructor arg, KingVelocityDF without r_t, uv-first
side-by-side-checkout install, not-on-PyPI banner, counts dropped (point to CI),
'9 protocols'. New tests/integration/test_readme_examples.py executes EVERY
python block (RED: TwoComponentConfig ImportError -> GREEN: 9 blocks pass).
installation.md: split-repo clone, real extras (dev/experimental/diagnostics),
split-key smoke test (was the reused-key anti-pattern).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…t D5/H4)

ic-redesign-history.md claimed 'pip install progenax-legacy still works' — it
does not exist (no PyPI package, no git tag/branch, no ../legacy/progenax-legacy;
../legacy has only gravax-legacy + stellax-legacy). Replaced with the honest
path: check out the pre-redesign commit from git history (redesign documented in
the development log). Zero progenax-legacy hits remain in live docs.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…cation, segregation, energy-budget, subgroup virial (audit S3/S4/S6/S10/S16/S15)

- rotation.py (S3): module + both transforms now state they are KINEMATIC
  OVERLAYS that inject KE + L_z -> NOT stationary (Q>0.5); an isotropic rescale
  can't restore stationarity. (S15) concrete zero axis now RAISES (was a silent
  no-op under a stale 'result will be NaN' comment) via _normalized_rotation_axis
  + 2 tests.
- kinematics/api.py (S3): Stage-3 target_Q note — isotropic rescale does not
  remove the rotation-injected L_z, so rescaling-to-0.5 is not a fix.
- tidal.py (S4): survivors are super-virial w.r.t. the truncated potential
  (some formally unbound near r_t); re-virialize or use King/LIMEPY.
- cluster/mass_segregation.py (S6): segregated mass-weighted density != parent
  -> self-consistent potential differs; finalize with a global virial rescale.
- binaries/diagnostics.py (S10): Q_resolved is DEFLATED (~0.31), not inflated;
  softening does NOT soften E_internal (S18).
- dynamics/virial.py (S16): per-group W_j is origin-DEPENDENT for subgroups;
  positions must be pre-centered on the cluster COM.

180 affected tests pass (incl. 2 new S15 zero-axis tests).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…ng (audit T5/L3/D6)

tests/README.md said 874, CLAUDE.md said 1163 — three documents, three numbers.
Updated both to the current released-core count (~1243: unit 956, integration 43,
validation 244; FAST/not-slow ~1192) and added 'see CI for the live count' so the
numbers stop rotting. STATUS.md count refreshed at the final checkpoint.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
drannarosen and others added 6 commits June 12, 2026 16:36
…eel metadata (audit D1/C3)

No LICENSE file existed and the wheel METADATA had no license/author/classifier/
URL — the README's bare 'MIT' was an unenforceable claim. Per Anna's call the
license is APACHE-2.0 (not MIT): added the full Apache 2.0 LICENSE (Copyright
2026 Anna Rosen), license='Apache-2.0' + license-files + authors + keywords +
classifiers + [project.urls] in pyproject, and fixed the MIT->Apache claims in
README + docs/website/myst.yml. Verified the built wheel METADATA carries
License-Expression: Apache-2.0, License-File, Author-email, Classifiers, and the
Repository URL, with no experimental/gravoturb leakage.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…ctionable errors (audit R9)

progenax.diagnostics imported numpy+scipy eagerly (substructure.py, mass_
segregation.py, pulled in by diagnostics/__init__.py), but neither was a declared
dependency -> 'from progenax.diagnostics import compute_q_parameter' (and even
'import progenax.diagnostics') exploded in a clean install. Moved numpy/scipy
behind a lazy _diagnostics_deps() helper (from __future__ import annotations so
the np.ndarray annotations don't need np at def time); 'import progenax.diagnostics'
+ the JAX-native q_approx now work without scipy, and the scipy-backed estimators
raise an ImportError naming 'progenax[diagnostics]'. Added the [diagnostics] extra
(numpy>=1.24, scipy>=1.11). RED (import crash) -> GREEN; diagnostics+substructure
62 pass; subprocess test masks scipy.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…oot (audit M6/T3/R1)

- wheel-smoke job: build the wheel, install in a CLEAN venv with only public
  jaxstro, import progenax + progenax.diagnostics (no scipy present -> exercises
  the R9 lazy import). Wired into the 'tests' aggregator's needs.
- physics-validation.yml: nightly version-matrix (3.10 + 3.13) running a memory-
  safe core slice (imf + profiles) -> covers requires-python>=3.10 (T3).
- OOM root cause: the unit-b shard kept getting SIGTERM (exit 143) on the 7 GB
  runner because JAX caches every compiled executable and the diffrax-ODE
  profile/DF families accumulate unboundedly. conftest now clears the JAX cache
  between test MODULES in CI (gated on $CI; PROGENAX_CLEAR_JAX_CACHES=1 locally)
  so memory is bounded. Also isolated the two heaviest modules (imf, profiles)
  into their own shards; unit-c is the catch-all.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Per-finding summary (R1-R10 + S/J/A/T/D handled) with commit refs, plus the
known-limitations/deferred section (R2 pip-installability strategy, A2 units
policy, R5 two-sided stretch). The R2 jaxstro-publish-vs-vendor decision is
flagged for Anna, not implemented unilaterally.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…x arc

Per-finding before/after evidence table (R1-R10 + S/J/A/T/D), lessons learned,
and the open Anna-decision list (R2 jaxstro strategy, merge/PR, deferred A2/
two-sided-stretch). STATUS next: line updated to reflect the completed arc +
the two blocking Anna decisions.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…r startrax

The D&K91 main-sequence relation is a static ZAMS approximation; flag that it
will be replaced by the startrax package (first Tout+1996 ZAMS radii, then MIST
and Hurley+2000 SSE tracks with real mass- and age-dependent stellar evolution).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: bc98083997

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".


## Figure

:::{figure} figures/cross_engine.png

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Commit the referenced science-demo figures

The new Science demos pages now render {figure} directives for figures/cross_engine.png, halo_core.png, and the IMF/equipartition PNGs, but I checked rg --files docs/website/60-science-demos and only the three Markdown files are present—there is no figures/ directory. In a docs build or published site this leaves every demo page with missing/broken images despite the text claiming the figures are embedded, so please add the generated PNGs under docs/website/60-science-demos/figures/ or remove the directives.

Useful? React with 👍 / 👎.

@drannarosen
drannarosen merged commit 7d1f402 into main Jun 13, 2026
9 checks passed
@drannarosen
drannarosen deleted the fix/prerelease-audit branch June 13, 2026 01:37
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant