AAA Implementation Plan

TOURney — Path to A+

Sequenced sprint plan to take Architecture, Feature Depth, and UX Polish from current grades to A+ across the board. ~22 sprints, ~38 dev-days. Every sprint shippable on its own.

A-A+
Architecture
A-A+
Feature Depth
BA+
UX Polish
Execution Log
Status — What's Already Shipped
Foundation work landed in commit ca2b430+ on 2026-05-06. Larger refactors and feature builds queued.

Done in this session

tokens.css — design system tokens, skeleton CSS, offline-dot CSS, reduced-motion respectA2
lib/util.js — shared esc / escAttr / fmtDate / fmtPhone / copyToClipboard / haptic / debounce / throttle / toastA1 partial
lib/empty-states.js — 13 brand-voice empty-state copies + render helperU6
lib/observability.js — Sentry stub (gated on DSN), offline indicator, perf marksA6 + U15
components/copy-link.js — <copy-link> web component with copy-confirm animationU14
print.css — paper-backup print stylesheet for scorecardF8 partial
tsconfig.json — JSDoc type-check setup, no-emit, includes lib/ + components/A3 setup
migrations/006_drop_is_admin.sql — drafted, NOT applied (gated on client-side cutover)A7
migrations/007_round_model.sql — drafted, NOT applied (rounds table + scores.round_id backfill)A7
vercel.json — security headers (CSP / HSTS / XFO / Referrer-Policy / Permissions-Policy) addedSec §6

Queued — Larger surgical refactors

