docs(data): add tournament data refresh runbook

Self-contained guide for daily maintenance of tournament data during the World
Cup (Jun 11 – Jul 19). Covers: scoring updates to results.json, one-time bracket
assignment fill, rare schedule changes, and verification checklist. Distinguishes
mutable (results) vs. frozen (teams, groups, stadiums) files.

Co-Authored-By: Claude Haiku 4.5 <noreply@anthropic.com>
This commit is contained in:
Lucas Kalil 2026-06-12 22:38:33 -03:00
parent 5c5169d838
commit 096c671c4e

133
how-refresh-data.md Normal file
View file

@ -0,0 +1,133 @@
# 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 6172) |
| `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`: **172 = group stage, chronological by UTC kickoff**;
**73104 = 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 1128 (UTC dates) · R32 Jun 28Jul 4 · R16 Jul 47
QF Jul 912 · SF Jul 1415 · 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.
---
## One-time: `thirdPlaceAssignment` (after ~Jun 2728)
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` 1104
unique, every entry has a valid `status`, `penalties` only on ids 73104.
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.
## After finishing
Per project convention, append a short dated line to `.agents/project-memory.md`
("results updated through match id N on YYYY-MM-DD") and tick anything completed
in `.agents/TODO.md` §6.