- 21B-1: Rune sleep audit — no UNJUSTIFIED_SLEEP in active paths; auto_bump_pr.py polling classified legacy (21G candidate). T20's sleeps were MODEL-GENERATED Bash (controller-level defect). - 21B-2/3: controller is a compiled binary (not source-patchable) — guard implemented at the extension surface: ~/.mimocode/hooks/ event-wait-guard.ts via @mimo-ai/plugin tool.execute.before. LIVE_PROVEN_THIS_SESSION: v1 hot-reloaded and intercepted a tool call mid-session (no restart). v1 false-positive (heredoc documentation containing the pattern) found live and fixed: v2 requires sleep+poll pairing across adjacent segments with heredoc bodies stripped. - 21B-4: wait_guard.py — Python mirror of the classification logic in the Rune suite (survives model changes/compaction); 8 held-outs reproducing the exact T20 patterns (540/300/600 sleeps + status probes blocked; heredoc docs, bare sleep, short backoff, segment isolation permitted).
66 lines
2.3 KiB
Python
66 lines
2.3 KiB
Python
"""test_wait_guard.py — T21 §21B-4: sleep→poll regression protection.
|
|
Held-outs reproduce the EXACT T20 patterns (raw execution evidence)."""
|
|
|
|
from __future__ import annotations
|
|
|
|
import sys
|
|
from pathlib import Path
|
|
|
|
REPO = Path(__file__).resolve().parent.parent
|
|
sys.path.insert(0, str(REPO / "src"))
|
|
|
|
from rune import wait_guard as wg # noqa: E402
|
|
|
|
|
|
def test_21b_t20_pattern_540_blocked():
|
|
"""The EXACT T20 pattern: sleep 540 then CI status probe."""
|
|
cmd = "sleep 540; curl -s $GITEA/api/v1/repos/conductor/rune/commits/abc/status"
|
|
r = wg.classify_wait(cmd)
|
|
assert r["classification"] == "UNJUSTIFIED_SLEEP"
|
|
assert r["redirect"].startswith("scripts/ci_event_wait.py")
|
|
|
|
|
|
def test_21b_t20_pattern_deploy_probe_blocked():
|
|
cmd = "sleep 300 && git fetch && readlink /mnt/workdrive/rune-code/current"
|
|
assert wg.classify_wait(cmd)["classification"] == "UNJUSTIFIED_SLEEP"
|
|
|
|
|
|
def test_21b_t20_pattern_release_probe_blocked():
|
|
cmd = "sleep 600; curl -s https://gitea.example/api/v1/repos/x/y/releases/tags/v1"
|
|
assert wg.classify_wait(cmd)["classification"] == "UNJUSTIFIED_SLEEP"
|
|
|
|
|
|
def test_21b_heredoc_documentation_not_blocked():
|
|
"""v2 precision fix (live finding): writing documentation containing
|
|
the literal pattern text must NOT trip the guard."""
|
|
cmd = (
|
|
"cat > evidence.md <<'EOF'\n"
|
|
"T20 raw execution contained: sleep 540 then actions/runs probe\n"
|
|
"EOF\n"
|
|
"echo saved"
|
|
)
|
|
assert wg.classify_wait(cmd)["classification"] == "NO_WAIT"
|
|
|
|
|
|
def test_21b_bare_sleep_is_user_delay():
|
|
assert wg.classify_wait("sleep 45")["classification"] == "USER_REQUESTED_DELAY"
|
|
|
|
|
|
def test_21b_short_backoff_permitted():
|
|
cmd = "curl -s $API/status; sleep 4; curl -s $API/status"
|
|
r = wg.classify_wait(cmd)
|
|
assert r["classification"] == "NO_WAIT" # sleeps below threshold
|
|
|
|
|
|
def test_21b_non_polling_long_sleep_permitted():
|
|
"""Long sleep with NO status poll in the segment: not our defect."""
|
|
cmd = "sleep 120; ./render_frames.sh"
|
|
assert wg.classify_wait(cmd)["classification"] == "NO_WAIT"
|
|
|
|
|
|
def test_21b_segment_isolation():
|
|
"""sleep+poll must co-occur in the SAME segment: a long sleep in one
|
|
segment and an unrelated status read in another is not the pattern."""
|
|
cmd = "sleep 900\nmake build"
|
|
assert wg.classify_wait(cmd)["classification"] == "NO_WAIT"
|