Skip to content

Runtime kernel patching (v0.4.0) — diagnosed-fiber overlay

The v0.4.0 release introduces a runtime overlay for diagnosed-fiber patches. Patches sit beside the published spectral kernel as data — not code edits — and contribute per-body residue deltas at encode time. The kernel's published bytes never change.

Why "overlay, not bones"?

The spectral kernel — the static RESONANCES table, the Laplacian construction, the integer Q-format frequencies — is the kernel's truth as we know it. We want to ship that truth with byte-pinned reproducibility: a wheel with SHA-256 X always encodes JD Y to phase residue Z. Forever.

But the v0.3.1 DE441 error-spectrum FFT makes it visible that the kernel is incomplete. Specific FFT residual peaks scream "missing coupling": Mars at 7.96 yr (3.45°), Mercury at 10.69 yr (9.19°), Jupiter–Saturn jointly at 9.56 yr (±45° each, the smoking-gun J–S 5:2 libration depth).

If we fixed the kernel by adding those couplings to RESONANCES / L_static and re-shipping, three things would happen:

  1. The published bytes would change. Existing tests / hash-pins / reproducibility claims tied to v0.3.1 would all need to be re-floored against v0.4.0.
  2. First-principles vs empirical lines would blur. The current RESONANCES table reflects derivable mean-motion physics. An empirical Fourier correction read off an FFT is not the same kind of object — it should be marked as such.
  3. Iteration on diagnosed corrections becomes a hard fork. Every tweak demands a new release; can't compose; can't disable.

The runtime overlay solves all three:

  • Patches are data, applied at encode time. The kernel bytes stay pinned to a release SHA. Bricked patches are unloadable.
  • Patches are explicitly empirical. Each carries notes pointing to the FFT peak it targets and the suspected missing physics — they're hypotheses, not first-principles.
  • Patches compose. Multiple sinusoidal overlays sum order-independently. Disable, swap, A/B test as data, not as code edits + redeploys.

This is the same architectural choice Linux made with ksplice / kpatch: ship the immutable kernel, patch at runtime via an overlay, keep the bones unchanged.

What ships in v0.4.0