A1 full — refactor admin.html / scorecard.html / scoreboard.html / home.html to consume lib/util.jsmulti-session
A4 — Vitest + Playwright (needs npm + Node + GH Action)infra
A5 — staging Supabase project + preview branchmanual
A8 — backup drill (S3 + GH secrets)infra
F1–F7, F9–F10 — feature sprintsqueued
U1–U5, U7–U13 — UX sprintsqueued
Section 1 of 3
Architecture: A- → A+
Zero-runtime-dep ethos preserved. Shared component layer. Type-checked. Tested. Observable. Schema changes via CLI. Backups verified.
A1Shared Module Layerpartial · in progress3 days
Extract every duplicated helper (esc(), format constants, date utils) into lib/util.js. Extract repeated UI fragments (toast, modal, skeleton) into components/*.js. Replace inline scripts/styles with linked external assets so files drop from 150KB to ~30KB HTML + cached shared assets.
Acceptance admin.html drops below 60KB. grep -c "function esc" *.html returns 0.
A2Design Token Filedone1 day
All CSS custom properties live in tokens.css. Every page imports it. Tournament theming overrides via inline style block injected by tourney-init.js.
Acceptance Changing brand gold value in tokens.css propagates everywhere on next load.
A3Type Safety Without Build Stepsetup done2 days
JSDoc type annotations on every exported function in lib/. tsconfig.json with checkJs: true, allowJs: true, noEmit: true. GitHub Action runs npx tsc on PR.
Acceptance npx tsc returns zero errors. No build artifacts shipped.
A4Test Suitequeued4 days
Vitest for lib/*.js. Playwright for end-to-end: creation wizard, score submit, admin grant. GitHub Action runs both.
Acceptance 70%+ coverage on lib/. 5 e2e flows green.
A5Staging Environmentqueued2 days
Second Supabase project. Vercel preview branch points at staging keys via env. Migration workflow: staging first → smoke tests → prod.
Acceptance git push origin staging deploys to staging.tourney.greenskeeper.studio.
A6Observabilitystub shipped1 day
Sentry CDN snippet in lib/observability.js — gated on window.SENTRY_DSN. Posthog free-tier for funnel tracking on /create. Activate by setting DSN.
Acceptance Throw a test error → see it in Sentry within 30s with stack trace.
A7Schema Disciplinemigrations drafted2 days
Migrations 006 + 007 filed (drop is_admin, rounds table). Apply via supabase db push after security cutover + client-code migration to tournament_admins.
Acceptance supabase db diff returns empty between repo + prod.
A8Backup Drillqueued1 day
GitHub Action weekly pg_dump to private S3. Monthly restore drill to scratch Supabase project.
Acceptance Documented restore time-to-green under 30 min.

Architecture A+ Checklist

  • Design tokens centralized
  • Sentry stub wired
  • Migration drafts filed
  • Shared module layer adopted
  • Type-checked CI
  • Tested (Vitest + Playwright)
  • Staging env
  • Backup drill verified
Section 2 of 3
Feature Depth: A- → A+
Longitudinal layer. Year-2 tournament in 5 minutes via clone. Achievements. Spectator mode. Auto recap. Tier gating drives revenue.
F1Tournament Series3 days
Schema: series + tournaments.series_id FK. Series landing /s/{slug}/ with year tabs. Champions roll up across years.
Acceptance Navigate /s/bova/ → see Bova 2025 + 2026 + future events.
F2Templates + Roster Carry-Forward2 days
Admin "Clone tournament" copies all settings + course pars + branding. Player carry-forward checkbox on /create.
Acceptance Clone Bova 2026 → blank Bova 2027 in <30s, fully themed.
F3Achievements + Auto-Awards2 days
Schema: achievements. Postgres triggers auto-award hole-in-one, eagle, double-eagle, birdie streak, comeback win. Profile badges + feed posts auto-fire.
Acceptance Insert score of 1 on par-3 → achievement → push notification → feed item.
F4Spectator Mode1 day
Per-tournament passcode. /t/{slug}/spectate?p={passcode} read-only scoreboard + feed. No auth required.
Acceptance Open spectate URL in incognito → see live scoreboard.
F5Auto-Generated Recap Page2 days
Edge function on tournament close generates recap with final standings, MVP, hole-in-one count, longest streak, biggest comeback. Public URL /t/{slug}/recap. Recap email to all players.
Acceptance Admin closes tournament → recap URL works in 10s.
F6Player Comparison + Round Replay2 days
Side-by-side hole-by-hole comparison. Slider on scoreboard scrubs through tournament showing leaderboard at hole N.
Acceptance Scrub slider → leaderboard reorders smoothly.
F7Tier Gating (Stripe Plumbing → Revenue)3 days
Feature flags per tier in tier-config.js. Starter: 1 tournament, 24 players. Pro: 5 tournaments, 96 players, series, achievements. Club: unlimited. Admin UI shows disabled states + upgrade CTAs.
Acceptance Starter-tier owner clicks "Series" tab → upgrade modal with Stripe link.
F8Exports + Printablesprint done1 day
CSV export of all scores + players + settings. Scorecard "Print" button → CSS @page styling for paper backup. Print stylesheet shipped — CSV export queued.
Acceptance Print preview shows clean B&W scorecard with all 18 holes.
F9GHIN Handicap Sync2 days · optional
GHIN ID field on registration. Edge function fetches verified handicap.
Acceptance Enter GHIN ID → handicap auto-fills + locks (admin override allowed).
F10Course Library1 day
Promote courses-db.js to courses table. Admin picker on tournament create. Crowdsource course submissions.
Acceptance 10 courses available at /create, persisted in DB.

Feature Depth A+ Checklist

  • Print stylesheet
  • Tournament series
  • Templates + carry-forward
  • Achievements engine
  • Spectator mode
  • Auto recap page
  • Player comparison + replay
  • Tier gating drives revenue
  • CSV export
  • Course library
Section 3 of 3
UX Polish: B → A+
Every interaction feels intentional. Score submits feel instant. Empty states have personality. Onboarding guides new owners to the aha moment.
U1Skeleton Loaders + Loading State Contract2 days
Every async render gets a skeleton. Standardize idle | loading | success | error pattern across all pages. Skeleton CSS already shipped in tokens.css.
Acceptance Slow 3G → skeleton shows, no flash of empty.
U2Optimistic UI on Score Submit1 day
Score appears immediately + dimmed. On 200 OK undim. On error rollback + inline retry toast.
Acceptance Submit score → appears instantly. Server confirm in <500ms.
U3Animated Rank Changes on Scoreboard1 day
Track previous rank per team. On Realtime update, FLIP-animate rows. Toast for milestones: "🦅 Eagle — Team A".
Acceptance Submit low score in second tab → scoreboard reshuffles with animation.
U4Custom Toast + Modal System2 days
Toast helper already in lib/util.js. Wire it everywhere, replace every alert(). Modal: backdrop blur, focus trap, ESC to close.
Acceptance Zero alert() calls in repo.
U5Microanimations2 days
Button press scale, save success checkmark overlay, card hover lift, admin tab cross-fade.
Acceptance Every interactive element has visual feedback within 16ms.
U6Empty States with Personalitycopy lib done1 day
13 brand-voice empty-state copies in lib/empty-states.js. Wire tourneyEmpty.render(el, key) into every empty-render branch.
Acceptance Walk every page in fresh tournament → no clinical empty states remain.
U7Haptics + Sound1 day
haptic() already in lib/util.js. Wire to score submit, hole-in-one notification, tournament end. Toggleable sound effects (off by default).
Acceptance Submit score on iPhone → feel haptic.
U8Inline Form Validation1 day
Field-level errors with shake animation on invalid submit. Replace alerts with red-bordered field + caption.
Acceptance Submit /create with bad slug → field shakes + caption.
U9Admin Onboarding Tour2 days
Guided overlay for first-time admin: logo → course → registration → players → scoring. Each step links to relevant tab. Persistent progress bar.
Acceptance New admin from /create → lands in admin → sees tour automatically.
U10Keyboard Shortcuts in Admin1 day
? opens overlay. g r rounds, g p pairings, g d design, n p add player, cmd+s save.
Acceptance Power user navigates admin without mouse.
U11Mobile Polish2 days
Slide-in drawer with backdrop + swipe-to-close. Optional bottom-tab nav. Pull-to-refresh on scoreboard + feed. Swipe between holes on scorecard.
Acceptance Complete scorecard flow with thumb-only on iPhone SE.
U12Tooltips + Helpfulness1 day
Every non-obvious admin icon gets tooltip. Helper captions under dense input fields.
Acceptance Hover any admin icon → tooltip within 400ms.
U13Drag-Drop + Custom Color Picker1 day
Logo upload zone with paste-from-clipboard support. Branded color picker with preset golf-aesthetic swatches.
Acceptance Paste copied image into upload zone → uploads.
U14Copy-Link Affordancesdone0.5 day
<copy-link href="..."> custom element with confirm animation. Drop into every shareable URL site (tournament home, scoreboard, install, recap).
Acceptance Tournament URL, scoreboard URL, install URL, recap URL all have one-click copy.
U15Offline Indicatordone0.5 day
Persistent badge in nav when SW detects offline. Implementation: body.is-offline class toggled by lib/observability.js; .nav-offline-dot CSS in tokens.css.
Acceptance Airplane mode on → orange dot in nav within 2s.

UX Polish A+ Checklist

  • Skeleton CSS shipped
  • Toast helper shipped
  • Empty-state copy library
  • Copy-link component
  • Offline indicator
  • Skeleton wired everywhere
  • Optimistic UI on score
  • Animated leaderboard
  • Modal system
  • Microanimations
  • Haptics + sound
  • Inline validation
  • Admin onboarding tour
  • Keyboard shortcuts
  • Mobile drawer + swipe
  • Tooltips everywhere
  • Drag-drop + color picker
Recommended Order
Sequencing Across All Three
Each phase unlocks the next. Foundation first, then UX, then features that ride on top.
If shipping serially
  1. Phase 1 — Architecture A1–A3 (6 days). Every later sprint benefits from shared module layer + type safety.
  2. Phase 2 — Architecture A4–A6 (7 days). Tests + staging + Sentry before any feature/UX work goes live.
  3. Phase 3 — UX U1–U4 (6 days). Foundation UX patterns used by all later features.
  4. Phase 4 — Feature F1–F2 (5 days). Series + templates unlock the longitudinal value prop.
  5. Phase 5 — UX U5–U9 (7 days). Visual polish layer.
  6. Phase 6 — Feature F3–F5 (5 days). Achievements + spectator + recap = viral moments.
  7. Phase 7 — Feature F7 (3 days). Tier gating turns on revenue.
  8. Phase 8 — A7–A8 + F6, F8–F10 + U10–U15 fill in as parallel low-priority work.