Queue what four lanes left, and where each one wrote it down

Three handoffs at the root carry the detail; NEXT.md carries the queue.
F2's Emacs half is seven items with two traps in it, F1 leaves a second
unexplained flake and the sweep that would let its section be deleted,
F3 leaves the two tests its own change did not get. The raylib examples
were never started, and Tier 1 item 6 still has not moved.

Also the worktree problem, because it nearly cost two lanes their work:
at least two were handed out about 485 commits stale, on a tree without
lib/dev.ml. Both noticed. One that did not would look like it worked.
This commit is contained in:
Joseph Ferano 2026-09-13 12:22:17 +07:00
parent 6592cff743
commit a016c3f226

109
NEXT.md
View File

@ -1,3 +1,106 @@
## Queued, 2026-09-13 (second session) — everything four lanes left behind
Four lanes ran in parallel worktrees and three landed; all are merged into `dev-loop` and `dune test` is green on
two consecutive full runs. Each lane wrote a handoff at the repository root, and those files are the detail — what
is below is the queue, not the reasoning. **Delete a handoff when its list is empty**, the same rule this file
follows.
**A note on the worktrees, because it nearly cost two lanes their work:** at least two were handed out at `2c232dd`,
roughly 485 commits stale, on a tree where `lib/dev.ml` does not exist. Both agents noticed and reset to the branch
tip themselves. A lane that did not notice would produce plausible work against the wrong tree. Check
`git log --oneline -1` before starting in a worktree.
### From `HANDOFF-f2.md``pause` marking, the Emacs half
The daemon half is built and works over the wire: `Ast.mark_pause` splices a `(pause)` call pre-order with first-hit
wins, `wire.ml` reads `:pause (LINE COL)`, `session.ml` applies it after `Load.qualify_decl`, and `dev.ml` echoes
`:pause "LINE:COL"`. With no `:pause` field the old behaviour is byte-identical. **Seven items remain, in order**,
and the handoff names the function for each. The two that are traps rather than work:
- `eval_expr`'s 5-second `wait` loop in `dev.ml` returns a timeout error, which is exactly what a thunk stopped in
the break loop does — so it would report the working feature as a failure. It has to become three-way and check
`state t = Stopped` **only when a pause was requested**, because `test_dev.ml:519560` pins the current shape for
the no-pause case and that must stay byte-identical.
- `flan-eval-last-sexp` sends a raw `buffer-substring` with no line padding, unlike `flan-dev--text`, so buffer
coordinates do not survive that path. It needs `:pause t`, not a position.
One decision the lane had to make alone and that is worth knowing before touching it: **a whole `defn` cannot be
wrapped**, so marking one means stopping on entry. DISCUSS.md §9's "replace the span with `(do (pause) …)`" does not
work for its own first target.
### From `HANDOFF-f1.md` — the socket flake is fixed, and two things follow it
The fix is ordering, not timing: the test checked for `agent.sock` before completing any round-trip, and
`merged_serve` does not accept until its own 10s await on that path. Moving the existing `describe` request ahead of
the check took it from 2/8 failures to 0/10, and closed a second race that was burning the full 10s timeout.
`lib/dev.ml` was not touched.
1. **A different flake, seen once and unexplained.** `the daemon never listened` — that exact wording comes from
`test_emacs.ml:50` and `test_repl.ml:71`, not from the block that was fixed, whose other daemons all use
qualified wording. Probably the same class of bug, a bounded `await` on a socket path under parallel-dune CPU
contention, but that is a hypothesis. Loop `dune test` capturing per-binary output to pin it down.
2. **Decide whether `merged_serve`'s 10s warning path deserves a test.** `lib/dev.ml:2322-2326` is exercised by
nothing now that the unlink race is closed.
3. **Confirm at a longer sweep if 0/10 is too thin, then delete this file's "One flaky test, measured rather than
suspected" section.** It was deliberately left standing — it should go when someone confirms the fix, not in the
change that made it.
Two things already tried and rejected, so they are not re-derived: wrapping the check in the file's `await` helper
makes the test observe the file at the *earliest* instant and so more likely to lose the race, and no timeout needed
lengthening.
### From `HANDOFF-f3.md` — the watch is built; two tests are not
`spy-num` landed and `PORTING.md` Tier 1 item 5 is closed. The `spy` half already existed — the finding that
reframed the task is that the table, the agent commands, the buffer and the ghost text were all there at `344e571`,
and only the numeric accumulator was missing. A slot keeps count, min, max, last and mean; the window is since the
editor's last tick rather than cumulative, because cumulative min/max reach the session's extremes within seconds of
play and then never move again. Reset is its own message and never a side effect of reading, so the reader never
writes the table.
1. **`emacs/test-flan-watch.el` — a `watch-num-i64` fixture site.** The regexp fix is the one change in that lane
with no regression test. `flan-watch-ghost-call-regexp` allowed a single hyphenated segment, so `watch-num-i64`
backtracked to failure and a numeric watch showed *no* inline value while looking normal in the buffer.
2. **`test/test_dev.ml` — assert the `n=0` render.** `watch_render_num` in `runtime/flan_dev.c` has a branch for a
slot that was never sampled and nothing reaches it.
### Not started: five more raylib core examples
Ten are in `examples/`. Five more were queued and the lane was stopped before it wrote anything, so there is nothing
to pick up — only the brief. Pick for what they exercise of the bindings rather than for how they look, since the
point of the corpus is finding gaps in `vendor/raylib/raylib.flan`. Upstream is
`vendor/raylib/web/raylib-5.5/examples/core/`. `PORTING.md` §1's rule binds: a raylib function on a real per-frame
path is hand-written and header-checked, not left to the opt-in `FLAN_RAYLIB_H` import, because the default build
has no `FLAN_RAYLIB_H` and still has to draw. An enum value has to be added by hand whatever happens — the importer
generates functions and only functions.
### Still not started: frame rollback, `PORTING.md` Tier 1 item 6
The last item on that list, and the only one of Tier 1 that has not moved. Not a language feature: `restart-case`,
struct assignment and fixed-arrays-as-values are all built. What is missing is the worked example —
`snapshot`/`restore` callbacks beside the `continue` restart, the way `sand.flan` is the worked example for the
loop — plus a headless case on the `dune test` path. `bounds-condition.flan` shows an abandoned frame leaving
half-written state behind; this is what finishes that thought, and a bad index has landed in `continue` rather than
`exit(134)` since this morning, so it is worth more than it was.
One thing to get right rather than discover: an *answered* bounds failure runs the function's defers, because it
leaves through the same unwind path a `return` does. A restore has to be ordered against them deliberately, and
getting it wrong is silent rather than loud.
### Landed: sand.flan is the game the other ports are
765 lines to 201, at parity with `lisp/sand.lisp`, `clojure/src/fnm/sand.clj` and `src/fnm/sand.jank` in
`~/Development/fnm`. Audio, the brush textures and the embedded PNG, the render-texture scene, the HUD font, the
camera, the world cursor, the HUD and the input-state read-out are gone — none is in any reference version. The
single-use helpers are inlined. **The physics is untouched on purpose**: the three references disagree there, CL
using `landing-col`/`move-grain` and Clojure and jank using `step-cell` with velocity parking, so parity does not
name a target, and `settle` is what the pinned hash covers. It still prints `15595743031174623232` at `-O2`, `-O0`
and as a dev build, which is what says the inlining changed nothing.
Interactively the controls are now `r` and left-mouse only. `test_web.ml`'s assertion that brush.png's bytes reach
the wasm module went with the embed: `web-files.flan` is web-built and *run* under node and asserts the embedded
bytes print, which is the same property checked harder.
## Queued: an idiomatic layer over the generated bindings
Thin Flan-shaped wrappers **over** the generated bindings, not instead of them. The generated set stays honest to C —
@ -137,7 +240,11 @@ state holds fixed arrays or `Vec`s.
**raylib 6.0 is not urgent:** all seven struct layouts on the game's path and every enum value it touches are
byte-identical between 5.5 and the vendored 6.0.
### One flaky test, measured rather than suspected
### One flaky test, measured rather than suspected — **fixed, pending a longer sweep**
The ordering fix is in (`f80a7a9`): 2/8 failures before, 0/10 after. This section stays until someone confirms it at
a sweep wide enough to be worth the claim, then goes. See `HANDOFF-f1.md`. The original reading follows.
`test_dev.ml`'s first block fails about one run in four with *"the merged program never bound
…/agent.sock"*. **It is not new** — reproduced at `54027ca`, before any of today's work, at the same rate. It is a