How to keep a live pack (e.g. worldcup-2026) fresh so the public Powflow visual updates each day. The visual fetches the pack JSON at runtime from a CDN, so a git push to main is the only deploy step — no app rebuild.
The visual reads the pack directly from this repo over a CDN:
| Source | URL (events.json shown) | Cache / propagation | Use when |
|---|---|---|---|
| GitHub Pages (recommended — gated) | https://causari.github.io/causari-data/packs/worldcup-2026/events.json |
Fastly, ~min; only republished when validation passes (see .github/workflows/pages.yml) |
Default — a broken commit never reaches the live URL |
| raw.githubusercontent | https://raw.githubusercontent.com/causari/causari-data/main/packs/worldcup-2026/events.json |
Fastly, ~5 min, CORS-enabled | Simple, but serves whatever is on main immediately (no deploy gate) |
| jsDelivr (scale) | https://cdn.jsdelivr.net/gh/causari/causari-data@main/packs/worldcup-2026/events.json |
~12 h on @main unless purged |
Higher traffic; pair with a purge step (below) |
Why GitHub Pages is the default: the pages workflow runs the validator first and skips the deploy if it fails, so a bad daily commit can't publish a broken graph to the live visual — the page keeps serving the last-good pack. (raw/jsDelivr serve main directly with no such gate.)
The Powflow frontend fetches events.json, links.json, insights.json from one of the bases above and merges them for display. Do not bundle the JSON at build time — that would require a redeploy per update.
After pushing an update, force the CDN to refresh:
for f in events links insights; do
curl -s "https://purge.jsdelivr.net/gh/causari/causari-data@main/packs/worldcup-2026/$f.json" >/dev/null
doneEach fixture moves through status as the tournament progresses:
scheduled → (kickoff) → live → (full time) → completed
scheduled— fixture exists as a node, no result yet. Modeled now as a forward target fornextWatchpoints.live— optional, while the match is in progress.completed— final result known; fill in the scoreline intitle/description, add asourcescitation.
Use the helper script — it does the mechanical wiring, enforces the honesty rule, and validates the whole pack in memory, writing nothing if anything is broken. The scheduled agent uses the same flow (see DAILY-AGENT-PROMPT.md).
- Write the day's input — copy
scripts/match-day.example.jsontoscripts/match-day.jsonand fill in the day's real, sourced results, next fixtures, and links. Every completed result needs asourcescitation (the script rejects results without one). - Apply + validate (writes only if clean):
If either exits non-zero, fix the input and retry — never commit a broken pack.
node scripts/add-match-day.mjs scripts/match-day.json node scripts/validate-pack.mjs worldcup-2026
- Commit + push to
main(CI re-validates; the gated Pages deploy republishes only if green):git add packs/worldcup-2026 git commit -m "data(worldcup): match-day update YYYY-MM-DD" git push rm scripts/match-day.json # scratch input, not part of the pack
- If serving via jsDelivr, run the purge snippet. GitHub Pages / raw.githubusercontent refresh within minutes.
Prefer hand-editing the JSON directly? You can — just run step 2's validator before committing. The script is the safer default because it can't leave the pack in a broken state.
CI runs validate-pack.mjs on every push/PR, so a broken pack is caught before it reaches the live visual.
A completed result is a factual claim. Every one must carry a source, e.g.:
"sources": [
{ "type": "official", "citation": "FIFA — World Cup 2026 match centre", "url": "https://www.fifa.com/..." }
]Until a result is sourced, keep the pack README's "illustrative / proof-of-concept" banner. Calibrated, sourced data is the whole brand promise — do not publish invented scorelines as fact.
- Links are event → event only; never point a link at an insight or an undefined node.
- Upcoming matches are
status: "scheduled"events, not free-text. - Insights attach to links via
instances. - Ids are kebab-case, globally unique, no
--inside an event id.