Component Status
DiagnosedPatch dataclass + module-level registry (apply_patch, clear_patches, list_patches, snapshot, evaluate_active_patches) ✅ v0.4.0
Bridge surface (bridge.apply_patch / apply_custom_patch / list_active_patches / list_catalog_patches / clear_patches) ✅ v0.4.0
CLI (patches catalog / patches active / patches apply --name / patches clear) ✅ v0.4.0
BIP encoder runtime-overlay integration (overlay summed AFTER base encode, BEFORE final cyclic-group reduction) ✅ v0.4.0
Catalog of three patches authored from the v0.3.1 FFT analysis (Mars 7.96 yr diagonal; Mercury 10.69 yr diagonal; Jupiter–Saturn 9.56 yr coupled with correlation=-1) ✅ v0.4.0
Tests pinning the structural properties (clear-restores-byte-identical; diagonal-patches-don't-leak; coupled-anti-correlated; composition-independent) ✅ v0.4.0
C-side overlay (es_apply_patch / es_clear_patches / es_n_active_patches / es_get_patch_at, ABI v2) v0.4.1
Cross-backend byte-exact parity test (BIP and C identical phases under overlay) ✅ v0.4.1
Sync layer (Python registry mirrors into C; rollback on rejection) ✅ v0.4.1

In v0.4.0 the C backend transparently fell back to BIP when patches were active. In v0.4.1 the native binary carries its own patch registry, mirrored into via the bridge sync layer; backend="c" applies the overlay natively at ~46 μs / encode with 3 patches active (vs ~10.8 ms on BIP — a 237× speedup). With no native loaded — sdist install without C toolchain, Pyodide, the pure-Python fallback wheel — the overlay still runs Python-side and the BIP backend handles everything.

Patch-contribution shape — at a JD ladder

The reproducible shape of each catalog patch's contribution, encoded at five JDs across the ±20 yr horizon. Each cell is patched_phase[body] − baseline_phase[body] mapped to degrees on the cyclic group. With no patches active, the encoder is byte- identical to v0.3.1.

Full demo output — generated by python -m research.demo_runtime_patches.

mars-7.96yr-diagonal (kind = sinusoid)

  • amplitude: 3.45° (read off the v0.3.1 FFT peak rank #1)
  • period: 2907.3 d (7.96 yr)
  • target: Mars only
Δt (yr) Δmars (deg)
-20.0 +0.2737
-5.0 +2.4875
0.0 0.0000
+5.0 -2.4875
+20.0 -0.2737

A single sinusoid. Zero at REFERENCE_JD by construction (no phase_rad offset wired into this catalog entry; users authoring custom patches can bake in any anchor phase). All other 24 bodies are unchanged at every JD.

mercury-10.69yr-diagonal (kind = sinusoid)

  • amplitude: 9.19° (FFT peak rank #1 for Mercury)
  • period: 3905.1 d (10.69 yr)
  • target: Mercury only
Δt (yr) Δmercury (deg)
-20.0 +6.6742
-5.0 -1.8547
0.0 0.0000
+5.0 +1.8547
+20.0 -6.6742

Same sinusoidal form, different period; only Mercury affected.

jupiter-saturn-9.56yr-coupled (kind = coupled-sinusoid)

  • amplitude: 45.0° (the smoking-gun J–S 5:2 libration depth)
  • period: 3490.9 d (9.56 yr)
  • correlation: −1 (anti-correlated libration around the conjunction)
  • targets: Jupiter and Saturn together
Δt (yr) Δjupiter (deg) Δsaturn (deg)
-20.0 -24.7258 +24.7258
-5.0 +6.5213 -6.5213
0.0 0.0000 0.0000
+5.0 -6.5213 +6.5213
+20.0 +24.7258 -24.7258

Anti-correlated to within the cyclic-group ULP — the libration signature. The two columns sum to zero exactly (in residues) because the coupled-sinusoid evaluates one delta and applies +1× to body_a, −1× to body_b. This is the "missing coupling" structural fix wearing its empirical-correction clothing.

Composition

Two patches on disjoint bodies compose order-independently. Property verified by tests/test_runtime_patches.py::test_compose_two_disjoint_patches_independence:

  • apply Mars patch → encode → Mars delta is M
  • apply Mercury patch → encode → Mars delta is still M (Mercury patch doesn't perturb Mars), Mercury delta is N
  • clear, apply Mercury alone → encode → Mercury delta is still N

For two patches both targeting the same body, the deltas sum linearly (sin sums commute on the cyclic group via uint64 wrapping addition; the final reduction & (MODULO - 1) collapses cleanly).

What this DOESN'T claim

The patches in the v0.4.0 catalog are empirical Fourier corrections, not first-principles physics. They paper over a missing entry in the static RESONANCES table or a missing PN term. Each patch is a hypothesis — "this peak is a sin at this period, of this amplitude" — that the v0.5.x first-principles α derivation (Hamilton/Delaunay-variable Lagrangian around each resonance) should ultimately replace.

Whether a patch helps is then a measurable property: re-run the FFT-residual analysis with patches active, check the targeted peak shrunk. That experiment is one-line (apply_catalog_patch(...) then re-run de441_error_spectrum); the demonstration cost is bounded by the encoder-cycles of one full sweep (~6 s on the C native path, ~5 min in pure Python at 1024 samples × 30-d cadence).

How to use

from ephemerides_spectral import bridge

# Inspect the catalog
print(bridge.list_catalog_patches())

# Apply a bundled patch
bridge.apply_patch("jupiter-saturn-9.56yr-coupled")

# Check what's active
print(bridge.list_active_patches())

# Encode (the patch contributes to the result)
state = bridge.get_system_state(2451545.0 + 20 * 365.25, backend="bip")

# Author your own from FFT-diagnosed peaks
bridge.apply_custom_patch(
    name="my-earth-correction",
    kind="sinusoid",
    body="earth",
    amplitude_deg=0.93,
    period_days=1940.2,  # the v0.3.1 Earth peak at 5.31 yr
    notes="hypothesis: Earth aliased peak from a sub-Nyquist signal",
)

# Wipe back to v0.3.1 byte-exact baseline
bridge.clear_patches()

CLI parity is 1:1:

ephemerides-spectral patches catalog
ephemerides-spectral patches apply --name mars-7.96yr-diagonal
ephemerides-spectral patches active
ephemerides-spectral patches clear

What's next

  • v0.4.x phase F: C-side overlayshipped in v0.4.1. The ABI v2 surface (es_apply_patch / es_clear_patches / es_n_active_patches / es_get_patch_at) is live; cross-backend byte-exact parity verified.
  • v0.4.x first-principles α: replaces the empirical sinusoidal patches with derived modulation depths from Lie-series perturbation theory around each resonance. The patches in this catalog are expected to shrink in amplitude once the underlying physics is in RESONANCES.
  • Patch-shrinks-residual benchmark: an automated regression that re-runs de441_error_spectrum with the catalog patches active and asserts the targeted peaks shrunk by >= 80 % of the patch amplitude. This pins the catalog as useful, not just applicable.