You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
{{ message }}
Repository navigation
[stapledons_godot] VM silently skips rest of block after deep non-tail recursion in a service handler (v0.52.0) #1576
AILANG v0.52.0 bytecode VM: a request handler silently drops its work (no output, no error, exit 0) when it builds a list of ~1100+ elements by non-tail recursion; the interpreter answers.
Repro: sunholo-data/stapledons-godot at commit 2b54a08 (branch sprint/ai10a-style-frame). ai/voice.ail stubPcm then used concatList(rep(n, period(p))) with rep(n,x) = if n<=0 then [] else x :: rep(n-1,x). Run the stub service
ailang run --quiet --bytecode --package-dir ai --caps IO,FS --entry main --args-file cfg.json ai/service.ail < in.ndjson
(cfg: {"provider":"stub","keys_present":["gemini","openrouter"],"text_only":false,"cache_dir":"/tmp/x","routing":"data/ai/routing.json","fixtures":"ai/fixtures"}) with a hello line then a voice request whose single segment is {"emotion":"grieving","text":"I keep thinking of the ones we left behind."} (n ~ 1170). Instrumenting stubLoop's Ask arm (println ASK; let s = handle(c,l,r); println HANDLED) prints ASK, never HANDLED; the loop then continues reading and exits 0, nothing on stderr. Without --bytecode it answers. n=984 works; n>=~1100 fails. A standalone module with rep(5000) in an effectful recursive loop does not reproduce, so the trigger depends on call context (handler reached through bindF closures from start()). Expected: work, or RT_REC_003 as the interpreter reports at depth 10000. Workaround in-tree: repeat by doubling (depth log2 n).
Both halves of this are now fixed on dev, shipping in v0.54.12.
1. Depth limit (v0.53.1, PR #1729). The VM frame cap was 1,000 vs the evaluator's 10,000 and --max-recursion-depth never reached the VM. Re-verified on v0.54.11 with the reported service shape: depths 1,170 and 9,000 answer identically on both engines; 12,000 fails loudly on both (RT_REC_003 / vm: stack overflow, exit 1).
2. Silent evaluator replay — B4 (acc23c5). After a VM failure, non-strict --bytecode re-ran the entry on the evaluator unless the VM itself had dispatched a native effect. IO done by an evaluator-only callee reached through the bridge was invisible to that guard, so the re-run duplicated output or read consumed stdin. Reproduced on v0.54.11: a @limit helper printing START before an over-limit recursion printed START twice. Fix: EffContext counts every capability check (shared across budget views) and the runner refuses the fallback when it moved during the VM run — the VM error surfaces instead. Regression test: TestBridgedEffectIsNotReplayed (mutation-checked).
Decision recorded in m-bytecode-vm-parity-bugs.md (B4 = option b, pre-effect-only fallback). Known consequence: stream_sse/stream_websocket examples now fail loudly under non-strict --bytecode — their previous parity 'match' was a second network connect (bridge can't convert the stream builtins' untyped values; M-BYTECODE-2E).
For stapledons-godot: the ai/voice.ail doubling workaround (W4) can be removed once on v0.54.12.
Activity
docs: fold review notes into m-vm-stack-limit-parity (Refs #1576)
[agent] design-doc-creator: Design doc: bytecode VM silently skips re…
docs(sprint): M-VM-STACK-LIMIT-PARITY plan review fixes
[agent] sprint-planner: Design doc: bytecode VM silently skips rest o…
Evaluate M-VM-STACK-LIMIT-PARITY round 2: pass 96/100
[agent] sprint-executor: Evaluation Failed (Round 1/3): changelog fra…
Add design doc: Stapledon's Voyage v0.54.6 bump workaround register (#…
[agent] sprint-executor: v0.54.6: 4 workarounds still needed (std/map…
sunholo-voight-kampff commentedon Oct 16, 2026
Both halves of this are now fixed on dev, shipping in v0.54.12.
1. Depth limit (v0.53.1, PR #1729). The VM frame cap was 1,000 vs the evaluator's 10,000 and
--max-recursion-depthnever reached the VM. Re-verified on v0.54.11 with the reported service shape: depths 1,170 and 9,000 answer identically on both engines; 12,000 fails loudly on both (RT_REC_003/vm: stack overflow, exit 1).2. Silent evaluator replay — B4 (acc23c5). After a VM failure, non-strict
--bytecodere-ran the entry on the evaluator unless the VM itself had dispatched a native effect. IO done by an evaluator-only callee reached through the bridge was invisible to that guard, so the re-run duplicated output or read consumed stdin. Reproduced on v0.54.11: a@limithelper printingSTARTbefore an over-limit recursion printedSTARTtwice. Fix:EffContextcounts every capability check (shared across budget views) and the runner refuses the fallback when it moved during the VM run — the VM error surfaces instead. Regression test:TestBridgedEffectIsNotReplayed(mutation-checked).Decision recorded in m-bytecode-vm-parity-bugs.md (B4 = option b, pre-effect-only fallback). Known consequence:
stream_sse/stream_websocketexamples now fail loudly under non-strict--bytecode— their previous parity 'match' was a second network connect (bridge can't convert the stream builtins' untyped values; M-BYTECODE-2E).For stapledons-godot: the
ai/voice.aildoubling workaround (W4) can be removed once on v0.54.12.