Files
2026-07-13 13:12:33 +08:00

411 lines
13 KiB
Python

from __future__ import annotations
from opensquilla.engine.progress_watchdog import ProgressObservation, ProgressWatchdog
def test_progress_watchdog_observes_progress_and_resets_repeated_errors() -> None:
watchdog = ProgressWatchdog(repeated_tool_error_threshold=2)
first = watchdog.observe(ProgressObservation(iteration=1, tool_error_signature="tool:error"))
progress = watchdog.observe(ProgressObservation(iteration=2, successful_tool_result=True))
after_reset = watchdog.observe(
ProgressObservation(iteration=3, tool_error_signature="tool:error")
)
assert first.action == "observe"
assert progress.reason == "progress"
assert after_reset.action == "observe"
def test_progress_watchdog_warns_in_observe_only_mode() -> None:
watchdog = ProgressWatchdog(repeated_tool_error_threshold=2, observe_only=True)
watchdog.observe(ProgressObservation(iteration=1, tool_error_signature="same"))
decision = watchdog.observe(ProgressObservation(iteration=2, tool_error_signature="same"))
assert decision.action == "warn"
assert decision.reason == "repeated_tool_error"
assert decision.details["count"] == 2
assert decision.details["iteration"] == 2
assert decision.details["provider_call_count"] == 0
def test_progress_watchdog_blocks_only_when_enabled() -> None:
watchdog = ProgressWatchdog(
repeated_provider_failure_threshold=2,
observe_only=False,
)
watchdog.observe(ProgressObservation(iteration=1, provider_failure_signature="timeout"))
decision = watchdog.observe(
ProgressObservation(iteration=2, provider_failure_signature="timeout")
)
assert decision.action == "block"
assert decision.reason == "repeated_provider_failure"
def test_progress_watchdog_warns_on_repeated_failure_anchor_despite_successful_tools() -> None:
watchdog = ProgressWatchdog(repeated_failure_anchor_threshold=2)
first = watchdog.observe(
ProgressObservation(
iteration=1,
successful_tool_result=True,
failure_anchor_signature="same-failure",
failure_anchor_summary="exec_command: FAILED test_app.py::test_bug",
)
)
second = watchdog.observe(
ProgressObservation(
iteration=2,
provider_call_count=2,
successful_tool_result=True,
failure_anchor_signature="same-failure",
failure_anchor_summary="exec_command: FAILED test_app.py::test_bug",
)
)
assert first.action == "observe"
assert second.action == "warn"
assert second.reason == "repeated_failure_anchor_without_workspace_write"
assert second.details["count"] == 2
assert second.details["threshold"] == 2
assert second.details["failure_anchor_summary"] == (
"exec_command: FAILED test_app.py::test_bug"
)
def test_progress_watchdog_resets_repeated_failure_anchor_after_workspace_write() -> None:
watchdog = ProgressWatchdog(repeated_failure_anchor_threshold=2)
watchdog.observe(
ProgressObservation(
iteration=1,
failure_anchor_signature="same-failure",
workspace_write_count=0,
)
)
after_write = watchdog.observe(
ProgressObservation(
iteration=2,
successful_tool_result=True,
workspace_write_count=1,
)
)
after_reset = watchdog.observe(
ProgressObservation(
iteration=3,
failure_anchor_signature="same-failure",
workspace_write_count=1,
)
)
assert after_write.action == "observe"
assert after_write.reason == "progress"
assert after_reset.action == "observe"
def test_progress_watchdog_warns_after_source_context_without_writes() -> None:
watchdog = ProgressWatchdog(
source_context_without_write_threshold=2,
observe_only=True,
)
first = watchdog.observe(
ProgressObservation(
iteration=1,
successful_tool_result=True,
successful_source_context_tool_result=True,
workspace_write_count=0,
)
)
second = watchdog.observe(
ProgressObservation(
iteration=2,
provider_call_count=2,
successful_tool_result=True,
successful_source_context_tool_result=True,
workspace_write_count=0,
)
)
assert first.action == "observe"
assert second.action == "warn"
assert second.reason == "source_context_without_workspace_write"
assert second.details["count"] == 2
assert second.details["threshold"] == 2
assert second.details["provider_call_count"] == 2
def test_noop_receipt_does_not_reset_source_context_without_write_loop() -> None:
watchdog = ProgressWatchdog(
source_context_without_write_threshold=2,
observe_only=False,
)
first = watchdog.observe(
ProgressObservation(
iteration=1,
successful_source_context_tool_result=True,
source_context_signature="read:src/a.py",
workspace_write_count=1,
changed_receipt_count=0,
noop_receipt_count=1,
)
)
second = watchdog.observe(
ProgressObservation(
iteration=2,
successful_source_context_tool_result=True,
source_context_signature="read:src/a.py",
workspace_write_count=1,
changed_receipt_count=0,
noop_receipt_count=1,
)
)
assert first.action == "observe"
assert second.reason == "source_context_without_workspace_write"
def test_changed_receipt_counts_as_workspace_progress() -> None:
watchdog = ProgressWatchdog(
source_context_without_write_threshold=1,
observe_only=False,
)
decision = watchdog.observe(
ProgressObservation(
iteration=1,
successful_source_context_tool_result=True,
source_context_signature="read:src/a.py",
workspace_write_count=0,
changed_receipt_count=1,
)
)
assert decision.reason == "progress"
def test_unchanged_changed_receipt_count_does_not_mask_repeated_tool_errors() -> None:
watchdog = ProgressWatchdog(
repeated_tool_error_threshold=3,
observe_only=False,
)
initial_progress = watchdog.observe(
ProgressObservation(iteration=1, changed_receipt_count=1)
)
first_error = watchdog.observe(
ProgressObservation(
iteration=2,
changed_receipt_count=1,
tool_error_signature="edit_file:failed",
)
)
second_error = watchdog.observe(
ProgressObservation(
iteration=3,
changed_receipt_count=1,
tool_error_signature="edit_file:failed",
)
)
blocked = watchdog.observe(
ProgressObservation(
iteration=4,
changed_receipt_count=1,
tool_error_signature="edit_file:failed",
)
)
assert initial_progress.reason == "progress"
assert first_error.reason == "no_signal"
assert second_error.reason == "no_signal"
assert blocked.action == "block"
assert blocked.reason == "repeated_tool_error"
assert blocked.details["count"] == 3
def test_progress_watchdog_warns_after_varied_source_context_without_writes() -> None:
watchdog = ProgressWatchdog(
source_context_exploration_without_write_threshold=3,
observe_only=True,
)
first = watchdog.observe(
ProgressObservation(
iteration=1,
provider_call_count=1,
successful_tool_result=True,
successful_source_context_tool_result=True,
source_context_signature="read:src/a.py",
workspace_write_count=0,
)
)
second = watchdog.observe(
ProgressObservation(
iteration=2,
provider_call_count=2,
successful_tool_result=True,
successful_source_context_tool_result=True,
source_context_signature="read:src/b.py",
workspace_write_count=0,
)
)
third = watchdog.observe(
ProgressObservation(
iteration=3,
provider_call_count=3,
successful_tool_result=True,
successful_source_context_tool_result=True,
source_context_signature="grep:src/c.py",
workspace_write_count=0,
)
)
assert first.action == "observe"
assert second.action == "observe"
assert third.action == "warn"
assert third.reason == "source_context_exploration_without_workspace_write"
assert third.details["count"] == 3
assert third.details["threshold"] == 3
assert third.details["provider_call_count"] == 3
assert third.details["source_context_signature"] == "grep:src/c.py"
def test_progress_watchdog_resets_varied_source_context_after_workspace_write() -> None:
watchdog = ProgressWatchdog(
source_context_exploration_without_write_threshold=2,
)
watchdog.observe(
ProgressObservation(
iteration=1,
successful_tool_result=True,
successful_source_context_tool_result=True,
source_context_signature="read:src/a.py",
workspace_write_count=0,
)
)
after_write = watchdog.observe(
ProgressObservation(
iteration=2,
successful_tool_result=True,
workspace_write_count=1,
)
)
after_reset = watchdog.observe(
ProgressObservation(
iteration=3,
successful_tool_result=True,
successful_source_context_tool_result=True,
source_context_signature="read:src/b.py",
workspace_write_count=1,
)
)
assert after_write.action == "observe"
assert after_write.reason == "progress"
assert after_reset.action == "observe"
def test_progress_watchdog_warns_after_tool_activity_without_writes() -> None:
watchdog = ProgressWatchdog(
tool_activity_without_write_threshold=2,
observe_only=True,
)
first = watchdog.observe(
ProgressObservation(
iteration=1,
provider_call_count=1,
successful_tool_result=True,
successful_execution_tool_result=True,
scratch_write_count=1,
workspace_write_count=0,
workspace_change_likely_required=True,
)
)
second = watchdog.observe(
ProgressObservation(
iteration=2,
provider_call_count=2,
successful_tool_result=True,
scratch_write_count=1,
workspace_write_count=0,
workspace_change_likely_required=True,
)
)
assert first.action == "observe"
assert second.action == "warn"
assert second.reason == "tool_activity_without_workspace_write"
assert second.details["count"] == 2
assert second.details["threshold"] == 2
assert second.details["scratch_write_count"] == 1
assert second.details["workspace_change_likely_required"] is True
def test_progress_watchdog_resets_tool_activity_after_workspace_write() -> None:
watchdog = ProgressWatchdog(tool_activity_without_write_threshold=2)
watchdog.observe(
ProgressObservation(
iteration=1,
successful_tool_result=True,
successful_execution_tool_result=True,
workspace_write_count=0,
)
)
after_write = watchdog.observe(
ProgressObservation(
iteration=2,
successful_tool_result=True,
workspace_write_count=1,
)
)
after_reset = watchdog.observe(
ProgressObservation(
iteration=3,
successful_tool_result=True,
successful_execution_tool_result=True,
workspace_write_count=1,
)
)
assert after_write.action == "observe"
assert after_write.reason == "progress"
assert after_reset.action == "observe"
def test_progress_watchdog_warns_after_verified_diff_continues_tool_activity() -> None:
watchdog = ProgressWatchdog(verified_post_write_activity_threshold=2)
first = watchdog.observe(
ProgressObservation(
iteration=1,
provider_call_count=1,
successful_tool_result=True,
successful_execution_tool_result=True,
workspace_write_count=1,
post_write_focused_verification_observed=True,
)
)
second = watchdog.observe(
ProgressObservation(
iteration=2,
provider_call_count=2,
successful_tool_result=True,
successful_source_context_tool_result=True,
workspace_write_count=1,
post_write_focused_verification_observed=True,
)
)
assert first.action == "observe"
assert second.action == "warn"
assert second.reason == "verified_workspace_diff_continued_tool_activity"
assert second.details["count"] == 2
assert second.details["threshold"] == 2