feat: use telemetry in guardrails
This commit is contained in:
@@ -5,6 +5,8 @@ from datetime import datetime, timedelta
|
||||
import re
|
||||
from pathlib import Path
|
||||
|
||||
from .telemetry import read_events
|
||||
|
||||
|
||||
def _read_safe(root: Path, relative: str) -> str | None:
|
||||
"""Read a file if it exists, otherwise return None."""
|
||||
@@ -95,6 +97,66 @@ def check_repeated_commands(root: Path) -> dict:
|
||||
}
|
||||
|
||||
|
||||
def check_repeated_commands_telemetry(root: Path) -> dict:
|
||||
"""Detect repeated commands from telemetry events.
|
||||
|
||||
Reads structured session-log.jsonl to find command targets
|
||||
that have been issued more than 3 times. Falls back gracefully
|
||||
when the telemetry log is absent.
|
||||
|
||||
Returns a dict with keys: status, details.
|
||||
"""
|
||||
events = read_events(root)
|
||||
if not events:
|
||||
return {"status": "ok", "details": "No telemetry data available."}
|
||||
|
||||
command_events = [e for e in events if e.event_type == "command"]
|
||||
if not command_events:
|
||||
return {"status": "ok", "details": "No command events found in telemetry."}
|
||||
|
||||
counts = Counter(e.target for e in command_events)
|
||||
repeated = {target: count for target, count in counts.items() if count > 3}
|
||||
|
||||
if not repeated:
|
||||
return {"status": "ok", "details": "No significantly repeated commands detected in telemetry."}
|
||||
|
||||
items = ", ".join(f"{t} ({c}x)" for t, c in list(repeated.items())[:5])
|
||||
return {
|
||||
"status": "warning",
|
||||
"details": f"Repeated commands: {items}",
|
||||
}
|
||||
|
||||
|
||||
def check_repeated_reads_telemetry(root: Path) -> dict:
|
||||
"""Detect repeated file reads from telemetry events.
|
||||
|
||||
Reads structured session-log.jsonl to find read/read_file target events
|
||||
that have been issued more than 3 times. Falls back gracefully
|
||||
when the telemetry log is absent.
|
||||
|
||||
Returns a dict with keys: status, details.
|
||||
"""
|
||||
events = read_events(root)
|
||||
if not events:
|
||||
return {"status": "ok", "details": "No telemetry data available."}
|
||||
|
||||
read_events_list = [e for e in events if e.event_type in ("read", "read_file")]
|
||||
if not read_events_list:
|
||||
return {"status": "ok", "details": "No file read events found in telemetry."}
|
||||
|
||||
counts = Counter(e.target for e in read_events_list)
|
||||
repeated = {target: count for target, count in counts.items() if count > 3}
|
||||
|
||||
if not repeated:
|
||||
return {"status": "ok", "details": "No significantly repeated file reads detected in telemetry."}
|
||||
|
||||
items = ", ".join(f"{t} ({c}x)" for t, c in list(repeated.items())[:5])
|
||||
return {
|
||||
"status": "warning",
|
||||
"details": f"Repeated file reads: {items}",
|
||||
}
|
||||
|
||||
|
||||
def check_no_recent_file_changes(root: Path) -> dict:
|
||||
"""Flag if project files have not been modified in a long time (> 48 h).
|
||||
|
||||
@@ -185,8 +247,10 @@ def check_run_log_updated(root: Path) -> dict:
|
||||
def run_all_guardrails(root: Path) -> dict:
|
||||
"""Run all guardrail checks and return combined results."""
|
||||
checks = {
|
||||
"Repeated reads": check_repeated_reads(root),
|
||||
"Repeated commands": check_repeated_commands(root),
|
||||
"Repeated reads (heuristics)": check_repeated_reads(root),
|
||||
"Repeated commands (RUN_LOG)": check_repeated_commands(root),
|
||||
"Repeated commands (telemetry)": check_repeated_commands_telemetry(root),
|
||||
"Repeated file reads (telemetry)": check_repeated_reads_telemetry(root),
|
||||
"No recent file changes": check_no_recent_file_changes(root),
|
||||
"No test run recorded": check_no_test_run_recorded(root),
|
||||
"Run log updated": check_run_log_updated(root),
|
||||
|
||||
Reference in New Issue
Block a user