world-2026-hub/how-refresh-data.md

194 lines
8 KiB
Markdown
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# 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.
### 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 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.
## 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 XY: HOME HA 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.