Reproducible export¶
Any session exports to a self-contained .ipynb whose cells run in a plain Jupyter kernel
— with no HelioAI installed, no agent, and no sandbox.
What gets rewritten, and why¶
The code the agent runs is not the code you want to keep. Inside the sandbox it uses helpers that only exist there, so a raw dump would be a notebook that cannot run. The export rewrites the boundary:
| Sandbox | Exported |
|---|---|
load_data('bz') |
fetch_series('amda/imf_bz', '2005-01-16', '2005-01-18') |
load_data('bz_events') |
fetch_events(id, [[s1,e1], [s2,e2], ...]) over the OK events |
export('name', arr, units=) |
print(...) of the same summary, dicts summarised key by key |
clean(arr), magnitude(v), interp_to(...) |
the same helpers, inlined |
param_card(...), document_method(...) |
stripped — agent-only UI helpers |
fetch_series and fetch_events wrap spz.get_data rather than being it. Inside the
session, get_timeseries blanked the dataset's declared FILLVAL to NaN before the
sandbox ever saw the array. A bare spz.get_data() hands the raw sentinel — 99999.9
for Wind/SWE — to the very same arithmetic: the same code gave a mean of 5.0 in the
session and 50002 exported, with no error either way. The wrappers redo that blanking
and mirror the attributes load_data exposed (.units, .param_id, .missing_pct).
The rewrite is possible because datastore.py records the param_id, start and stop
behind every dataset key in a manifest, so a load_data call can be turned back into the
speasy call that produced it. Imports are added at the top, and anything the export
cannot resolve is left untouched rather than guessed at.
This came out of the first external demo. The reviewer's objection was blunt and correct: the code shown was not code he could take away and re-run. Rewriting the boundary was the answer.
What the notebook contains¶
- Setup — imports and any shims still required.
- One cell per analysis step, in order, as standalone code. A step that ran a shipped
recipe through
run_recipeis exported as its own lines only — the input bindings and the call — and the recipe's source sits once, in a collapsed cell just before its first use, with a line saying whether it is identical to the recipe shipped with the helioai installed now (sha256 of the text as it ran).run_recipe(name, inputs, call)in the setup cell executes that source the way the sandbox did, on a copy of the notebook's namespace with__name__set so a recipe's demo stays off. A session that ransuperposed_epochfive times used to export the same 385 lines five times; it now exports them once, and each run is a dozen lines. - Methods & data acknowledgements — every recipe and reference used, assembled by scanning the session's tool calls and the sub-agents' results, plus the data-provider acknowledgements.
- Attempts that did not run — scripts the session reported as failed, kept as
fenced prose rather than executable cells.
code_N.pyis written before it runs, so a raised attempt exported as a cell used to stop Run All before its fix.
Verifying it really runs¶
The claim is only worth something if it is checked. The repository ships
verify_export.sh, which copies a notebook into a temporary directory without the
data/ tree and executes it with nbconvert:
Running it outside the repo is the whole point — it proves the notebook depends on
speasy and public data, not on your local workspace.
Getting the code without leaving the session¶
The web UI's code panel shows the same standalone rewrite for each step, and the /code
endpoint returns it directly. You do not have to export the whole session just to copy one
analysis.
Why this matters beyond convenience¶
An agent that produces a number and a plot is a black box you have to trust. An agent that produces the script, the method citation and a runnable notebook is a tool a reviewer can check. That is the difference this project is built around — see Recipes and provenance for the other half.