diff --git a/plans/initial-implementation/PLAN.md b/plans/initial-implementation/PLAN.md
index 432a74c..086ff6d 100644
--- a/plans/initial-implementation/PLAN.md
+++ b/plans/initial-implementation/PLAN.md
@@ -38,7 +38,7 @@ parameter configurable.
- [x] Phase 1: Solution skeleton + Domain model [domain: backend] → [subplan](./phase-1-solution-and-domain.md)
- [ ] Phase 2: Infrastructure — Storage [domain: backend] → [subplan](./phase-2-storage.md)
- [ ] Phase 3: Infrastructure — Scraping [domain: backend] → [subplan](./phase-3-scraping.md)
-- [ ] Phase 4: Application layer + Background workers [domain: backend] → [subplan](./phase-4-application-and-workers.md)
+- [x] Phase 4: Application layer + Background workers [domain: backend] → [subplan](./phase-4-application-and-workers.md)
- [ ] Phase 5: Blazor Hybrid host + Theme + i18n [domain: frontend] → [subplan](./phase-5-host-theme-i18n.md)
- [ ] Phase 6: Event browsing UI [domain: frontend] → [subplan](./phase-6-event-browsing-ui.md)
- [ ] Phase 7: Anomaly detection [domain: fullstack] → [subplan](./phase-7-anomaly-detection.md)
@@ -66,7 +66,7 @@ parameter configurable.
| Phase 1: Solution + Domain | backend | ✅ Done | ⚠️ Pass with notes (Sonnet) | ✅ Build OK + 96/96 Domain tests | ✅ 61114ea |
| Phase 2: Storage | backend | ✅ Done | ⚠️ Pass with notes (Sonnet, combined batch) | ✅ Build OK + 77/77 Infra tests | ✅ batch (e4d8476…686550d…+) |
| Phase 3: Scraping | backend | ✅ Done | ⚠️ Pass with notes (Sonnet, combined batch) | ✅ Build OK + 77/77 Infra tests | ✅ batch (e4d8476…686550d…+) |
-| Phase 4: Application + Workers | backend | ✅ Done | ⬜ | ✅ Build OK + 202/202 tests | ⬜ |
+| Phase 4: Application + Workers | backend | ✅ Done | ⚠️ Pass with notes (Sonnet) | ✅ Build OK + 202/202 tests | ✅ 2acbaa5 |
| Phase 5: Host + Theme + i18n | frontend | ✅ Done | ⚠️ Pass with notes (Sonnet, combined batch) | ✅ Build OK + 11/11 UI tests | ✅ batch (e4d8476…686550d…+) |
| Phase 6: Event browsing UI | frontend | ⬜ Not Started | ⬜ | ⏭️ Big Bang | ⬜ |
| Phase 7: Anomaly detection | fullstack | ⬜ Not Started | ⬜ | ⏭️ Big Bang | ⬜ |
diff --git a/src/Marathon.Application/UseCases/PullLiveOddsUseCase.cs b/src/Marathon.Application/UseCases/PullLiveOddsUseCase.cs
index 6fea2cf..a0bdfbe 100644
--- a/src/Marathon.Application/UseCases/PullLiveOddsUseCase.cs
+++ b/src/Marathon.Application/UseCases/PullLiveOddsUseCase.cs
@@ -36,13 +36,11 @@ public sealed class PullLiveOddsUseCase
{
_logger.LogInformation("PullLiveOddsUseCase: cycle started");
- // Fetch live events from scraper — returns only events currently live on site.
- var liveEvents = await _scraper.ScrapeUpcomingAsync(sportFilter: null, ct);
-
- // Note: the scraper's ScrapeUpcomingAsync returns pre-match by default.
- // For the live cycle, we load known events from the DB and refresh each one.
- // The "live vs pre-match" distinction is handled by OddsSource.Live in the snapshot.
- // We use the DB list because the site's live listing may differ from what we track.
+ // Refresh odds for every event we already track. The "live vs pre-match"
+ // distinction is recorded by stamping each snapshot with OddsSource.Live.
+ // TODO(phase-6/8): once IEventRepository.ListLiveAsync(cutoff) ships, swap
+ // this for a filter that only returns currently-live events to avoid
+ // hammering the scraper with non-live IDs.
var allEvents = await _eventRepo.ListAsync(ct);
int snapshotsCaptured = 0;
diff --git a/src/Marathon.UI/Services/WorkerOptions.cs b/src/Marathon.UI/Services/WorkerOptions.cs
index e61c96c..5876449 100644
--- a/src/Marathon.UI/Services/WorkerOptions.cs
+++ b/src/Marathon.UI/Services/WorkerOptions.cs
@@ -9,7 +9,7 @@ public sealed class WorkerOptions
public const string SectionName = "Workers";
/// Cron expression that drives the upcoming-schedule poller.
- public string UpcomingScheduleCron { get; set; } = "0 */5 * * * *";
+ public string UpcomingScheduleCron { get; set; } = "0 0 */6 * * *";
/// Whether the live odds poller should run at startup.
public bool LivePollerEnabled { get; set; } = true;