Skip to content
Merged
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
17 changes: 9 additions & 8 deletions .console/log.md
Original file line number Diff line number Diff line change
@@ -1,11 +1,12 @@
## 2026-06-13 — fix(reviewer): GC orphaned PR-review state files

state/pr_reviews/<repo>-<n>.json is unlinked by _merge_and_done / _close_and_requeue, but only when
THIS watcher terminates the PR. PRs merged/closed by other means (manual gh merge, another host, or
while the watcher was down/stale) leave their state files behind forever (observed: 73 files, ~41 for
already-terminal OC PRs from May). Added _prune_orphan_state_files, called each _poll_once after a
SUCCESSFUL list_open_prs: any state file for that repo whose PR isn't in the open set is pruned. A
false prune is self-healing (next poll re-discovers the open PR and re-creates state). +2 tests.
## 2026-06-13 — fix(tests): date-bomb snapshot/session retention tests (main was red)

3 observer tests hardcoded observed_at/session dates as 2026-06-07 and asserted retention/recency
counts that only hold within a fixed window. As wall-clock passed 2026-06-14 those dates aged past
the cutoffs (load_recent_sessions(days=7), retention_days), so cleanup deleted more / loaded fewer
than the hardcoded expectations — turning main's full pytest RED (and blocking every PR from merging
green). Fixed: anchor the dates to now (now-1day+i*hours, preserving relative order so sort tests
still pass; today's date dir for the session test). Full unit suite green (7007). These are time-bomb
tests; using relative dates is the durable fix.

## 2026-06-13 — fix(spec-hygiene): active.json projects only active campaigns (campaign GC)

Expand Down
14 changes: 7 additions & 7 deletions tests/unit/observer/test_flaky_test_storage.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
"""Unit tests for flaky test storage manager."""

import json
from datetime import UTC, datetime, timedelta
from datetime import datetime, timedelta, timezone

import pytest

Expand Down Expand Up @@ -134,7 +134,7 @@ def test_cleanup_old_sessions(self, tmp_path):
"""Test cleanup of old session reports."""
storage = FlakyTestStorageManager(tmp_path, session_retention_days=3)

today = datetime.now(UTC).date()
today = datetime.now(timezone.utc).date()
old_date = (today - timedelta(days=10)).strftime("%Y-%m-%d")
recent_date = today.strftime("%Y-%m-%d")

Expand Down Expand Up @@ -162,7 +162,7 @@ def test_cleanup_old_aggregations(self, tmp_path):
"""Test cleanup of old aggregation reports."""
storage = FlakyTestStorageManager(tmp_path, aggregation_retention_days=30)

today = datetime.now(UTC).date()
today = datetime.now(timezone.utc).date()
old_date = (today - timedelta(days=60)).strftime("%Y-%m-%d")
recent_date = today.strftime("%Y-%m-%d")

Expand Down Expand Up @@ -220,7 +220,7 @@ def test_storage_handles_corrupted_json(self, tmp_path):
storage = FlakyTestStorageManager(tmp_path)

# Create a corrupted JSON file
date_dir = storage.session_dir / "2026-06-07"
date_dir = storage.session_dir / datetime.now(timezone.utc).strftime("%Y-%m-%d")
date_dir.mkdir(parents=True, exist_ok=True)
corrupted_file = date_dir / "10-00-00-session.json"
corrupted_file.write_text("{invalid json")
Expand Down Expand Up @@ -282,7 +282,7 @@ def test_load_recent_sessions_when_dir_not_exists(self, tmp_path):
def test_load_recent_sessions_skips_old_dates(self, tmp_path):
"""Test load_recent_sessions skips directories older than cutoff."""
storage = FlakyTestStorageManager(tmp_path)
old_date = (datetime.now(UTC).date() - timedelta(days=30)).strftime("%Y-%m-%d")
old_date = (datetime.now(timezone.utc).date() - timedelta(days=30)).strftime("%Y-%m-%d")
old_dir = storage.session_dir / old_date
old_dir.mkdir(parents=True, exist_ok=True)
(old_dir / "10-00-00-session.json").write_text('{"session_id": "old"}')
Expand Down Expand Up @@ -319,7 +319,7 @@ def test_load_recent_aggregations_skips_old_dates(self, tmp_path):
"""Test load_recent_aggregations skips files older than cutoff."""
storage = FlakyTestStorageManager(tmp_path)
storage.aggregation_dir.mkdir(parents=True, exist_ok=True)
old_date = (datetime.now(UTC).date() - timedelta(days=120)).strftime("%Y-%m-%d")
old_date = (datetime.now(timezone.utc).date() - timedelta(days=120)).strftime("%Y-%m-%d")
(storage.aggregation_dir / f"{old_date}-aggregation.json").write_text("{}")

aggs = storage.load_recent_aggregations(days=30)
Expand All @@ -338,7 +338,7 @@ def test_load_recent_aggregations_handles_corrupted_json(self, tmp_path):
"""Test load_recent_aggregations skips corrupted JSON files."""
storage = FlakyTestStorageManager(tmp_path)
storage.aggregation_dir.mkdir(parents=True, exist_ok=True)
today = datetime.now(UTC).date().strftime("%Y-%m-%d")
today = datetime.now(timezone.utc).date().strftime("%Y-%m-%d")
(storage.aggregation_dir / f"{today}-aggregation.json").write_text("{bad json")

aggs = storage.load_recent_aggregations(days=30)
Expand Down
12 changes: 7 additions & 5 deletions tests/unit/observer/test_snapshot_manager.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
# Copyright (C) 2026 ProtocolWarden
"""Tests for snapshot manager."""

from datetime import datetime, timezone
from datetime import datetime, timezone, timedelta
from pathlib import Path

import pytest
Expand Down Expand Up @@ -152,9 +152,10 @@ def test_get_snapshots_with_limit(
) -> None:
"""Test getting snapshots with a limit."""
for i in range(5):
ts = datetime.now(timezone.utc) - timedelta(days=1) + timedelta(hours=i)
snap = RepoStateSnapshot(
run_id=f"test_obs_20260607T{i:02d}0000Z_abc123_x7k9m",
observed_at=datetime(2026, 6, 7, i, 0, 0, tzinfo=timezone.utc),
run_id=f"test_obs_{ts.strftime('%Y%m%dT%H%M%S')}Z_abc123_x7k9m",
observed_at=ts,
observer_version=1,
source_command="test",
repo=test_snapshot.repo,
Expand Down Expand Up @@ -266,9 +267,10 @@ def test_cleanup_old_snapshots(
)

for i in range(4):
ts = datetime.now(timezone.utc) - timedelta(days=1) + timedelta(hours=i)
snap = RepoStateSnapshot(
run_id=f"test_obs_20260607T{i:02d}0000Z_abc123_x7k9m",
observed_at=datetime(2026, 6, 7, i, 0, 0, tzinfo=timezone.utc),
run_id=f"test_obs_{ts.strftime('%Y%m%dT%H%M%S')}Z_abc123_x7k9m",
observed_at=ts,
observer_version=1,
source_command="test",
repo=test_snapshot.repo,
Expand Down
22 changes: 13 additions & 9 deletions tests/unit/observer/test_snapshot_repository.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
"""Tests for snapshot repository implementations."""

import json
from datetime import datetime, timezone
from datetime import datetime, timezone, timedelta
from pathlib import Path

import pytest
Expand Down Expand Up @@ -227,9 +227,10 @@ def test_list_snapshots_with_limit(
) -> None:
"""Test listing snapshots with a limit."""
for i in range(5):
ts = datetime.now(timezone.utc) - timedelta(days=1) + timedelta(hours=i)
snap = RepoStateSnapshot(
run_id=f"test_obs_20260607T{i:02d}0000Z_abc123_x7k9m",
observed_at=datetime(2026, 6, 7, i, 0, 0, tzinfo=timezone.utc),
run_id=f"test_obs_{ts.strftime('%Y%m%dT%H%M%S')}Z_abc123_x7k9m",
observed_at=ts,
observer_version=1,
source_command="test",
repo=test_snapshot.repo,
Expand All @@ -246,9 +247,10 @@ def test_list_snapshots_sorted_by_date(
"""Test that snapshots are listed in reverse chronological order."""
snaps = []
for i in range(3):
ts = datetime.now(timezone.utc) - timedelta(days=1) + timedelta(hours=i)
snap = RepoStateSnapshot(
run_id=f"test_obs_20260607T{i:02d}0000Z_abc123_x7k9m",
observed_at=datetime(2026, 6, 7, i, 0, 0, tzinfo=timezone.utc),
run_id=f"test_obs_{ts.strftime('%Y%m%dT%H%M%S')}Z_abc123_x7k9m",
observed_at=ts,
observer_version=1,
source_command="test",
repo=test_snapshot.repo,
Expand Down Expand Up @@ -356,9 +358,10 @@ def test_cleanup_retains_recent_snapshots(
"""Test that recent snapshots are retained."""
# Store snapshots within retention period
for i in range(3):
ts = datetime.now(timezone.utc) - timedelta(days=1) + timedelta(hours=i)
snap = RepoStateSnapshot(
run_id=f"test_obs_20260607T{i:02d}0000Z_abc123_x7k9m",
observed_at=datetime(2026, 6, 7, i, 0, 0, tzinfo=timezone.utc),
run_id=f"test_obs_{ts.strftime('%Y%m%dT%H%M%S')}Z_abc123_x7k9m",
observed_at=ts,
observer_version=1,
source_command="test",
repo=test_snapshot.repo,
Expand All @@ -384,9 +387,10 @@ def test_cleanup_respects_count_limit(

# Store 5 snapshots
for i in range(5):
ts = datetime.now(timezone.utc) - timedelta(days=1) + timedelta(hours=i)
snap = RepoStateSnapshot(
run_id=f"test_obs_20260607T{i:02d}0000Z_abc123_x7k9m",
observed_at=datetime(2026, 6, 7, i, 0, 0, tzinfo=timezone.utc),
run_id=f"test_obs_{ts.strftime('%Y%m%dT%H%M%S')}Z_abc123_x7k9m",
observed_at=ts,
observer_version=1,
source_command="test",
repo=test_snapshot.repo,
Expand Down
Loading