Until last week every nativegate worked example lived in its
own repo: the ~4,800-line petro library that ships with the
project. A showcase like that is the easy case — whoever
writes the binding generator and whoever owns the example
library share a repo, a CI setup, and an author's assumptions,
and neither party is ever really surprised. So the generator
was pointed at someone else's code: netlib
specfun, S. Zhang and J. Jin's 1996 Fortran
collection of special functions (gamma, digamma, Dawson's
integral, error functions, Bessel I0/I1/K0/K1), fetched
straight from netlib.
The artifact this produced is
specfun-py,
and it is not a demo: the repository carries the upstream
.f files, a COMMITTED golden tape pinning the
return values of all 14 entry points, generated CMake and
pytest, and CI on ubuntu and macOS. The claim of most interest
to people who re-host numerical code is specific, so write it
exactly below:
- CI regenerates the bindings from the committed Fortran, from scratch, on every push — on ubuntu and macOS — and runs the generated pytest suite against the result.
- The golden tape pins the return values of all 14 entry points (rtol 1e-9, atol 1e-12), plus the SHA-256 of every upstream source file. A rebuild that moves any number, or an edit that touches any byte, fails the build.
Actual numbers
erf, checked independently against the CPython standard library itself and matching bitwise over the pinned point, is the cleanest single indicator:
DERF(1.0) = 0.8427007929497148 math.erf(1.0) = 0.8427007929497148
PSI(1.0) = -0.5772156649015329 (negated Euler–Mascheroni, 16 digits)
DGAMMA(0.5)= 1.772453850905516 sqrt(pi) = 1.7724538509055159
BESK0(1.0) = 0.4210244382407083 known value: 0.4210244382407083
DAW(1.0) = 0.5380795069127684 known value: 0.5380795069127685
EI(1.0) = 1.8951178163559368 known value: 1.8951178163559368
Thirty-year-old code agreeing with references to sixteen digits, with no Fortran written or edited here except one dialect choice, documented in PROVENANCE.md. erf is the cleanest single indicator: the check above is against CPython's own stdlib, not a re-paste of the same formula.
What broke — and why this matters
Three failures were produced by this contact, none of them invented; each is a contract hole the in-repo test suite never covered:
1. Service names that are not Python module names
ngate quickstart gamma.f --name specfun-gamma
scaffolded, generated, and produced
from ._native.specfun-gamma import … — invalid
Python. The compile-step gate in the generator that checks
generated code did refuse to write it, but three commands and a
scaffold directory later. The scaffold commands now reject the
name immediately, with the reason on the first line. This is a
bug everyone hits on their first try, which is exactly the
argument for third-party contact.
2. The CS/CD dialect trick
Netlib sources from this era tag machine precision by commenting every line in both dialects and asking the installer to comment out one:
CS REAL FUNCTION GAMMA(X)
CD DOUBLE PRECISION FUNCTION DGAMMA(X)
With both lines present, the fixed-form grammar sees a floating
continuation with no statement. fparser2 refuses the whole
file; the regex fallback accepts it and then finds no routine
to expose, because the FUNCTION statements are marked too.
Since nativegate 0.1.3 this is solved, not worked
around: `dialect: cd` (or `cs`) in nativegate.yaml — or
`--dialect cd` on quickstart — resolves the marking into the
build copy, the chosen half live in place and the other half
commented, with the untouched netlib bytes staying under
native/ so the golden hashes still describe the upstream
source. One hole in the first implementation (dialect-marked
statement labels like CD900 DGAMMA = RES) was
caught by actually building specfun-py from the pristine
downloads and fixed in 0.1.5. The full loader is documented in
nativegate's
Fortran guide.
3. Generated packaging assumed generate had run
Every commit of a service tree that never ran generate
breaks its own CMake reference to the INCLUDE-expansion
directory and fails with "no known rule to make it". The
consumer-side error was not even in the output nativegate owns.
Workaround documented; a template-level fix is tracked (DEFECTS
D11).
Why the golden tape is the load-bearing part
Four different stages stand between source and wheel: parse,
expand, bind, build. Each one can be wrong in a way that ships
a module that imports cleanly and answers with different
numbers. Nothing that ships with a typical binding generator
catches that, because the failure is invisible to import-time
checks. The golden record is the counterweight: 14 entry
points, tolerances, and a SHA-256 of every source byte. It
costs one golden record command and one CI job,
and it is what let us tell the three upstream failures apart
from "probably fine" with no Fortran reading on our part.