Parent: #628 Related: #100, #429
Problem
Interpreter.evalFor always calls perIterEnv after evaluating a lexical classic-for initializer. That allocates a GC Environment, binding-table storage, and duplicate binding names before the first condition even when the initializer environment was not captured. Later iterations already use the correct observability rule: allocate a fresh copy only when the current environment was captured; otherwise reuse it in place.
On Linux exact Zig dev.1818, the unchanged 4 MiB recovery witness reaches this unnecessary initial copy after its deliberate pressure OOM and fails duplicating the one-byte r binding name. Raising the heap cap or weakening the witness would hide the engine allocation and is not acceptable.
Scope
- Reuse the lexical initializer environment for the initial iteration when it was not captured.
- Preserve CreatePerIterationEnvironment semantics when an initializer closure captures the head binding.
- Add direct controls for uncaptured reuse and captured-initializer isolation.
- Keep heap accounting, allocation failure propagation, loop updates, const behavior, and closure identity exact.
Acceptance
- Exact Zig dev.1818 bounded-heap recovery witness passes unchanged on Linux and locally.
- Initializer-captured and body-captured classic-for closures retain per-iteration values.
- Focused interpreter/context tests and full unit shards have zero regressions.
- TSan unit coverage passes.
No-workaround rules
No heap-limit increase, smaller pressure workload, swallowed allocation error, arena leak, disabled GC, or weakened closure semantics.