From 0700dce88158568d4810350e706c7dc300cc5fe3 Mon Sep 17 00:00:00 2001 From: Markus Fritsche Date: Sat, 16 May 2026 21:05:34 +0000 Subject: [PATCH] repl: enforce budget per Norris step, not just post-loop (closes #51) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit PHASE3.md §2 specifies sliding-window eviction "including mid-Norris- session if the loop runs long". Implementation only called enforce_budget() once, after the planning loop exited — so for a tight max_turns with a multi-step Norris session the model saw the FULL conversation throughout, defeating context budgeting and preventing R-C3 (NORRIS suffix goal anchor surviving eviction) from being exercised end-to-end. Move status_evictions(ctx:enforce_budget()) inside the while loop so it runs after every safety.norris_step return. Drop the now-redundant post-loop call. Surfaced during TC #38 (Qwen3-30B-A3B, max_turns=4) where the "oldest 4 turns evicted" status arrived AFTER NORRIS DONE. Co-Authored-By: Claude Opus 4.7 (1M context) --- repl.lua | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/repl.lua b/repl.lua index fa826d0..0abb4ce 100644 --- a/repl.lua +++ b/repl.lua @@ -690,6 +690,11 @@ function M.run(config) max_steps = max_norris_steps, cfg = config, }) + -- Issue #51: enforce budget after every step (was post-loop only). + -- PHASE3.md §2 specifies sliding-window eviction mid-Norris-session + -- when the loop runs long; this is what makes R-C3 (NORRIS suffix + -- goal anchor surviving eviction) observable end-to-end. + status_evictions(ctx:enforce_budget()) if result.status == "continue" then step_n = step_n + 1 else @@ -701,7 +706,6 @@ function M.run(config) ctx.norris_active = false ctx.norris_goal = nil renderer.norris_end(final_status, final_reason) - status_evictions(ctx:enforce_budget()) end -- Meta dispatch table.