Upstream RFC drafts
Status: drafts, 2026-10-13, filed upstream as issues on 2026-10-23 (the Issue column). Needed-by dates updated 2026-10-14 for milestone M4b (D37). Upstream progress updated 2026-10-26 (the table below the list). The 2026-10-14 reuse audit (survey §6) verified that each ask names a real gap: none of U1–U15 duplicates existing upstream code.
These are the changes EarthSciIntegrator needs in sibling repos. Each draft follows the EarthSciAST RFC format and cites current upstream code by file:line. Anything a draft could not verify is labelled as an assumption. The integrator-side context is in ../architecture.md
§13 and ../decisions.md
.
| # | Target | Draft | Issue | Needed by |
|---|---|---|---|---|
| U1 | EarthSciAST spec | solver.external
: a document points at its run config (esm 1.3.0) | EarthSciAST#538 | M2 |
| U2 | EarthSciAST flatten | Per-term provenance
: equation_contributions beside flattened equations | EarthSciAST#539 | M2 |
| U3 | EarthSciAST runtimes | Range-restricted RHS : operator subsets over tiles with halos, and lanes; bitwise assembly contract | EarthSciAST#540 | M3, M5 |
| U4 | EarthSciAST runtimes | Reentrant RHS
: Rust RhsProgram (Send + Sync), plus a reentrant Julia entry | EarthSciAST#541 | M3 |
| U5 | EarthSciAST | Float32 integration : what a declared Float32 governs, and what the integrator owns | EarthSciAST#542 | M5g, M7 |
| U6 | EarthSciAST | Strip and expose : events, parameter updates and algebraic equations returned to the caller; standalone observed kernels | EarthSciAST#543 | M4b (eqice on shared memory, D37) |
| U7 | EarthSciASTDiff | Forcing and array-parameter columns | EarthSciASTDiff#1 | M4 |
| U8 | EarthSciASTDiff | Second-order bands for Rosenbrock discrete adjoints | EarthSciASTDiff#2 | M4 |
| U9 | EarthSciASTDiff | Filtered faq, non-+ reductions, recurrences
; column_block structure; VJP models | EarthSciASTDiff#3 | M4, M4b, M7 |
| U10 | EarthSciASTDiff | Production-rate Rust assembly , after a catch-up step | EarthSciASTDiff#4 | M3 |
| U11 | EarthSciIO | Async prefetch and rank windows | EarthSciIO#8 | M5 (prefetch from M3) |
| U12 | EarthSciIO | Rust streaming region writer : commit protocol, indexed records | EarthSciIO#9 | M4, M5 |
| U13 | EarthSciAST.jl (Reactant ext) | Traceable per-operator StableHLO for the Julia GPU path | EarthSciAST#544 | M5g |
| U14 | EarthSciAST spec | Declared vs unspecified precision (D29) | EarthSciAST#545 | M5g, M7 |
| U15 | EarthSciAST.jl | Recurrence execution , native and StableHLO | EarthSciAST#546 | M7 |
| U16 | EarthSciAST spec | solver.time_span
: the run interval a document asks for (D60) | EarthSciAST#550 | M2 |
| U17 | EarthSciASTDiff | Time-derivative bands
: ∂f/∂t as a specified, golden-gated band model, for Rosenbrock’s f_t | EarthSciASTDiff#11 | M3 (restores M2’s time-dependent Rosenbrock cases) |
Upstream progress (2026-10-26):
| # | State |
|---|---|
| U1 | Done: released in EarthSciAST v0.4.0 (esm 1.3.0), esm-spec §2.2.6, with the location accessor. |
| U2 | Done: released in EarthSciAST v0.4.0, equation_contributions (esm-libraries-spec §4.7.5), plus Rust’s equation_lifted. |
| U3 | Done: released in EarthSciAST v0.5.0 (EarthSciAST#557; esm stays 1.3.0), range-restricted right-hand sides over tiles and lanes in Julia and Rust (API_SPEC §5.8, CONFORMANCE_SPEC §5.50). Published 2026-10-26 to Julia’s General registry, crates.io and PyPI. |
| U4 | Done: released in EarthSciAST v0.5.0 (EarthSciAST#561), rhs_program with forcing sets in Julia and Rust’s RhsProgram (Send + Sync) (API_SPEC §5.8, CONFORMANCE_SPEC §5.51). Rust’s M2 end-to-end runs can use it (spec/toy-problems.md §4). |
| U10 | Merged on EarthSciASTDiff main, closing EarthSciASTDiff#4: catch-up (W0, #5), trimmed evaluation document (W1, #8), scatter plans (W3, #10), Rust production assembly (W2, W4, W5, #12). To be released with EarthSciASTDiff 0.2. |
| U16 | Done: released in EarthSciAST v0.4.0 (esm 1.3.0), esm-spec §2.2.7, with resolve_time_span in every binding. |
| U17 | Done on EarthSciASTDiff main (EarthSciASTDiff#13, closing #11): esm-jacobian-spec.md §4.2 specifies wrt: "time" bands, §8 their evaluation document, and the tgrad_photolysis goldens gate them in Julia, Rust and Python. To be released with EarthSciASTDiff 0.2. |
| others | Open. EarthSciASTDiff 0.2 (its Rust crate is on EarthSciAST 0.6; its Julia package is 0.1.0 with compat EarthSciAST “0.4, 0.5” and SciMLBase “2”, so it cannot yet share an environment with EarthSciIntegrator.jl, spec/plan.md §10 item 12) is expected before M2’s stage 2; spec/plan.md is written against its main. |
Cross-cutting prerequisites (architecture §13):
- esm 1.3.0 for the three model repos.
- EarthSciAST’s unmerged
fix/f59-mounted-leaf-precisionbranch (for U14). - A spec clarification permitting batched recurrence positions (for U15).
- The EarthSciASTDiff Rust/Python catch-up (U10 step W0).
- `solver.external`: a document points at its own run configuration
One optional string field in the top-level `solver` block that names an external run-configuration document (a schedule, per-operator algorithms, tolerances, adjoint settings). EarthSciAST parses, validates, version-gates, round-trips and resolves the reference, and never opens the file. The `.esm` stays free of algorithm names; the run config has its own schema and version.
- `solver.time_span`: the run interval a document asks for
One optional `{start, end}` field in the top-level `solver` block giving the simulation interval the document is meant to run over, resolved like `abstol` and `reltol`: an explicit argument at the call site wins, then this field. Purely additive and advisory; it changes no flattened system.
- A public, Send + Sync right-hand side for earthsci-ast-rs
Make the compiled right-hand side a stable public object that is built once, shared by reference across threads, and evaluated concurrently with one caller-owned workspace per thread. Forcing becomes an immutable, epoch-stamped input set rather than a model-owned Rc<RefCell<…>>. The parameter vector ABI is the positional order the tape already uses. Julia's f! has the same non-reentrancy and gets the same split. No schema change.
- A Rust streaming Zarr v3 writer, with region writes, a commit protocol and indexed records
Give the Rust track the open / write_record / flush / close streaming handle the Julia and Python writers already have. Then extend the writer contract in all three bindings so that each MPI rank writes its own horizontal block without a gather (the reserved `region=`). One owner commits shared metadata from tokens the caller passes. Records can be written by index in any order: the adjoint emits forcing gradients in reverse and keeps checkpoint rings. Static variables and per-rank blobs cover gradient fields and bitwise restart. EarthSciIO stays MPI-free.
- Asynchronous epoch prefetch and per-rank index-range windows
Two additions to the EarthSciIO Provider, specified once and implemented natively in Python, Julia and Rust. (a) A PrefetchGroup that decodes upcoming forcing epochs on background workers while the caller computes the current one, in forward, reverse or an explicit order, under a memory budget, with cancellation, and with results bitwise identical to synchronous refresh. (b) Named-dimension windows bound to a provider, so each MPI rank fetches and decodes only its horizontal block plus halo, plus a multi-window decode for node-level aggregation. No cache-format change, no MPI dependency.
- Declared and unspecified precision: what `element_type` binds, and what an external integrator may choose
Separate semantic precision from numerical precision (D29). A declared `element_type`, on the document or on a variable, stays binding exactly as esm-spec §11.3.1 says. An omitted one becomes unspecified: every built-in evaluator still runs it in binary64, but a consumer acting on a run configuration may store state and evaluate a group of equations in binary32. It converts only between groups, is held to the integration and test tolerances, and must report what it chose. Jacobian, factor, preconditioner and table storage are the integrator's under any declaration. Format 1.3.0, with U01. A root below 1.3.0 that omits the field keeps its Float64 meaning, so no existing document or fixture changes.
- Derivatives through filtered faq, non-"+" reductions and causal recurrences
Define EarthSciASTDiff's derivative rules and band representation for the three faq constructs it refuses today: filtered faq nodes (prefix scans and windowed searches), non-"+" reductions (max, min, *), and causal self-reference recurrences (Thomas sweeps, RRTM up and down sweeps, falling-precipitation sweeps). Filters become an exact point predicate on band entries. max and min select the first argmax. Products use exclusion products. Recurrences differentiate into a tangent recurrence and a reverse-axis adjoint recurrence, with a dense lower-triangular Jacobian per line that a new column_block structure class reports. Fixes two silent inliner paths found along the way.
- Executing causal recurrences in EarthSciAST.jl: one ordered sweep on every route, native and StableHLO
EarthSciAST.jl validates causal self-reference recurrences but refuses to run them. Specify how its native compiler executes them, matching Rust and Python bit for bit. A recurrence becomes a materialized buffer that one sweep function fills in recurrence-axis order. Self-reads are fail-closed and guards are lazy. Every Julia route calls the same sweep: in-place and out-of-place right-hand sides, the output-time observed program, build-time fills, and the interpreter. A spec clarification lets cells that share one axis position be evaluated together, since they are independent. The native kernels need this, and so does a level-major StableHLO lowering with a fault latch. No schema change.
- Forcing-buffer and array-parameter columns: ∂f/∂(forcing) bands
Add a `forcing` differentiation axis to EarthSciASTDiff whose columns are the cells of live forcing buffers, including their record axis, and make array-valued parameter columns real columns on the `parameters` axis. Because EarthSciIO hands the model a two-record bracket as an ordinary leading length-2 axis and the model writes its own time blend, the chain rule into both bracketing records falls out of the existing band calculus. The integrator turns the transposed bands into vjp_forcing and accumulates per provider record.
- Integrating a Float32 document: what the declaration governs, and what the integrator owns
Replace the blanket refusal to time-integrate a `domain.element_type: "Float32"` document with a contract. The declaration governs every document value (right-hand side, observeds, events, and the state itself); the integrator owns its clock, step size, norms, tolerances and linear algebra, and reports their precision. Rust's in-crate solver integrates Float32 documents on a binary32 diffsol; an external integrator may keep an f64 clock and f64 norms. No schema change.
- Per-term provenance through flatten: which component wrote each contribution
Flatten sums operator_compose and couple contributions into one right-hand side and forgets where each summand came from. This adds an ordered contribution list beside every flattened equation, each entry carrying the component (or coupling entry) that authored it. The list lives outside the expression tree, so canonical form, interning, CSE and equality are untouched. It is a FlattenedSystem field, not a document-format change.
- Production-rate Jacobian assembly in Rust
Give earthsci-astdiff-rs a production numeric path: band coefficients compiled once, through earthsci-ast-rs's own lane and tile programs, from a derived evaluation document that is byte-identical across Julia and Rust. Values are scattered by precomputed, golden-gated plans straight into the integrator's storage: lane-SoA per-cell blocks, KPP-style sparse lane blocks, column bands and CSR. Matrix-free J·v and Jᵀ·λ are also provided. The program is Send + Sync with one workspace per thread and is bitwise deterministic. The target is at least as fast as Julia's jac! per call. First, the binding catches up with EarthSciAST: the faq tag and the 1.0.0 variable model. Not diffsol integration.
- Range-restricted, operator-restricted right-hand sides: tiles with halos, and lanes
Evaluate one operator's right-hand side — a subset of additive terms selected by provenance, writing only that operator's variables — over a box of cells (a horizontal tile with whole columns) that reads halo cells from an extended local array, or over a runtime list of cells gathered into lanes. Index space stays global, so makearray boundary regions apply exactly where the global domain has a boundary and interior stencils apply at every other tile edge. The contract is bitwise: the tiles' outputs, assembled, equal the operator's global right-hand side bit for bit.
- Second-order bands for Rosenbrock discrete adjoints
A Rosenbrock step contains the Jacobian J(u_n) explicitly, so its discrete adjoint needs ∇_u(μᵀ J(u) w): one second-derivative contraction per step. Mixed versions are needed for parameters and forcing, plus ∂f_t/∂u when the method uses the time gradient. This RFC compares bands of bands, a dedicated HVP model and directional second derivatives. It proposes second-order bands as the spec'd artifact, obtained by differentiating band coefficients with the existing calculus, and evaluated by a contraction kernel once per adjoint step. Costs are worked for per-cell chemistry (SuperFast, CBM-Z, RADM2); stencil and column operators do not need second order.
- Strip and expose: events, parameter updates and algebraic equations handed to the caller, plus standalone observed kernels
An opt-in build mode in which the fast compilers remove the constructs CONFORMANCE_SPEC §5.39 makes them refuse (events, symbolic parameter updates, implicit equations) and return them as structured objects, and an API that compiles named observeds and expressions (residuals, conditions, affects, step limits, step maps) as standalone kernels sharing the right-hand side's state, parameter and forcing ABI, with algebraic unknowns passed in as inputs. Refusal stays the default.
- Time-derivative bands
A Rosenbrock step needs f_t = ∂f/∂t of every operator that depends on time explicitly, such as photolysis that follows the sun. EarthSciASTDiff's main already differentiates with respect to t in Julia, Python and Rust (wrt = time), but its spec still lists tgrad as out of scope and no cross-binding golden gates it. This RFC asks to make time-derivative bands a specified, golden-gated artifact, with the band shape, the serialized block, the evaluation document, and a clear statement of what the derivative leaves out (time dependence that enters through forcing data).
- Traceable per-operator StableHLO: RHS, tile, lane and band-model fragments for an integrator's own XLA program
Extend EarthSciAST.jl's direct StableHLO emitter so an external integrator can get, for one operator and one tile or lane capacity, a pure traceable fragment that emits StableHLO into the integrator's own Reactant trace — inside its @trace while loop — with every input explicit: state, read-only state, a dense parameter vector, time, forcing buffers and, for lanes, the cell indices that define shard composition. Forcing refreshes and re-sharding never recompile. Adds Float32, deterministic folds, observed-output (band-model) fragments, and trace hygiene the integrator can rely on.