docs: drop DATA_VERSION from workflow and project memory

This commit is contained in:
Lucas Kalil 2026-06-18 18:25:50 -03:00
parent fd7d644a66
commit e7c92eacef
5 changed files with 46 additions and 40 deletions

View file

@ -68,7 +68,7 @@ Use checkboxes to track progress. Items marked **🔴 BLOCKER** prevent release;
### 🟢 OPTIONAL ### 🟢 OPTIONAL
- [x] ~~PWA Tier 1 — instalável (manifest + ícones + meta tags; 2026-06-16). Atende todos os critérios de aceitação da issue.~~ - [x] ~~PWA Tier 1 — instalável (manifest + ícones + meta tags; 2026-06-16). Atende todos os critérios de aceitação da issue.~~
- [ ] PWA Tier 2 — service worker + offline (deferido; ver `.agents/issues.md` → "PWA Tier 2". DEVE excluir `data/*.json` do cache p/ não quebrar o live-refresh + `DATA_VERSION`). - [ ] PWA Tier 2 — service worker + offline (deferido; ver `.agents/issues.md` → "PWA Tier 2". DEVE excluir `data/*.json` do cache p/ não quebrar o live-refresh).
--- ---
@ -88,13 +88,13 @@ champion path, debuts champion) once the final lands. The **data-layer stages (G
- [x] ~~Stage D — auto record-cards + "format-48 debuts" band~~ - [x] ~~Stage D — auto record-cards + "format-48 debuts" band~~
- [x] ~~Stage E — in-tab results archive~~**SKIPPED (Option B):** kept the "See all matches → Matches" link; the Matches tab already covers browsing. - [x] ~~Stage E — in-tab results archive~~**SKIPPED (Option B):** kept the "See all matches → Matches" link; the Matches tab already covers browsing.
- [x] ~~Stage F — team comparator (diverging bars)~~ — teams only; the Teams/Players toggle is deferred to Stage H per graceful degradation. - [x] ~~Stage F — team comparator (diverging bars)~~ — teams only; the Teams/Players toggle is deferred to Stage H per graceful degradation.
- [x] ~~Stage J (round 1, release polish) — a11y/responsive/i18n/README audit on AF~~ — passed clean (no code fixes). **Lighthouse + final `DATA_VERSION` bump still pending an actual deploy.** - [x] ~~Stage J (round 1, release polish) — a11y/responsive/i18n/README audit on AF~~ — passed clean (no code fixes). **Lighthouse still pending an actual deploy** (the once-planned final `DATA_VERSION` bump is moot — `DATA_VERSION` removed 2026-06-18).
### 🔭 Future (data layers + 2nd polish — near/after the Cup) ### 🔭 Future (data layers + 2nd polish — near/after the Cup)
- [ ] **Stage G — Layer 2 cheap data.** Extend `results.json` (attendance, **`cards`→{y,r} migration** — breaking for `modal.js` + `stats.js` `aggregateTeams`, add a backward-compatible reader; `decidedIn`; backfill `stats`), `teams.json` (`ranking`/`wcDebut`/`confederation`), `stadiums.json` (`lat`/`lng`). Light up records: attendance, discipline/fair-play, ranking upsets, confederation performance, distance. **SCHEDULE LATE — conflicts with master's daily `results.json` refreshes.** Bump `DATA_VERSION`. - [ ] **Stage G — Layer 2 cheap data.** Extend `results.json` (attendance, **`cards`→{y,r} migration** — breaking for `modal.js` + `stats.js` `aggregateTeams`, add a backward-compatible reader; `decidedIn`; backfill `stats`), `teams.json` (`ranking`/`wcDebut`/`confederation`), `stadiums.json` (`lat`/`lng`). Light up records: attendance, discipline/fair-play, ranking upsets, confederation performance, distance. **SCHEDULE LATE — conflicts with master's daily `results.json` refreshes.**
- [ ] **Stage H — Layer 3 players.** `players.json` + `player-events.json` + `awards.json` (+ optional `keeper-stats.json`). Top-scorers podium, assists/cards/saves chips, awards block, Squad of the Tournament, **Teams/Players toggle in the comparator** (the deferred half of Stage F), goal-time records. Relative photo paths (gotcha #7). - [ ] **Stage H — Layer 3 players.** `players.json` + `player-events.json` + `awards.json` (+ optional `keeper-stats.json`). Top-scorers podium, assists/cards/saves chips, awards block, Squad of the Tournament, **Teams/Players toggle in the comparator** (the deferred half of Stage F), goal-time records. Relative photo paths (gotcha #7).
- [ ] **Stage I — Layer 4 editorial.** `curiosities.json` (bilingual EN+PT) + `all-time-baselines.json`; editorial record-cards + "this Cup vs history" panel. - [ ] **Stage I — Layer 4 editorial.** `curiosities.json` (bilingual EN+PT) + `all-time-baselines.json`; editorial record-cards + "this Cup vs history" panel.
- [ ] **Stage J (round 2) — polish over the NEW G/H/I features.** Repeat the audit on the added sections (a11y/responsive/perf, Lighthouse, EN/PT review) + bump `DATA_VERSION` for the new data files + README refresh. - [ ] **Stage J (round 2) — polish over the NEW G/H/I features.** Repeat the audit on the added sections (a11y/responsive/perf, Lighthouse, EN/PT review) + README refresh.
--- ---

View file

@ -73,16 +73,17 @@ Chrome/Edge.
### Why deferred (the real risk) ### Why deferred (the real risk)
A naïve precaching SW would cache `data/*.json` and **silently defeat the 2026-06-16 live-refresh A naïve precaching SW would cache `data/*.json` and **silently defeat the 2026-06-16 live-refresh
system** (the 90s `results.json` poll with `cache:'no-store'` + the `DATA_VERSION` cache-buster) — system** (the 90s `results.json` poll with `cache:'no-store'`, plus the `?t=Date.now()` cache-buster
open tabs would stop seeing new scores, and `DATA_VERSION` bumps would do nothing. It would also make on every data fetch) — open tabs would stop seeing new scores. It would also make
the "stale JS module" gotcha (#5) *permanent* (cached assets live until the cache name changes). the "stale JS module" gotcha (#5) *permanent* (cached assets live until the cache name changes).
### How to implement (if revisited) — constraints, not optional ### How to implement (if revisited) — constraints, not optional
1. **Never cache `data/*.json`.** Use network-only, or network-first with the cache only as an 1. **Never cache `data/*.json`.** Use network-only, or network-first with the cache only as an
offline fallback (so an offline launch shows the last-seen results). The 90s poll must stay the offline fallback (so an offline launch shows the last-seen results). The 90s poll must stay the
owner of freshness. owner of freshness.
2. **Version the SW cache** with a constant mirroring/derived from `DATA_VERSION`; clean up old caches 2. **Version the SW cache** with a dedicated cache-name constant bumped on each deploy (there is no
on `activate` — otherwise every code deploy risks serving stale JS forever (gotcha #5). longer a `DATA_VERSION` to mirror — data freshness is handled by `?t=Date.now()`); clean up old
caches on `activate` — otherwise every code deploy risks serving stale JS forever (gotcha #5).
3. **Register at the subpath** (`worldcup2026/sw.js`) so the SW scope matches the deploy (gotcha #7); 3. **Register at the subpath** (`worldcup2026/sw.js`) so the SW scope matches the deploy (gotcha #7);
keep `start_url`/`scope` relative as they already are. keep `start_url`/`scope` relative as they already are.
4. App-shell strategy: cache-first (versioned) for `index.html` + `assets/css` + `assets/js` + 4. App-shell strategy: cache-first (versioned) for `index.html` + `assets/css` + `assets/js` +
@ -109,8 +110,9 @@ published `results.json` (daily push) only appeared after F5.
- **Not a live-feed problem.** `results.json` is a manual post-match push, so there is no new server - **Not a live-feed problem.** `results.json` is a manual post-match push, so there is no new server
data *during* a match — a faster "during live" poll buys nothing. A **fixed** 90s poll is correct; data *during* a match — a faster "during live" poll buys nothing. A **fixed** 90s poll is correct;
dynamic/state-based polling was rejected (complexity for no gain, double-schedule risk per gotcha #6). dynamic/state-based polling was rejected (complexity for no gain, double-schedule risk per gotcha #6).
- **Cache-busting must use `?t=${Date.now()}` + `cache:'no-store'`, NOT `?v=DATA_VERSION`** — that - **Cache-busting must use `?t=${Date.now()}` + `cache:'no-store'`** — a frozen per-tab constant
constant is frozen in the open tab and Hostinger sends no cache headers (gotcha #2). would never refetch and Hostinger sends no cache headers. (As of 2026-06-18 the initial
`loadData()` fetch also uses `?t=Date.now()`; the old `?v=DATA_VERSION` constant was removed.)
- **Signature = full response text**, not a finished-count (a count misses score corrections, `stats` - **Signature = full response text**, not a finished-count (a count misses score corrections, `stats`
backfill on an already-finished match, and added penalties). backfill on an already-finished match, and added penalties).
- **`thirdPlaceAssignment` lives in `bracket-config.json`, not `results.json`** → on a detected change - **`thirdPlaceAssignment` lives in `bracket-config.json`, not `results.json`** → on a detected change

View file

@ -138,8 +138,9 @@ automated tests / linter (explicit spec constraint).
- The data is **not live** — it's a manual push after each match. So poll is **fixed** - The data is **not live** — it's a manual push after each match. So poll is **fixed**
(`POLL_INTERVAL_MS = 90s`), not state-based. `startResultsPolling()` (called at the end of (`POLL_INTERVAL_MS = 90s`), not state-based. `startResultsPolling()` (called at the end of
`init()`, after views register listeners) arms one `setInterval` (`if (pollTimer) return`). `init()`, after views register listeners) arms one `setInterval` (`if (pollTimer) return`).
`pollResults()` fetches `data/results.json?t=${Date.now()}` with `cache:'no-store'` — **NOT** `pollResults()` fetches `data/results.json?t=${Date.now()}` with `cache:'no-store'`. (As of
`?v=DATA_VERSION` (frozen in the tab + no Hostinger cache headers → would serve cached; gotcha #2). 2026-06-18 the initial `loadData()` fetch also uses `?t=Date.now()`; the old hand-bumped
`?v=DATA_VERSION` cache-buster was removed — see Cache-busting runbook.)
- **Signature = full response text** (catches score corrections, `stats` backfill, penalties — a - **Signature = full response text** (catches score corrections, `stats` backfill, penalties — a
finished-count signature would miss them). On change: rewrite `data.results` **and rebuild finished-count signature would miss them). On change: rewrite `data.results` **and rebuild
`data.resultByMatchId`** (the derived map), `invalidateBracket()`, dispatch `datachange`. `data.resultByMatchId`** (the derived map), `invalidateBracket()`, dispatch `datachange`.
@ -200,8 +201,8 @@ automated tests / linter (explicit spec constraint).
### Daily data refresh ### Daily data refresh
Follow **`how-refresh-data.md`** (project root) before touching any `data/*.json`. In short: edit Follow **`how-refresh-data.md`** (project root) before touching any `data/*.json`. In short: edit
`data/results.json` (scores/status, two-source rule, `penalties` only on knockout ids 73104) → bump `data/results.json` (scores/status, two-source rule, `penalties` only on knockout ids 73104) →
`DATA_VERSION`verify in preview → commit (two-commit convention) → push (user's go) → deploy. verify in preview → commit (two-commit convention) → push (user's go) → deploy.
Frozen files (never edit): `stadiums/teams/groups/bracket-config.round32/assets/code`. Frozen files (never edit): `stadiums/teams/groups/bracket-config.round32/assets/code`.
`how-update.md` stays as the schema reference for the (completed) mock→real migration. `how-update.md` stays as the schema reference for the (completed) mock→real migration.
@ -221,13 +222,15 @@ letter appears in **at most one** slot; unfilled slots stay `null`:
| 7 | M85 (vs Winner B) | E/F/G/I/J | | 7 | M85 (vs Winner B) | E/F/G/I/J |
| 8 | M87 (vs Winner K) | D/E/I/J/L | | 8 | M87 (vs Winner K) | D/E/I/J/L |
### DATA_VERSION (cache-busting) ### Cache-busting (2026-06-18: DATA_VERSION removed)
`app.js` `loadData()` appends `?v=${DATA_VERSION}` to every `data/*.json` fetch (constant near the `app.js` `loadData()` appends `?t=${Date.now()}` to every `data/*.json` fetch — same scheme the
top, currently `'2026-06-17-rev3'`). **Bump on every data refresh** (format `YYYY-MM-DD-revN`; live-refresh poll already used. **There is no `DATA_VERSION` constant to bump anymore** (removed
increment `revN` for same-day re-edits). The live-refresh poll deliberately uses `?t=` + `no-store` 2026-06-18); every load gets a unique URL, so Hostinger can never serve a stale `results.json` and
instead (see Live data refresh). Note: **JS/CSS are not versioned** (no build step) — on Hostinger the daily refresh has zero cache step. ~~Previously appended `?v=${DATA_VERSION}` (a hand-bumped
returning visitors may serve stale code until their browser re-fetches; new visitors / hard-refresh `YYYY-MM-DD-revN` constant) — retired because the manual bump was easy to forget and `Date.now()`
see it at once. Accepted. guarantees freshness.~~ Note: **JS/CSS are not versioned** (no build step) — on Hostinger returning
visitors may serve stale code until their browser re-fetches; new visitors / hard-refresh see it at
once. Accepted.
### App version (footer) ### App version (footer)
Single source of truth: **`assets/js/i18n.js` line 9** — `const APP_VERSION = 'v1.0.X'`. Auto-shown Single source of truth: **`assets/js/i18n.js` line 9** — `const APP_VERSION = 'v1.0.X'`. Auto-shown
@ -236,7 +239,7 @@ bugfix, schema change, deploy). Commit e.g. `refactor(footer): bump version to v
### Commit convention (standardized 2026-06-15) ### Commit convention (standardized 2026-06-15)
Every `/update-worldcup` run = **two commits** (full spec in `how-refresh-data.md`): Every `/update-worldcup` run = **two commits** (full spec in `how-refresh-data.md`):
1. **Data commit** (`results.json` + `DATA_VERSION`, + `bracket-config.json` on the 3rd-place day): 1. **Data commit** (`results.json`, + `bracket-config.json` on the 3rd-place day):
- 1 game → `data: update DD/MM/YYYY HH:MM HOMExAWAY HxA` - 1 game → `data: update DD/MM/YYYY HH:MM HOMExAWAY HxA`
- N games → `data: update DD/MM/YYYY — N jogos` + one body line per game. - N games → `data: update DD/MM/YYYY — N jogos` + one body line per game.
- Penalties (knockout only): suffix `(pen HxA)`. - Penalties (knockout only): suffix `(pen HxA)`.
@ -360,7 +363,8 @@ variant (Option C) was the recommended shape.
Polish over AF: i18n audit (no hardcoded strings), a11y (sections `aria-label`led + `tabindex=-1`, Polish over AF: i18n audit (no hardcoded strings), a11y (sections `aria-label`led + `tabindex=-1`,
table caption + sort buttons + `aria-sort`, sub-nav is a `<nav>`), reduced-motion gating, cross-tab table caption + sort buttons + `aria-sort`, sub-nav is a `<nav>`), reduced-motion gating, cross-tab
regression — **no code fixes were needed**. README got a Stats bullet. **Deferred to the actual regression — **no code fixes were needed**. README got a Stats bullet. **Deferred to the actual
deploy:** the Lighthouse run + a final `DATA_VERSION` bump. **Merge sequence:** merge latest deploy:** the Lighthouse run (the once-deferred final `DATA_VERSION` bump is moot — `DATA_VERSION`
was removed 2026-06-18). **Merge sequence:** merge latest
`master`→branch (resolve conflicts on the branch, never on master), re-verify, then `master ← branch `master`→branch (resolve conflicts on the branch, never on master), re-verify, then `master ← branch
--no-ff`. Pushing to origin (which triggers the deploy) is the user's explicit final go. --no-ff`. Pushing to origin (which triggers the deploy) is the user's explicit final go.
@ -426,7 +430,7 @@ block (manifest link, `<meta theme-color #081421>`, favicons, apple-mobile-web-a
`.webmanifest`) for safe MIME on Hostinger. **To change the icon:** edit the SVG(s) and re-run the `.webmanifest`) for safe MIME on Hostinger. **To change the icon:** edit the SVG(s) and re-run the
ImageMagick rasterize commands (`magick -background none icon.svg -resize NxN ...`; favicon.ico = ImageMagick rasterize commands (`magick -background none icon.svg -resize NxN ...`; favicon.ico =
16+32). **Tier 2 (service worker / offline) is deliberately deferred** — see `issues.md`; if built it 16+32). **Tier 2 (service worker / offline) is deliberately deferred** — see `issues.md`; if built it
**must exclude `data/*.json`** from the cache or it breaks the 90s poll + `DATA_VERSION`. **must exclude `data/*.json`** from the cache or it breaks the live-refresh poll.
### Responsive header — 2 bands + scrollable tabs (2026-06-15) ### Responsive header — 2 bands + scrollable tabs (2026-06-15)
Single-row flip (`.tabs { flex:0 1 auto; margin-inline:auto }`) moved from `@media (min-width:768px)` Single-row flip (`.tabs { flex:0 1 auto; margin-inline:auto }`) moved from `@media (min-width:768px)`
@ -449,7 +453,7 @@ supersedes the old "7681439 single-row header" note.
**Updated 2026-06-18.** Data: **results through match 26/104** (26 of 72 group-stage matches **Updated 2026-06-18.** Data: **results through match 26/104** (26 of 72 group-stage matches
finished; group stage in progress). `thirdPlaceAssignment` still all `null` (fill ~Jun 27). finished; group stage in progress). `thirdPlaceAssignment` still all `null` (fill ~Jun 27).
`DATA_VERSION = 2026-06-18-rev3`. `APP_VERSION = v1.0.1`. Build: all 12 steps + real-data migration Cache-busting is now automatic (`?t=Date.now()`; `DATA_VERSION` removed 2026-06-18). `APP_VERSION = v1.0.1`. Build: all 12 steps + real-data migration
done; Stats stages AD + F + J(r1) merged to `master` and live (E skipped). done; Stats stages AD + F + J(r1) merged to `master` and live (E skipped).
### Recent refreshes (rolling — keep the last 3, prune older; full detail in git) ### Recent refreshes (rolling — keep the last 3, prune older; full detail in git)
@ -461,7 +465,7 @@ done; Stats stages AD + F + J(r1) merged to `master` and live (E skipped).
### Pending / next ### Pending / next
- **`thirdPlaceAssignment` fill** once the group stage ends (~Jun 27) — slot→group table above. - **`thirdPlaceAssignment` fill** once the group stage ends (~Jun 27) — slot→group table above.
- **Lighthouse > 90** run (needs a deployed URL) + a final `DATA_VERSION` bump at deploy. - **Lighthouse > 90** run (needs a deployed URL).
- **Post-Cup home state** — when the Final goes `over` the hero is empty; build a champion/epilogue - **Post-Cup home state** — when the Final goes `over` the hero is empty; build a champion/epilogue
state (likely converges with the Stats screen). state (likely converges with the Stats screen).
- **Stats Stage G** (Layer-2 cheap data — `cards`→{y,r} migration is breaking for `modal.js` + - **Stats Stage G** (Layer-2 cheap data — `cards`→{y,r} migration is breaking for `modal.js` +

View file

@ -87,8 +87,8 @@ assets/css/stats.css (NOVO — como bracket.css; componentes da tela de stats)
- `resolveBracketTeams`, `computeStandings`, `getBracketTree`, `calculateChallengeScore` → reusados - `resolveBracketTeams`, `computeStandings`, `getBracketTree`, `calculateChallengeScore` → reusados
para ranking, caminho do campeão, fase alcançada. para ranking, caminho do campeão, fase alcançada.
- `t(key)`, eventos `langchange`/`favchange`/`timemodechange`, `.glass`, `.slide-up`, `.container`. - `t(key)`, eventos `langchange`/`favchange`/`timemodechange`, `.glass`, `.slide-up`, `.container`.
- `DATA_VERSION` (cache-busting) **deve ser bumpado** quando os novos `data/*.json` entrarem no - Cache-busting é automático: `loadData()` já anexa `?t=Date.now()` a cada fetch de `data/*.json`,
`Promise.all` de `loadData()`. então novos arquivos de dados não exigem nenhum passo de versão (`DATA_VERSION` foi removido em 2026-06-18).
--- ---
@ -219,7 +219,7 @@ framework/bundler/CDN) e o orçamento de **JS < 300KB (hoje ~74KB)**.
| **G** | **Camada 2 — dados baratos** | estende `results.json` (attendance, cards y/r, decidedIn), `teams.json` (ranking/wcDebut/confederation), `stadiums.json` (coords); backfill `stats`; liga recordes de público, disciplina, zebras, distância | 🟡🧩2 | **M** + entrada de dados | | **G** | **Camada 2 — dados baratos** | estende `results.json` (attendance, cards y/r, decidedIn), `teams.json` (ranking/wcDebut/confederation), `stadiums.json` (coords); backfill `stats`; liga recordes de público, disciplina, zebras, distância | 🟡🧩2 | **M** + entrada de dados |
| **H** | **Camada 3 — jogadores** | `players.json`+`player-events.json`+`awards.json`; pódio artilharia, chips (assist/cartões/defesas), bloco de prêmios, **Seleção do Torneio** (formação), comparador de jogadores, recordes de tempo de gol | 🔴3 | **L** (maior) + entrada contínua | | **H** | **Camada 3 — jogadores** | `players.json`+`player-events.json`+`awards.json`; pódio artilharia, chips (assist/cartões/defesas), bloco de prêmios, **Seleção do Torneio** (formação), comparador de jogadores, recordes de tempo de gol | 🔴3 | **L** (maior) + entrada contínua |
| **I** | **Camada 4 — editorial** | `curiosities.json` + `all-time-baselines.json`; render de cards editoriais + painel "esta Copa vs história" | 📝4 | **M** + redação | | **I** | **Camada 4 — editorial** | `curiosities.json` + `all-time-baselines.json`; render de cards editoriais + painel "esta Copa vs história" | 📝4 | **M** + redação |
| **J** | **Polimento** | auditoria responsiva/a11y, performance (lazy, sem blur em cards repetidos), Lighthouse, bump `DATA_VERSION`, README + i18n review | todas | **M** | | **J** | **Polimento** | auditoria responsiva/a11y, performance (lazy, sem blur em cards repetidos), Lighthouse, README + i18n review | todas | **M** |
Caminho mínimo para uma tela publicável e bonita: **A→B→C→D→E→F** (tudo com dados de hoje). Caminho mínimo para uma tela publicável e bonita: **A→B→C→D→E→F** (tudo com dados de hoje).
G/H/I são aditivos e podem entrar em qualquer ordem conforme os dados aparecem (graças a §0). G/H/I são aditivos e podem entrar em qualquer ordem conforme os dados aparecem (graças a §0).
@ -246,7 +246,8 @@ G/H/I são aditivos e podem entrar em qualquer ordem conforme os dados aparecem
*fallback* para o outro (não renderiza em branco). *fallback* para o outro (não renderiza em branco).
8. **Performance.** Render preguiçoso por seção; **sem `backdrop-filter` em cards repetidos** (custo de 8. **Performance.** Render preguiçoso por seção; **sem `backdrop-filter` em cards repetidos** (custo de
paint — regra já vigente no projeto); charts em SVG/CSS sem lib; memoizar o modelo. paint — regra já vigente no projeto); charts em SVG/CSS sem lib; memoizar o modelo.
9. **Cache-busting & deploy.** Bumpar `DATA_VERSION` quando novos `data/*.json` entrarem; novos 9. **Cache-busting & deploy.** Automático via `?t=Date.now()` em cada fetch (sem `DATA_VERSION` a
bumpar — removido em 2026-06-18); novos
arquivos em `data/` **são** deployados (bom); `.agents/` (este plano) é excluído (bom). Paths arquivos em `data/` **são** deployados (bom); `.agents/` (este plano) é excluído (bom). Paths
relativos para fotos de jogador (gotcha #7 — subpath Hostinger/Pages). relativos para fotos de jogador (gotcha #7 — subpath Hostinger/Pages).
10. **Não quebrar o existente.** Manter o padrão de import circular com `app.js`; novo tab não pode 10. **Não quebrar o existente.** Manter o padrão de import circular com `app.js`; novo tab não pode

View file

@ -65,13 +65,12 @@ Rules:
> ⚠️ First pending item: **match id 4 (USA vs Paraguay)** kicked off 2026-06-13 > ⚠️ First pending item: **match id 4 (USA vs Paraguay)** kicked off 2026-06-13
> 01:00 UTC and is still `scheduled` in the data. > 01:00 UTC and is still `scheduled` in the data.
### 4. Bump the cache-busting version ### 4. Cache-busting (no longer a manual step)
Any time `data/*.json` changes, update `DATA_VERSION` in `assets/js/app.js` There is **no `DATA_VERSION` to bump**. `loadData()` appends `?t=${Date.now()}`
(top of `loadData()`) to today's date, e.g. `'2026-06-14-rev1'`. This is to every `data/*.json` fetch, so each page load gets a fresh URL and Hostinger
appended as `?v=...` to every data fetch — without bumping it, visitors on can never serve a stale `results.json`. Just edit the data and ship — nothing
Hostinger may keep getting yesterday's cached `results.json`. If you edit to bump.
data more than once in the same day, increment the `revN` suffix.
--- ---
@ -139,7 +138,7 @@ single-source (Wikipedia 17:00 PDT vs one ESPN summary implying 14:00 PDT).
Every `/update-worldcup` run produces **two commits**, in this order, with these Every `/update-worldcup` run produces **two commits**, in this order, with these
exact subject formats — do not improvise per run. exact subject formats — do not improvise per run.
### 1. Data commit — `results.json` + `DATA_VERSION` (+ `bracket-config.json` when the third-place fill happens) ### 1. Data commit — `results.json` (+ `bracket-config.json` when the third-place fill happens)
- **One match:** - **One match:**
``` ```
@ -161,6 +160,7 @@ Rules:
(`HOMExAWAY` and `HxA`). (`HOMExAWAY` and `HxA`).
- **Penalties** (knockout only): append `(pen HxA)`, e.g. - **Penalties** (knockout only): append `(pen HxA)`, e.g.
`data: update 04/07/2026 20:00 BRAxARG 1x1 (pen 4x3)`. `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 - The one-time `thirdPlaceAssignment` fill rides in the same data commit; note it
in the body line (`+ thirdPlaceAssignment filled`). in the body line (`+ thirdPlaceAssignment filled`).
@ -182,7 +182,7 @@ the data commit (the one that actually changes the live site) a clean diff.
Update `.agents/project-memory.md`**Current State** section (do **not** append a new dated Update `.agents/project-memory.md`**Current State** section (do **not** append a new dated
section — that habit bloated the file and duplicated git): section — that habit bloated the file and duplicated git):
1. Refresh the header line: data through match id N, finished count, `DATA_VERSION`, `APP_VERSION`. 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 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 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 (`DD/MM or YYYY-MM-DD (revN) — matches XY: HOME HA AWAY, …`); per-match sources live in the git
@ -190,6 +190,5 @@ section — that habit bloated the file and duplicated git):
3. Adjust **Pending / next** if a milestone was reached (e.g. `thirdPlaceAssignment` filled). 3. Adjust **Pending / next** if a milestone was reached (e.g. `thirdPlaceAssignment` filled).
4. Tick anything completed in `.agents/TODO.md` §6. 4. Tick anything completed in `.agents/TODO.md` §6.
Confirm `DATA_VERSION` in `assets/js/app.js` was bumped to today's date (step 4 above) before Only **durable** new facts (a new gotcha, a decision) go into
committing with the two commits above. Only **durable** new facts (a new gotcha, a decision) go into
the other sections of `project-memory.md` — never a daily refresh log. the other sections of `project-memory.md` — never a daily refresh log.