Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -84,6 +84,7 @@ landing/tsconfig.tsbuildinfo
.llm_costs.jsonl
coverage.json
coverage.xml
autopilot/staleness_state.json

# MCP server (local tooling, not part of the pipeline)
mcp_server/
Expand Down
32 changes: 32 additions & 0 deletions autopilot/autopilot.py
Original file line number Diff line number Diff line change
Expand Up @@ -291,6 +291,33 @@ def generate_summary(self, priorities: list[dict]) -> str:

return "\n".join(summary)

def _run_staleness_check(self, staleness_cfg: dict) -> None:
"""Invoke StalenessEngine as part of the daily run."""
try:
from autopilot.staleness_engine import StalenessEngine

state_file = staleness_cfg.get("state_file")
engine = StalenessEngine(config=staleness_cfg, state_file=state_file)

# On very first run the state file won't exist — seed it silently.
if not engine.state_file.exists():
print("[staleness] First run detected — seeding state (no comments posted).")
engine.init_state_from_prs(self.repo_data)
return

actions = engine.process_prs(self.repo_data)
posted = sum(1 for a in actions if a.get("posted"))
dry_run_count = sum(1 for a in actions if a.get("dry_run"))
print(
f"[staleness] {len(actions)} escalation(s) processed "
f"(posted={posted}, dry_run={dry_run_count})"
)
except Exception as exc:
print(
f"[staleness] WARNING: staleness check failed (non-fatal, daily run continues): {exc}",
file=sys.stderr,
)

def run(self, output_file: str | None = None) -> str:
"""Main execution: fetch data, analyze, generate summary"""
print("=" * 60)
Expand Down Expand Up @@ -346,6 +373,11 @@ def run(self, output_file: str | None = None) -> str:
f.write(summary)
print(f"✅ Summary written to: {output_file_path}")

# Optional: run staleness sentinel if enabled in config
staleness_cfg = self.config.get("staleness", {})
if staleness_cfg.get("enabled", False):
self._run_staleness_check(staleness_cfg)

runtime = time.time() - self.start_time
print(f"\n✅ Complete in {runtime:.1f}s")

Expand Down
23 changes: 23 additions & 0 deletions autopilot/config.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -71,3 +71,26 @@ api:
rate_limit_buffer: 100 # Keep this many API calls in reserve
timeout: 30 # seconds
retry_attempts: 3

# Staleness Sentinel — PR Aging & Escalation
# Set ENABLE_LIVE_MODE=true in the environment to post real GitHub comments.
# Dry-run mode is the default: actions are logged but no comments are posted.
staleness:
enabled: true

# Maximum number of Claude LLM calls per run for comment generation.
# Override with MAX_CLAUDE_CALLS_PER_RUN env var.
max_claude_calls_per_run: 20

# Tier thresholds (days open). Matches TIERS in staleness_engine.py.
# T0 (0-6d): fresh — no action
# T1 (7-29d): aging — informational comment
# T2 (30-89d): stale — escalation comment
# T3 (90+d): critical — urgent escalation comment
tiers:
T1_days: 7
T2_days: 30
T3_days: 90

# Path to the state file that persists per-PR tier assignments.
state_file: autopilot/staleness_state.json
Loading
Loading