diff --git a/BUILT.md b/BUILT.md
index b6b7d8d..2152c32 100644
--- a/BUILT.md
+++ b/BUILT.md
@@ -162,6 +162,8 @@ described is refused with that reason, so `vendor/raylib` describing thirteen
structs is what makes the import thirteen structs wide, and describing a
fourteenth widens it. Of raylib 5.5's 581 functions, 256 import, 153 are
refused, and 172 are left alone because the package already binds them by hand.
+(253 and 156 once `bindings` excludes raylib's three allocator entry points —
+see the next section.)
Not generating `defstruct`s is what makes the check possible at all. Generate
them and the header becomes the authority on layout, and comparing the
@@ -355,6 +357,93 @@ header cache and the same build is 0.92s against 0.83s. So the header read is
compiling a shim with 428 wrappers in it, which is the object cache's business
and already warm after one build.
+### The bindings are committed now — `generated.flan`, `bindings`, `flan generate-c`
+
+The section above reads the header at build time, behind an opt-in
+`?${FLAN_RAYLIB_H}` in `headers`, because a build should need libraylib
+linkable and not raylib-devel installed. That opt-in was doing two jobs and
+only one of them was defensible: it decided whether a *check* ran, which is
+fine to make optional, and it decided whether a package had 172 bindings or
+428, which is not. `DrawTexturePro` being reachable only by exporting an
+environment variable is the shape of that second job, and it blocked a real
+game — see PORTING.md.
+
+**Generate once, commit the result, regenerate when raylib moves.**
+`flan generate-c vendor/raylib` reads the header named by `headers`, writes
+`vendor/raylib/generated.flan`, and that file is checked in. No header is
+needed by anybody: every build gets all 425 declarations, they are greppable,
+and they show up in a diff when the library moves. **Caching is not the
+argument** — the dump is already cached on disk and in memory, so a build that
+reads a header pays for it once either way. The argument is the dependency and
+the diff.
+
+**What it costs is the check, so regeneration runs it and the check gates the
+write.** A file on disk has no second opinion, so nothing compares the bindings
+against the library on an ordinary build any more. The one function that writes
+`generated.flan` therefore compares first — every `defstruct` against the
+header's record, every hand-written `declare-c` against the header's signature
+— and writes nothing when they disagree. It is not possible to regenerate
+without comparing, because there is no other way to write the file. Pointed at
+the 5.1-dev header on this machine while the package is written for 5.5, it
+reports the same ten real differences the diff above found and writes nothing.
+
+**The 172 hand-written lines stay, and the reason is not caution.** It was
+tempting to delete them: 136 of the 172 are exactly what the kebab rule would
+have produced, and the other 36 could be spelled as name overrides, so the
+generated set really is a superset. The argument against is the one this
+section is about. Everything the generator emits agrees with the header *by
+construction* — the declaration and the prototype come from one dump — so
+diffing generated output against the header it came from is a tautology, and
+replacing the hand-written set would quietly reduce the signature half of the
+check to nothing. The hand-written lines were transcribed from raylib's
+documentation by a person; they are the only declarations in the package a
+header can actually contradict. All ten of the 5.1-dev differences came from
+them. They are not a parallel set to maintain — they are the second opinion,
+and the check is what maintains them.
+
+The opt-in did not go away, it stopped deciding anything important. With
+`FLAN_RAYLIB_H` set, an ordinary build still reads the header, and since every
+C symbol is now bound — by hand or by generation — the importer generates
+nothing and the read is purely the check. It is a *better* check than before:
+425 declarations rather than 172, because `generated.flan` is a package file
+like any other and is checked like one.
+
+**`bindings`, beside `headers`, is what survives regeneration.** A committed
+generated file cannot be hand-corrected — the next run overwrites it and the
+edit is destroyed without anybody being told, which is the worst shape an edit
+can have — so the corrections have to live somewhere regeneration *reads*. Two
+directives, which are the two things the header cannot decide: `exclude No library header is read, deliberately, so a build needs the shared library to be
-linkable and not the No library header is read during a build, deliberately, so a build needs the shared
+library to be linkable and not the -devel package to be installed.
+-devel package to be installed.
Guaranteed: the C typedef
and the Flan struct come from the same defstruct, so they cannot disagree,
and clang type-checks the wrapper against the generated prototype.
Trusted: that the defstruct matches the library's real
struct, and that the declare-c signature is the function's real signature.
A scalar's width now carries ABI weight — f64 where the library says
-float emits double, and the library reads garbage.float emits double, and the library reads garbage. That
+trusted half is what the generator below checks.
Writing a binding per function by hand does not scale past the ones a program
+happens to call, so most of raylib's package is not written by hand. A package
+directory may carry a headers file naming the library's own C header;
+flan generate-c <package-dir> reads it with
+clang -Xclang -ast-dump=json, turns every function it can represent into
+the same declare-c line a person would have written, and writes them to
+generated.flan in the package — which is committed.
$ export FLAN_RAYLIB_H=/path/to/raylib-5.5/src/raylib.h
+$ flan generate-c vendor/raylib
+wrote vendor/raylib/generated.flan: 253 declarations, 156 refused, of 581 functions.
+Every defstruct and every hand-written declare-c agrees with it.
+
+Committing the output rather than generating at build time is what keeps the +no-header property honest: the declarations are in the repository, so every build gets +all of them, they are greppable, and they show up in a diff when the library moves. The +argument is not caching — the clang dump is already cached on disk and in memory.
+ +Regeneration is the check. The cost of committing the output is that
+nothing compares the bindings against reality on every build any more, so the one
+function that writes the file compares first and refuses to write when the
+package and the header disagree: every defstruct against the header's
+record, and every hand-written declare-c against the header's signature.
+Pointed at a raylib 5.1-dev header while the package is written for 5.5, it reports ten
+real differences and writes nothing — which is exactly the silent version skew a
+generated file would otherwise bake in and make look reviewed.
This is also why the hand-written bindings are kept rather than replaced by generated +ones. Everything the generator emits agrees with the header by construction, so diffing +generated output against the header it came from proves nothing; the hand-written lines +were transcribed by a person, so they are the only declarations a header can actually +contradict. All ten of those differences came from them.
+ +A committed generated file cannot be hand-corrected — the next regeneration destroys
+the edit without telling anybody — so the corrections live in a bindings
+file beside headers, which is read while the declarations are made.
+Two directives:
# raylib's own malloc/realloc/free, which would be a second untracked heap
+# behind three innocuous Flan names.
+exclude Mem*
+
+# The kebab rule gives is-window-ready. Lisp spells a predicate with a ?.
+name IsWindowReady window-ready?
+
+An excluded function still says it was excluded rather than going quiet, and a name
+override changes only the Flan face — the C symbol is kept verbatim in the declaration,
+so it is still what is called and still what the check compares. Renaming is also the
+way out of a collision: Spin2D and spin2d both kebab to
+spin-2d, so neither takes the name, because which one won would otherwise
+depend on the order the header happens to declare them in.
Anything neither directive can express is a hand-written declare-c in the
+package's own source, which wins over the generated file and is left alone by the
+generator. That is the escape hatch for a signature the importer gets wrong and for a
+Flan face the header cannot describe — raylib keeps two, each a raw binding wrapped by a
+Flan function of the same name, one taking a slice and one answering with an
+Option.
Everything the boundary cannot represent is refused by name with the reason, rather
than half-supported: an Option, a union, a fixed array, a map, a returned