pub const MAX_RECURSION_DEPTH: usize = 48;Expand description
Maximum depth of dynamic statement-engine re-entry — command substitution
($(…)), shell-function calls, and .kai script sourcing — before a
loud error is returned instead of letting the native call stack
overflow (a SIGSEGV/abort with no diagnostic). Mirrors the intent of the
alias re-entry cap (10) and the lexer’s MAX_PAREN_DEPTH (256): a runaway
or mutually recursive script hits a catchable ceiling, not a signal.
Each level stacks the dispatch chain between re-entries. After the GH #48
allocation pass this measures ~50 KB (release) / ~57 KB (debug at the default
opt-level = 1 dev profile — see the root Cargo.toml) / ~193 KB (a fully
unoptimized debug build) of native stack per level, down from ~80 / ~380 KB
before; the recursion_stack_cost_tests probe reports the live figure.
This cap and RECOMMENDED_STACK_SIZE are a matched pair: the cap must
trip before cap × (worst-case per-level stack) can exceed the floor, so a
runaway is caught, not a SIGSEGV. The worst case is the ~193 KB unoptimized
figure — the opt-level = 1 dev profile above is local to this workspace and
does not propagate to embedders, whose own debug builds of the kernel pay
the full unoptimized cost. 48 × 193 KB ≈ 9.3 MB under the 12 MiB floor
keeps the same ~1.3× margin the pre-#48 pair had (32 × 380 KB ≈ 12 MB under
16 MiB); #48’s smaller frames are what let the cap rise 32→48 and the floor
drop 16→12 MiB together. The guard only fires before the stack overflows
on a thread that meets that floor — this is why the REPL sizes its threads
to it and embedders must too (see docs/EMBEDDING.md). Forks (background
jobs, scatter workers, pipeline stages) run on fresh stacks and get a fresh
counter, bounding each chain independently. GH #46 / #47 / #48.