# How to Refresh Data — Daily Tournament Updates Self-contained runbook (can be pasted as a prompt to an AI agent) for keeping the hub current **during** the World Cup (2026-06-11 → 2026-07-19). The full mock→real migration is done (2026-06-12, see `.agents/project-memory.md`) — this guide covers only what changes **day after day** from here on. --- ## TL;DR — what changes vs what never changes | File | Changes? | When | |---|---|---| | `data/results.json` | ✅ **Daily** | After every matchday — scores + status | | `data/bracket-config.json` → `thirdPlaceAssignment` | ✅ **Once** | After the group stage ends (last matches ~2026-06-27, ids 61–72) | | `data/matches.json` | ⚠️ Rare | Only if FIFA officially reschedules a kickoff/venue | | `data/stadiums.json` | ❌ Never | Venues are fixed for the whole tournament | | `data/teams.json` | ❌ Never | The 48 qualifiers are final | | `data/groups.json` | ❌ Never | The draw is final | | `data/bracket-config.json` → `round32` | ❌ Never | Official bracket structure, already encoded | | `assets/`, code, `index.html` | ❌ Never | Data-only updates by design | If you find yourself editing anything in the ❌ rows, stop — something is wrong. --- ## Daily routine: update `results.json` ### 1. Find which matches happened since the last update Match ids in `matches.json`: **1–72 = group stage, chronological by UTC kickoff**; **73–104 = knockout, FIFA official match numbers**. Look up matches by `date` (UTC — a 9 p.m. PDT game lands on the *next* UTC day) and team ids, never assume id ranges by group. ``` Group stage runs Jun 11–28 (UTC dates) · R32 Jun 28–Jul 4 · R16 Jul 4–7 QF Jul 9–12 · SF Jul 14–15 · 3rd place Jul 18 · Final Jul 19 (UTC dates) ``` ### 2. Get real scores — never invent Web-search each played match and cross-check **two sources** (Wikipedia group/ knockout articles update fast; ESPN/FOX/olympics.com as second source). If a result can't be confirmed in two sources, leave it `scheduled` and tell the user. ### 3. Edit the entries Keep the exact schema — only flip these fields: ```json { "matchId": 4, "homeScore": null, "awayScore": null, "status": "scheduled" } ← before { "matchId": 4, "homeScore": 2, "awayScore": 1, "status": "finished" } ← after full-time { "matchId": 5, "homeScore": 1, "awayScore": 0, "status": "live" } ← only if in progress right now ``` Rules: - `status` enum is exactly `"scheduled"` | `"live"` | `"finished"` — nothing else. - Only `finished` feeds standings and bracket resolution; `live` is display-only. - **Knockout matches only:** if decided on penalties, keep `homeScore`/`awayScore` as the 90+30-min score (can be equal) and add `"penalties": { "home": 4, "away": 3 }`. Never add `penalties` to group matches. - Never delete/reorder entries; never change a `matchId`. > ⚠️ First pending item: **match id 4 (USA vs Paraguay)** kicked off 2026-06-13 > 01:00 UTC and is still `scheduled` in the data. ### 4. Cache-busting (no longer a manual step) There is **no `DATA_VERSION` to bump**. `loadData()` appends `?t=${Date.now()}` to every `data/*.json` fetch, so each page load gets a fresh URL and Hostinger can never serve a stale `results.json`. Just edit the data and ship — nothing to bump. --- ## One-time: `thirdPlaceAssignment` (after ~Jun 27–28) When all 72 group matches are `finished`, FIFA publishes the ranking of third-placed teams and which group's 3rd goes to which R32 slot. Fill `data/bracket-config.json`: ```json "thirdPlaceAssignment": { "1": "C", "2": "G", ... } // slot → group LETTER, not team id ``` Each slot only accepts certain groups (official draw constraint — use FIFA's published allocation, it will respect this): | Slot | Feeds (FIFA match) | Allowed groups | |---|---|---| | 1 | M74 (vs Winner E) | A/B/C/D/F | | 2 | M77 (vs Winner I) | C/D/F/G/H | | 3 | M81 (vs Winner D) | B/E/F/I/J | | 4 | M82 (vs Winner G) | A/E/H/I/J | | 5 | M79 (vs Winner A) | C/E/F/H/I | | 6 | M80 (vs Winner L) | E/H/I/J/K | | 7 | M85 (vs Winner B) | E/F/G/I/J | | 8 | M87 (vs Winner K) | D/E/I/J/L | Each group letter appears in **at most one** slot. Slots without a published assignment stay `null` (the UI shows "Best 3rd #N" — that's correct). Partially-finished group stage: you can fill slots early only if FIFA has; do not derive assignments yourself. --- ## Rare: schedule changes in `matches.json` Only if FIFA officially changes a kickoff time or venue: - `date`/`time` are **UTC** — convert from the announced local time yourself. - `stadium`/`city` must exactly match a `name`/`city` pair in `stadiums.json`. - Never touch `id`, `phase`, `homeTeam`/`awayTeam`, `bracketRef`. Known caveat to re-verify near Jul 6: **match 94** (R16, Lumen Field) kickoff was single-source (Wikipedia 17:00 PDT vs one ESPN summary implying 14:00 PDT). --- ## Verify after every refresh 1. Quick integrity scan of `results.json`: exactly 104 entries, `matchId` 1–104 unique, every entry has a valid `status`, `penalties` only on ids 73–104. 2. Serve the app (Claude Preview `worldcup2026`, port 8126 — never `file://`), hard-reload with cache bypass (browser caches JSON aggressively): ```js Promise.all(['data/results.json','data/bracket-config.json'] .map(f => fetch(f, { cache: 'reload' }))).then(() => location.reload()) ``` 3. Spot-check: **Groups** standings of the groups that played vs a real-world table; **Home** hero shows the right live/next match; after `thirdPlaceAssignment` is filled, **Knockout** R32 shows real team names. 4. Console must stay free of errors. ## Commit convention (standardized) Every `/update-worldcup` run produces **two commits**, in this order, with these exact subject formats — do not improvise per run. ### 1. Data commit — `results.json` (+ `bracket-config.json` when the third-place fill happens) - **One match:** ``` data: update DD/MM/YYYY HH:MM HOMExAWAY HxA ``` e.g. `data: update 15/06/2026 18:00 BELxEGY 1x1` - **Multiple matches:** short subject, one body line per match: ``` data: update DD/MM/YYYY — N jogos HH:MM HOMExAWAY HxA HH:MM HOMExAWAY HxA ``` Rules: - `DD/MM/YYYY` and `HH:MM` are the match's **UTC** date/kickoff (same UTC used in `matches.json` — a 9 p.m. PDT game is the next UTC day). - Team codes are the 3-letter uppercase ids; separators are lowercase `x` (`HOMExAWAY` and `HxA`). - **Penalties** (knockout only): append `(pen HxA)`, e.g. `data: update 04/07/2026 20:00 BRAxARG 1x1 (pen 4x3)`. - (No `DATA_VERSION` bump rides along anymore — cache-busting is automatic via `?t=Date.now()`.) - The one-time `thirdPlaceAssignment` fill rides in the same data commit; note it in the body line (`+ thirdPlaceAssignment filled`). ### 2. Docs commit — `.agents/` memory + TODO log ``` docs: log daily refresh DD/MM/YYYY ``` `.agents/` is excluded from the FTP deploy, so keeping it a separate commit keeps the data commit (the one that actually changes the live site) a clean diff. > The previous habit of letting `/git-semantic-commit` invent a fresh subject > each run (`data: update match 13 result and stats`, `data: update match 12 …`) > is **retired** — use the formats above verbatim. ## After finishing Update `.agents/project-memory.md` → **Current State** section (do **not** append a new dated section — that habit bloated the file and duplicated git): 1. Refresh the header line: data through match id N, finished count, `APP_VERSION`. 2. Update the **"Recent refreshes (rolling — keep the last 3)"** list: add today's entry at the top and **delete the oldest** so only the last 3 dated entries remain. Each entry is one compact line (`DD/MM or YYYY-MM-DD (revN) — matches X–Y: HOME H–A AWAY, …`); per-match sources live in the git commit, not here. 3. Adjust **Pending / next** if a milestone was reached (e.g. `thirdPlaceAssignment` filled). 4. Tick anything completed in `.agents/TODO.md` §6. Only **durable** new facts (a new gotcha, a decision) go into the other sections of `project-memory.md` — never a daily refresh log.