---
name: project-single-machine-consolidation
description: 2026-08-20 — the soccer pipeline ran on TWO machines at once for days; it is now consolidated onto the MINI box only. Full scheduled-task inventory, the E: drive mapping that broke in the migration, and the symptom that exposes a second publisher.
metadata:
  type: project
---

## What happened

After the folder moved off `E:\soccer` (see [[feedback-file-locations]]), a SECOND
machine — the options box — was still running a complete copy of the soccer
pipeline. For roughly two days both machines ran against the same Kalshi
account, the same API-Football key, and the same GitHub Pages repo, each with
its own local ledger.

**The symptom that exposed it:** dashboard edits made on this machine did not
appear on the published page, while the page kept updating every 30 minutes.
The published data still matched this machine's maker exactly (same resting
count, same balance) because those figures come from the Kalshi API, not local
files — which is also why the page showed `Live maker STALE`: the other machine
read the API fine but its local `mm_live_state.json` was days old.

**If that ever recurs** — page updates but your code changes don't land — check
`git log origin/main` sizes for the published `index.html`. A page that keeps
changing size by ~1KB while your change should add 100KB+ means someone else is
publishing.

## Current state (authoritative)

Everything runs on the MINI box, from `C:\Users\lirse\soccer\soccer`, reached as
`E:\soccer` via a PERSISTENT machine-wide mapping in
`HKLM\SYSTEM\CurrentControlSet\Control\Session Manager\DOS Devices` → `E: = \??\C:\Users\lirse\soccer`.
That registry entry matters: all 44 scripts in `main/` hardcode `E:\soccer`, and
a `subst` does NOT survive a reboot (it is per-logon-session). The old machine
has all 21 soccer/Betting tasks **Disabled**; its options pipeline is untouched
and still runs there.

## Scheduled tasks on this machine

| Task | Schedule | Runs |
|---|---|---|
| SoccerMakerLiveGuard | every 5 min, **SYSTEM** | `mm_maker_live_guard.ps1` |
| SoccerMonitorGuard | every 15 min | `monitor_guard.ps1` |
| SoccerPinFreshCapture | every 20 min | `pin_fresh_capture.py --once` |
| SoccerDashboardPublish | every 30 min | `publish_dashboard.py` (also resolves the ledger) |
| SoccerDailyWatchlist | 00:00 | `run_daily_watchlist.bat` |
| SoccerResolveLiveData | 03:00 | `resolve_live_data.py` |
| BettingSignals | 04:00 | `run_daily_signals.bat` |
| SoccerForwardTestLog | 04:12 | `research/forward_test_unique_book.py` |
| SoccerDailyWatchlist8AM | 08:00 | `run_daily_watchlist.bat` |
| SoccerRefreshData | Mon 06:00 | `run_refresh_league_data.bat` |
| SoccerRefreshAPIFootball | Mon 06:30 | `run_refresh_api_football.bat` |
| SoccerWeeklyStrategyMetrics | Tue 08:15 | `weekly_strategy_metrics.py` |
| SoccerReminderLineAgeTest | **ONCE 2026-08-28 09:17** | `reminder_line_age_test.py` |
| SoccerReminderXhighReview | **ONCE 2026-09-13 09:23** | `reminder_xhigh_review.py` |

The two Reminders are single-shot and carry `StartWhenAvailable`, so a machine
that is off at the trigger minute still fires them when it next wakes. Without
that flag a one-off reminder is missed SILENTLY and never returns — do not
recreate them with `schtasks`, which cannot set it. `SoccerLeagueOddsWatch` was
deliberately NOT recreated: its EndBoundary was 2026-08-15, so it was already
dead.

**Known weakness:** every task above except SoccerMakerLiveGuard runs as `lirse`
without a stored password, so they only fire while someone is logged on. The
2026-08-19 outage was exactly this — an unattended reboot left the box at the
login screen. Upgrading them to SYSTEM needs an elevated shell.

## Deliberately NOT re-registered

`SoccerMMQuoter`, `SoccerMMQuoterPolyUS`, `SoccerMMSettle` (paper maker — the
dashboard panels they fed were removed 2026-08-20, see
[[project-mm-ratchet-vs-chase]]), `SoccerWeeklySummary` and `SoccerResolveNightly`
(the latter emails from the ledger; the 30-min publish already resolves it
silently via `resolve_pending_bets()`, so re-adding it would double-report).

## Related lesson: alive is not the same as working

The same night, the live maker kept running but its cycles degraded from ~70s to
~300s after a connection reset, leaving the book unquoted ~70% of the time with
a 90s order TTL. The guard only checked "is the process alive" and saw nothing
wrong. `mm_maker_live_guard.ps1` now also checks that cycles are still landing
(>240s stale ⇒ restart via `restart_maker.py`, with a 7-min grace and 30-min
cooldown). Restarting fixed it instantly; a fresh process hitting the same API
at the same moment was fast, so the degradation was process-local.
