🗺️ Shipping Truth & Hidden Work — the decision cheat sheet
Why this exists. Work kept looking finished while users still got the broken thing. Three structural traps caused it. This page names them, shows exactly what is currently hidden, and tells you what to pick next.
Read time: 4 min. Written for someone who does not know git.
🧠 The one-paragraph mental model
Funday has no remote backup. Everything lives on one disk. Work can hide in four places, only one of which is safe:
| Place | Safe? | Why |
|---|---|---|
🟢 Committed to master | ✅ | Permanent. Survives everything. |
| 🟡 A branch | ✅ here | All 61 branches are already inside master. Sticky notes, nothing more. |
| 🔴 A stash | ❌ | Invisible to git status. Easy to forget for months. 5 exist. |
| 🔴 A dirty worktree | ❌ | A second checkout with unsaved edits. 2 exist. |
| 🔴 A gitignored build artifact | ❌❌ | Live in production, in git nowhere. One git clean -fdx reverts the site. |
The nastiest one is the last
Production serves files that git has never seen. They are real, they are live, and they are one careless command from gone.
⚖️ The 3 laws that caused the hidden work
1️⃣ 🚚 Iframe games ship without a deploy
GAME_PLUGINS_DIR=/home/usr/funday/games — the server reads games straight off disk.
Edit games/<id>/dist/** → LIVE INSTANTLY. No build. No deploy. No commit.
- ✅ Fast.
- ❌ Production can drift from git silently, forever.
2️⃣ 🏗️ entryPoint decides whether your edit reaches anyone
Open games/<id>/funday-plugin.json:
entryPoint | Type | Editing src/ alone… |
|---|---|---|
src/Game.svelte | svelte-component | ✅ works → needs a frontend deploy |
dist/index.html · build/index.html | iframe-themeable | ❌ does nothing → run that game’s npm run build |
This exact trap nearly shipped as a false success
pacman/index.htmlloads the generated./pacman.js, notsrc/*. Fixing the source and declaring victory would have left every user on the old broken bundle.scripts/build-static-iframe-plugins.shonly covers 9 games — everything else is built by hand and goes stale invisibly.
3️⃣ 🎨 DaisyUI owns all colour
Strip it down. Stock semantic classes only: bg-primary + text-primary-content, badge-*,
base-content.
- 🚫 No
.text-on-base-*, nocolor-mix()helpers, no new colour CSS variables, no raw hex. - ✅ Deviate only with an explicit stated reason, written in the code.
- ⚠️ Token patches (
--color-*-content) only unlayered inapp.css, onlylight/dark, only when a class cannot clear AA.
*-softis a trap
alert-soft/badge-soft/btn-softoverride the matched content token and paint the hue as text on an 8%-tinted base. Measured 2.10:1 — fails AA. ~30 sites remain.
📦 The hidden-work ledger — per case
Everything below is backed up to ~/funday-wip-backups/ and nothing has been dropped.
🥇 stash@{0} — take this one
| What | 12 lines in scripts/build-atomic.sh |
| Does | Blocks Svelte dev-compiled code from reaching production |
| In master? | ❌ No — verified zero occurrences |
| Verdict | ⭐ Unique + valuable + tiny. Apply it. |
❓ stash@{1} — “before Orbit C1 deploy” · 12 files
games/starter (5) · dev/docs (3) · frontend/src (2) · .env.example
→ Base is ~4 days old. Small enough to eyeball. Needs your call.
❓ stash@{2} — “skill updates + sisterbrawl dev” · 33 files
frontend/src (9) · games/sisterbrawl (7) · .agents/skills (5) · 3 build scripts
→ Overlaps work that has since merged. Needs your call.
❓ stash@{3} — “rift-plan-temp” · 229 files
starter (40) · pebble (38) · mines (38) · frontend/src (26) · turtle-cards · mahjong
→ Looks like an abandoned mass refactor. Likely obsolete.
🗑️ stash@{4} — “cleanup-game-elements” · 602 files, −128,816 lines
yamzy (181) · yamzy2 (122) · panda-publishing (64) · yatzy (44)
→ 🔍 yamzy, yamzy2, yatzy no longer exist — that cleanup already happened another way.
→ Mostly obsolete, but it also touches panda-publishing, which is still live. Safest to drop, after a glance.
❓ funday-undo/ worktree — 119 lines
4 scribblaz canvas files, pinned to an 8-day-old commit. Not obviously superseded.
❓ orbit-snatch-2.0.1/ worktree — −12,060 lines
games/settlers pruned + 2 lines in nakama-modules. Looks like deliberate release prep.
The trap when judging these
Every file reports “differs from master” — that mostly means master moved on, not that the stash is newer. Only
stash@{0}was small enough to prove either way.
🔎 The verification arsenal
Three committed scripts. Run these instead of trusting a green feeling.
node scripts/audit-contrast.mjs # WCAG AA: 2 viewports × 2 themes × 9 routes + hover
node scripts/audit-blocked-storage.mjs # survives a browser that denies storage
node scripts/audit-console-consumers.mjs # both Nakama console consumers actually workUseful flags: --eval-chunks (module-load coverage) · --scan-chunks (static bundle scan) ·
--routes /play/x · --no-hover · --json out.json
💀 Hall of traps — every one produced a false all-clear
| Trap | The lie it told |
|---|---|
| 🪞 Compositing started behind the element | Every filled button “failed” at ~1.2 |
| 🚪 Auth redirect counted as a pass | 6 /dev routes “passed” — all measuring the home page |
| 📜 Only the first viewport measured | ”0 failures” meant 0 above the fold (508 → 1680 nodes) |
| ⏱️ Guessed delay instead of waiting | Mid-fade sampling → a passing 5.14 badge read as 1.03 |
| 🎞️ Scroll-reveal race | text-base-content read 2.73 on near-white |
🕵️ typeof localStorage guard | Guards nothing — the getter itself throws |
🔌 drop_console: true | Crash fixed, but the diagnostic silently stripped from prod |
The habit that catches all of these
Confirm every failure with a second reading. A real defect survives a re-read; an artifact does not. Measure the deployed thing, not the source.
🎯 What to pick next
| # | Task | Why now | Risk |
|---|---|---|---|
| A | ⭐ Apply stash@{0}, adjudicate the other 6 | Only item where delay itself is the risk | 🟢 Low — all backed up |
| B | 🧹 Delete 60 merged branches | git branch -d refuses unmerged work — self-protecting | 🟢 None |
| D | 🔒 Stop shipping untracked prod code | A fresh clone builds a different site | 🟡 Policy choice |
| E | 🎨 *-soft AA sweep (~30 sites) | Last known contrast defect family | 🟢 Low |
Recommended: A → B.
Already closed
C — pinball is fixed. It reloaded itself 2,262 times per 6s; a hardcoded
paths.basefought the platform’s asset rewriter, so SvelteKit hard-navigated forever. Fixed in the game (base: '',relative: true— matching turtle-cards and sudoku), not the platform. Now 164 requests, 3 navigations, canvas renders.A platform-side fix was written and reverted: it broke 3 tests encoding deliberate canonical-root behaviour for blackjack and turtle-cards. Trading two working games for one is not a fix.
✅ Before you say “done”
- Did I run the audit against PRODUCTION, not the source?
- For an iframe game: did I rebuild, or only edit src/?
- Is the artifact gitignored? → tar czf ~/funday-wip-backups/<n>-$(date +%s).tar.gz <paths>
- Did I confirm each failure twice?
- Colour: is it stock DaisyUI, or did I invent a vocabulary?
- Branch → ancestry check → empty dirty-overlap → ff-merge (never commit straight to master)See also: AGENTS.md §01.4a (game serving) · §01.5 (shared tree) · §01.6 (proof of execution)