diff --git a/NEXT.md b/NEXT.md index cd6658e..2225e6c 100644 --- a/NEXT.md +++ b/NEXT.md @@ -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:519–560` 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