From f72f8477125e62cf3d35dc4a0576b28acbb92f69 Mon Sep 17 00:00:00 2001 From: mmprotest Date: Fri, 1 May 2026 08:41:17 +1000 Subject: [PATCH 1/2] Nudge one extra turn when edit intent has no file change --- tests/test_state_runtime.py | 10 ++++++++++ villani_code/state.py | 26 ++++++++++++++++++++++++++ villani_code/state_runtime.py | 22 ++++++++++++++++++++++ 3 files changed, 58 insertions(+) diff --git a/tests/test_state_runtime.py b/tests/test_state_runtime.py index 16e604a7..07523cf4 100644 --- a/tests/test_state_runtime.py +++ b/tests/test_state_runtime.py @@ -664,3 +664,13 @@ def test_diagnosis_confidence_weak_without_file_evidence(tmp_path: Path) -> None } confidence = state_runtime.classify_diagnosis_target_confidence(runner, diagnosis, failure_evidence=None) assert confidence == "weak" + +def test_response_commits_to_code_edit_detects_concrete_intent() -> None: + assert state_runtime.response_commits_to_code_edit("I'll update x.py to handle empty values.") + assert state_runtime.response_commits_to_code_edit("The fix is to add this handler to the registry.") + + +def test_response_commits_to_code_edit_ignores_suggestions_or_non_edits() -> None: + assert not state_runtime.response_commits_to_code_edit("One possible fix would be to adjust the parser.") + assert not state_runtime.response_commits_to_code_edit("You could update the default value.") + assert not state_runtime.response_commits_to_code_edit("No code changes are needed.") diff --git a/villani_code/state.py b/villani_code/state.py index 1a2c80d4..69c262fc 100644 --- a/villani_code/state.py +++ b/villani_code/state.py @@ -528,6 +528,7 @@ def __init__( self._patch_sanity_retry_pending = False self._first_attempt_write_lock_active = False self._first_attempt_locked_target = "" + self._edit_intent_nudge_used = False self._context_governance = ContextGovernanceManager(self.repo) self._planning_read_only = False self._runtime_mode: Literal["execution", "planning"] = "execution" @@ -1329,6 +1330,31 @@ def _budget_reason( self.event_callback({"type": "benchmark_scope_reminder_injected", "task_id": self.benchmark_config.task_id, "reason": "no_meaningful_edit"}) messages.append({"role": "user", "content": [{"type": "text", "text": reminder}]}) continue + if ( + not self._planning_read_only + and not self._edit_intent_nudge_used + and state_runtime.response_commits_to_code_edit( + " ".join( + block.get("text", "") + for block in response.get("content", []) + if isinstance(block, dict) and block.get("type") == "text" + ) + ) + ): + self._edit_intent_nudge_used = True + messages.append( + { + "role": "user", + "content": [ + { + "type": "text", + "text": "You described a concrete code change, but no file was modified. Apply the minimal edit now, then verify if possible.", + } + ], + } + ) + self.event_callback({"type": "edit_intent_without_edit_nudged"}) + continue reason = _budget_reason(completed=True) if reason: return _finish_bounded(response, reason, reason == "completed") diff --git a/villani_code/state_runtime.py b/villani_code/state_runtime.py index 9e03824d..b02be1a4 100644 --- a/villani_code/state_runtime.py +++ b/villani_code/state_runtime.py @@ -1130,6 +1130,28 @@ def is_no_progress_response(response: dict[str, Any]) -> bool: return len(text) <= 2 +def response_commits_to_code_edit(text: str) -> bool: + normalized = " ".join(str(text or "").lower().split()) + if not normalized: + return False + if any( + phrase in normalized + for phrase in ( + "one possible fix would be", + "you could update", + "i recommend changing", + "no code changes are needed", + ) + ): + return False + edit_verbs = r"(update|modify|change|patch|edit|fix|add)" + if re.search(rf"\bi(?:'ll| will| need to)\s+{edit_verbs}\b", normalized): + return True + if re.search(rf"\bthe fix is to\s+{edit_verbs}\b", normalized): + return True + return False + + def save_session_snapshot(runner: Any, messages: list[dict[str, Any]]) -> None: if getattr(runner, "_mission_state", None) is not None and getattr(runner, "_mission_dir", None) is not None: mission_dir = runner._mission_dir From 182e2b6b06a917a90578a989260066887ce30e3d Mon Sep 17 00:00:00 2001 From: mmprotest Date: Fri, 1 May 2026 09:21:28 +1000 Subject: [PATCH 2/2] Fix local state_runtime shadowing in run edit-intent check --- villani_code/state.py | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/villani_code/state.py b/villani_code/state.py index 69c262fc..1437c4c4 100644 --- a/villani_code/state.py +++ b/villani_code/state.py @@ -1333,7 +1333,7 @@ def _budget_reason( if ( not self._planning_read_only and not self._edit_intent_nudge_used - and state_runtime.response_commits_to_code_edit( + and self._response_commits_to_code_edit( " ".join( block.get("text", "") for block in response.get("content", []) @@ -1796,6 +1796,11 @@ def _is_no_progress_response(self, response: dict[str, Any]) -> bool: return state_runtime.is_no_progress_response(response) + def _response_commits_to_code_edit(self, text: str) -> bool: + from villani_code import state_runtime + + return state_runtime.response_commits_to_code_edit(text) + def _save_session_snapshot(self, messages: list[dict[str, Any]]) -> None: from villani_code import state_runtime