<?xml version="1.0" encoding="UTF-8"?><rss version="2.0" xmlns:content="http://purl.org/rss/1.0/modules/content/"><channel><title>Numra changelog</title><description>Release notes for the Numra Rust numerical-methods workspace.</description><link>https://numra-rs.org/</link><language>en-us</language><item><title>Numra Unreleased</title><link>https://numra-rs.org/changelog#unreleased---unreleased/</link><guid isPermaLink="true">https://numra-rs.org/changelog#unreleased---unreleased/</guid><description>Release notes for Numra Unreleased (Unreleased).</description><content:encoded/></item><item><title>Numra 0.1.5</title><link>https://numra-rs.org/changelog#015---2026-05-22/</link><guid isPermaLink="true">https://numra-rs.org/changelog#015---2026-05-22/</guid><description>Release notes for Numra 0.1.5 (2026-05-22).</description><pubDate>Fri, 22 May 2026 00:00:00 GMT</pubDate><content:encoded>&lt;h3 id=&quot;added&quot;&gt;Added&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;F-IC-SENS-MASS-SURFACE (additive, non-breaking)&lt;/strong&gt; — &lt;code&gt;numra-ode&lt;/code&gt;: &lt;code&gt;ParametricOdeSystem&lt;/code&gt; gains the four-method mass-matrix surface mirroring &lt;code&gt;OdeSystem&lt;/code&gt; — &lt;code&gt;has_mass_matrix() -&gt; bool&lt;/code&gt; (default &lt;code&gt;false&lt;/code&gt;), &lt;code&gt;mass_matrix(&amp;#x26;self, mass: &amp;#x26;mut [S])&lt;/code&gt; (default fills row-major identity, length &lt;code&gt;n²&lt;/code&gt;), &lt;code&gt;is_singular_mass() -&gt; bool&lt;/code&gt; (default &lt;code&gt;false&lt;/code&gt;), &lt;code&gt;algebraic_indices() -&gt; Vec&amp;#x3C;usize&gt;&lt;/code&gt; (default empty) — plus &lt;code&gt;is_autonomous() -&gt; bool&lt;/code&gt; (default &lt;code&gt;false&lt;/code&gt;). Defaults reproduce the pre-0.1.5 implicit behavior exactly: every existing implementor (&lt;code&gt;AugmentedSystem&lt;/code&gt;, &lt;code&gt;ClosureSystem&lt;/code&gt;, &lt;code&gt;ParametricMOLSystem2D/3D&lt;/code&gt;, &lt;code&gt;IcAsParametric&lt;/code&gt;, the &lt;code&gt;&amp;#x26;T&lt;/code&gt; blanket impl, every user-defined external impl) compiles unchanged with no source-level changes. The &lt;code&gt;&amp;#x26;T&lt;/code&gt; blanket impl forwards the new methods so &lt;code&gt;&amp;#x26;Sys&lt;/code&gt; carries M into &lt;code&gt;solve_forward_sensitivity&lt;/code&gt; without ownership transfer. Foundation Spec &lt;strong&gt;§3.8 added&lt;/strong&gt; (was missing for &lt;code&gt;ParametricOdeSystem&lt;/code&gt; — a documentation drift this entry closes); §3.3 “Mass matrix as a first-class composable” deferred bullet updated to note partial concretization; §6 #14 records the decision; §7 #3 updated (split into two independent sub-questions with separate triggers: identity-default removal vs DaeSystem peer trait — see §7 #3); §7 #9 added (IC manifold-projection follow-up, four-axis design space).&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&quot;fixed&quot;&gt;Fixed&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;F-IC-SENS-MASS-SURFACE (correctness fix)&lt;/strong&gt; — &lt;code&gt;numra-ode&lt;/code&gt;: &lt;code&gt;solve_initial_condition_sensitivity{,_with}&lt;/code&gt; now honors the host &lt;code&gt;OdeSystem&lt;/code&gt;’s mass matrix. &lt;strong&gt;Closes a correctness gap in 0.1.4’s F-IC-SENS:&lt;/strong&gt; the &lt;code&gt;IcAsParametric&lt;/code&gt; wrapper introduced in 0.1.4 (§6 #13) silently discarded the host’s &lt;code&gt;has_mass_matrix&lt;/code&gt; / &lt;code&gt;mass_matrix&lt;/code&gt; / &lt;code&gt;is_singular_mass&lt;/code&gt; / &lt;code&gt;algebraic_indices&lt;/code&gt; / &lt;code&gt;is_autonomous&lt;/code&gt; declarations when re-presenting the system as &lt;code&gt;ParametricOdeSystem&lt;/code&gt;, so the augmented variational integration ran against an identity mass matrix regardless of what the user’s &lt;code&gt;OdeSystem&lt;/code&gt; declared. Fixed at two boundaries: (i) &lt;code&gt;IcAsParametric&lt;/code&gt; now forwards all five methods from the wrapped &lt;code&gt;OdeSystem&lt;/code&gt; into the now-richer &lt;code&gt;ParametricOdeSystem&lt;/code&gt;; (ii) &lt;code&gt;AugmentedSystem&lt;/code&gt;’s &lt;code&gt;OdeSystem&lt;/code&gt; impl lifts the host’s &lt;code&gt;N × N&lt;/code&gt; mass matrix block-diagonally onto the augmented &lt;code&gt;(N · (1 + N_s)) × (N · (1 + N_s))&lt;/code&gt; state, with &lt;code&gt;(N_s + 1)&lt;/code&gt; copies of &lt;code&gt;M&lt;/code&gt; on the diagonal (one for the state block, one per sensitivity-column block — the variational equation &lt;code&gt;M · (∂y/∂p_k)&apos; = J_y · (∂y/∂p_k) + J_p_{:,k}&lt;/code&gt; places each sensitivity column in the same M-weighted space as the state). Host algebraic indices lift correspondingly: host index &lt;code&gt;i&lt;/code&gt; maps to augmented indices &lt;code&gt;{i, N+i, 2N+i, …, N_s·N+i}&lt;/code&gt;. The &lt;code&gt;has_mass_matrix() = false&lt;/code&gt; branch of the lifted &lt;code&gt;mass_matrix&lt;/code&gt; preserves the semantic distinction between “host has M, M happens to be identity” and “host has no M” — downstream solvers (Radau5, BDF) dispatch on it.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Precisely-supported affected-scope claim for 0.1.4&lt;/strong&gt; (verified empirically against &lt;code&gt;HEAD:numra-ode/src/sensitivity.rs:152-281&lt;/code&gt; at commit &lt;code&gt;98ee6ca&lt;/code&gt;, 0.1.4 head: the pre-extension trait body contains exactly &lt;code&gt;n_states&lt;/code&gt;, &lt;code&gt;n_params&lt;/code&gt;, &lt;code&gt;params&lt;/code&gt;, &lt;code&gt;rhs_with_params&lt;/code&gt;, &lt;code&gt;rhs&lt;/code&gt;, &lt;code&gt;jacobian_y&lt;/code&gt;, &lt;code&gt;jacobian_p&lt;/code&gt;, &lt;code&gt;initial_sensitivity&lt;/code&gt;, &lt;code&gt;has_analytical_jacobian_y&lt;/code&gt;, &lt;code&gt;has_analytical_jacobian_p&lt;/code&gt; and &lt;strong&gt;none&lt;/strong&gt; of the M-surface methods):&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Users of &lt;code&gt;solve_forward_sensitivity{,_with}&lt;/code&gt; with &lt;code&gt;ParametricOdeSystem&lt;/code&gt; implementors: &lt;strong&gt;unaffected&lt;/strong&gt; — the pre-0.1.5 trait had no mass-matrix surface, so users had no way to declare M through this path; the missing delegation in &lt;code&gt;AugmentedSystem&lt;/code&gt; was a &lt;em&gt;compile-time&lt;/em&gt; fact (the problem couldn’t be expressed), not a runtime numerical failure.&lt;/li&gt;
&lt;li&gt;Users of &lt;code&gt;solve_initial_condition_sensitivity{,_with}&lt;/code&gt; with identity-M &lt;code&gt;OdeSystem&lt;/code&gt; (the common ODE case): &lt;strong&gt;unaffected&lt;/strong&gt; — the silently-dropped M was identity anyway.&lt;/li&gt;
&lt;li&gt;Users of &lt;code&gt;solve_initial_condition_sensitivity{,_with}&lt;/code&gt; with &lt;strong&gt;non-identity M&lt;/strong&gt; (e.g. finite-element-discretized PDEs): received &lt;code&gt;Φ&lt;/code&gt; off by an M⁻¹ factor in the matrix-exponential’s argument — &lt;code&gt;Φ_bug(t) = expm(A·t)&lt;/code&gt; instead of the analytical &lt;code&gt;Φ_truth(t) = expm(M⁻¹·A·t)&lt;/code&gt;. Pinned by the pre-fix-bug numerical value &lt;code&gt;exp(-2) ≈ 0.13534&lt;/code&gt; in &lt;code&gt;numra-ode/tests/ic_sensitivity_mass_matrix.rs::path_b_non_identity_mass_ic_sensitivity&lt;/code&gt;, derived in closed form: the test uses the scalar DAE &lt;code&gt;2y&apos; = -y&lt;/code&gt; (M = 2, A = -1); M-blind integration drops M and runs &lt;code&gt;y&apos; = -y&lt;/code&gt;, giving &lt;code&gt;y_bug(t) = exp(-t)&lt;/code&gt; and &lt;code&gt;Φ_bug(t) = exp(-t)&lt;/code&gt;, versus truth &lt;code&gt;Φ_truth(t) = exp(-t/2)&lt;/code&gt;. At t = 2: &lt;code&gt;Φ_bug = exp(-2) ≈ 0.13534&lt;/code&gt;, &lt;code&gt;Φ_truth = exp(-1) ≈ 0.36788&lt;/code&gt;. The scalar choice makes the closed-form derivation visible; higher-dimensional non-identity-M systems exhibit the same shape (matrix-exponential argument off by M⁻¹) but produce no scalar reduction. Revert-confirm protocol verified: revert the wiring → got &lt;code&gt;0.13534&lt;/code&gt;, restore → got &lt;code&gt;0.36788 = exp(-1)&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;Users with &lt;strong&gt;singular M&lt;/strong&gt; (semi-explicit index-1 DAEs): received variational integrations that treated algebraic rows as differential — wrong dynamics, not just wrong scaling — pinned by the pre-fix-bug numerical value &lt;code&gt;y₂(2) ≈ 0.73039&lt;/code&gt; (= &lt;code&gt;(1/2)·(sin(2) − cos(2) + e⁻²)&lt;/code&gt;, the closed-form solution to &lt;code&gt;y₂&apos; + y₂ = sin(t)&lt;/code&gt; that the M-blind integration actually solves) in &lt;code&gt;path_b_singular_mass_dae_ic_sensitivity&lt;/code&gt;.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;strong&gt;Upgrade guidance, severity-split:&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Upgrade recommended&lt;/strong&gt; for any 0.1.4 user of &lt;code&gt;solve_initial_condition_sensitivity{,_with}&lt;/code&gt; with non-identity &lt;code&gt;M&lt;/code&gt;: the pre-fix result is &lt;em&gt;quantitatively&lt;/em&gt; wrong (an &lt;code&gt;M⁻¹&lt;/code&gt;-factor distortion of Φ), not qualitatively. Existing analyses may be approximately correct depending on how the user used Φ.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Upgrade required&lt;/strong&gt; for any 0.1.4 user of &lt;code&gt;solve_initial_condition_sensitivity{,_with}&lt;/code&gt; with &lt;strong&gt;singular &lt;code&gt;M&lt;/code&gt; (DAE)&lt;/strong&gt;: the pre-fix result is &lt;em&gt;qualitatively&lt;/em&gt; wrong — algebraic constraints were treated as differential equations, producing trajectories that satisfy the wrong dynamical system entirely. Any downstream analysis using the pre-fix Φ or state trajectory on a singular-M system should be re-run on 0.1.5.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;strong&gt;Yank decision: 0.1.4 is not yanked.&lt;/strong&gt; Bounded scope (specific feature path × specific user category); academic citation record references 0.1.4 immutably (Zenodo DOI back-port landed in &lt;code&gt;47d25a9&lt;/code&gt;); 0.1.5 supersedes with a clear CHANGELOG connection.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Out-of-scope, tracked separately:&lt;/strong&gt; the finite-difference IC perturbation in &lt;code&gt;fd_jacobian_y_inline&lt;/code&gt; (sensitivity.rs:394) does not project the perturbed initial condition onto the constraint manifold for singular-M systems — post-fix DAE sensitivity is “wrong sensitivity-w.r.t.-perturbed-IC because the perturbed IC may drift off-manifold, correctly integrated because the variational integration is now M-aware” — strictly improved over 0.1.4’s both-wrong state, but not manifold-projected. Tracked as Foundation Spec §7 #9 (four-axis design space: when to project, in which direction, via which API, under which correctness criterion — linear vs nonlinear constraints have different criteria).&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Six tests&lt;/strong&gt; pin the fix end-to-end at &lt;code&gt;numra-ode/tests/ic_sensitivity_mass_matrix.rs&lt;/code&gt; plus the symmetric structural test at &lt;code&gt;numra-ode/src/sensitivity.rs::tests::ic_as_parametric_forwards_mass_surface&lt;/code&gt; (lives in the lib because &lt;code&gt;IcAsParametric&lt;/code&gt; is private): two Path A analytic-oracle tests (post-fix only — compile-time fact, would not compile against 0.1.4), two Path B analytic-oracle tests with falsifiability-gate assertions on the pre-fix-bug values (runtime fact, exact numerical match verified), and two structural unit tests completing the boundary-symmetry pair. Revert-confirm-restore protocol applied: temporarily reverting &lt;code&gt;IcAsParametric::has_mass_matrix&lt;/code&gt; / &lt;code&gt;mass_matrix&lt;/code&gt; / &lt;code&gt;is_singular_mass&lt;/code&gt; / &lt;code&gt;algebraic_indices&lt;/code&gt; to defaults → Path B tests fail with the exact predicted pre-fix-bug numerical values (&lt;code&gt;exp(-2)&lt;/code&gt; for non-identity-M, &lt;code&gt;0.73039&lt;/code&gt; for singular-M DAE) → restore wiring → tests pass. A seventh sanity test (&lt;code&gt;augmented_system_identity_m_default_path_unchanged&lt;/code&gt;) pins the identity-M path so the semantic distinction between “host has no M” and “host has identity M” survives future refactors.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Saved memory&lt;/strong&gt; (&lt;code&gt;feedback_wrapper_boundary_symmetry&lt;/code&gt;): wrappers between foundation types verify boundary discipline in &lt;em&gt;both directions&lt;/em&gt; (outward: no leakage; inward: no loss of foundation capabilities). F-IC-SENS’s 0.1.4 boundary check was outward-only; this entry closes the inward gap and records the lesson for future wrappers.&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&quot;changed&quot;&gt;Changed&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Workspace version 0.1.4 → 0.1.5 prep&lt;/strong&gt; (additive correctness release): workspace &lt;code&gt;[workspace.package].version&lt;/code&gt; and all 20 internal-dependency pins in &lt;code&gt;[workspace.dependencies]&lt;/code&gt; bumped in lockstep (all 21 publishable crates inherit via &lt;code&gt;version.workspace = true&lt;/code&gt;); &lt;code&gt;CITATION.cff&lt;/code&gt; &lt;code&gt;version:&lt;/code&gt; field 0.1.4 → 0.1.5, &lt;code&gt;date-released&lt;/code&gt; 2026-05-18 → 2026-05-22. The version-DOI block in &lt;code&gt;identifiers&lt;/code&gt; is intentionally left at 0.1.4 values — Zenodo mints the 0.1.5 DOI on GitHub Release, after which a follow-up &lt;code&gt;docs(citation): back-port Zenodo DOIs for v0.1.5&lt;/code&gt; commit fills it in. Same pattern as &lt;code&gt;5aa85d0&lt;/code&gt; (0.1.2) / &lt;code&gt;8b3ec0a&lt;/code&gt; (0.1.3) / &lt;code&gt;47d25a9&lt;/code&gt; (0.1.4). &lt;strong&gt;No existing public signature changes meaning&lt;/strong&gt;: every pre-0.1.5 &lt;code&gt;ParametricOdeSystem&lt;/code&gt; implementor remains source-compatible with the new trait default-impl additions; &lt;code&gt;solve_forward_sensitivity{,_with}&lt;/code&gt; users are unaffected at runtime; &lt;code&gt;solve_initial_condition_sensitivity{,_with}&lt;/code&gt; users see only the corrected M-aware variational integration (no change for identity-M; correctness restoration for non-identity-M and singular-M).&lt;/li&gt;
&lt;/ul&gt;</content:encoded></item><item><title>Numra 0.1.4</title><link>https://numra-rs.org/changelog#014---2026-05-18/</link><guid isPermaLink="true">https://numra-rs.org/changelog#014---2026-05-18/</guid><description>Release notes for Numra 0.1.4 (2026-05-18).</description><pubDate>Mon, 18 May 2026 00:00:00 GMT</pubDate><content:encoded>&lt;h3 id=&quot;added&quot;&gt;Added&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;F-IC-SENS (additive)&lt;/strong&gt; — &lt;code&gt;numra-ode&lt;/code&gt;: first-class initial-condition sensitivity. New public functions &lt;code&gt;solve_initial_condition_sensitivity&lt;/code&gt; and &lt;code&gt;solve_initial_condition_sensitivity_with&lt;/code&gt;, plus the result type &lt;code&gt;StateTransitionResult&amp;#x3C;S&gt;&lt;/code&gt;, compute the state-transition matrix &lt;code&gt;Φ(t) = ∂y(t)/∂y₀&lt;/code&gt; of the linearised flow &lt;code&gt;dy/dt = f(t, y)&lt;/code&gt; over any [&lt;code&gt;Solver&lt;/code&gt;]. Implemented as a private &lt;code&gt;IcAsParametric&amp;#x3C;&apos;a, S, Sys&gt;&lt;/code&gt; wrapper that reinterprets a plain [&lt;code&gt;OdeSystem&amp;#x3C;S&gt;&lt;/code&gt;] as a [&lt;code&gt;ParametricOdeSystem&amp;#x3C;S&gt;&lt;/code&gt;] with &lt;code&gt;n_params := n_states&lt;/code&gt;, &lt;code&gt;J_p ≡ 0&lt;/code&gt;, and &lt;code&gt;S(t₀) = I_N&lt;/code&gt;, then delegates to the existing &lt;code&gt;solve_forward_sensitivity&lt;/code&gt; / &lt;code&gt;AugmentedSystem&lt;/code&gt; machinery — &lt;strong&gt;no fork of the variational integrator&lt;/strong&gt;. The Phase-0 discovery established two architecturally available routes (variational seeding vs AD-through-y₀); the AD-through-y₀ route would require either making &lt;code&gt;Dual&amp;#x3C;f64&gt;: Scalar&lt;/code&gt; with dual-aware step-control norms across every solver (the existing &lt;code&gt;Dual&amp;#x3C;S&gt;::PartialOrd&lt;/code&gt; is primal-only, so the tangent — the STM itself — would integrate with no adaptive error control) or a fork of the integrator. Both are forbidden by the foundation/composability story for this scope; the variational route is the architecturally correct choice given the actual codebase, not a lesser-route-for-task-scope compromise. The would-be unification of Features 1+2 via AD-through-y₀ remains a sensible future foundation-design pass (Foundation Spec §7 #1’s per-call/per-system/global question) but is not preempted by this PR. &lt;strong&gt;Constraint-1 seam&lt;/strong&gt;: the &lt;code&gt;ParametricOdeSystem&lt;/code&gt; length assertion at &lt;code&gt;sensitivity.rs:852-858&lt;/code&gt; requires a &lt;code&gt;params()&lt;/code&gt; slice of length &lt;code&gt;n_params&lt;/code&gt;; the IC wrapper holds a zeroed dummy &lt;code&gt;Vec&amp;#x3C;S&gt;&lt;/code&gt; of length &lt;code&gt;N&lt;/code&gt; for this purpose, fully encapsulated — the user never constructs, passes, or reasons about a parameter vector, and no parameter concept appears in &lt;code&gt;solve_initial_condition_sensitivity&lt;/code&gt; / &lt;code&gt;StateTransitionResult&lt;/code&gt;’s signatures or rustdoc examples. &lt;strong&gt;Invariant IC-NO-PARAM-READ&lt;/strong&gt; is stated as a mathematical fact: &lt;code&gt;IcAsParametric::rhs_with_params(t, y, p, dy)&lt;/code&gt; provably never reads &lt;code&gt;p&lt;/code&gt; because the wrapped &lt;code&gt;OdeSystem::rhs(t, y, dy)&lt;/code&gt; has no &lt;code&gt;p&lt;/code&gt; argument; therefore &lt;code&gt;∂f/∂p ≡ 0&lt;/code&gt; is mathematically exact, the wrapper’s &lt;code&gt;jacobian_p&lt;/code&gt; writing zeros with &lt;code&gt;has_analytical_jacobian_p() = true&lt;/code&gt; is a truthful analytical declaration, and the AugmentedSystem debug consistency check at &lt;code&gt;sensitivity.rs:373&lt;/code&gt; is correctly suppressed by the flag (not silenced). The invariant is pinned by &lt;code&gt;ic_dummy_params_are_unread&lt;/code&gt; (&lt;code&gt;numra-ode/tests/ic_sensitivity_regression.rs&lt;/code&gt;), which runs the variational integration three times against the same wrapper shape with dummy &lt;code&gt;params() = [0.0]&lt;/code&gt;, &lt;code&gt;[f64::NAN]&lt;/code&gt;, &lt;code&gt;[f64::INFINITY]&lt;/code&gt; — bit-for-bit identical results across all three prove the RHS does not consume &lt;code&gt;p&lt;/code&gt; (if it did, NaN/∞ would propagate through the augmented RHS into &lt;code&gt;y(t)&lt;/code&gt; and &lt;code&gt;Φ(t)&lt;/code&gt;). Same &lt;code&gt;revert-confirm-restore&lt;/code&gt; falsifiability shape as F-SOLVER-FIELDS’s pinning test. &lt;strong&gt;Specialization-vs-core regression&lt;/strong&gt; pinned by &lt;code&gt;ic_matches_hand_seeded_variational&lt;/code&gt;: asserts bit-for-bit equality between the IC API result and a hand-built &lt;code&gt;ParametricOdeSystem&lt;/code&gt; configured with &lt;code&gt;N_s = N&lt;/code&gt;, &lt;code&gt;J_p ≡ 0&lt;/code&gt;, &lt;code&gt;S₀ = I&lt;/code&gt; (the same shape the wrapper produces). This test passes before the analytic-oracle tests (sequencing established during Phase 2 implementation) so wrapper-correctness is isolated from variational-math correctness — a Phase-2 failure would be diagnostic rather than ambiguous. Analytic-oracle tests &lt;code&gt;scalar_linear_matches_exp_minus_kt&lt;/code&gt; and &lt;code&gt;linear_2x2_matches_expm_a_t&lt;/code&gt; verify &lt;code&gt;Φ(t) = exp(-k t)&lt;/code&gt; on &lt;code&gt;dy/dt = -k y&lt;/code&gt; and &lt;code&gt;Φ(t) = expm(A·t)&lt;/code&gt; on a 2×2 system with closed-form solution. &lt;strong&gt;Boundary check (deliverable E)&lt;/strong&gt;: the public API surface mentions only &lt;code&gt;t&lt;/code&gt;, &lt;code&gt;y&lt;/code&gt;, &lt;code&gt;phi&lt;/code&gt;, &lt;code&gt;phi_ij&lt;/code&gt;, &lt;code&gt;phi_column&lt;/code&gt;, &lt;code&gt;final_phi&lt;/code&gt;, &lt;code&gt;final_y&lt;/code&gt;, &lt;code&gt;dim&lt;/code&gt;, &lt;code&gt;len&lt;/code&gt;, &lt;code&gt;is_empty&lt;/code&gt;, &lt;code&gt;success&lt;/code&gt;, &lt;code&gt;message&lt;/code&gt;, &lt;code&gt;stats&lt;/code&gt;, with parameter names &lt;code&gt;i&lt;/code&gt; (time index), &lt;code&gt;row&lt;/code&gt;, &lt;code&gt;col&lt;/code&gt; (state-transition matrix indices in &lt;code&gt;[0, N)&lt;/code&gt;); no name in the public API mentions parameter, sensitivity, p, sigma, uncertainty, measurement, or any application-domain concept; &lt;code&gt;StateTransitionResult&amp;#x3C;S&gt;&lt;/code&gt; holds the underlying &lt;code&gt;SensitivityResult&amp;#x3C;S&gt;&lt;/code&gt; in a private field with no &lt;code&gt;Deref&lt;/code&gt; impl and no re-export, so no &lt;code&gt;*_param&lt;/code&gt;-style accessor leaks through. The rustdoc worked example is &lt;code&gt;dy/dt = -k y ⇒ Φ(t) = exp(-k t)&lt;/code&gt;, scalar, no parameters present.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;F-AUTODIFF-JY (additive, behind cargo feature &lt;code&gt;autodiff&lt;/code&gt;)&lt;/strong&gt; — &lt;code&gt;numra-ode&lt;/code&gt;: an [&lt;code&gt;OdeSystem&amp;#x3C;S&gt;&lt;/code&gt;] adapter &lt;code&gt;AutodiffJacobianSystem&amp;#x3C;S, F&gt;&lt;/code&gt; whose &lt;code&gt;jacobian()&lt;/code&gt; is computed by forward-mode automatic differentiation through &lt;code&gt;numra-autodiff::Dual&amp;#x3C;S&gt;&lt;/code&gt;. The adapter owns a &lt;code&gt;Dual&amp;#x3C;S&gt;&lt;/code&gt;-typed RHS closure and a pair of pre-allocated dual buffers (&lt;code&gt;RefCell&amp;#x3C;Vec&amp;#x3C;Dual&amp;#x3C;S&gt;&gt;&gt;&lt;/code&gt; × 2), so per-step cost is &lt;strong&gt;zero allocation&lt;/strong&gt; plus &lt;strong&gt;N + 1&lt;/strong&gt; &lt;code&gt;Dual&amp;#x3C;S&gt;&lt;/code&gt;-RHS evaluations per integrator step (1 for the primal RHS, &lt;code&gt;N&lt;/code&gt; for the &lt;code&gt;N&lt;/code&gt;-direction Jacobian sweep). The existing FD-default Jacobian path performs &lt;strong&gt;N + 1&lt;/strong&gt; &lt;code&gt;S&lt;/code&gt;-RHS evaluations; the AD adapter is therefore in the &lt;strong&gt;same complexity class&lt;/strong&gt; (&lt;code&gt;O(N)&lt;/code&gt; RHS evals per Jacobian), at a ~2× per-eval cost (each &lt;code&gt;Dual&amp;#x3C;S&gt;&lt;/code&gt; op carries one extra fused-multiply-add for the tangent), in exchange for a Jacobian accurate to round-off rather than &lt;code&gt;~sqrt(S::EPSILON) ≈ 1.5e-8&lt;/code&gt;. The accuracy delta materially benefits stiff implicit solvers (Radau5, BDF, ESDIRK*) whose Newton convergence depends on Jacobian fidelity. &lt;strong&gt;Behind cargo feature &lt;code&gt;autodiff&lt;/code&gt;&lt;/strong&gt; (default off): callers who do not opt in do not compile or pull &lt;code&gt;numra-autodiff&lt;/code&gt; and pay zero in compile time and binary size. The autodiff path is &lt;strong&gt;never silently substituted&lt;/strong&gt; — selection is explicit by constructor (&lt;code&gt;AutodiffJacobianSystem::new(rhs_dual, dim)&lt;/code&gt;); a user supplying analytical &lt;code&gt;J_y&lt;/code&gt; via &lt;code&gt;OdeSystem::jacobian&lt;/code&gt; override is entirely unaffected in behavior and performance. The adapter composes orthogonally with F-IC-SENS: passing an &lt;code&gt;AutodiffJacobianSystem&lt;/code&gt; to &lt;code&gt;solve_initial_condition_sensitivity&lt;/code&gt; obtains &lt;code&gt;Φ(t)&lt;/code&gt; with AD-exact &lt;code&gt;J_y&lt;/code&gt;. &lt;strong&gt;Correctness pinned&lt;/strong&gt; by &lt;code&gt;autodiff_jy_matches_analytic_jy_on_lotka_volterra&lt;/code&gt; (&lt;code&gt;numra-ode/tests/ic_sensitivity_autodiff.rs&lt;/code&gt;, &lt;code&gt;#[cfg(feature = &quot;autodiff&quot;)]&lt;/code&gt;): runs the IC API twice on Lotka-Volterra — once with a hand-written analytical Jacobian, once with &lt;code&gt;AutodiffJacobianSystem&lt;/code&gt; wrapping the same RHS — and asserts &lt;code&gt;Φ(t_f)&lt;/code&gt; agrees to &lt;code&gt;&amp;#x3C; 1e-8&lt;/code&gt; and &lt;code&gt;y(t_f)&lt;/code&gt; to &lt;code&gt;&amp;#x3C; 1e-9&lt;/code&gt;. Three additional unit tests (&lt;code&gt;ad_jacobian_2x2_nonlinear&lt;/code&gt;, &lt;code&gt;ad_rhs_matches_f64_primal&lt;/code&gt;, &lt;code&gt;ad_jacobian_reentrant&lt;/code&gt;) cover the adapter in isolation. &lt;strong&gt;Boundary check (deliverable E)&lt;/strong&gt;: &lt;code&gt;AutodiffJacobianSystem&amp;#x3C;S, F&gt;::new(rhs_dual, dim)&lt;/code&gt; and the &lt;code&gt;OdeSystem&amp;#x3C;S&gt;&lt;/code&gt; methods name only &lt;code&gt;t&lt;/code&gt;, &lt;code&gt;y&lt;/code&gt;, &lt;code&gt;dy&lt;/code&gt;, &lt;code&gt;jy&lt;/code&gt;, &lt;code&gt;dim&lt;/code&gt; — standard ODE / Jacobian vocabulary; the rustdoc worked example is the Lotka-Volterra RHS typed against &lt;code&gt;Dual&amp;#x3C;f64&gt;&lt;/code&gt;, no parameter / sigma / uncertainty / domain concept in the signature or example. &lt;strong&gt;Resolves Foundation Specification §7 #1&lt;/strong&gt; (open question “Autodiff integration shape — per-call adapter, per-system constructor, or global feature flag”) for the Jacobian-source axis: the answer this PR commits to is &lt;em&gt;per-system adapter (&lt;code&gt;OdeSystem&amp;#x3C;S&gt;&lt;/code&gt; impl, bound to the system once at construction) made opt-in via cargo feature gate&lt;/em&gt;. The three options in §7 #1 collapse: adapter-as-&lt;code&gt;OdeSystem&lt;/code&gt; (option a) is the API mechanism; per-system constructor (option b) is the entry-point shape; cargo-feature gate (option c) is the compile-time discoverability. §3.3’s deferred “Jacobian enum (AutoDiff | Analytical | FD) would change the trait” foundational change is &lt;strong&gt;not&lt;/strong&gt; preempted by this PR — the adapter composes below the &lt;code&gt;OdeSystem&lt;/code&gt; trait shape; if a future foundation-design pass adopts the trait-shape change, this adapter may be refactored or absorbed, but the public API surface today is not load-bearing on that future decision. The §7 #1 narrower scope (“every Jacobian/gradient call site”) is &lt;strong&gt;not&lt;/strong&gt; fully closed here — gradient-side wiring (numra-optim, numra-fit) remains open; only the &lt;code&gt;OdeSystem::jacobian&lt;/code&gt; axis concretises.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Foundation Specification §6 decision-log entry (13)&lt;/strong&gt; — pending SHA: 0.1.4 prep PR (will substitute the actual commit SHA at merge time, per the two-commit &lt;code&gt;&amp;#x3C;pending&gt;&lt;/code&gt; → SHA pattern established by §6 #12). Records the F-AUTODIFF-JY resolution above as the per-system-adapter-plus-cargo-feature answer to §7 #1’s Jacobian-source axis; §7 #1 is rewritten in the same PR to reflect the narrower remaining scope (gradient-side wiring open). The &lt;code&gt;Foundation Spec §3.3&lt;/code&gt; deferred Jacobian-enum question remains untouched by this entry.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Interop workflow 7&lt;/strong&gt; (&lt;code&gt;numra/tests/interop_workflows.rs&lt;/code&gt;): &lt;code&gt;workflow_ic_sensitivity_interp_integrate&lt;/code&gt; — solves the IC sensitivity equations for &lt;code&gt;dy/dt = -k y&lt;/code&gt;, extracts &lt;code&gt;Φ_{0,0}(t)&lt;/code&gt; across the trajectory, fits a &lt;code&gt;numra-interp::CubicSpline&lt;/code&gt;, and integrates the spline with &lt;code&gt;numra-integrate::quad&lt;/code&gt;. Verifies &lt;code&gt;StateTransitionResult&lt;/code&gt; flows into both &lt;code&gt;numra-interp&lt;/code&gt; and &lt;code&gt;numra-integrate&lt;/code&gt; without adapter code, returns &lt;code&gt;Result&amp;#x3C;(), NumraError&gt;&lt;/code&gt; via &lt;code&gt;?&lt;/code&gt; across three crate boundaries — closing &lt;strong&gt;composability contract item 5 (foundation-flowable output) + item 7 (interop test)&lt;/strong&gt; for the IC API. Analytical oracle: &lt;code&gt;∫_0^3 exp(-k t) dt = (1 - exp(-3k)) / k&lt;/code&gt;; assertion tolerance &lt;code&gt;&amp;#x3C; 1e-3&lt;/code&gt;.&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&quot;changed&quot;&gt;Changed&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Workspace version 0.1.3 → 0.1.4 prep&lt;/strong&gt; (additive minor release): workspace &lt;code&gt;[workspace.package].version&lt;/code&gt; and all 21 internal-dependency pins in the workspace &lt;code&gt;Cargo.toml&lt;/code&gt; bumped; &lt;code&gt;CITATION.cff&lt;/code&gt; &lt;code&gt;version:&lt;/code&gt; field bumped (date stamp deferred to the actual release-prep PR, per the established convention from &lt;code&gt;514fdf7&lt;/code&gt;). &lt;strong&gt;No existing public signature changes meaning&lt;/strong&gt;: &lt;code&gt;ParametricOdeSystem&lt;/code&gt;, &lt;code&gt;AugmentedSystem&lt;/code&gt;, &lt;code&gt;SensitivityResult&lt;/code&gt;, &lt;code&gt;solve_forward_sensitivity{,_with}&lt;/code&gt;, &lt;code&gt;ClosureSystem&lt;/code&gt;, &lt;code&gt;OdeSystem&lt;/code&gt; — all unchanged in shape and semantics; the 20 pre-existing sensitivity tests (10 unit + 10 regression) remain green (D5 baseline re-run during Phase 2 sign-off). &lt;code&gt;numra-ode&lt;/code&gt; adds the optional &lt;code&gt;numra-autodiff&lt;/code&gt; workspace-dep and a new &lt;code&gt;autodiff&lt;/code&gt; cargo feature (default off). The numra facade (&lt;code&gt;numra/src/lib.rs&lt;/code&gt;) re-exports &lt;code&gt;solve_initial_condition_sensitivity&lt;/code&gt;, &lt;code&gt;solve_initial_condition_sensitivity_with&lt;/code&gt;, and &lt;code&gt;StateTransitionResult&lt;/code&gt; at the top level, symmetric with the existing &lt;code&gt;solve_forward_sensitivity&lt;/code&gt; promotion.&lt;/li&gt;
&lt;/ul&gt;</content:encoded></item><item><title>Numra 0.1.3</title><link>https://numra-rs.org/changelog#013---2026-05-17/</link><guid isPermaLink="true">https://numra-rs.org/changelog#013---2026-05-17/</guid><description>Release notes for Numra 0.1.3 (2026-05-17).</description><pubDate>Sun, 17 May 2026 00:00:00 GMT</pubDate><content:encoded>&lt;h3 id=&quot;changed&quot;&gt;Changed&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;F-SOLVER-FIELDS (additive)&lt;/strong&gt; — &lt;code&gt;numra-ode&lt;/code&gt;: &lt;code&gt;SolverOptions&lt;/code&gt; gains two top-level &lt;code&gt;Option&amp;#x3C;usize&gt;&lt;/code&gt; fields with corresponding builders, &lt;code&gt;max_order&lt;/code&gt; and &lt;code&gt;min_order&lt;/code&gt;, which control the BDF adaptive-order window (clamped to &lt;code&gt;[1, MAX_ORDER=5]&lt;/code&gt;). Default &lt;code&gt;None&lt;/code&gt; preserves prior behavior exactly (BDF cap 5, floor 1) — every existing &lt;code&gt;Bdf::solve&lt;/code&gt; call site produces byte-identical results. The new fields are consumed by BDF and ignored by every other solver, matching the established “applicable to some solvers, ignored by the rest” pattern of &lt;code&gt;dense_output&lt;/code&gt; and &lt;code&gt;events&lt;/code&gt;. Closes the F-SOLVER-FIELDS functional gap: &lt;code&gt;Bdf::with_max_order&lt;/code&gt; / &lt;code&gt;Bdf::fixed_order&lt;/code&gt; were silently non-functional at the static &lt;code&gt;Solver::solve&lt;/code&gt; trait surface (the trait method discarded &lt;code&gt;self&lt;/code&gt; and re-instantiated &lt;code&gt;Bdf::new()&lt;/code&gt;); the wiring now flows through &lt;code&gt;SolverOptions&lt;/code&gt;. &lt;strong&gt;Also closes a documented Foundation Specification §3.7 spec-vs-reality drift&lt;/strong&gt;: the spec previously claimed BDF’s max order lived in &lt;code&gt;SolverOptions&lt;/code&gt; while reality had it on the &lt;code&gt;Bdf&lt;/code&gt; struct as a non-functional builder; the §3.7 paragraph is rewritten in the same PR to describe the now-real mechanism. Per Foundation Spec decision-log §6 #12, which records the rationale (option (b) of the original tension, top-level shape per §2.5’s centralized-configuration principle, sub-struct path explicitly deferred to a future foundation-design pass triggered by multi-family namespace pressure). Migration: &lt;code&gt;SolverOptions::default().max_order(2)&lt;/code&gt; caps BDF order; &lt;code&gt;.max_order(n).min_order(n)&lt;/code&gt; pins it once adaptive selection reaches &lt;code&gt;n&lt;/code&gt; (BDF always starts at order 1). Regression pinned by &lt;code&gt;numra-ode/tests/solver_options_order_regression.rs&lt;/code&gt; with revert-confirm-restore protocol applied — temporarily reverting the wiring in &lt;code&gt;bdf.rs::solve_internal&lt;/code&gt; makes the pinning test fail (&lt;code&gt;pinned == default&lt;/code&gt; n_accept proves the knob has no effect); restoring it makes the test pass (&lt;code&gt;pinned ≫ 5 × default&lt;/code&gt; proves the wiring is real).&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;F-OPTS&lt;/strong&gt; (rustdoc only, no behavior change): the five divergent solver-family options structs (&lt;code&gt;numra_sde::SdeOptions&lt;/code&gt;, &lt;code&gt;numra_fde::FdeOptions&lt;/code&gt;, &lt;code&gt;numra_ide::IdeOptions&lt;/code&gt;, &lt;code&gt;numra_dde::DdeOptions&lt;/code&gt;, &lt;code&gt;numra_spde::SpdeOptions&lt;/code&gt;) now carry paragraph-form rustdoc rationales explaining why each diverges from &lt;code&gt;numra_ode::SolverOptions&lt;/code&gt;, per Foundation Spec §2.5’s “Configuration objects are shared across solver families” principle (which explicitly names F-OPTS as this documentation follow-up at §2.5’s audit baseline). Per-family rationales: SDE/SPDE carry stochastic-noise time control plus &lt;code&gt;seed&lt;/code&gt; for reproducibility; FDE/IDE are inherently fixed-step (Caputo-derivative convolution memory; integral-kernel quadrature) so adaptive &lt;code&gt;rtol&lt;/code&gt;/&lt;code&gt;atol&lt;/code&gt; would be dead knobs; DDE adds discontinuity-propagation fields with no ODE analog and defaults &lt;code&gt;dense_output&lt;/code&gt; to &lt;code&gt;true&lt;/code&gt; because Method-of-Steps history interpolation requires it. Audit pass surfaced 2 additional sites beyond the entry’s named 3 (&lt;code&gt;DdeOptions&lt;/code&gt;, &lt;code&gt;SpdeOptions&lt;/code&gt;) — same audit-surfaces-more-than-named pattern as F-FD-STEP / F-CI-NODE20 / F-FD-CROSSCRATE / F-ERR (5-of-6 follow-ups now). Per-family claims pre-commit-verified against consuming solver code: SRA-family (&lt;code&gt;numra-sde/src/sra.rs:128,296&lt;/code&gt;) confirmed reading &lt;code&gt;options.rtol&lt;/code&gt;/&lt;code&gt;atol&lt;/code&gt; for adaptive error scaling; DDE &lt;code&gt;dense_output: true&lt;/code&gt; default confirmed at &lt;code&gt;numra-dde/src/system.rs:87&lt;/code&gt;; SPDE &lt;code&gt;adaptive: bool&lt;/code&gt; gates real branching at &lt;code&gt;numra-spde/src/solver.rs:395&lt;/code&gt; between fixed-step EM/Milstein dispatch and adaptive-variant code. The optional “consider whether the divergence is still justified” retrofit question in the original entry is &lt;strong&gt;not&lt;/strong&gt; rolled into this close — &lt;code&gt;DdeOptions&lt;/code&gt; is the most plausible retrofit candidate (closest shape to &lt;code&gt;SolverOptions&lt;/code&gt;); if pursued it would open as a separate follow-up rather than being absorbed speculatively. Closes F-OPTS. Remaining trio items F-SOLVER-FIELDS and F-MATRIX-SHAPE are deferred foundation work tracked in &lt;code&gt;docs/internal-followups.md&lt;/code&gt;, not on the 0.1.3 path.&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&quot;fixed&quot;&gt;Fixed&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;F-WEBSITE-AUDIT-GATES&lt;/strong&gt; (partial close — CI/infrastructure, not a crate change): the four website audit gates (&lt;code&gt;Lighthouse (marketing site)&lt;/code&gt;, &lt;code&gt;Lighthouse (book)&lt;/code&gt;, &lt;code&gt;Accessibility (pa11y-ci)&lt;/code&gt;, &lt;code&gt;Playwright (dark-mode regression)&lt;/code&gt;) had never executed in the repo’s history before F-CI-NODE20’s PR #5 — every prior website-touching commit landed via direct-push to main, where their &lt;code&gt;if: github.event_name == &apos;pull_request&apos;&lt;/code&gt; guards keep them dormant. The investigative audit pass revealed three distinct failure modes, not the single “stale Lighthouse config” mode the follow-up entry pre-diagnosed. Fixed in this PR:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Config staleness on both Lighthouse configs&lt;/strong&gt; (&lt;code&gt;website/ci/lighthouserc.json&lt;/code&gt;, &lt;code&gt;lighthouserc-book.json&lt;/code&gt;): the &lt;code&gt;_comment_pwa&lt;/code&gt;, &lt;code&gt;_comment_third_party_summary&lt;/code&gt;, and &lt;code&gt;_comment_book_specific&lt;/code&gt; keys sitting inside &lt;code&gt;assertions{}&lt;/code&gt; removed (LHCI parses any key in that object as an audit ID and reports “is not a known audit”). Three deprecated audit assertions removed (&lt;code&gt;no-unload-listeners&lt;/code&gt;, &lt;code&gt;no-vulnerable-libraries&lt;/code&gt;, &lt;code&gt;uses-https&lt;/code&gt; — all removed upstream in Lighthouse 12; &lt;code&gt;is-on-https&lt;/code&gt; is the surviving HTTPS-validation audit and remains). The &lt;code&gt;render-blocking-resources&lt;/code&gt; &lt;code&gt;maxNumericValue&lt;/code&gt; override removed (no-op in Lighthouse 12, which reshaped the audit to return a list of items — the preset’s &lt;code&gt;maxLength&lt;/code&gt; is what actually fires). Top-level &lt;code&gt;_comment&lt;/code&gt; fields in both configs expanded to document the audits that were removed upstream and to warn future readers that &lt;code&gt;assertions{}&lt;/code&gt; keys are parsed as audit IDs.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Workflow URL list mismatch on the book Lighthouse job&lt;/strong&gt; (&lt;code&gt;.github/workflows/website.yml&lt;/code&gt;): the hardcoded chapter URL &lt;code&gt;/ch01-fundamentals/numerical-stability/&lt;/code&gt; doesn’t exist in the deployed book — the actual directory is &lt;code&gt;ch01-introduction/&lt;/code&gt; with no &lt;code&gt;numerical-stability&lt;/code&gt; page, likely a holdover from an early outline. The 404 short-circuited Lighthouse-book’s run before any assertions could fire. Replaced with &lt;code&gt;/ch01-introduction/installation/&lt;/code&gt; (the hero-linked entry from the book homepage); a short inline comment documents the &lt;code&gt;trailingSlash: &apos;always&apos;&lt;/code&gt; convention from &lt;code&gt;website/book/astro.config.mjs&lt;/code&gt; so the next URL-list edit doesn’t drop the trailing slashes.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Playwright wrong-expectation correction&lt;/strong&gt; (&lt;code&gt;website/tests/specs/dark-mode.spec.ts&lt;/code&gt;): three marketing dark-mode tests removed — two &lt;code&gt;system-dark&lt;/code&gt; paint assertions and one &lt;code&gt;stored-preference&lt;/code&gt; &lt;code&gt;data-theme=&quot;dark&quot;&lt;/code&gt; assertion. The marketing site is deliberately light-only by design; the removed assertions encoded a non-requirement that had been silently failing since the gate first ran on a &lt;code&gt;pull_request&lt;/code&gt; event. The spec file’s docstring rewritten to state the intentional asymmetry with the book (which DOES support dark mode via Starlight and whose dark-mode tests are kept), and to forbid future contributors from re-introducing the wrong assertions for “consistency”. This is gate-correction-not-gate-silencing, same shape as removing the stale Lighthouse audit IDs above — the gate now honestly asserts the actual design intent.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Partial close, with four genuine-site-issue follow-ups opened&lt;/strong&gt;: the audit surfaced that &lt;strong&gt;three of the four gates are catching real deployed-site regressions&lt;/strong&gt;, not config drift. The marketing site has 78 WCAG2AA color-contrast violations across two pages (one Astro component instanced many times), &lt;code&gt;is-crawlable: 0&lt;/code&gt; site-wide on &lt;strong&gt;both&lt;/strong&gt; the marketing site and the book (the entire Numra web presence is currently un-indexable by search engines), real CLS / render-blocking / image-delivery issues on marketing, and book-specific Lighthouse failures (&lt;code&gt;label-content-name-mismatch&lt;/code&gt;, &lt;code&gt;network-dependency-tree-insight&lt;/code&gt;, &lt;code&gt;font-display-insight&lt;/code&gt;, plus &lt;code&gt;lcp-discovery-insight&lt;/code&gt; + &lt;code&gt;lcp-lazy-loaded&lt;/code&gt; on &lt;code&gt;/ch13-performance/comparisons/&lt;/code&gt;) unmasked by this PR’s URL fix. Tracked as four new follow-ups in &lt;code&gt;docs/internal-followups.md&lt;/code&gt;: F-WEBSITE-SEO (&lt;strong&gt;highest priority in the entire backlog&lt;/strong&gt; — fixes site-wide discoverability across both subdomains; entry instructs that if the root cause is a trivial &lt;code&gt;_headers&lt;/code&gt; / &lt;code&gt;robots.txt&lt;/code&gt; config fix, ship as an expedited tiny PR rather than waiting for normal scheduling), F-WEBSITE-MARKETING-A11Y, F-WEBSITE-MARKETING-PERF, F-WEBSITE-BOOK-LHC-FIXES. &lt;code&gt;label-content-name-mismatch&lt;/code&gt; appears in both the marketing-A11Y and book-LHC-FIXES entries; the two share an audit name but not a fix surface (custom Astro components vs. Starlight upstream templates) — both entries explicitly note the same-symptom, different-source relationship.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Release-independence note&lt;/strong&gt;: this is CI configuration plus a website-workflow URL-list fix plus a Playwright test removal; it does not change any crate code and does not ship through &lt;code&gt;cargo publish&lt;/code&gt;. The four split-out follow-ups are also website-quality work that lands on &lt;code&gt;main&lt;/code&gt; and redeploys via Cloudflare Pages — release-independent in the same way. This entry sits in &lt;code&gt;Unreleased&lt;/code&gt; because the convention is to record CHANGELOG-relevant &lt;code&gt;main&lt;/code&gt;-branch changes here, not because it gates a crate release.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Audit’s evolution and the hard-stop discipline&lt;/strong&gt;: the audit accurately diagnosed each gate’s &lt;em&gt;failure&lt;/em&gt; from the PR #5 CI logs, but two corrections landed during pre-merge hard-stop review: (1) the URL-fix hypothesis (Lighthouse-book greens under the URL fix alone) was falsified by this PR’s own gate run, which unmasked book-specific Lighthouse failures previously hidden by the 404 short-circuit — handled by adding F-WEBSITE-BOOK-LHC-FIXES and rescoping F-WEBSITE-MARKETING-SEO → F-WEBSITE-SEO once &lt;code&gt;is-crawlable: 0&lt;/code&gt; was observed on the book as well; (2) the Playwright marketing dark-mode failure was initially audited as a “real regression” because the audit had no access to the design-intent input that the marketing site is deliberately light-only — surfaced by the user during pre-merge review, corrected here by removing the wrong assertions in-scope rather than opening a “regression” follow-up that didn’t exist. The investigative-audit framing was load-bearing: had this been treated as enumerable config-fixing work per the entry’s pre-diagnosis, the resulting PR would have “fixed” the gates by silencing them while leaving 78 a11y violations and a site-wide search-indexing block live in production. The hard-stop discipline was equally load-bearing: had the URL-fix and Playwright corrections not been gated through pre-merge review, falsifiable claims (a “Lighthouse-book expected to pass” prediction; a non-existent dark-mode regression) would have landed in PR history.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Gate-greenness on this PR’s own run&lt;/strong&gt;: the Lighthouse-book gate is expected to remain failing under the in-scope fixes — the URL fix unmasks genuine book-side Lighthouse failures (tracked as F-WEBSITE-BOOK-LHC-FIXES). The Playwright gate is expected to pass under the test removal (the remaining tests — book dark, book light, marketing light — already passed in the prior run). The Lighthouse-marketing and pa11y gates still fail because they correctly detect the genuine site regressions; this PR is admin-merged over those three gates with &lt;strong&gt;per-gate&lt;/strong&gt; documentation citing each split-out follow-up, same documented-exception discipline as F-CI-NODE20’s PR #5.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;F-WEBSITE-SEO&lt;/strong&gt; (closed via reframe — CI/configuration, no crate change, no site fix): the follow-up entry’s “the entire Numra web presence is currently un-indexable by search engines” framing (cited in F-WEBSITE-AUDIT-GATES above and in the originally-opened F-WEBSITE-SEO entry) was based on a misread: the &lt;code&gt;is-crawlable: 0&lt;/code&gt; Lighthouse audit ran against PR preview deployments, which Cloudflare Pages deliberately injects &lt;code&gt;X-Robots-Tag: noindex&lt;/code&gt; on to prevent duplicate-content with production. Direct checks against production on 2026-05-16 disconfirm the config-block hypothesis: &lt;code&gt;curl -I&lt;/code&gt; against &lt;code&gt;numra-rs.org/&lt;/code&gt; and &lt;code&gt;book.numra-rs.org/&lt;/code&gt; returned HTTP 200 with no &lt;code&gt;x-robots-tag&lt;/code&gt; header; zero &lt;code&gt;&amp;#x3C;meta name=&quot;robots&quot;&gt;&lt;/code&gt; in either site’s built HTML; neither &lt;code&gt;_headers&lt;/code&gt; file declares an &lt;code&gt;X-Robots-Tag&lt;/code&gt;; &lt;code&gt;website/site/public/robots.txt&lt;/code&gt; is &lt;code&gt;Allow: /&lt;/code&gt;; Cloudflare’s documented preview-only noindex behavior explicitly excludes production custom domains. &lt;strong&gt;Production custom domains are not config-blocked from indexing&lt;/strong&gt; as of those checks. &lt;strong&gt;Whether the production sites are actually indexed in practice&lt;/strong&gt; (sitemap pickup, Search Console coverage, organic crawler discovery) &lt;strong&gt;is unverified and separately tracked&lt;/strong&gt; — “config isn’t blocking” is necessary-not-sufficient, and that narrower question is held open as F-WEBSITE-SEO-VERIFY rather than collapsed into this retraction. The audit caught the misread at the hard-stop before any edit. &lt;strong&gt;In-scope fix&lt;/strong&gt;: disabled &lt;code&gt;is-crawlable&lt;/code&gt; in both Lighthouse preset configs (&lt;code&gt;website/ci/lighthouserc.json&lt;/code&gt;, &lt;code&gt;website/ci/lighthouserc-book.json&lt;/code&gt;) with a foreclosing &lt;code&gt;_comment&lt;/code&gt; documenting the preview-noindex semantics and forbidding re-enablement without first implementing a production-URL audit path. Gate-correction-not-gate-silencing — same preview-only-false-signal shape as the marketing dark-mode Playwright test removal in F-WEBSITE-AUDIT-GATES above, though structurally narrower: the dark-mode case was a wrong property (marketing is light-only by design); the SEO case is the right property against the wrong URL set. Re-introducing the SEO assertion against production URLs would be correct (tracked as F-WEBSITE-SEO-PROD-PROBE); re-introducing dark-mode marketing assertions against any URL would not. &lt;strong&gt;The “highest priority in the entire backlog” / “every day of delay” / expedite-if-trivial framing is fully retracted&lt;/strong&gt;: it was built on the demonstrated misread of preview-noindex as production-block, and the direct checks disconfirm that misread. Two narrower follow-ups forked from the audit’s residual observations and tracked in &lt;code&gt;docs/internal-followups.md&lt;/code&gt;: F-WEBSITE-SEO-PROD-PROBE (medium — the unimplemented production-URL audit path the workflow comment at &lt;code&gt;.github/workflows/website.yml:213-215&lt;/code&gt; aspires to but doesn’t ship), F-WEBSITE-SEO-VERIFY (low — non-engineering go-look-at-the-world sanity check that production is actually indexed in practice, since “config isn’t blocking” is necessary-not-sufficient). Neither fork inherits the highest-priority framing. &lt;strong&gt;Honest framing of this correction&lt;/strong&gt;: the F-WEBSITE-AUDIT-GATES entry above and its companion &lt;code&gt;docs/internal-followups.md&lt;/code&gt; entry both amplified the un-indexable-in-production claim; both records are amended in the same pass as this retirement (&lt;code&gt;docs/internal-followups.md&lt;/code&gt; gains an explicit Amendment 3 to F-WEBSITE-AUDIT-GATES retracting the production-block framing in the same record that made the claim, and F-WEBSITE-PR-FLOW’s worked-example list is recategorized so &lt;code&gt;is-crawlable&lt;/code&gt; joins the dark-mode tests as a “preview-only false signal” example rather than a “gate catches real bug” example, with the structural narrower-distinction noted above). The original F-WEBSITE-AUDIT-GATES bullet’s “site-wide search-indexing block” phrasing earlier in this CHANGELOG is left verbatim with this cross-referencing retraction, rather than amended in place — preserves the audit trail of the original wrong claim, consistent with how Amendments 1 and 2 handle their own corrections. &lt;strong&gt;Pattern lesson&lt;/strong&gt;: gates that run against preview URLs cannot assert properties whose semantics differ between preview and production — third worked example after F-WEBSITE-AUDIT-GATES’s stale-config and dark-mode-wrong-expectation corrections. The hard-stop audit discipline caught the misread before any code edit, any tiny-PR expedite, or any CHANGELOG claim of “site-wide search-indexing block fixed.” &lt;strong&gt;Symmetric-overclaim guard&lt;/strong&gt;: this retraction was further reviewed against the inverse risk — replacing “production is blocked” with “production was always correctly indexable” would have been one unverified absolute swapped for another, an emotionally-satisfying shape a fourth correction in this arc was specifically vulnerable to. The replacement claim is stated at the precision the direct checks support (“not config-blocked from indexing, as of 2026-05-16 checks”); the in-practice question is held open via F-WEBSITE-SEO-VERIFY rather than closed by inference. Same necessary-not-sufficient discipline that caught the original preview-vs-production misread, applied one level deeper, to the correction itself. &lt;strong&gt;Release-independence note&lt;/strong&gt;: this is CI configuration plus &lt;code&gt;docs/internal-followups.md&lt;/code&gt; record corrections; no crate code changed, no &lt;code&gt;cargo publish&lt;/code&gt; involved. The CI-config change’s practical effect is “the gate stops asserting a deterministically-failing thing on future PR previews,” not a production site change (production was not-config-blocked as of the direct checks on 2026-05-16; in-practice indexing separately tracked).&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;F-FD-NOSCALE-BUG&lt;/strong&gt;: Four FD utilities used a hardcoded &lt;code&gt;h = 1e-8&lt;/code&gt; step without scaling by &lt;code&gt;|x|&lt;/code&gt;, which silently degraded gradient / Jacobian outputs for callers with &lt;code&gt;|x| &gt; ~5e7&lt;/code&gt; (the precision floor where &lt;code&gt;x + 1e-8&lt;/code&gt; rounds back to &lt;code&gt;x&lt;/code&gt; in &lt;code&gt;f64&lt;/code&gt;). All four now use canonical precision-aware steps with additive scaling: central-FD sites get &lt;code&gt;cbrt(S::EPSILON) * (1 + |x|)&lt;/code&gt;; the forward-FD &lt;code&gt;diffusion_derivative&lt;/code&gt; gets &lt;code&gt;sqrt(S::EPSILON) * (1 + |x|)&lt;/code&gt;.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Affected utilities&lt;/strong&gt;: &lt;code&gt;numra-optim::finite_diff_gradient&lt;/code&gt; (public free function), &lt;code&gt;numra-optim::finite_diff_jacobian&lt;/code&gt; (public free function), &lt;code&gt;numra-dde::History::evaluate_derivative&lt;/code&gt; (initial-history branch), &lt;code&gt;numra-sde::SdeSystem::diffusion_derivative&lt;/code&gt; (trait default for the Milstein method). The first three are central FD; the fourth is forward FD (different canonical step).&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Affected users&lt;/strong&gt;: &lt;code&gt;OptimProblem::solve()&lt;/code&gt; callers with large-magnitude parameters and no analytical gradient — the bug propagates through &lt;code&gt;numra-optim::auto&lt;/code&gt;’s solver dispatcher (4 &lt;code&gt;finite_diff_gradient&lt;/code&gt; / 1 &lt;code&gt;finite_diff_jacobian&lt;/code&gt; call sites) and &lt;code&gt;numra-optim::augmented_lagrangian&lt;/code&gt;’s inner loops (5 call sites); DDE callers querying derivatives in the initial-history region with large-magnitude time arguments; SDE Milstein-method users who don’t override &lt;code&gt;diffusion_derivative&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Failure-mode shape&lt;/strong&gt;: at &lt;code&gt;|x| = 1e8&lt;/code&gt;, the previous unscaled formula returns either &lt;code&gt;0&lt;/code&gt; (clean catastrophic case for the central-FD sites — both &lt;code&gt;x+h&lt;/code&gt; and &lt;code&gt;x-h&lt;/code&gt; round back to &lt;code&gt;x&lt;/code&gt;) or a &lt;code&gt;~50%&lt;/code&gt;-distorted answer (the forward-FD site, where &lt;code&gt;x+h&lt;/code&gt; rounds to &lt;code&gt;x + ulp&lt;/code&gt; rather than &lt;code&gt;x&lt;/code&gt;). Both regimes pass through the optimiser as silently-wrong gradient information. The fix recovers analytical accuracy in both regimes.&lt;/li&gt;
&lt;li&gt;Pinned with four regression tests at &lt;code&gt;|x| = 1e8&lt;/code&gt;: &lt;code&gt;test_finite_diff_gradient_large_x_no_scaling_bug&lt;/code&gt; and &lt;code&gt;test_finite_diff_jacobian_large_x_no_scaling_bug&lt;/code&gt; in &lt;code&gt;numra-optim/src/problem.rs&lt;/code&gt;; &lt;code&gt;test_evaluate_derivative_large_t_no_scaling_bug&lt;/code&gt; in &lt;code&gt;numra-dde/src/history.rs&lt;/code&gt;; &lt;code&gt;test_diffusion_derivative_default_large_x_no_scaling_bug&lt;/code&gt; in &lt;code&gt;numra-sde/src/system.rs&lt;/code&gt;. Each test asserts proximity to the analytical value within &lt;code&gt;1e-3&lt;/code&gt; relative tolerance — wide enough to be CI-stable across f64 platforms, narrow enough that the previous formula’s outputs (0 or ~50% off) fail by orders of magnitude. Structural-correctness check verified for the forward-FD site (revert formula → confirm test fails → restore).&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Public-API rustdoc&lt;/strong&gt;: &lt;code&gt;finite_diff_gradient&lt;/code&gt; and &lt;code&gt;finite_diff_jacobian&lt;/code&gt; rustdoc updated to document the canonical step formula, the additive-scaling rationale, and the &lt;code&gt;|x| &gt; ~5e7&lt;/code&gt; precision floor — durable contract documentation for the next reader of the public API. The two trait/method sites (&lt;code&gt;History::evaluate_derivative&lt;/code&gt;, &lt;code&gt;SdeSystem::diffusion_derivative&lt;/code&gt;) were not annotated since they don’t have established rustdoc conventions for the FD-step choice.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;This closes the no-scaling correctness portion of the FD-step audit. The 0.1.2 CHANGELOG’s F-FD-NOSCALE-BUG follow-up note over-listed &lt;code&gt;numra-optim::robust&lt;/code&gt; as a no-scaling site; the F-FD-CROSSCRATE audit subsequently surfaced that those sites already have proper additive scaling and are F-FD-CROSSCRATE-class hygiene (hardcoded &lt;code&gt;1e-8&lt;/code&gt;, additive-scaled), not F-FD-NOSCALE-BUG-class correctness. The robust.rs sites remain a small leftover hygiene item, deferred.&lt;/p&gt;
&lt;h3 id=&quot;removed&quot;&gt;Removed&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;F-SOLVER-FIELDS (breaking removal)&lt;/strong&gt; — &lt;code&gt;numra-ode&lt;/code&gt;: deleted vestigial public surface that was silently non-functional under the pre-fix code path. All removed items had &lt;strong&gt;no correct behavior reachable through them&lt;/strong&gt; prior to this PR; the breaking-ness is documented honestly but no correct program depended on them because no correct behavior was reachable. Removed items:
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;Bdf::new()&lt;/code&gt;, &lt;code&gt;Bdf::with_max_order(n)&lt;/code&gt;, &lt;code&gt;Bdf::fixed_order(n)&lt;/code&gt; — the silently-non-functional instance builders; their &lt;code&gt;max_order&lt;/code&gt; / &lt;code&gt;min_order&lt;/code&gt; fields on the &lt;code&gt;Bdf&lt;/code&gt; struct were unreadable from the static &lt;code&gt;Solver::solve&lt;/code&gt; trait method that discarded &lt;code&gt;self&lt;/code&gt;. Every caller of these builders got their order-control request silently ignored at solve time. The &lt;code&gt;Bdf&lt;/code&gt; struct is now a unit struct; order control flows through &lt;code&gt;SolverOptions::max_order&lt;/code&gt; / &lt;code&gt;SolverOptions::min_order&lt;/code&gt; (see the additive &lt;code&gt;### Changed&lt;/code&gt; entry above).&lt;/li&gt;
&lt;li&gt;&lt;code&gt;Auto&lt;/code&gt; struct, &lt;code&gt;Auto::new&lt;/code&gt;, &lt;code&gt;Auto::with_hints(hints)&lt;/code&gt;, &lt;code&gt;impl Solver&amp;#x3C;S&gt; for Auto&lt;/code&gt;, the &lt;code&gt;Auto::hints&lt;/code&gt; field — same trait-discards-&lt;code&gt;self&lt;/code&gt; pattern as the BDF builders. &lt;code&gt;Auto::solve(...)&lt;/code&gt; and &lt;code&gt;&amp;#x3C;Auto as Solver&amp;#x3C;S&gt;&gt;::solve(...)&lt;/code&gt; ignored the configured &lt;code&gt;SolverHints&lt;/code&gt; and called &lt;code&gt;SolverHints::new()&lt;/code&gt; internally. The &lt;code&gt;Accuracy&lt;/code&gt;, &lt;code&gt;Stiffness&lt;/code&gt;, and &lt;code&gt;SolverHints&lt;/code&gt; types and the free-function user-facing API (&lt;code&gt;auto_solve&lt;/code&gt;, &lt;code&gt;auto_solve_with_hints&lt;/code&gt;) are &lt;strong&gt;unaffected&lt;/strong&gt; — &lt;code&gt;auto_solve&lt;/code&gt; and &lt;code&gt;auto_solve_with_hints&lt;/code&gt; are now the only entry points to automatic solver selection. The previously-exported &lt;code&gt;Auto&lt;/code&gt; symbol is removed from the public re-export list at &lt;code&gt;numra-ode/src/lib.rs&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Justification — silent hint-loss producing potentially-inaccurate uncertainty quantification, not just vestigial surface&lt;/strong&gt;: the deleted &lt;code&gt;impl Solver&amp;#x3C;S&gt; for Auto&lt;/code&gt; silently discarded any &lt;code&gt;SolverHints&lt;/code&gt; configured by a caller — &lt;code&gt;&amp;#x3C;Auto as Solver&amp;#x3C;S&gt;&gt;::solve(...)&lt;/code&gt; constructed &lt;code&gt;SolverHints::new()&lt;/code&gt; internally and ignored anything &lt;code&gt;Auto::with_hints(...)&lt;/code&gt; had been passed. The documented &lt;code&gt;solve_with_uncertainty::&amp;#x3C;Auto, f64&gt;(...)&lt;/code&gt; example (formerly in &lt;code&gt;numra-ode/src/uncertainty.rs&lt;/code&gt;) therefore used auto-detected dispatch regardless of user intent. &lt;em&gt;This can&lt;/em&gt; produce inaccurate uncertainty-quantification results when the user’s hints encoded problem structure that auto-detection’s heuristics don’t reconstruct (e.g., stiffness profiles the simple Jacobian-ratio detector at &lt;code&gt;detect_stiffness&lt;/code&gt; misses, or accuracy targets the rtol-classifier categorizes differently than the user) — not unconditionally, only when hints carry auto-undetectable information. That conditional is the precisely-supported scope of the claim: a user who never built &lt;code&gt;SolverHints&lt;/code&gt; and called &lt;code&gt;auto_solve&lt;/code&gt; directly was unaffected; a user who built hints and passed &lt;code&gt;Auto&lt;/code&gt; as a &lt;code&gt;Solver&amp;#x3C;S&gt;&lt;/code&gt; type parameter could be silently degraded. This is still a stronger removal justification than “vestigial”: the surface was structurally lossy in a way that could silently degrade scientific output for the documented use case, not merely unused. The doc example is updated to use &lt;code&gt;::&amp;#x3C;Tsit5&gt;&lt;/code&gt; / &lt;code&gt;::&amp;#x3C;DoPri5&gt;&lt;/code&gt; / &lt;code&gt;::&amp;#x3C;Radau5&gt;&lt;/code&gt; (working solvers); users who want auto-selection should call &lt;code&gt;auto_solve&lt;/code&gt; / &lt;code&gt;auto_solve_with_hints&lt;/code&gt; directly on a single problem.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Migration&lt;/strong&gt;:
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;Bdf::with_max_order(n)&lt;/code&gt; → &lt;code&gt;SolverOptions::default().max_order(n)&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;Bdf::fixed_order(n)&lt;/code&gt; → &lt;code&gt;SolverOptions::default().max_order(n).min_order(n)&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;Bdf::new()&lt;/code&gt; → &lt;code&gt;Bdf&lt;/code&gt; (the struct is now a zero-size unit type; just construct it directly, or use the &lt;code&gt;Bdf::solve&lt;/code&gt; static method as every workspace call site already does)&lt;/li&gt;
&lt;li&gt;&lt;code&gt;Auto::solve(problem, t0, tf, y0, options)&lt;/code&gt; → &lt;code&gt;auto_solve(problem, t0, tf, y0, options)&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;Auto::with_hints(hints).solve(problem, t0, tf, y0, options)&lt;/code&gt; → &lt;code&gt;auto_solve_with_hints(problem, t0, tf, y0, options, &amp;#x26;hints)&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;solve_with_uncertainty::&amp;#x3C;Auto, f64&gt;(...)&lt;/code&gt; → choose an explicit solver type parameter (&lt;code&gt;Tsit5&lt;/code&gt;, &lt;code&gt;DoPri5&lt;/code&gt;, &lt;code&gt;Radau5&lt;/code&gt;, or any &lt;code&gt;Solver&amp;#x3C;S&gt;&lt;/code&gt; implementor); automatic selection within an uncertainty quantification call is not supported.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;Foundation Spec §3.4 / §3.7 / §6 / §7 updated in the same PR: §3.4’s (a)/(b)/(c) design-tension paragraph rewritten to reflect the resolution; §3.7 drift closed (the spec’s claim that BDF max order lives in &lt;code&gt;SolverOptions&lt;/code&gt; is now true); §6 decision-log entry #12 records the change with full rationale; §7 #8 records option (c) (reshape &lt;code&gt;Solver::solve&lt;/code&gt; to &lt;code&gt;&amp;#x26;self&lt;/code&gt;) as a standing foundation open question — deferred, not foreclosed.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;</content:encoded></item><item><title>Numra 0.1.2</title><link>https://numra-rs.org/changelog#012---2026-05-15/</link><guid isPermaLink="true">https://numra-rs.org/changelog#012---2026-05-15/</guid><description>Release notes for Numra 0.1.2 (2026-05-15).</description><pubDate>Fri, 15 May 2026 00:00:00 GMT</pubDate><content:encoded>&lt;h3 id=&quot;changed&quot;&gt;Changed&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;numra-ode&lt;/code&gt;: &lt;code&gt;OdeSystem::jacobian&lt;/code&gt; default’s finite-difference step changed from a hardcoded &lt;code&gt;1e-8&lt;/code&gt; to the textbook precision-aware &lt;code&gt;sqrt(S::EPSILON) * (1 + |y_j|)&lt;/code&gt;. The previous step was below &lt;code&gt;f32::EPSILON ≈ 1.19e-7&lt;/code&gt;, so the FD perturbation quantised to zero on &lt;code&gt;f32&lt;/code&gt; and the default Jacobian came back as all-zeros — silently. The new form is correct on every &lt;code&gt;Scalar&lt;/code&gt; precision (&lt;code&gt;f64&lt;/code&gt; lands at &lt;code&gt;≈1.49e-8&lt;/code&gt;, within ~50% of the prior value; &lt;code&gt;f32&lt;/code&gt; at &lt;code&gt;≈3.45e-4&lt;/code&gt;, no longer quantised). Behavioural impact on &lt;code&gt;f64&lt;/code&gt;: Jacobian columns shift in the last 1-2 digits at states with &lt;code&gt;|y_j| ≈ 1&lt;/code&gt;. No regression-suite test asserted on the prior values; the &lt;code&gt;numra-ode&lt;/code&gt; test suite passes unchanged. Pinned with a new &lt;code&gt;f32&lt;/code&gt; regression test.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;numra-core&lt;/code&gt;: &lt;code&gt;Signal::eval_derivative&lt;/code&gt; default’s central-difference step changed from a hardcoded &lt;code&gt;1e-8&lt;/code&gt; (no scaling) to the textbook precision-aware &lt;code&gt;cbrt(S::EPSILON) * (1 + |t|)&lt;/code&gt; — optimal for central FD by the truncation/round-off balance, and useful at every &lt;code&gt;Scalar&lt;/code&gt; precision (&lt;code&gt;f64&lt;/code&gt; at &lt;code&gt;≈6.06e-6&lt;/code&gt;, &lt;code&gt;f32&lt;/code&gt; at &lt;code&gt;≈4.92e-3&lt;/code&gt;). Same &lt;code&gt;f32&lt;/code&gt; quantisation-to-zero failure mode as &lt;code&gt;OdeSystem::jacobian&lt;/code&gt;; same fix shape. Most built-in &lt;code&gt;Signal&lt;/code&gt; impls (Harmonic, Ramp, Sum, Product, Scaled, Constant, Zero) override with closed-form derivatives, so the FD path mainly affects &lt;code&gt;Tabulated&lt;/code&gt;, &lt;code&gt;FromFile&lt;/code&gt;, and user-supplied signals without an analytical override. Pinned with a new &lt;code&gt;f32&lt;/code&gt; regression test.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;numra-pde&lt;/code&gt;: six MOL hybrid analytical-Jacobian implementations updated in lockstep with the trait-default formula change (&lt;code&gt;MOLSystem2D::jacobian&lt;/code&gt;, &lt;code&gt;MOLSystem3D::jacobian&lt;/code&gt;, and the four &lt;code&gt;ParametricMOLSystem{2,3}D::jacobian_y/_p&lt;/code&gt;). The reaction-FD diagonal in each cited the trait default in code comments — keeping the comment-vs-code consistency the comment claims required moving the implementations together. The 2D / 3D / parametric MOL &lt;code&gt;analytical-vs-FD-agreement&lt;/code&gt; regression tests pass unchanged at their existing tolerances.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;numra-ode&lt;/code&gt;: &lt;code&gt;Radau5&lt;/code&gt; module-level rustdoc updated to cite the new FD step formula. The &lt;code&gt;AugmentedSystem&lt;/code&gt; inline FD paths in &lt;code&gt;numra-ode/src/sensitivity.rs&lt;/code&gt; were already on &lt;code&gt;sqrt(S::EPSILON)&lt;/code&gt; from the original forward-sensitivity work; no change.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;numra-bench&lt;/code&gt;: &lt;code&gt;pde_mol::FdJacobianMol2D&lt;/code&gt; wrapper takes no jacobian override, so it automatically tracks whatever the current trait default is. &lt;code&gt;bench_mol2d_radau5_jacobian_path&lt;/code&gt;’s “FD baseline” therefore reports the actual FD path a user without an analytical override would hit today; the wrapper rustdoc is updated to make this explicit.&lt;/li&gt;
&lt;li&gt;Cross-crate finite-difference step formulas reconciled to the canonical precision-aware forms across 18 sites in &lt;code&gt;numra-nonlinear::newton&lt;/code&gt;, &lt;code&gt;numra-ode::{auto, dae_init, esdirk, index_reduction}&lt;/code&gt;, &lt;code&gt;numra-ocp::adjoint&lt;/code&gt;, &lt;code&gt;numra-fit::curve_fit&lt;/code&gt;, &lt;code&gt;numra-core::uncertainty&lt;/code&gt;, and &lt;code&gt;numra-optim::optim_sensitivity&lt;/code&gt;. Forward-FD sites adopt &lt;code&gt;sqrt(S::EPSILON) * (1 + |x_j|)&lt;/code&gt;; central-FD sites adopt &lt;code&gt;cbrt(S::EPSILON) * (1 + |x_j|)&lt;/code&gt;. Previously these used a mix of hardcoded &lt;code&gt;1e-7&lt;/code&gt;, &lt;code&gt;1e-8&lt;/code&gt;, or &lt;code&gt;1e-5&lt;/code&gt; constants — three families of recipes scattered across the workspace. The audit pass surfaced 3 sites beyond the entry’s named 15 (two &lt;code&gt;ReducedDaeSystem.fd_eps&lt;/code&gt; field initializers in &lt;code&gt;numra-ode/src/index_reduction.rs&lt;/code&gt; and the &lt;code&gt;compute_param_sensitivity&lt;/code&gt; default in &lt;code&gt;numra-optim/src/optim_sensitivity.rs&lt;/code&gt;); same audit-surfaces-more-than-named pattern as F-FD-STEP and F-CI-NODE20.
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Behavioural delta to flag for users&lt;/strong&gt;: adjoint and DAE-initialisation paths previously used &lt;code&gt;1e-7&lt;/code&gt; and now use &lt;code&gt;sqrt(f64::EPSILON) ≈ 1.49e-8&lt;/code&gt; — a 6.6× smaller forward-FD step. Adjoint gradients (&lt;code&gt;numra-ocp::adjoint::adjoint_gradient&lt;/code&gt; and friends) and &lt;code&gt;dae_init&lt;/code&gt;/&lt;code&gt;index_reduction&lt;/code&gt; structure-detection paths shift in the last few digits at states with &lt;code&gt;|x| ≈ 1&lt;/code&gt;. Numerically this is a roundoff/discretization improvement (canonical step balances both); the existing regression suites pass unchanged at their existing tolerances. Users depending on bit-exact reproducibility of adjoint gradients should rerun their reference values.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Behavioural delta in &lt;code&gt;numra-fit::curve_fit&lt;/code&gt;&lt;/strong&gt;: the multiplicative central-FD recipe (&lt;code&gt;h = if |orig| &gt; 1e-10 { orig * 1e-7 } else { 1e-7 }&lt;/code&gt;) is replaced by the canonical additive form (&lt;code&gt;h = cbrt(S::EPSILON) * (1 + |orig|)&lt;/code&gt;). At &lt;code&gt;|orig| ≈ 1&lt;/code&gt; the new step is ~120× larger (~1.21e-5 vs the prior ~1e-7), with better roundoff/discretization balance. Levenberg–Marquardt convergence paths may shift in the first few iterations on problems sensitive to Jacobian noise; final fits remain within tolerance and the existing curve-fit regression tests pass unchanged.&lt;/li&gt;
&lt;li&gt;Pinned with a new regression test (&lt;code&gt;numra-optim::optim_sensitivity::tests::test_sensitivity_canonical_default_eps_at_large_param&lt;/code&gt;) exercising &lt;code&gt;compute_param_sensitivity&lt;/code&gt; with &lt;code&gt;eps = None&lt;/code&gt; at &lt;code&gt;|p| = 100&lt;/code&gt; — asserts the canonical default reproduces the analytical sensitivity of &lt;code&gt;dx*/dp = 1&lt;/code&gt; for &lt;code&gt;min (x - p)^2&lt;/code&gt; to within &lt;code&gt;1e-2&lt;/code&gt;.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;This closes the cross-crate-consistency portion of the FD-step audit. &lt;strong&gt;One related follow-up remains open&lt;/strong&gt; and is not addressed here:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;F-FD-NOSCALE-BUG&lt;/strong&gt; — the no-scaling correctness bug in &lt;code&gt;numra-optim::finite_diff_gradient&lt;/code&gt;, &lt;code&gt;numra-optim::finite_diff_jacobian&lt;/code&gt;, &lt;code&gt;numra-optim::robust&lt;/code&gt;, &lt;code&gt;numra-dde::history&lt;/code&gt;, &lt;code&gt;numra-sde::system::diffusion_derivative&lt;/code&gt;. These public-API sites use &lt;code&gt;h = eps&lt;/code&gt; with no &lt;code&gt;(1 + |x|)&lt;/code&gt; scaling, so &lt;code&gt;f64&lt;/code&gt; callers with large-magnitude states see the perturbation quantised. Independent of the f32 framing in F-FD-STEP and the consistency framing in F-FD-CROSSCRATE; warrants its own correctness analysis and PR.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Closes F-FD-STEP and F-FD-CROSSCRATE. Remaining follow-up tracked in &lt;code&gt;docs/internal-followups.md&lt;/code&gt;.&lt;/p&gt;
&lt;h3 id=&quot;tooling&quot;&gt;Tooling&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;CI workflow actions upgraded to Node.js 24-runtime majors ahead of GitHub’s 2026-06-02 deprecation deadline: &lt;code&gt;actions/checkout@v4 → @v6&lt;/code&gt;, &lt;code&gt;actions/setup-node@v4 → @v6&lt;/code&gt;, &lt;code&gt;actions/upload-artifact@v4 → @v7&lt;/code&gt;, &lt;code&gt;pnpm/action-setup@v3 → @v6&lt;/code&gt;, &lt;code&gt;cloudflare/wrangler-action@v3 → @v4&lt;/code&gt;. &lt;code&gt;pnpm/action-setup&lt;/code&gt; blocks also drop the redundant &lt;code&gt;with: version: 9&lt;/code&gt; in favour of the &lt;code&gt;packageManager: pnpm@9.15.0&lt;/code&gt; field in &lt;code&gt;package.json&lt;/code&gt; as the single source of truth (required for the v4+ strict version-disagreement check). &lt;code&gt;cloudflare/wrangler-action@v4&lt;/code&gt; defaults wrangler to v4; &lt;code&gt;pages deploy&lt;/code&gt; syntax is stable across the bump. Closes F-CI-NODE20.&lt;/li&gt;
&lt;/ul&gt;</content:encoded></item><item><title>Numra 0.1.1</title><link>https://numra-rs.org/changelog#011---2026-05-14/</link><guid isPermaLink="true">https://numra-rs.org/changelog#011---2026-05-14/</guid><description>Release notes for Numra 0.1.1 (2026-05-14).</description><pubDate>Thu, 14 May 2026 00:00:00 GMT</pubDate><content:encoded>&lt;h3 id=&quot;changed-breaking&quot;&gt;Changed (breaking)&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;numra-core&lt;/code&gt;: &lt;code&gt;NumraError&lt;/code&gt; is now &lt;code&gt;#[non_exhaustive]&lt;/code&gt;. Exhaustive &lt;code&gt;match&lt;/code&gt; arms over &lt;code&gt;NumraError&lt;/code&gt; outside of &lt;code&gt;numra-core&lt;/code&gt; must add a &lt;code&gt;_ =&gt; …&lt;/code&gt; catch-all. Rationale: future-proofs additive variant changes, so subsequent cross-crate &lt;code&gt;From&lt;/code&gt; impls can extend the workspace error story without forcing a semver-major bump. Workspace convention going forward: every public enum should be &lt;code&gt;#[non_exhaustive]&lt;/code&gt; by default unless there’s a specific reason not to.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;numra-core&lt;/code&gt;: &lt;code&gt;NumraError::Optimization&lt;/code&gt; variant renamed to &lt;code&gt;NumraError::NumericalOptim&lt;/code&gt;. The wrapped type stays &lt;code&gt;numra_core::OptimizationError&lt;/code&gt;; only the &lt;code&gt;NumraError&lt;/code&gt; variant tag changes. Rationale: disambiguates from the new &lt;code&gt;NumraError::Optim&lt;/code&gt; variant (which carries &lt;code&gt;numra-optim::OptimError&lt;/code&gt;, the optimization crate’s API-level errors). The renamed variant clarifies its scope — low-level numerics-failure modes from inside algorithms (line search, descent direction, convergence) — distinct from crate-level optimization errors.&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&quot;added&quot;&gt;Added&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;numra-core&lt;/code&gt;: ten new &lt;code&gt;NumraError&lt;/code&gt; variants for cross-crate error propagation — &lt;code&gt;Ode(String)&lt;/code&gt;, &lt;code&gt;Optim(String)&lt;/code&gt;, &lt;code&gt;Ocp(String)&lt;/code&gt;, &lt;code&gt;Fit(String)&lt;/code&gt;, &lt;code&gt;Signal(String)&lt;/code&gt;, &lt;code&gt;LineSearch(String)&lt;/code&gt;, &lt;code&gt;Interp(String)&lt;/code&gt;, &lt;code&gt;Integrate(String)&lt;/code&gt;, &lt;code&gt;Special(String)&lt;/code&gt;, &lt;code&gt;Stats(String)&lt;/code&gt;. Each variant tags the source crate so callers can &lt;code&gt;match&lt;/code&gt; on which subsystem failed; the per-crate error’s detail flattens into the carried &lt;code&gt;String&lt;/code&gt; via &lt;code&gt;Display&lt;/code&gt;. Resolves the workspace composability contract item 3 (errors compose into the workspace error type).&lt;/li&gt;
&lt;li&gt;&lt;code&gt;numra-ode&lt;/code&gt;, &lt;code&gt;numra-optim&lt;/code&gt;, &lt;code&gt;numra-ocp&lt;/code&gt;, &lt;code&gt;numra-fit&lt;/code&gt;, &lt;code&gt;numra-signal&lt;/code&gt;, &lt;code&gt;numra-nonlinear&lt;/code&gt;: &lt;code&gt;From&amp;#x3C;CrateError&gt; for NumraError&lt;/code&gt; impls covering &lt;code&gt;SolverError&lt;/code&gt;, &lt;code&gt;OptimError&lt;/code&gt;, &lt;code&gt;OcpError&lt;/code&gt;, &lt;code&gt;FitError&lt;/code&gt;, &lt;code&gt;SignalError&lt;/code&gt;, and &lt;code&gt;LineSearchError&lt;/code&gt; respectively. Six previously-missing bridges now land directly into &lt;code&gt;NumraError&lt;/code&gt;, so &lt;code&gt;?&lt;/code&gt; propagation across these crate boundaries works without manual &lt;code&gt;.map_err&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;numra/tests/interop_workflows.rs&lt;/code&gt;: &lt;code&gt;workflow_ode_interp_integrate&lt;/code&gt; rewritten to return &lt;code&gt;Result&amp;#x3C;(), NumraError&gt;&lt;/code&gt; and use &lt;code&gt;?&lt;/code&gt; across three crate boundaries (numra-ode → numra-interp → numra-integrate). Canonical structural CI signal that the workspace &lt;code&gt;?&lt;/code&gt;-propagation property is real — the test compiles only if all three &lt;code&gt;From&lt;/code&gt; impls are present. Closes the gap surfaced in the foundation-pass verification (finding D4): no interop test previously exercised cross-crate &lt;code&gt;?&lt;/code&gt;-propagation.&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&quot;changed&quot;&gt;Changed&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;numra-interp&lt;/code&gt;, &lt;code&gt;numra-integrate&lt;/code&gt;, &lt;code&gt;numra-special&lt;/code&gt;, &lt;code&gt;numra-stats&lt;/code&gt;: existing &lt;code&gt;From&amp;#x3C;…&gt; for NumraError&lt;/code&gt; impls migrated from collapsing into &lt;code&gt;NumraError::InvalidInput(String)&lt;/code&gt; to the new tagged-variant pattern (&lt;code&gt;NumraError::Interp&lt;/code&gt;, &lt;code&gt;NumraError::Integrate&lt;/code&gt;, &lt;code&gt;NumraError::Special&lt;/code&gt;, &lt;code&gt;NumraError::Stats&lt;/code&gt;). Workspace-uniform error story: every external-crate error now lands in a tagged variant rather than half-tagged-half-flattened. &lt;code&gt;Display&lt;/code&gt; output for these errors changes (e.g. &lt;code&gt;&quot;Invalid input: …&quot;&lt;/code&gt; → &lt;code&gt;&quot;Interpolation error: …&quot;&lt;/code&gt;); &lt;code&gt;Display&lt;/code&gt; is not part of any stability guarantee in 0.1.x.&lt;/li&gt;
&lt;/ul&gt;</content:encoded></item><item><title>Numra 0.1.0</title><link>https://numra-rs.org/changelog#010---2026-05-13/</link><guid isPermaLink="true">https://numra-rs.org/changelog#010---2026-05-13/</guid><description>Release notes for Numra 0.1.0 (2026-05-13).</description><pubDate>Wed, 13 May 2026 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;First public release. Archived on Zenodo: concept DOI &lt;a href=&quot;https://doi.org/10.5281/zenodo.20159709&quot;&gt;10.5281/zenodo.20159709&lt;/a&gt; (all versions; preferred for citation); version DOI &lt;a href=&quot;https://doi.org/10.5281/zenodo.20159710&quot;&gt;10.5281/zenodo.20159710&lt;/a&gt; (this 0.1.0 release; for reproducibility).&lt;/p&gt;
&lt;h3 id=&quot;added&quot;&gt;Added&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;Workspace facade crate covering ODE, SDE, DDE, FDE, IDE, PDE, SPDE, optimization, optimal control, linear algebra, quadrature, interpolation, special functions, FFT, statistics, fitting, signal processing, and autodiff.&lt;/li&gt;
&lt;li&gt;Public release audit artifacts under &lt;code&gt;docs/audit/&lt;/code&gt;, including API inventory, book coverage matrix, correctness test map, and release checklist.&lt;/li&gt;
&lt;li&gt;CI and local audit gates for formatting, clippy, tests, docs, MSRV, and supply-chain checks.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;SolverResult.dense_output: Option&amp;#x3C;DenseOutput&amp;#x3C;S&gt;&gt;&lt;/code&gt; field so callers can actually use the dense interpolant they requested via &lt;code&gt;SolverOptions::dense()&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;numra&lt;/code&gt; facade: top-level promotion of the canonical forward-sensitivity surface — &lt;code&gt;solve_forward_sensitivity&lt;/code&gt;, &lt;code&gt;solve_forward_sensitivity_with&lt;/code&gt;, &lt;code&gt;ParametricOdeSystem&lt;/code&gt;, &lt;code&gt;SensitivityResult&lt;/code&gt; — symmetric with the existing &lt;code&gt;compute_sensitivities&lt;/code&gt; parameter-uncertainty API. Advanced building blocks (&lt;code&gt;AugmentedSystem&lt;/code&gt;, &lt;code&gt;ClosureSystem&lt;/code&gt;) remain reachable via the &lt;code&gt;numra::ode&lt;/code&gt; submodule. The crate-level docstring distinguishes the two distinct sensitivity concepts (parameter-uncertainty vs ODE forward sensitivity) so users land on the right one from the doc index.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;numra/examples/robertson_sensitivity.rs&lt;/code&gt;: complete worked forward-sensitivity example on the canonical Robertson stiff kinetics benchmark. Demonstrates analytical state and parameter Jacobians (with the matching &lt;code&gt;has_analytical_jacobian_*&lt;/code&gt; flags), &lt;code&gt;Radau5&lt;/code&gt;-driven augmented integration, the full accessor surface (&lt;code&gt;final_state&lt;/code&gt;, &lt;code&gt;dyi_dpj&lt;/code&gt;, &lt;code&gt;sensitivity_for_param&lt;/code&gt;, &lt;code&gt;normalized_sensitivity_at&lt;/code&gt;), and dimensionless influence ranking. Internal sanity checks pass on output: state mass conservation to 12 digits and &lt;code&gt;∑ ∂y/∂k_i = 0&lt;/code&gt; per parameter.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;numra-bench/benches/sensitivity.rs&lt;/code&gt;: publication-grade Criterion harness for forward sensitivity. Three groups: &lt;code&gt;sensitivity_param_scaling&lt;/code&gt; (cost vs &lt;code&gt;N_s&lt;/code&gt; on a 2-state oscillator), &lt;code&gt;sensitivity_jacobian_mode&lt;/code&gt; (FD vs analytical-&lt;code&gt;J_y&lt;/code&gt; vs analytical-&lt;code&gt;J_y&lt;/code&gt;+&lt;code&gt;J_p&lt;/code&gt; on Lotka–Volterra under Radau5), &lt;code&gt;sensitivity_solver_choice&lt;/code&gt; (Radau5/BDF/DoPri5/Tsit5 on Robertson). 20s measurement / 3s warmup per point. Flattened JSON in &lt;code&gt;numra-bench/results/sensitivity_*.json&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;website/figures/perf/sensitivity_{param_scaling,jacobian_mode,solver_choice}.py&lt;/code&gt;: three SPEC §5.2-compliant figure scripts. Each consumes its respective Criterion-flattened JSON and renders an SVG (with provenance sidecar) into &lt;code&gt;website/book/src/assets/figures/perf_sensitivity_*.svg&lt;/code&gt;. &lt;code&gt;make perf&lt;/code&gt; auto-includes them via the existing &lt;code&gt;perf/*.py&lt;/code&gt; glob.&lt;/li&gt;
&lt;li&gt;Book: comprehensive forward-sensitivity chapter at &lt;code&gt;ch11-uncertainty/sensitivity-analysis&lt;/code&gt; covering the math (sensitivity equation, simultaneous-corrector augmented system, initial sensitivity), the API (&lt;code&gt;solve_forward_sensitivity{,_with}&lt;/code&gt;, &lt;code&gt;ParametricOdeSystem&lt;/code&gt; trait with default-FD vs analytical Jacobians and the &lt;code&gt;has_analytical_jacobian_*&lt;/code&gt; flag contract + debug-build safety net), the result layout (column-major over parameters), a Robertson worked example tying back to &lt;code&gt;numra/examples/robertson_sensitivity.rs&lt;/code&gt;, solver-choice guidance with the bench chart embedded, performance characterisation with the param-scaling and Jacobian-mode bench charts embedded, and edge-case discussion (parameter scaling, identifiability, tolerance inheritance, non-trivial IC, closure-form perf). Also includes a forward-vs-adjoint trade-off table pointing at the adjoint chapter for large parameter counts.&lt;/li&gt;
&lt;li&gt;Book: &lt;code&gt;ch11-uncertainty/parameter-importance&lt;/code&gt; sibling page holding the relocated &lt;code&gt;compute_sensitivities&lt;/code&gt; (numra-core) content — local sensitivity coefficients of a scalar function via central FD. The two pages now cleanly separate scalar-function parameter importance from full ODE-trajectory forward sensitivity, with each page pointing at the other for the complementary use case. Sidebar order in &lt;code&gt;ch11-uncertainty&lt;/code&gt; group: Error Propagation → Parameter Importance → Sensitivity Analysis → Interval Arithmetic → Monte Carlo with ODEs.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;numra-pde&lt;/code&gt;: &lt;code&gt;MOLSystem3D&lt;/code&gt; wrapper for 3D method-of-lines on uniform tensor-product grids (&lt;code&gt;numra-pde/src/mol3d.rs&lt;/code&gt;). Mirrors the &lt;code&gt;MOLSystem2D&lt;/code&gt; API shape — &lt;code&gt;heat&lt;/code&gt;, &lt;code&gt;laplacian&lt;/code&gt;, &lt;code&gt;with_operator&lt;/code&gt;, &lt;code&gt;with_reaction&lt;/code&gt;, &lt;code&gt;OdeSystem&amp;#x3C;S&gt;&lt;/code&gt; impl, &lt;code&gt;build_full_solution&lt;/code&gt; over column-major interior layout — backed by the new &lt;code&gt;Operator3DCoefficients&lt;/code&gt; (&lt;code&gt;a*u_xx + b*u_yy + c*u_zz + d*u_x + e*u_y + f*u_z + g*u&lt;/code&gt;) and &lt;code&gt;assemble_operator_3d&lt;/code&gt;. &lt;code&gt;assemble_laplacian_3d&lt;/code&gt; now delegates to the general assembler, removing duplicated stencil logic and matching how the 2D file is organised. Closes the audited 3D-MOL gap (the foundation pieces — &lt;code&gt;Grid3D&lt;/code&gt;, &lt;code&gt;BoundaryConditions3D&lt;/code&gt;, sparse 7-point Laplacian — were already shipping; only the wrapper was missing).&lt;/li&gt;
&lt;li&gt;&lt;code&gt;numra-pde&lt;/code&gt;: 3D convenience builders &lt;code&gt;HeatEquation3D::build&lt;/code&gt;, &lt;code&gt;AdvectionDiffusion3D::build&lt;/code&gt;, &lt;code&gt;ReactionDiffusion3D::build&lt;/code&gt;, and &lt;code&gt;ReactionDiffusion3D::fisher&lt;/code&gt; (&lt;code&gt;numra-pde/src/equations3d.rs&lt;/code&gt;), mirroring &lt;code&gt;equations2d.rs&lt;/code&gt; exactly with a &lt;code&gt;z&lt;/code&gt;-axis added.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;numra-pde&lt;/code&gt;: &lt;code&gt;MOLSystem2D&lt;/code&gt; and &lt;code&gt;MOLSystem3D&lt;/code&gt; both override &lt;code&gt;OdeSystem::jacobian&lt;/code&gt;. The override is a direct CSC-to-row-major-dense copy of the assembled spatial operator (which already equals &lt;code&gt;∂(L[u])/∂u&lt;/code&gt; by construction) plus a diagonal-FD reaction term. The diagonal property holds rigorously because pointwise reactions cannot populate off-diagonal entries — &lt;code&gt;R(t, x_i, ...; u_i)&lt;/code&gt; depends only on local state, so &lt;code&gt;∂R_i/∂u_m = 0&lt;/code&gt; for &lt;code&gt;m ≠ i&lt;/code&gt;. Implicit solvers (&lt;code&gt;Radau5&lt;/code&gt;, &lt;code&gt;Bdf&lt;/code&gt;) consume the override automatically with no opt-in. A future nonlocal / integro-PDE / multi-component reaction would not satisfy the diagonal property and falls through to the trait-default full FD path; that’s tracked as a named follow-up.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;numra-bench&lt;/code&gt;: new &lt;code&gt;bench_jacobian_unification&lt;/code&gt; group in &lt;code&gt;solvers.rs&lt;/code&gt; covering Robertson at &lt;code&gt;rtol=1e-8&lt;/code&gt;, Van der Pol stiff at &lt;code&gt;rtol=1e-6&lt;/code&gt; for &lt;code&gt;μ ∈ {10, 1000}&lt;/code&gt;, and a 2-state smooth-linear case — locking in regression coverage on the workload shapes most likely to surface alloc / extra-rhs overhead from the Jacobian unification work. New &lt;code&gt;bench_mol2d_radau5_jacobian_path&lt;/code&gt; group in &lt;code&gt;pde_mol.rs&lt;/code&gt; directly compares the analytical-Jacobian path against an &lt;code&gt;FdJacobianMol2D&lt;/code&gt; wrapper that delegates rhs to &lt;code&gt;MOLSystem2D&lt;/code&gt; but does not override &lt;code&gt;jacobian&lt;/code&gt;, exercising both code paths in the same Criterion run.&lt;/li&gt;
&lt;li&gt;Book: 3D Problems section in &lt;code&gt;ch04-beyond-odes/partial-des.md&lt;/code&gt; documenting &lt;code&gt;MOLSystem3D&lt;/code&gt;, &lt;code&gt;Operator3DCoefficients&lt;/code&gt;, the convenience builders, and the analytical-Jacobian path that ships with the MOL systems. Three worked examples (3D heat with the analytic &lt;code&gt;exp(-3π²αt)&lt;/code&gt; decay, 3D Fisher–KPP via &lt;code&gt;with_reaction&lt;/code&gt;, advection–diffusion via &lt;code&gt;Operator3DCoefficients&lt;/code&gt;) and a memory/runtime scaling table for grids up to &lt;code&gt;65³&lt;/code&gt;.&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&quot;changed&quot;&gt;Changed&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;&lt;code&gt;numra-ode&lt;/code&gt;: &lt;code&gt;Radau5&lt;/code&gt; and &lt;code&gt;Bdf&lt;/code&gt; now route their Jacobian computation through &lt;code&gt;OdeSystem::jacobian&lt;/code&gt; instead of a solver-inlined finite-difference path. Systems that override the trait method (e.g. &lt;code&gt;MOLSystem2D&lt;/code&gt;, &lt;code&gt;MOLSystem3D&lt;/code&gt;) get an analytical Jacobian for free; systems that don’t fall through to the canonical forward-FD default in &lt;code&gt;numra-ode/src/problem.rs&lt;/code&gt; (eps = 1e-8, step = &lt;code&gt;eps * (1 + |y_j|)&lt;/code&gt;, row-major dense output). The unified FD step formula resolves prior drift between &lt;code&gt;Radau5&lt;/code&gt;’s inlined &lt;code&gt;eps * max(1, |y_j|)&lt;/code&gt; and the canonical &lt;code&gt;(1 + |y_j|)&lt;/code&gt; form used by &lt;code&gt;Bdf&lt;/code&gt;, the trait default, and the Hairer–Wanner reference; the smooth &lt;code&gt;(1 + |y_j|)&lt;/code&gt; version has no discontinuity at &lt;code&gt;|y_j| = 1&lt;/code&gt; and is what every textbook / Hairer-style implementation specifies. Behavioural impact: &lt;code&gt;Radau5&lt;/code&gt; Newton residuals and Jacobian-rebuild step counts shift in the last 2-3 digits at states with &lt;code&gt;|y_j| ≈ 1&lt;/code&gt;. No test in the regression suite asserted on those exact values; the entire numra-ode test suite (138 unit tests + integration tests) passes unchanged.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;code&gt;numra-core&lt;/code&gt;: renamed &lt;code&gt;Sensitivity&lt;/code&gt; → &lt;code&gt;ParameterSensitivity&lt;/code&gt; and &lt;code&gt;SensitivityResult&lt;/code&gt; → &lt;code&gt;ParameterSensitivityResult&lt;/code&gt; (and the corresponding &lt;code&gt;numra&lt;/code&gt; facade re-exports) to disambiguate from the new ODE forward-sensitivity types. The &lt;code&gt;compute_sensitivities&lt;/code&gt; function name is preserved; only the type names changed.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;code&gt;numra-ode&lt;/code&gt;: renamed the &lt;code&gt;SensitivityEquations&lt;/code&gt; trait to &lt;code&gt;ParametricOdeSystem&lt;/code&gt; and reshaped it to take &lt;code&gt;params()&lt;/code&gt; and &lt;code&gt;rhs_with_params(t, y, p, dydt)&lt;/code&gt; (which lets the trait drive its own FD on &lt;code&gt;J_p&lt;/code&gt;); added FD-default &lt;code&gt;jacobian_y&lt;/code&gt; and &lt;code&gt;jacobian_p&lt;/code&gt; overrides so a minimal implementation supplies only &lt;code&gt;n_states&lt;/code&gt;, &lt;code&gt;n_params&lt;/code&gt;, &lt;code&gt;params&lt;/code&gt;, and &lt;code&gt;rhs_with_params&lt;/code&gt;. Removed the unused &lt;code&gt;SensitivityState&lt;/code&gt; helper. &lt;code&gt;AugmentedSystem&lt;/code&gt; now implements &lt;code&gt;OdeSystem&lt;/code&gt; directly and can be solved by any &lt;code&gt;Solver&lt;/code&gt;. Sensitivity flattening is column-major (parameter-major), matching CVODES indexing.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;code&gt;numra-ode&lt;/code&gt;: &lt;code&gt;SensitivityResult&lt;/code&gt; reshaped to flat row-major over time and column-major over parameters within each time block, with &lt;code&gt;y_at&lt;/code&gt;, &lt;code&gt;sensitivity_at&lt;/code&gt;, &lt;code&gt;sensitivity_for_param&lt;/code&gt;, &lt;code&gt;dyi_dpj&lt;/code&gt;, &lt;code&gt;final_state&lt;/code&gt;, &lt;code&gt;final_sensitivity&lt;/code&gt;, and &lt;code&gt;normalized_sensitivity_at&lt;/code&gt; accessors.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;code&gt;numra-ode&lt;/code&gt;: &lt;code&gt;solve_trajectory&lt;/code&gt; (uncertainty.rs) reimplemented on top of the canonical &lt;code&gt;solve_forward_sensitivity_with&lt;/code&gt; primitive. The private &lt;code&gt;AugmentedOdeSystem&lt;/code&gt; is gone — single source of truth. Public API surface unchanged; the closure bound relaxed from &lt;code&gt;Fn(...) + Send + Sync&lt;/code&gt; to &lt;code&gt;Fn(...)&lt;/code&gt; (loosening, non-breaking). Added &lt;code&gt;ParametricOdeSystem::has_analytical_jacobian_y&lt;/code&gt; / &lt;code&gt;has_analytical_jacobian_p&lt;/code&gt; flag methods (default &lt;code&gt;false&lt;/code&gt;) so &lt;code&gt;AugmentedSystem&lt;/code&gt;’s hot path can inline FD with reused scratch buffers when the user hasn’t supplied analytical overrides — and call the override directly when they have. Document contract: implementors of &lt;code&gt;jacobian_y&lt;/code&gt; / &lt;code&gt;jacobian_p&lt;/code&gt; should also override the corresponding flag method to return &lt;code&gt;true&lt;/code&gt;, otherwise their override is silently bypassed when running through &lt;code&gt;solve_forward_sensitivity&lt;/code&gt;.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;code&gt;numra-ocp&lt;/code&gt;: &lt;code&gt;forward_sensitivity&lt;/code&gt; reimplemented as a thin wrapper over the canonical &lt;code&gt;numra_ode::sensitivity::solve_forward_sensitivity_with&lt;/code&gt; primitive; the duplicated private &lt;code&gt;augmented_rhs&lt;/code&gt; helper is gone. &lt;code&gt;numra_ocp::SensitivityResult&lt;/code&gt; is now a re-export of &lt;code&gt;numra_ode::SensitivityResult&lt;/code&gt; — single shape, single accessor surface, single test suite as the source of truth. The sensitivity layout therefore changes from row-major over states (&lt;code&gt;s[i*N*N_s + state*N_s + param]&lt;/code&gt;) to column-major over parameters (&lt;code&gt;s[i*N*N_s + param*N + state]&lt;/code&gt;), matching CVODES indexing. Callers using the typed accessors (&lt;code&gt;sensitivity_at&lt;/code&gt;, &lt;code&gt;y_at&lt;/code&gt;) are unaffected at the call site for state-1 / param-1 problems but should switch to &lt;code&gt;dyi_dpj(time, state, param)&lt;/code&gt; and &lt;code&gt;sensitivity_for_param(time, param)&lt;/code&gt; for forward-compatible multi-state usage. The &lt;code&gt;t_eval&lt;/code&gt; parameter was renamed &lt;code&gt;output_times&lt;/code&gt; in the function signature; positional callers are unaffected (Rust has no named-argument calls).&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&quot;performance&quot;&gt;Performance&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;numra-ode&lt;/code&gt;: &lt;code&gt;solve_trajectory&lt;/code&gt; (uncertainty.rs) is substantially faster (~40% on the LV benchmark with 4 parameters at rtol=1e-9) after unification onto the canonical sensitivity primitive eliminated per-call heap allocations in the augmented RHS hot path. The previous private &lt;code&gt;AugmentedOdeSystem&lt;/code&gt; allocated six fresh &lt;code&gt;Vec&lt;/code&gt;s per RHS evaluation; the new path reuses scratch via &lt;code&gt;RefCell&lt;/code&gt; buffers on &lt;code&gt;AugmentedSystem&lt;/code&gt;. Magnitudes will vary with problem size, parameter count, and tolerance — the underlying mechanism (no per-call allocation) applies to all &lt;code&gt;solve_trajectory&lt;/code&gt; workloads.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;numra-pde&lt;/code&gt;: stiff PDE workloads on &lt;code&gt;Radau5&lt;/code&gt; / &lt;code&gt;Bdf&lt;/code&gt; are faster because &lt;code&gt;MOLSystem2D&lt;/code&gt; and &lt;code&gt;MOLSystem3D&lt;/code&gt; now supply analytical state Jacobians instead of forcing the solver into trait-default forward FD. Measured ~2.8% faster on the canonical stiff 2D heat-with-reaction workload (Allen-Cahn-like &lt;code&gt;u_t = α∇²u + u - u³&lt;/code&gt;, &lt;code&gt;α=0.5&lt;/code&gt;, &lt;code&gt;19²=361&lt;/code&gt; interior points, &lt;code&gt;rtol=1e-6&lt;/code&gt;); the win comes from skipping the &lt;code&gt;N+1&lt;/code&gt; rhs evaluations FD needs per Jacobian rebuild, but its size is bounded by how often the solver rebuilds the Jacobian (Radau5 caches across steps when Newton converges quickly). Magnitudes will vary with problem size, stiffness, grid resolution, and Jacobian-rebuild frequency — workloads with more rebuilds (looser tolerances triggering more rejected steps, larger &lt;code&gt;N&lt;/code&gt; where the FD sweep cost grows, stiffer problems where Newton struggles more often) see proportionally larger speedups. Tracked permanently by &lt;code&gt;bench_mol2d_radau5_jacobian_path&lt;/code&gt; in &lt;code&gt;numra-bench/benches/pde_mol.rs&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;numra-ode&lt;/code&gt;: the &lt;code&gt;OdeSystem::jacobian&lt;/code&gt; unification is itself perf-neutral on workloads that don’t override the trait method. Across &lt;code&gt;bench_jacobian_unification&lt;/code&gt; (Robertson at &lt;code&gt;rtol=1e-8&lt;/code&gt;, Van der Pol stiff at &lt;code&gt;rtol=1e-6&lt;/code&gt; for &lt;code&gt;μ ∈ {10, 1000}&lt;/code&gt;, smooth 2D linear at &lt;code&gt;rtol=1e-6&lt;/code&gt;), Radau5 deltas span &lt;code&gt;−0.58%&lt;/code&gt; to &lt;code&gt;+0.58%&lt;/code&gt;, all within Criterion’s noise threshold. BDF on Van der Pol stiff at the legacy &lt;code&gt;rtol=1e-4&lt;/code&gt; shows &lt;code&gt;+3.45%&lt;/code&gt; / &lt;code&gt;+1.70%&lt;/code&gt; / &lt;code&gt;+0.74%&lt;/code&gt; for &lt;code&gt;μ=10/100/1000&lt;/code&gt; — well within the &lt;code&gt;≤5%&lt;/code&gt; regression bound and explained by one extra rhs evaluation per Jacobian rebuild (BDF previously passed an externally-computed &lt;code&gt;f_0&lt;/code&gt; into its inlined FD, while the trait default recomputes &lt;code&gt;f_0&lt;/code&gt; internally).&lt;/li&gt;
&lt;/ul&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Forward-sensitivity v1 scope.&lt;/strong&gt; What shipped this release: the &lt;code&gt;ParametricOdeSystem&lt;/code&gt; trait with FD-default and analytical-Jacobian paths plus the &lt;code&gt;has_analytical_jacobian_*&lt;/code&gt; flag contract and a debug-build consistency-check safety net; &lt;code&gt;AugmentedSystem&lt;/code&gt; and &lt;code&gt;ClosureSystem&lt;/code&gt; impls of &lt;code&gt;OdeSystem&lt;/code&gt;; &lt;code&gt;solve_forward_sensitivity&lt;/code&gt; and &lt;code&gt;solve_forward_sensitivity_with&lt;/code&gt; entry points usable with any &lt;code&gt;Solver&lt;/code&gt; (including &lt;code&gt;Auto&lt;/code&gt;); flat row-major-over-time, column-major-over-parameters &lt;code&gt;SensitivityResult&lt;/code&gt; with the typed accessor surface; ports of &lt;code&gt;numra-ocp::forward_sensitivity&lt;/code&gt; and &lt;code&gt;numra-ode::uncertainty::solve_trajectory&lt;/code&gt; onto the canonical primitive (no more parallel implementations); a 10-test regression suite at &lt;code&gt;numra-ode/tests/sensitivity_regression.rs&lt;/code&gt; (eight numerical, two API-contract); the &lt;code&gt;numra/examples/robertson_sensitivity.rs&lt;/code&gt; worked example; a publication-grade Criterion harness at &lt;code&gt;numra-bench/benches/sensitivity.rs&lt;/code&gt; with three perf figures; and the rewritten &lt;code&gt;ch11-uncertainty/sensitivity-analysis&lt;/code&gt; book chapter plus the &lt;code&gt;parameter-importance&lt;/code&gt; sibling page. What did &lt;strong&gt;not&lt;/strong&gt; ship and is tracked as named follow-ups: block-diagonal-aware factorisation (the augmented Jacobian is &lt;code&gt;block_diag(J_y, …, J_y)&lt;/code&gt; and Radau5/BDF currently dense-LU the full &lt;code&gt;N(N_s+1)²&lt;/code&gt; matrix); a JVP variant of the trait for matrix-free / sparse paths; an AD-based &lt;code&gt;ParametricOdeSystem&lt;/code&gt; impl using &lt;code&gt;numra-autodiff&lt;/code&gt;; staggered (&lt;code&gt;CV_STAGGERED&lt;/code&gt;) and per-parameter staggered (&lt;code&gt;CV_STAGGERED1&lt;/code&gt;) correction strategies; separate sensitivity tolerances; ergonomic alternatives to the &lt;code&gt;has_analytical_jacobian_*&lt;/code&gt; flag (proc macro / typestate / trait redesign).&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h3 id=&quot;fixed&quot;&gt;Fixed&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;numra-ode&lt;/code&gt;: the blanket &lt;code&gt;impl ParametricOdeSystem for &amp;#x26;T&lt;/code&gt; forwards &lt;code&gt;n_states&lt;/code&gt;, &lt;code&gt;n_params&lt;/code&gt;, &lt;code&gt;params&lt;/code&gt;, &lt;code&gt;rhs_with_params&lt;/code&gt;, &lt;code&gt;jacobian_y&lt;/code&gt;, &lt;code&gt;jacobian_p&lt;/code&gt;, and &lt;code&gt;initial_sensitivity&lt;/code&gt;, but did not forward &lt;code&gt;has_analytical_jacobian_y&lt;/code&gt; / &lt;code&gt;has_analytical_jacobian_p&lt;/code&gt;. Since those flags have a default &lt;code&gt;false&lt;/code&gt; impl, every caller using the documented &lt;code&gt;solve_forward_sensitivity::&amp;#x3C;_, _, _&gt;(&amp;#x26;system, ...)&lt;/code&gt; pattern was silently dispatching through the FD path even when the underlying system had analytical overrides — measurable slowdown on stiff problems where analytical Jacobians materially change runtime. The blanket impl now forwards the flag methods. Pinned by &lt;code&gt;blanket_ref_impl_forwards_analytical_flags&lt;/code&gt; in the regression suite.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;numra-ode&lt;/code&gt;: &lt;code&gt;AugmentedSystem&lt;/code&gt; now ships a debug-build consistency check that catches the related “user implemented &lt;code&gt;jacobian_*&lt;/code&gt; analytically but forgot to override &lt;code&gt;has_analytical_jacobian_*&lt;/code&gt;” misconfiguration on the first RHS call. The check evaluates the user’s &lt;code&gt;jacobian_*&lt;/code&gt; and inline FD at the initial state and panics in debug builds (zero overhead in release) if the Frobenius-relative deviation exceeds &lt;code&gt;1e-3&lt;/code&gt;. Pinned by &lt;code&gt;analytical_jacobian_y_flag_forgotten_panics&lt;/code&gt;. The required-flag-override contract is itself a known footgun; a follow-up tracks ergonomic alternatives (proc macro, typestate, trait redesign) for post-1.0.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;numra-ode&lt;/code&gt;: DoPri5 was building a &lt;code&gt;DenseOutput&lt;/code&gt; when &lt;code&gt;SolverOptions::dense()&lt;/code&gt; was set but never returning it, so the interpolant was silently dropped at the end of integration. The new &lt;code&gt;SolverResult.dense_output&lt;/code&gt; field is now populated on both the normal exit and the early-termination event path.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;numra-ode&lt;/code&gt;: Radau5 step controller rewritten against Hairer–Wanner ODE II §IV.8 and the SciPy &lt;code&gt;Radau&lt;/code&gt; reference. Eight bugs fixed (most importantly: the &lt;code&gt;error_estimate&lt;/code&gt; forcing term used &lt;code&gt;y&lt;/code&gt; instead of &lt;code&gt;f(t,y)&lt;/code&gt;; Newton’s initial guess now extrapolates the previous step’s collocation polynomial; LU is reused unless the step ratio leaves [1.0, 1.2]; Gustafsson predictive controller). Step counts on the standard reference suite are now within ~1.5–2× of SciPy’s, and Van der Pol μ=10 at rtol=1e-4 runs in ~0.66 ms (vs ~200 ms before, and ~12 ms for SciPy).&lt;/li&gt;
&lt;li&gt;&lt;code&gt;numra-ode&lt;/code&gt;: &lt;code&gt;SolverOptions::t_eval&lt;/code&gt; is now honored by every solver. Previously it was parsed, stored, cloned, and ignored — every solver returned its natural step grid regardless of what the caller requested, including &lt;code&gt;solve_forward_sensitivity&lt;/code&gt;. Now each solver emits &lt;code&gt;(t, y)&lt;/code&gt; pairs at exactly the requested times via Hermite cubic interpolation between accepted step endpoints (closes GitHub #1). Endpoints (&lt;code&gt;t_eval[0] == t0&lt;/code&gt;, &lt;code&gt;t_eval[-1] == tf&lt;/code&gt;) come back bit-exact; interior points are 3rd-order accurate locally, which dominates the error budget only for very-high-order solvers (Vern8) at large step sizes. Backward integration and out-of-range / unsorted grids are validated up front. Pinned by &lt;code&gt;numra-ode/tests/t_eval_regression.rs&lt;/code&gt; (covers DoPri5, Tsit5, Vern6/7/8, Radau5, BDF, Esdirk32/43/54, plus &lt;code&gt;solve_forward_sensitivity&lt;/code&gt; and edge cases: bit-exact endpoints, dense grids that span multiple steps, backward integration).&lt;/li&gt;
&lt;li&gt;&lt;code&gt;numra-ode&lt;/code&gt;: BDF rewritten as a Rust port of SciPy’s &lt;code&gt;BDF&lt;/code&gt; (the canonical Shampine–Reichelt NDF / &lt;code&gt;ode15s&lt;/code&gt; algorithm). Three independent algorithmic bugs fixed: (1) the Newton convergence test now compares the &lt;em&gt;correction&lt;/em&gt; norm against tolerance rather than the residual norm — the previous test silently declared convergence at iteration 0 with &lt;code&gt;y_new == y_predict&lt;/code&gt;, producing the reported “success=true with &lt;code&gt;y_final == y_initial&lt;/code&gt;” mode; (2) the local-truncation-error estimate now uses the &lt;code&gt;(order+1)&lt;/code&gt;-th modified divided difference (the cumulative Newton correction &lt;code&gt;d&lt;/code&gt;), not a stale &lt;code&gt;history[0]&lt;/code&gt; reference that always evaluated to ~0; (3) BDF coefficients are applied to a &lt;em&gt;modified-divided-differences&lt;/em&gt; table that is rescaled in place when &lt;code&gt;h&lt;/code&gt; changes, restoring formal order accuracy under variable steps. Newton failure with a stale Jacobian now retries at the same &lt;code&gt;h&lt;/code&gt; after refreshing &lt;code&gt;J&lt;/code&gt; (previously it shrank &lt;code&gt;h&lt;/code&gt; on every Newton failure, which combined with bug 1 drove &lt;code&gt;h&lt;/code&gt; to zero on benign problems). Step counts now match SciPy within ~1× on smooth problems and ~2× on stiff ones; the regression test &lt;code&gt;test_bdf_regression_silent_wrong_answer&lt;/code&gt; pins the silent-corruption mode out.&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&quot;policy&quot;&gt;Policy&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;Root &lt;code&gt;Cargo.lock&lt;/code&gt; is committed for reproducible public CI.&lt;/li&gt;
&lt;li&gt;The user-facing book is built from &lt;code&gt;website/book/&lt;/code&gt; (Astro + Starlight) and deployed to &lt;code&gt;book.numra-rs.org&lt;/code&gt; by the &lt;code&gt;Website&lt;/code&gt; workflow.&lt;/li&gt;
&lt;/ul&gt;</content:encoded></item></channel></rss>