diff --git a/NEXT.md b/NEXT.md index 4eeb59e..d179b35 100644 --- a/NEXT.md +++ b/NEXT.md @@ -77,45 +77,21 @@ Odin's `where N >= 0` is a predicate over a **value**, not a type. The `where` m type predicates (`ordered?`, `copyable?`). Whether it also takes value predicates over a length parameter is a separate question and should be answered deliberately rather than falling out of the implementation. -## To discuss: two decisions parked during the ownership discussion +## ~~To discuss: two decisions parked during the ownership discussion~~ — **both answered and built** -Both came out of the lane that closed the enum-layout and constant-checking gaps. Its work is sound and committed; -these are questions it raised rather than defects in it. +1. **The header is tracked now.** `vendor/raylib/raylib-5.5.h` is committed beside the package and + `vendor/raylib/headers` names it directly. **`FLAN_RAYLIB_H` is gone** — there is no variable to export and no + way for the check to silently not run, which is what `vendor/raylib/web/` being gitignored had been doing to + every worktree. The old argument for opt-in was that requiring a header makes a build need raylib-devel; that + dissolved once the header shipped with the repository. Measured before committing to it: a warm build of + `raylib-font.flan` is 0.10-0.11s against a 0.10s baseline, because the header read is cached and keyed like the + object cache. Verified it fires by putting `f64` where raylib says `int`. -### 1. The header check cannot run in a worktree, and nothing says so - -`.gitignore:60` ignores `vendor/raylib/web/`, so the vendored `raylib.h` exists in the main tree and **never appears -in a worktree checkout**. Every parallel lane that touches bindings is therefore checking against nothing, or -against whatever it can find. The lane that hit this found a copy in `~/.local/share/Trash` and validated it the -right way — `generate-c` rewrote `generated.flan` byte-identically before it changed anything, and the two headers -are md5-identical — but a header in the Trash is not a corpus, and the next lane may be less careful or less lucky. - -The options: - -- **Vendor `raylib.h` as a tracked file.** It is one header. `BUILT.md` records that requiring a header was - rejected so that "a build needs libraylib linkable and not raylib-devel installed" stays true — but tracking the - header in this repository is not the same as requiring one on the machine, since `headers` is still - `?${FLAN_RAYLIB_H}` and still opt-in. This looks like the answer and the old argument may not actually apply. -- **Fail loudly when `FLAN_RAYLIB_H` names a path that is not there.** Already the behaviour for a set-and-wrong - path; what is missing is any signal when the *default* corpus has vanished, which is the worktree case. -- Leave it, and put the warning in the agent briefs instead. Weakest — it depends on whoever writes the brief - remembering. - -### 2. A missing `bindings` line does not stop a build — a judgment call, flagged for review - -The lane split its findings by severity: - -- A **value mismatch or a missing C name** stops an ordinary build, the way `check_structs` does. -- A **coverage or typo finding about the `bindings` file itself** gates `generate-c` only. - -The consequence is the part to decide: a lane that adds a `defenum` to `raylib.flan` and forgets its `bindings` -line gets **no warning from a plain build**, only from `generate-c`. Its reasoning was that failing somebody's -build over a config file, in a message shaped like "your layout is wrong", is the wrong place to stop them. - -Arguable in both directions, and the argument against it is the one this whole feature exists for: an unchecked -enum member is exactly the silent wrongness the header read was built to catch, and "you have to remember to run -`generate-c`" is the same shape as "you have to remember to check the header by eye". Overruled by dropping the -`cmapping` guard in `lib/load.ml`. +2. **A `bindings` gap stops a build, and says which file to edit.** The findings stayed split by kind but both are + now fatal. The objection had really been to the *wording* — a coverage gap arrived wrapped in "the package + disagrees with the header", which describes a layout bug and sends the reader to the wrong file. Gating it to + `generate-c` instead meant an unchecked enum member went unreported on every ordinary build, and "remember to + run generate-c" is the same shape as "remember to check the header by eye", which is what this replaced. ## Decided 2026-09-13: generics by monomorphisation, checked abstractly, with `where` predicates @@ -260,45 +236,18 @@ which is an absence rule, and absence is a bad thing to give meaning to. length rather than only element type. Not in the spike unless it falls out for free; the spike is to report what it would cost. -## To discuss: five gaps the raylib examples hit and could not close +## ~~To discuss: five gaps the raylib examples hit and could not close~~ — **three closed, two live elsewhere** -Found by the lane that ported `core-2d-camera`, `core-scissor-test`, `core-window-flags`, -`core-world-screen` and `core-window-should-close`. None blocked those five. All five blocked -something else, and each is a language or checker question rather than a missing binding, which is -why they are here and not in a binding list. - -1. **A `(Ptr T)` returned from C cannot be indexed.** `indexed` in `lib/check.ml` accepts `Array` - and `Slice` only, so a C function answering `int*` is readable at element 0 through `deref` and - nowhere else. `LoadRandomSequence` is the case that hit it and `core_random_sequence` is - unportable until it moves. The question is what the answer should be: a length has to come from - somewhere before a pointer can become a slice, and C does not supply one. Possibly a - `(slice-from-ptr p n)` where the caller states the length and owns being right about it. - -2. ~~**An enum-typed `defstruct` field is refused by the layout check.**~~ **Closed.** The layout - check now accepts an enum where the header says `int`, symmetrically, and still refuses - anything that is not four bytes. `Camera3D.projection` is a `CameraProjection` again and the - `rl/camera-projection` helper is gone; `.projection :perspective` resolves at the construction - site, so the keyword half of the problem went away with it. See BUILT.md. - -3. ~~**The header check does not reach `defconst` or `defenum`.**~~ **Closed.** It reaches both. - `bindings` gained `enum`, `const` and `constant` lines that say what a Flan constant is called in - C; every mapped name is compared by value, and a name the mapping cannot find, a rule that - reaches nothing, and a `defenum` with no line at all are each reported rather than skipped. All - eight raylib enums and all 16 `ConfigFlags` bits check out against 5.5. See BUILT.md. - -4. **raymath is `static inline`, so there is no symbol to bind.** `Clamp`, `Vector2Add`, `Remap` and - the rest exist only in the header. `declare-c` has nothing to name. rlgl's matrix stack is - unbound for a different reason. Together they make `core_2d_camera_mouse_zoom` unfaithful rather - than merely awkward, so it was skipped. The options are writing the arithmetic in Flan, which is - what the prelude would do anyway, or compiling a small C file that re-exports them as real - symbols. - -5. **Four families are still refused by the importer for want of a `defstruct`.** `FilePathList` - (a `char**`, blocks `core_drop_files`), `Model`/`Mesh`/`Ray`/`BoundingBox` (the model and - 3D-collision families), and **function-pointer parameters** (`SetTraceLogCallback`, which blocks - `core_custom_logging`, and the audio stream processors). The first three are ordinary widening — - write the `defstruct` and they import. The function-pointer one is not, and is the interesting - one: it is the callback direction of the FFI, which nothing has needed yet. +- **Closed:** an enum-typed `defstruct` field is no longer refused by the layout check (an enum *is* an `i32`, and + the predicate is symmetric now, so the enum may be on either side); the header check reaches `defconst` and + `defenum` through name-mapping directives in `vendor/raylib/bindings`, so a wrong flag bit is no longer silent; + and the `Ptr`-indexing gap has its own queue entry at the top of this file, because it now blocks three + examples rather than being a note. +- **Still open, and both are here rather than in a queue entry because neither has a customer pressing:** + **raymath is `static inline`**, so `Clamp`, `Vector2Add` and `Remap` have no symbol to `declare-c` at all — a lane + measured the alternative and found writing the arithmetic in Flan cost nothing, which is what makes this a + non-problem rather than a gap; and **function-pointer parameters** are refused (`SetTraceLogCallback`, the audio + stream processors), which is the callback direction of the FFI and nothing has needed it yet. ## Queued, 2026-09-13 (second session) — everything four lanes left behind