;;;; What a re-run does to a global, which is decided by the form that ;;;; defined it and not by the daemon. ;;;; ;;;; A [defvar] is Common Lisp's [defvar]: its initialiser runs only if the ;;;; variable is not already initialised, so its value survives a re-run. A ;;;; plain zeroed one always did — .bss is untouched by a second entry into ;;;; main — and a computed one did not, because the startup function [main] ;;;; calls ran again from the top and stored the initial value back over ;;;; whatever the last run had left. A [defconst] whose initialiser is a ;;;; compile-time constant is written into the image and no startup code ;;;; reaches it at all, so the question does not arise for it. Since ;;;; 2026-09-20 there is no other kind: a defconst's initialiser has to be a ;;;; compile-time constant, refused in the checker so that both backends ;;;; refuse the same program. Before that the LLVM backend refused a computed ;;;; one while x86 guarded it at startup like a defvar. ;;;; ;;;; So each line printed below is a claim about one of those cases, and the ;;;; run number is the first of them: [runs] is computed, so before the fix it ;;;; counted 1, 1, 1. (import agent "vendor:agent") (defconst base i64 40) ;; Computed, and the whole reproduction: the initialiser is a call, so it is ;; lifted into the startup function rather than written into the image. (defn start [] i64 base) (defvar counter i64 (start)) ;; Zero-valued, which needs no startup at all and must keep needing none. (defvar zeroed i64) ;; A computed dyn global: the map is built by a function, rooted before the ;; startup function runs, and mutated by every run. Its contents have to ;; survive a re-run for the same reason [counter]'s value does, and its root ;; has to survive collection either way. (defn table [] dyn {:runs 0}) (defvar state dyn (table)) ;; The guard flags the fix adds are the compiler's own globals, and they used ;; to be spelled [.init-once.] — a name a program can write, since [.] ;; is an ordinary symbol constituent. This one is exactly the old spelling of ;; [counter]'s flag. It compiles only because the flag is mangled with a [~] ;; now; before that the dev build died at the assembler with the symbol ;; defined twice, and the flag's Bool retyped this i64 on the way. It stays ;; unprinted on purpose — the expected output is what it was. (defvar .init-once.counter i64 7) (defn main [] i32 (agent/start "/tmp/flan-dev-rerun-fallback.sock") (set counter (+ counter 1)) (set zeroed (+ zeroed 2)) (set .init-once.counter (+ .init-once.counter 1)) (put state :runs (+ (get state :runs) 1)) (print "counter ") (print counter) (println "") (print "zeroed ") (print zeroed) (println "") (print "runs ") (print (get state :runs)) (println "") (print "base ") (print base) (println "") ;; Long enough for a client to be served, short enough to park well inside ;; any watchdog — dev-macro.flan's clock, for its reason. (dotimes [i 100] (agent/wait 5)) 0)