From 477d8a76ccbf66c4cedc6d104274cef7141d1fe4 Mon Sep 17 00:00:00 2001 From: Markus Fritsche Date: Sun, 17 May 2026 08:18:24 +0000 Subject: [PATCH] context: norris_tasks anchor + task-hint composition + reset clear MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Phase 10 C2. Three additive changes; no regression. - compose_norris_task_hint(self) — module-scope helper. Returns "" when norris_tasks is nil OR list empty OR current pointer past end. Otherwise returns "\n\nCurrent step k/N:\n ". - Context:to_messages appends the hint AFTER the NORRIS suffix, inside the existing `if self.norris_active and self.norris_goal` branch. NORRIS_SUFFIX_TEMPLATE is UNCHANGED (R2 fix); the hint is a separate concatenation. Goal anchor stays the primary per-step instruction; task hint sharpens current focus. - Context:reset() now clears self.norris_tasks (R6 fix). :reset is unreachable mid-Norris (planner runs without readline prompt), but if a Norris session crashed leaving stale state, :reset recovers cleanly. One line; defensive. 15 unit cases verified: - nil/empty/exhausted norris_tasks -> no hint block - current=1/3 -> "Current step 1/3" + task text in output - NORRIS suffix precedes hint (ordering preserved) - hint suppressed when norris_active=false even if tasks set - self.turns + self.norris_tasks table identity unmutated - Context:reset clears norris_tasks AND turns Regression: 87/87 safety, 31/31 router_model, repl loads. C2 isn't called from anywhere yet (ctx.norris_tasks is always nil until C4 wires the preplan call). No behavior change in the live tree until then. Co-Authored-By: Claude Opus 4.7 (1M context) --- context.lua | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+) diff --git a/context.lua b/context.lua index 895dff7..97d50a8 100644 --- a/context.lua +++ b/context.lua @@ -228,6 +228,20 @@ The user will be prompted to confirm destructive actions; expect their verdict in the next turn as a synthesized "[aish] ... skipped by user" message if they declined.]] +-- Phase 10 / #89: optional task-hint block appended AFTER the NORRIS +-- suffix when the cloud preplanner emitted a TASK list at :norris +-- launch. self.norris_tasks shape: { current = 1, list = {...} }. +-- Returns "" when no tasks (preplan disabled OR preplan failed OR +-- list exhausted) — keeps the NORRIS suffix backward-compatible. +local function compose_norris_task_hint(self) + if not (self.norris_tasks and self.norris_tasks.list) then return "" end + local k = self.norris_tasks.current + local n = #self.norris_tasks.list + local task = self.norris_tasks.list[k] + if not task then return "" end -- exhausted → no hint + return string.format("\n\nCurrent step %d/%d:\n %s", k, n, task) +end + -- #87: route-aware context compression. Keeps the LAST keep_turns -- turns; tail-truncates any turn whose content exceeds max_turn_chars. -- Drops tool turns at the slice head (they'd be orphaned without @@ -285,6 +299,7 @@ function Context:to_messages(opts) if self.norris_active and self.norris_goal then sys_content = sys_content .. string.format(NORRIS_SUFFIX_TEMPLATE, self.norris_goal) + .. compose_norris_task_hint(self) end local msgs = { { role = "system", content = sys_content } } @@ -517,6 +532,11 @@ function Context:reset() self.turns = {} self.pending_exec_output = nil self.summary = nil + -- Phase 10 R6: clear norris_tasks defensively. :reset is + -- unreachable mid-Norris (no readline prompt while the planner + -- runs), but if a Norris session crashed leaving the field stale, + -- :reset gives the user a clean recovery path. + self.norris_tasks = nil -- R8 parity: usage_totals + cost_warn_state preserved (matches -- memory_items + project — "ambient context survives a user- -- driven conversation reset"). Use :reset_usage to zero the