* Stuff I've found ** Why do I need to call flan-dev to open another window? let: flan: the program exited; restart flan dev ** I can't eval a toplevel defvar, need to eval-defun (C-c C-c) For Flan’s intended live-program workflow, C-x C-e on any complete top-level form should do the natural thing: - expression → compile/run temporary thunk; print its value - defn, defvar, defmacro, etc. → compile/install it; report what changed The compiler already has both paths. The current split is an editor/UI artifact: C-x C-e is wired directly to eval-expr, while C-c C-c is wired to declaration reload. It is not a fundamental limitation. A good fix would make C-x C-e context-aware: if the enclosing form is top-level, send it through the declaration evaluator; otherwise use expression evaluation. Then C-c C-c can remain a convenient explicit “reload this definition” alias, but not the only way defvar works. ** I can't eval a top level Vec slurp returns (Vec u8), an owning, move-only buffer. Flan currently forbids every move-only global because it has no global ownership/lifetime model: any function could read and free it, while ownership tracking only exists within one function. For data that is fixed at build time, use an embedded immutable array instead: (defconst the-data (embed "game-data.edn")) That produces a fixed [u8], not a heap-owning Vec, so it can live globally. It also resolves relative to sand.flan. If game-data.edn genuinely must be loaded at runtime, then today it has to be owned by a local—typically load it in main and pass it through the functions that need it. For a game-wide runtime-owned data asset, that is a missing language/runtime feature, not a bad use case on your part. ** The edn module seems to need a struct declaration, it should do both; go into a struct but also return a Map with Vecs and Sets when we don't provide a type ** defenum needs optional autoincrementing discriminants ** We need a javascript backend so we can reach the world ** We need to have C-style unions, maybe those are called defunion, and then sum types are defdata or deftype * Decisions, 2026-09-17 ** 1. Re-runnable main — DISPATCHED The process does not actually die. [flan_exit_hook] is [flan_merged_exit] (lib/dev.ml:2669): it flushes, reclaims fd 1, and parks in [for (;;) pause()]. What is missing is a way to wake it. The park becomes a condvar wait, a daemon op signals it, and the main thread — not a new one, because raylib wants the main thread — re-enters [flan_program_main]. [alive] gains a third state, parked-and-re-runnable, and each of the ten guard sites decides for itself whether it accepts one. Globals are NOT reset between runs. That is the CL/Clojure semantics asked for: the process never died, so a second (main) sees what the first one left. ** 2. C-x C-e on a top-level form — QUEUED behind 1 Same file as 1 (emacs/flan-dev.el), so it waits rather than merging by hand. No design questions; the note specifies it. ** 3. Runtime-loaded owning globals — DISPATCHED Not a missing global ownership model. One rule: a move-only global is legal, and reading one is always a borrow, never a move. Nothing takes ownership, nothing frees it, its lifetime is the process's. Sound precisely because the lifetime question has a constant answer. Mutable in place as well — a global Vec can be pushed to. Aliasing follows whatever locals already do; no new borrow regime for globals that locals lack. [embed] (lib/check.ml:4265) still covers build-time data and is untouched. ** 4. edn both typed and dynamic — REDIRECTED to arenas, drop parked Two projects, not one. (read-edn T bytes) does not exist — vendor/edn/edn.flan is only a tokenizer, and the compile-time struct walk is NEXT.md item 9. [drop] was dispatched to unblock the dynamic half and is being PARKED unmerged on its branch, not reverted, because the premise was wrong. The refusal at check.ml:599 is about *teardown*, not ownership: the type-erased runtime releases slots bytewise and cannot walk a move-only element. An arena never releases a slot — [free-all] takes the whole region — so the premise does not hold there. That is also what Odin does, which NEXT.md:1620 already recorded: no destructors, no drop, no finalizers; [delete] frees container memory and nothing else. core:encoding/json ships a hand-written recursive [destroy_value] in the *library*, and the idiomatic alternative is to parse against temp_allocator and [free_all]. Neither is a language feature. Building [drop] was a departure from NEXT.md:1589's settled "defer stays the answer", taken on the assistant's prompting and withdrawn. So: lift check.ml:599 for arena-allocated containers, and let read-edn take an allocator — which is already the idiom, since spec-memory.md:283 makes the allocator part of the calling convention with an explicit override. The real cost, stated because it is not free: [can-free] is a RUNTIME capability on the allocator value while check.ml:599 is a COMPILE-TIME refusal, and the compiler cannot generally know statically that a construction site's allocator is an arena. The spec's answer for the analogous drop case is a check at the point of construction, one branch per container — a runtime branch. This likely becomes a runtime trap rather than a static guarantee. Ownership tracking itself is untouched. Moves are still tracked; what is given up is freeing one element individually, which is the point of an arena. ** 5. defenum autoincrement — DISPATCHED C's rule: no value means previous+1, the first is 0, explicit and implicit mix. Duplicates: an explicitly written one is an intended alias and is allowed. One produced by autoincrement walking into a value another member holds is an accident and is refused, naming both members. ** 6. JavaScript backend — HELD wasm32 already works: test/wasm-run.mjs is a WASI host, the test table runs wasm32 builds, web/index.html is in the tree. A second backend beside emit.ml and x86.ml is the largest item here and the dev loop comes first. ** 7. defdata and defunion — QUEUED last Today's [defunion] is already the tagged sum type. It is renamed [defdata], and [defunion] becomes the C-style untagged one. Serves both FFI and type punning, and cimport verifies it against the header where one exists — cimport.ml:295 currently skips any record holding an anonymous union, leaving the defstruct beside it unchecked. Last, because the rename sweeps parse/check/emit/prelude/docs and every .flan file, and would conflict with everything above. * Open, found while working the list ** A transient signal -11 on the globals daemon Seen once, in one of three consecutive test runs, by the agent doing the C-x C-e work; the runs either side of it were clean. Not reproduced since — three forced full runs (dune test --force) are green, 232 checks, 0 failures. Worth remembering rather than chasing, because the daemon it appeared on is one the move-only-global work (c124df3) changed: test_reload's fixture gained a host global Vec and a run-time-new one. A teardown or a reload module that defines rather than declares a global Vec would strand the block the live process is using, which is exactly the shape a rare SIGSEGV takes. If it comes back, start there. * Status, end of 2026-09-17 Six of seven items are merged on dev-loop and green (dune build, dune test --force, @x86, @page). One agent is still running: the arena work for item 4. | item | what | state | |------+------+-------| | 1 | re-runnable main after the window closes | merged | | 2 | C-x C-e installs a top-level form | merged | | 3 | move-only globals, borrowed never moved | merged | | 4 | edn dynamic value | arena route, IN FLIGHT | | 5 | defenum autoincrement | merged | | 6 | javascript backend | held, not started | | 7 | defdata rename + C-style defunion | merged | Also merged, not from the list: macro-module symbol visibility, which unblocked [flan dev --x86] in one process. sand.flan --x86 builds in 292ms against 985ms on LLVM. ** Parked branches, kept deliberately - worktree-agent-a18e9e62485eaedb5 — [drop] and recursive teardown. Finished and green, not merged. See docs/handoffs/HANDOFF-drop.md, which is the part worth keeping. Withdrawn because the refusal it answered is about teardown, and an arena has none; see item 4 above. ** Open, carried forward - A transient signal -11 on the globals daemon, seen once, not reproduced. Recorded below. - [drop]'s handoff flags that the [clone] / [get] / [map-next!] refusals are needed by the arena route too — they are about a copy of a header, which an arena does not make safe — and that a Map has no operation answering *where* a value lives, which is what reading an arena-parsed EDN document back would need. Both were relayed to the arena agent. - [Tast.Addr (Tast.Pfield ...)] on an Option fails in both backends. The working route is [Prim (AddrOf, [Field ...])]. Found by the drop lane, not fixed. - Re-run still does not work under --two-process: a finished child is genuinely gone. It now works under --x86 because --x86 runs merged. - sand.flan still holds an uncommitted experiment line that is refused with a message naming the fix: use (defvar the-data (Vec u8)) and fill it in a function. * Landed on dev-loop Items 1, 2, 3 and 5 are merged and green (dune test --force, 232 elisp checks, 0 failures). Items 4 (drop) and 7 (defdata) are still being written. Item 6 is held. ** Re-run is merged, and does not work under --x86 Park and re-run live in the merged entry point's main(), and --x86 refuses the merged daemon by design: a merged host exports every flan.* body for -rdynamic and so interposes the prelude bodies of the LLVM-built macro module the compiler loads into itself. --x86 therefore runs --two-process, where the program is a child, and a child that finishes is genuinely Gone — there is nothing to wake. [Program.rerun] answers with the two-process refusal rather than the merged one, and a test pins it. Re-run on x86 needs the merged daemon to accept --x86 first. Separate work. ** The headline complaint is verified fixed on sand.flan Window opened, closed, the daemon reported parked, (:op "rerun") returned ok, and xdotool found a live window from the second run. The earlier claim that [flan dev sand.flan] failed with "unknown function begin-drawing" was true only on the stale base the work started from, and was retracted after a re-test. Nothing to chase.