Bug 1: NET_LINE_LIMIT = 10 → 500
The PR review bot rejected every PR with net +10 lines, which is
virtually all real work. Raised to 500 to only catch bulk commits.
Bug 2: memory_compress reads wrong action path
tick_record['actions'] doesn't exist — actions are nested under
tick_record['decision']['actions']. Overnight alerts were always empty.
Bug 3: good_morning_report reads today's ticks instead of yesterday's
At 6 AM, now.strftime('%Y%m%d') gives today — the log is nearly empty.
Fixed to (now - timedelta(days=1)) for yesterday's full overnight data.
Bug 4: review_prs rejection comment now includes the file list
Authors couldn't tell which files were bloated. Now shows top 10 files.
Tests: 4 new tests in tests/test_tasks_bugfixes.py (all pass).
Signed-off-by: gemini <gemini@hermes.local>
144 lines
6.0 KiB
Python
144 lines
6.0 KiB
Python
"""Tests for bugfixes in tasks.py from 2026-03-30 audit.
|
|
|
|
Covers:
|
|
- NET_LINE_LIMIT raised from 10 → 500 to stop false-positive PR rejections
|
|
- memory_compress reads actions from tick_record["decision"]["actions"]
|
|
- good_morning_report reads yesterday's tick log, not today's
|
|
"""
|
|
|
|
import json
|
|
from datetime import datetime, timezone, timedelta
|
|
from pathlib import Path
|
|
|
|
|
|
# ── NET_LINE_LIMIT ───────────────────────────────────────────────────
|
|
|
|
def test_net_line_limit_is_sane():
|
|
"""NET_LINE_LIMIT = 10 caused every real PR to be spam-rejected.
|
|
|
|
Any value below ~200 is dangerously restrictive for a production repo.
|
|
500 is the current target: large enough for feature PRs, small enough
|
|
to flag bulk commits.
|
|
"""
|
|
# Import at top level would pull in huey/orchestration; just grep instead.
|
|
tasks_path = Path(__file__).resolve().parent.parent / "tasks.py"
|
|
text = tasks_path.read_text()
|
|
|
|
# Find the NET_LINE_LIMIT assignment
|
|
for line in text.splitlines():
|
|
stripped = line.strip()
|
|
if stripped.startswith("NET_LINE_LIMIT") and "=" in stripped:
|
|
value = int(stripped.split("=")[1].strip())
|
|
assert value >= 200, (
|
|
f"NET_LINE_LIMIT = {value} is too low. "
|
|
"Any value < 200 will reject most real PRs as over-limit."
|
|
)
|
|
assert value <= 2000, (
|
|
f"NET_LINE_LIMIT = {value} is too high — it won't catch bulk commits."
|
|
)
|
|
break
|
|
else:
|
|
raise AssertionError("NET_LINE_LIMIT not found in tasks.py")
|
|
|
|
|
|
# ── memory_compress action path ──────────────────────────────────────
|
|
|
|
def test_memory_compress_reads_decision_actions():
|
|
"""Actions live in tick_record['decision']['actions'], not tick_record['actions'].
|
|
|
|
The old code read t.get("actions", []) which always returned [] because
|
|
the key is nested inside the decision dict.
|
|
"""
|
|
tasks_path = Path(__file__).resolve().parent.parent / "tasks.py"
|
|
text = tasks_path.read_text()
|
|
|
|
# Find the memory_compress function body and verify the action path.
|
|
# We look for the specific pattern that reads decision.get("actions")
|
|
# within the ticks loop inside memory_compress.
|
|
in_memory_compress = False
|
|
found_correct_pattern = False
|
|
for line in text.splitlines():
|
|
if "def memory_compress" in line or "def _memory_compress" in line:
|
|
in_memory_compress = True
|
|
elif in_memory_compress and line.strip().startswith("def "):
|
|
break
|
|
elif in_memory_compress:
|
|
# The correct pattern: decision = t.get("decision", {})
|
|
if 'decision' in line and 't.get(' in line and '"decision"' in line:
|
|
found_correct_pattern = True
|
|
# The OLD bug: directly reading t.get("actions")
|
|
if 't.get("actions"' in line and 'decision' not in line:
|
|
raise AssertionError(
|
|
"Bug: memory_compress reads t.get('actions') directly. "
|
|
"Actions are nested under t['decision']['actions']."
|
|
)
|
|
|
|
assert found_correct_pattern, (
|
|
"memory_compress does not read decision = t.get('decision', {})"
|
|
)
|
|
|
|
|
|
# ── good_morning_report date bug ────────────────────────────────────
|
|
|
|
def test_good_morning_report_reads_yesterday_ticks():
|
|
"""good_morning_report runs at 6 AM. It should read YESTERDAY'S tick log,
|
|
not today's (which is mostly empty at 6 AM).
|
|
|
|
The old code used `now.strftime('%Y%m%d')` which gives today.
|
|
The fix uses `(now - timedelta(days=1)).strftime('%Y%m%d')`.
|
|
"""
|
|
tasks_path = Path(__file__).resolve().parent.parent / "tasks.py"
|
|
text = tasks_path.read_text()
|
|
|
|
# Find the good_morning_report function and check for the timedelta fix
|
|
in_gmr = False
|
|
uses_timedelta_for_yesterday = False
|
|
old_bug_pattern = False
|
|
for line in text.splitlines():
|
|
if "def good_morning_report" in line:
|
|
in_gmr = True
|
|
elif in_gmr and line.strip().startswith("def "):
|
|
break
|
|
elif in_gmr:
|
|
# Check for the corrected pattern: timedelta subtraction
|
|
if "timedelta" in line and "days=1" in line:
|
|
uses_timedelta_for_yesterday = True
|
|
# Check for the old bug: yesterday = now.strftime(...)
|
|
# This is the direct assignment without timedelta
|
|
if 'yesterday = now.strftime' in line and 'timedelta' not in line:
|
|
old_bug_pattern = True
|
|
|
|
assert not old_bug_pattern, (
|
|
"Bug: good_morning_report sets yesterday = now.strftime(...) "
|
|
"which gives TODAY's date, not yesterday's."
|
|
)
|
|
assert uses_timedelta_for_yesterday, (
|
|
"good_morning_report should use timedelta(days=1) to compute yesterday's date."
|
|
)
|
|
|
|
|
|
# ── review_prs includes file list ────────────────────────────────────
|
|
|
|
def test_review_prs_rejection_includes_file_list():
|
|
"""When review_prs rejects a PR, the comment should include the file list
|
|
so the author knows WHERE the bloat is, not just the net line count.
|
|
"""
|
|
tasks_path = Path(__file__).resolve().parent.parent / "tasks.py"
|
|
text = tasks_path.read_text()
|
|
|
|
in_review_prs = False
|
|
has_file_list = False
|
|
for line in text.splitlines():
|
|
if "def review_prs" in line:
|
|
in_review_prs = True
|
|
elif in_review_prs and line.strip().startswith("def "):
|
|
break
|
|
elif in_review_prs:
|
|
if "file_list" in line and "filename" in line:
|
|
has_file_list = True
|
|
|
|
assert has_file_list, (
|
|
"review_prs rejection comment should include a file_list "
|
|
"so the author knows which files contribute to the net diff."
|
|
)
|