The Model That Refused to Move
What it takes to put a governed agent loop inside a Fortran 2008 simulation — and why two partial surfaces matter
A few months ago, someone asked a question that reframed the project:
“What does an agent framework look like for a Fortran model that has been validated for twenty years?”
It is a good question because it exposes the assumption underneath most agent tooling. We assume the agent lives in Python, the model is a service, and the integration is an API call.
For a lot of software, that is fine. For a validated numerical model, it is a revalidation program.
A Fortran model is not just syntax. It is a numerical kernel, a set of libraries, a performance profile, a set of regression cases, and often a scientific or regulatory validation history. Porting it to Python is not a refactor. It is a new implementation, with a new set of failure modes and a new validation burden.
So we tried a different answer: keep the model in Fortran, and put a governed agent loop inside the same process.
The default answer
The default answer is either “port it to Python” or “wrap it in a Python service.”
The first is a multi-month or multi-year revalidation project. It may be the right decision for other reasons, but it is not a prerequisite for using an agent.
The second is often the right engineering choice. A Python sidecar isolates the agent runtime and avoids touching a fragile simulation code. But the governance boundary moves with the agent. Policy, tool authorization, checkpoints, audit events, and human approval now live in the sidecar. Every control has to cross a process or network boundary.
That can be made to work. It is just a larger integration surface than it first appears.
The third path
PRAXO-CORE’s Fortran SDK is a native Fortran 2008 implementation. It is not a bridge to Go, C, Python, or another SDK. The agent loop, JSON parsing, schema validation, tool-call parsing, context estimation, checkpoint envelopes, memory, policy, events, middleware, and PII redaction are native Fortran modules.
The core call is small:
options%provider => provider
options%tools => tools
options%events => sink
call run_agent(options, state, 'hello', 'caller-generated-run-id', result, error)
if (error%failed()) print *, error%codeCallback targets must remain alive for the run. Each run has local history and does not mutate the input session or options. Sharing a mutable model, registry, store, or sink across threads requires caller-managed synchronization.
The evidence is specific: 62 normative vectors across 11 groups, native regression tests, an API snapshot, and the fortran-conformance CI job. The source tag is fortran/v0.2.0.
GNU Fortran is the target. Python 3.10 or later is used by the test driver; every behavior under test runs in the compiled Fortran implementation. A compiled consumer does not need Python at runtime.
The two partial surfaces
The honest version of the story is more interesting than “full parity.”
Get Huiwen Han’s stories in your inbox
Join Medium for free to get updates from this writer.
Two surfaces are explicitly partial in the parity inventory:
- Asynchronous stream support.
praxo_streamprovides the part record and six contract kind strings. It is not an async runtime. - The bounded JSON Schema subset. It covers the vector-defined behavior, but it is not full JSON Schema.
The rest of the declared Core scope passes. The limits are explicit:
- JSON input: 262,144 bytes, 4096 nodes, 64 nested containers.
- Invalid UTF-8, lone surrogates, malformed numbers, and trailing data are rejected.
- Schema patterns: bounded ASCII subset; unsupported patterns fail explicitly.
- Path policy: lexical normalization, not OS confinement.
- Plugin parsing: manifest validation only; no execution or signature verification.
- PII redaction: email, SSN, credit-card, and phone patterns. Not a complete compliance program.
- Multimodal message blocks and optional Go hooks not in the parity inventory are not implemented.
These are not footnotes. They are the boundary that makes the native claim credible.
Why this matters
A research group or regulated model owner does not need an agent that can talk about Fortran. It needs an agent that can participate in a process without bypassing the controls around that process.
That means policy hooks, tool authorization, checkpoints, structured events, and human approval. If the loop lives in the Fortran process, the number of places where a policy decision can get lost is smaller. The model, the tool, and the approval boundary can stay close together.
This does not automatically make the system reproducible or secure. It reduces one kind of boundary crossing. The rest is engineering and validation.
What not to claim
Do not say:
- “Full Go parity in Fortran.”
- “Async streaming.”
- “PII compliance.”
- “Install from a package registry.”
- “MPI/OpenMP/GPU included.”
- “Production-ready.”
- “Python is obsolete.”
The Fortran SDK is a source release with two partial surfaces. That is a legitimate engineering statement. It does not need to be inflated.
An invitation
The interesting question is not “Can Fortran call a model?” It can.
The interesting question is whether a governed agent loop can be made dependable in the process that already owns a validated numerical model — and whether the partial surfaces can be stated clearly enough that an operator trusts the failure modes.
If you run Fortran simulation, research, or numerical code, I would value the hard questions. What would you test first? Which boundary should be native, and which should stay behind a bridge? What would you need to see before letting an agent call a production tool from Fortran?
Repo: https://github.com/axisvela/praxo-core Fortran docs: https://axisvela.github.io/praxo-core/languages/fortran/
That conversation is more useful than another porting demo.








