Changelog¶
All notable changes to fortranspire are documented in this file.
The format is based on Keep a Changelog and this project adheres to Semantic Versioning.
Unreleased¶
[0.2.1] — 2026-06-24¶
Patch release: pre-JOSS-submission hardening. No API breaks; the only default-behaviour change is the security pass below.
Security¶
run_shellMCP exposure is now opt-in via theFORTRANSPIRE_ENABLE_SHELL=1environment variable (fortranspire/tools/__init__.py). The ReAct agent previously shippedrun_shell(shell=True)inALL_TOOLS, reachable from theask_agentMCP tool — a path that would have exposed unauthenticated remote shell execution on any HTTP-deployed MCP server. Default-off closes the blast radius.MCP file/directory arguments are now jailed to the workspace root through a new
_jail()helper infortranspire/server.py, applied across the seven file-accepting tools (translate_kernel_gpu,translate_kernel,profile_kernels,analyze_kernels,explain_port_cost,build_call_graph,generate_docs). Resolves the directory listed inconfig.workspace_dirplus the colon-separated extra roots inFORTRANSPIRE_WORKSPACE_EXTRA_ROOTS. Auto-disabled in stdio mode (the IDE owns the trust boundary); enforced by default in HTTP/SSE mode. Override withFORTRANSPIRE_DISABLE_JAIL=1.
Added¶
.github/workflows/tests.yml— pytest runs on every push and PR. Fast job (no gfortran) on a Python 3.11 / 3.12 matrix; slow job (gfortran -fopenaccequivalence harness) on push and manual dispatch. README now shows a Tests badge alongside the JOSS draft one.JOSS draft paper musclé then trimmed for concision: Related Work section across three threads of prior art (deterministic source-to-source compilers and stencil DSLs / monolithic neural transpilers / agentic LLM workflows), Quality control section describing the equivalence harness, Limitations and roadmap section. Currently 1103 body words. The full pipeline architecture (8-stage table, LLM intervention rationale, agent-loop motivation) lives in
docs/concepts/architecture.md.
Changed¶
Repo reorganisation. Paper artefacts moved from the root to
paper/(paper/paper.md,paper/paper.bib). Container artefacts moved from the root tocontainers/(containers/Dockerfile,containers/Dockerfile.hpc,containers/apptainer.def,containers/docker-compose.yml). Two stray root-level markdown files moved into the Sphinx tree:OPTIMIZATION_REVIEW.md→docs/concepts/jax-optimization.mdandTUTORIAL_IDE.md→docs/integrations/ide-interactive-mode.md. The repo root now holds only the five.mdfiles (README,ROADMAP,CONTRIBUTING,CODE_OF_CONDUCT,SECURITY) expected by GitHub / OSSF conventions.uv tool installinstall instructions corrected touv tool install 'fortranspire[mcp]'in README anddocs/getting-started/with-mistral-vibe.md. The[mcp]extra is required for the stdio handshake; the previous instructions would have produced afastmcp-missing crash on the first vibe message.README walkthrough link now points at the full PHYEX walkthrough (
docs/integrations/mistral-vibe.md) rather than the 60-second quickstart, matching the link text.Hardcoded
/opt/homebrewbrew path removed from the vibe walkthrough —vibeis onPATHafterbrew install, which is/opt/homebrewon Apple Silicon and/usr/localon Intel Macs.agent-*/run-mcplegacy CLI names swept out ofdocs/getting-started/quickstart.md,docs/getting-started/installation.md,docs/concepts/legacy-documentation.md, anddocs/concepts/mistral-integration.mdin favour of the unifiedfortranspire <verb>form.EU-regulation backing for “sovereign” added to
docs/concepts/llm-endpoints.md: GDPR (Regulation (EU) 2016/679), AI Act (Regulation (EU) 2024/1689), NIS2 Directive (Directive (EU) 2022/2555) cited with permanent EUR-Lex URLs. The term is now used in the regulatory sense throughout the documentation.Paper tone pass. Dropped marketing-flavoured phrasing (“sovereign European”, “leadership-class GPU-equipped supercomputers”, “minority partner”, “keeping the engineer in the loop”, “in practice”); fixed the 6-vs-8-stage contradiction with the README diagram; reworded the orphan self-reference at the end of the agent-loop section; corrected the over-claim that all 11 patterns are covered by fixtures (only
wave_kernelsships today).Pytest default gate.
pyproject.toml[tool.pytest.ini_options]now setstestpaths = ["tests"](so the Loki redistribution undervendor/is no longer collected) andaddopts = "-m 'not slow'"(so the slow gfortran-driven equivalence harness is opt-in by default).
Removed¶
fortranspire/main.py— dead REPL entry point referencing removedCodeAgentimport path and a removedconfig.ollama_base_urlattribute; no caller, no test exercised it.fortranspire/brain/<uuid>/scratch/— local Loki-introspection experiments that were accidentally shipped in the wheel.Dockerfile.ciandApptainer.analyze— referenced the removedlocal_code_agent/path; replaced by the consolidated container files undercontainers/.Bibliography orphans dropped from
paper/paper.bib:@clausecker2018claw,@gpufort2023,@gfortran,@flang,@langgraph2024. Their content moved todocs/concepts/architecture.md(plain-text mentions, not bibtex citations).
Fixed¶
codemeta.jsonversion field synchronised to the actual release version (was stuck at0.1.3after the 0.2.0 cut).integration/le-chat-connector.jsonconnector version synchronised to 0.2.0 / 0.2.1.docs/conf.pySphinx release fallback for unbuilt envs:0.1.0→0.2.1.Dangling reference labels in
docs/changelog.md: added the[0.1.2]/[0.1.3]/[0.2.0]link definitions and bumped the[Unreleased]compare base fromv0.1.1to the current release.
0.2.0 — 2026-06-24¶
Minor release. Mistral-vibe becomes a first-class integration surface alongside Claude Code, with end-to-end stdio support proven on real Météo-France PHYEX kernels.
Added¶
fortranspire mcp --stdiotransport (issue #39). The MCP server now speaks the JSON-RPC stdio framing in addition to SSE. mistral-vibe and Claude Code Desktop can spawn it as a subprocess — no port, no auth middleware, local-by-construction.MISTRAL_API_KEYis propagated to the spawned process via the host’s[mcp_servers.env]block in~/.vibe/config.toml.Stdio smoke test at
tests/server/test_mcp_stdio.py— pytest-driveninitialize→notifications/initialized→tools/listhandshake against the installed console script; asserts the 9-tool surface intact.Equivalence harness on real kernels (issue #45). Test suite at
tests/test_equivalence_real_kernels.pyand fixture directory attests/fixtures/equivalence/<kernel>/(original.f90,openacc.f90,driver.f90,TOLERANCE.md). For each kernel, the harness compiles both the serial and OpenACC variants withgfortran(the OpenACC one viagfortran -fopenacc), runs both binaries on a deterministic input, and assertsnp.allclosewithin a per-kernel tolerance. First kernel landed:wave_kernels(two 2D FD stencils, 20 time-steps). Tests are markedslowand skip when gfortran is not in PATH.PEP 735 marker registration for
slowin[tool.pytest.ini_options].PHYEX walkthrough in
docs/integrations/mistral-vibe.md— guided 3-step demo (triage → call-graph → Phase-1 port) onsrc/common/turb/mode_compute_function_thermo.F90. Validates the end-to-end natural-language flow on a real Météo-France kernel.docs/getting-started/with-mistral-vibe.md— 60-second quickstart page for readers coming from LinkedIn / external announcements.
Changed¶
Mistral-vibe integration documentation rewritten around the stdio transport (recommended path). HTTP/SSE moved to “alternative” section with LaunchAgent template for permanent-service deployments.
README promotes the mistral-vibe quick-start above the architectural detail (better signal for first-time visitors).
0.1.3 — 2026-06-22¶
Patch release. Restores a single-step PyPI install by depending on the
new loki-ifs PyPI distribution.
Added¶
loki-ifs>=0.3.7is now a core dependency.pip install fortranspireresolves Loki in one step.loki-ifsis an unofficial PyPI redistribution ofecmwf-ifs/loki@0.3.7published under our control (source: https://github.com/maurinl26/loki-ifs). The Python import name remainsloki, so all parser code is unchanged.
Changed¶
Removed the PEP 735
[dependency-groups] lokiblock and the[tool.uv.sources]Loki override. Both were workarounds for the v0.1.2 era when Loki had to be installed by hand.uv syncnow resolves Loki transparently from PyPI.
Documentation¶
README +
docs/getting-started/installation.mdrewritten to reflect the single-steppip install fortranspire. The “two-step install” callout is replaced with an explanation of howloki-ifsrelates to upstream ECMWF Loki and how to override it if you want a different Loki version.
0.1.2 — 2026-06-22¶
Patch release. Unblocks the first PyPI upload by removing the direct git-URL dependency on ECMWF Loki, which PEP 715 forbids in published metadata.
Changed¶
ECMWF Loki is no longer a declared dependency. PyPI rejected the v0.1.0 / v0.1.1 wheels with
400 Can't have direct dependency: loki @ git+https://github.com/ecmwf-ifs/loki@0.3.7. Loki is not on PyPI under that name (the PyPIlokiis an unrelated astronomy package), and PEP 715 disallowsgit+https://…URLs inRequires-Dist. fortranspire now ships with no Loki entry; users install it in a second step:pip install fortranspire pip install "loki @ git+https://github.com/ecmwf-ifs/loki@0.3.7"
The parser already falls back to a regex frontend when Loki is absent, so the package imports and runs without it —
analyzeis simply more accurate when Loki is available.Local development uses a PEP 735 dependency group. A new
[dependency-groups]block declaresloki = ["loki"]and[tool.uv.sources]pins it to the git tag. Developers runuv sync --group lokito pull Loki in for local work; this metadata is not published to PyPI.[tool.hatch.metadata] allow-direct-references = trueremoved. No longer required now that no direct URL dep remains.
Documentation¶
README +
docs/getting-started/installation.mdrewritten with the new two-step install path and an explanatory callout on why Loki is separate.
0.1.1 — 2026-06-22¶
Patch release. Fixes two cosmetic issues found by the post-tag smoke test of the v0.1.0 wheel.
Fixed¶
Unified
fortranspire <verb>CLI no longer prints the legacyagent-*deprecation notice when dispatching togpu,translate, orprofile. Root cause: the dispatch table pointed atrun_*wrappers that include the deprecation message. Split each into an internal_*_main()function (called by the unified CLI, no deprecation) and a legacyrun_*wrapper (called by theagent-*scripts, prints the notice). The other 10 subcommands were already wired this way.Package docstring in
fortranspire/__init__.pywas a relic from the pre-rename project (referenced “Local Code Agent — LangChain + Mistral NeMo 12B via Ollama”). Replaced with the current project description and added__version__ = "0.1.1".
0.1.0 — 2026-06-22¶
First public release on PyPI. The project was renamed from coding-agent /
local-code-agent to fortranspire and shipped under a single
fortranspire <verb> entry point covering 13 subcommands. The release
bundles the JOSS submission package, a Sphinx documentation site, and
Zenodo / CITATION.cff / codemeta.json metadata for academic
citation.
The release closed 19 issues opened during the development sprint (#1 → #20), summarised below by theme.
Added¶
Unified CLI (#8)¶
fortranspire <verb>console script — single entry point with subcommand dispatch (cargo / kubectl / git pattern). 13 subcommands:analyze,doc,explain,format,graph,diff,report,bench,gpu,port-batch,translate,profile,mcp.Legacy
agent-analyze,agent-doc,agent-explain,agent-format,agent-port-batch,agent-gpu,agent-translate,agent-profile,run-mcpkept for backwards compatibility. Each prints a one-line stderr deprecation notice pointing at the new form. Removal scheduled for 0.3 (pinned in the message so users can plan).
Standalone subcommands — no LLM, CI-friendly¶
fortranspire analyze— Loki-only static analyzer. 11 rules covering COMMON blocks, SAVE, I/O in kernels, missing IMPLICIT NONE / KIND, POINTER, derived types, suspected loop-carried deps, plus a toolchain check (compiler on PATH + OpenACC capability classification fornvfortran/gfortran/ifx/flang/lfortran). Outputs human-readable text, JSON, or SARIF 2.1.0. Ships a GitHub Actions workflow that uploads SARIF to Code Scanning, plus a lightweightApptainer.analyzerecipe for HPC sites.fortranspire explain(#14) — pre-flight cost + risk estimator. Runs Loki only and sums per-stage token estimates against the price table from #5 to produce a stakeholder-ready Markdown report (Summary+ per-file breakdown + FORT001-009 risk roll-up). Zero LLM calls, zero tokens.fortranspire format— Fortran source formatter wrappingfprettifywith sensible defaults (lowercase keywords, 2-space indent). Works standalone and as a post-step of the Phase 1 pipeline.fortranspire graph(#15) — module-level call-graph report. One Mermaidflowchart LRper file, external callees marked with a dashed border so cross-file edges are obvious.--narrateadds a 1-paragraph LLM intro per file.fortranspire diff(#19) — semantic before/after viewer. Classifies each changed line into[pragma],[purity],[type],[refactor],[common],[save],[other]. Terminal output with per-category ANSI colors;--htmlwrites a self-contained single-file page (no CDN, no JS) suitable for PR review or air-gapped reviewer machines.fortranspire report(#20) — single-file HTML audit dashboard per kernel. Embeds original source, refactored MODULE, OpenACC driver, Cython wrapper + header, validation log, and equivalence harness from #11; each section collapsible via<details>(pure HTML, no JavaScript). Pygments syntax highlighting if installed.fortranspire bench(#17) — pipeline-output metrics + regression detector. Counts files, routines, pragmas, generated bytes, LLM tokens (from observability traces), gfortran compilation timing.--compare baseline.jsonexits 1 if any metric regresses beyond--tolerance(default ±10 %,--strict= ±5 %).
LLM-driven subcommands¶
fortranspire doc— documentation generator. Idempotent inline Doxygen!>blocks above each routine (@brief+@details+@paramper argument). With--sphinx/--site-only, also generates a self-contained Sphinx site (Furo + MyST + sphinx-designsphinx-togglebutton) with a Show source dropdown per routine.
--no-llmruns Loki signature extraction only.
fortranspire gpu --gpu-pragma {acc,omp}(#18) — Phase 1 port, now supporting two GPU directive families.acc(default, backwards-compatible) emits!$acc parallel loop/!$acc data.ompemits!$omp target teams distribute parallel do/!$omp target data— broader compiler audience (gfortran 13+, nvfortran -mp=gpu, ifx, AMD AOMP, IBM XL).fortranspire port-batch(#13) — parallel Phase 1 port across many files. Per-file output isolation viaContextVarso concurrent workers don’t clobberoutput/fortran_gpu/. Default concurrencymin(4, cpu/2), configurable via--concurrency.
Server, observability, security¶
fortranspire mcp— FastMCP server exposing every transformation as an MCP tool over HTTP/SSE.Telemetry + cost tracking (#5) —
fortranspire/observability/package with a JSONLTracer, per-model price table (Mistral La Plateforme rates), and a LangChainBaseCallbackHandlerthat captures token usage even whenwith_structured_output()swallows the raw message. Per-tenant accounting viaFORTRANSPIRE_TENANT_ID. Optional OpenTelemetry export viaFORTRANSPIRE_OTEL_ENDPOINT.MCP server hardening (#10) —
fortranspire/security/package with a JSON-file token registry (legacyAPI_KEYenv auto-promoted), per-tenant rate limiter (sliding window), HMAC-signed audit log (FORTRANSPIRE_AUDIT_SECRET). Backwards-compatible fallback to no-auth when nothing is configured.Content-addressed LLM cache (#7) —
fortranspire/cache/package wiring an SQLite-backedBaseCacheas LangChain’s process-wide LLM cache. Identical re-runs return instantly with zero token cost. LRU eviction at configurable byte cap (default 1 GiB,FORTRANSPIRE_CACHE_MAX_GB). Disable withFORTRANSPIRE_CACHE=off.
LLM backend dispatch (#9)¶
Native
mistralaiSDK auto-selected when the endpoint matches Mistral La Plateforme — gets first-class function calling, JSON mode, streaming, Mistral safety guards. Falls back gracefully toChatOpenAIfor self-hosted vLLM / TGI / Ollama on an OpenStack tenant. Override viaFORTRANSPIRE_LLM_BACKEND={mistral,openai}.
Equivalence harness (#11)¶
Auto-generated CPU↔GPU test file per ported kernel (
output/tests/test_<kernel>_equivalence.py). Loads f2py-wrapped CPU reference and Cython-wrapped GPU port, runs random inputs, assertsnp.testing.assert_allcloseon every INTENT(OUT|INOUT). Skips cleanly when either build is missing. Tolerances configurable viaFORTRANSPIRE_TOLERANCE_ATOL/RTOL.
Prompts, schemas, i18n¶
Externalized prompts (#3) — every system prompt moved out of Python source into
fortranspire/prompts/<name>/<lang>/<version>.md. Loaderload_prompt(name, version=, lang=, **vars)with versioning (so prompt evolution is auditable), locale fallback (FR → EN), and per-deployment override viaFORTRANSPIRE_PROMPTS_DIR. LRU-cached file reads.French translations (#16) — 11 new FR prompt variants covering all 6 LLM-emitting stages. Activate with
FORTRANSPIRE_LANG=frorload_prompt(..., lang="fr"). Initial audience (Météo-France, CEA, EDF R&D, ENM Toulouse) is francophone; Mistral handles French exceptionally well at zero quality cost.Pydantic structured outputs (#4) — every LLM-emitting stage now returns a typed Pydantic object via
llm.with_structured_output(SchemaModel)instead of regex-parsing free-form text. Schemas:ExtractorOutput,OpenACCKernelOutput,OpenACCDriverOutput,CythonPyxOutput,CythonHeaderOutput,DocRoutineOutput. v2 prompts emit JSON; v1 kept as legacy fallback for backends without JSON-schema mode (triple fallback in the extractor for robustness).fortlsoracle (#12) — the Fortran Language Server is now wired into the documenter prompts. Neighbouring-symbol context fetched per routine reduces hallucinations on names/relationships. Graceful no-op whenfortlsis missing.
Architecture refactors¶
LangGraph nodes split (#2) —
translation_graph_phase1.py(~1400 lines) decomposed into per-node modules underfortranspire/agent/nodes/(init,parser,extractor,pure_elemental,openacc,cython_wrapper,equivalence_harness,validation). The main file is now ~80 lines of graph wiring. Public surface preserved via re-exports for backwards compat.
Documentation & publishing¶
JOSS submission package —
CONTRIBUTING.md,SECURITY.md,CODE_OF_CONDUCT.md(Contributor Covenant 2.1),paper.md,paper.bib,.zenodo.json(DOI metadata, ORCID0009-0004-8117-4850, affiliation: Independent Researcher),.readthedocs.yaml, and.github/workflows/draft-paper.ymlto render the JOSS draft PDF on every push.CITATION.cff+codemeta.jsonat the repo root — GitHub surfaces the “Cite this repository” button;codemeta.jsonis the semantic-web companion consumed by Zenodo, Software Heritage, and academic search engines.Sphinx documentation site under
docs/(Furo + MyST + extensions), with sections for installation, quickstart, configuration, pipeline architecture, Fortran patterns, LLM endpoints, Mistral integration, Le Chat connector preparation, and the standalone documentation feature.Mistral integration documentation —
docs/concepts/mistral-integration.mdcovers four integration paths (LLM consumer, per-stage models, MCP provider, Le Chat connector directory). Connector manifest prepared inintegration/le-chat-connector.jsonand documented indocs/concepts/le-chat-connector.md. Runnable smoke test atexamples/mistral_agents_api_smoke_test.py.
Changed¶
Project renamed from
coding-agent/local-code-agentto fortranspire. Python package directory, PyPI distribution name, GitHub URLs, docs, CI workflows, and the inline!> @generated_bymarker were all updated. Usepip install fortranspireandfrom fortranspire.agent....Loki dependency repointed from a hard-coded local path (
file://localhost/Users/loicmaurin/PycharmProjects/...) to a pinned git tag (git+https://github.com/ecmwf-ifs/loki@0.3.7). The repo is now installable on any fresh machine.Dependencies split into extras: default
uv syncinstalls ~50 MB (Loki + NumPy + LangGraph + python-dotenv) — enough foranalyze,explain,format(--no-llm),graph,diff,report,bench. Pull[gpu]for the LLM stack + Cython + fprettify + fortls,[mcp]for the FastMCP server,[jax]for Phase 2, or[all].LLM model selection per pipeline stage —
MISTRAL_MODEL_REASONING(defaultmistral-large-latest, used byextractorandopenacc) andMISTRAL_MODEL_CODE(defaultcodestral-latest, used bycython_wrapper). LegacyMISTRAL_MODELstill works as a single fallback. Cuts cost per kernel ~50 % on default settings.Pipeline node order — the equivalence-harness node from #11 is inserted between
cython_wrapperandvalidation, generating the test harness file before the final validation step.
Fixed¶
Parser missed module-contained routines —
parser_phase1only looked atsource.routines(top-level), which is empty for modern Fortran 90 codes that wrap subroutines inmodule ... contains ... end module. Now also walkssource.modules[].subroutines.Off-by-one in
agent-docline numbers —\sin the routine declaration regex was matching\n, letting the match start on the preceding blank line. Same bug caused the indent capture to take a newline as whitespace. Switched to[ \t]*so docstring blocks land above the right line with the routine’s own indent.LangChain imports in
translation_graph_phase1.pywere unconditionally loaded at module import — forcingagent-analyzeand friends to require[gpu]. Now lazy inside the three LLM-using node functions, souv sync(core only) suffices for every no-LLM command.agent-port-batch --helpcrashed on the core-only install for the same reason. Same lazy-import fix applies infortranspire/agent/cli.py.Cache
langchain.globalsimport path — modern langchain movedset_llm_cachetolangchain_core.globals; the install hook follows the new location.
0.0.x — pre-rename snapshot (2026-06-17)¶
The pre-rename project was published informally as coding-agent. It
shipped the initial MCP server (run-mcp), the legacy CLI
(agent-gpu, agent-translate, agent-profile, agent-pipeline),
the original Phase 1 and Phase 2 LangGraph pipelines, Loki-based
deterministic Fortran AST analysis, Docker / docker-compose / Apptainer
recipes, and the pivot from Azure to a sovereign Mistral endpoint
(commit ccfe221). All of that is preserved under fortranspire 0.1.0.