✨ New features added for you 3 SHIPPED
👥 Role-by-role walk — every committee function verified end-to-end 33/33 VERIFIED
We personally drove every role through its core journeys on the live site. Click any screenshot to see exactly what should appear. Open the URL to follow the same route yourself.
Anonymous / Spectator 12/12 ✓
Player 2/2 ✓
Captain 5/5 ✓
Judge / Referee 2/2 ✓
Registrar 1/1 ✓
Finance 1/1 ✓
Division Rep 1/1 ✓
Editorial 1/1 ✓
Tenant Admin 4/4 ✓
Super Admin 4/4 ✓
For a larger view of each journey (with sign-in steps + the "what we checked" detail panel), open the standalone role walkthrough page.
📥 Committee bug reports — this batch 22 CLOSEDclick any screenshot to see it full-size
| Bug · fix marker · date | Problem | What you reported | Screenshot | How we fixed it | How to test |
|---|---|---|---|---|---|
| bug-010 renderTeamSearchPanel 2026-06 (prior) |
"Find your Team" went to wrong page
Filed: 2026-05-25
|
"Find your Team" goes to Player Database page
EN: "Find your Team" goes to Player Database page
URL: /app.html
|
(no screenshot) | Removed link to /players. Replaced with in-page "Find a team to join" panel + #team-q search input. |
Verify: /app.html
As role: spectator
Sign in as Spectator. Land on /app.html. Look for the "Find a team to join" panel — no Player Database redirect.
|
| bug-013 BUG-013-FIX-V1 2026-06-29 |
/admin "Search failed (403)" with no explanation
Filed: 2026-05-26
|
stand in real users shoes. search error
EN: stand in real users shoes. search error
URL: /admin.html
|
![]() |
Frontend now reads j.detail from the 403 response and shows the helpful sentence. requireAuth() returns your_role + required_roles. |
Verify: /admin.html#super
As role: a non-SUPER_ADMIN role
Sign in as Captain. Hit the impersonate search. Expect a clear "This action needs role SUPER_ADMIN, but your account is X..." message.
|
| bug-017 BUG-017-FIX-V1 2026-06-29 |
/club.html — players not clickable, lost profile link
Filed: 2026-05-30
|
而家我哋有時間 player,但係個 player 揀唔到 team,又冇咗 profile,要睇吓係咩回事。
EN: Now we have time player, but that player cannot choose team, also lost profile, need to look what is matter.
URL: /club.html?id=1
|
(no screenshot) | Wrapped every .player-row in an anchor → /profile/{dupr_id} when DUPR present. Captain/co-captain names also wrapped. |
Verify: /club.html?id=1
As role: anonymous
Open /club.html?id=1. Click any player row — lands on /profile/<dupr_id> with the player's page.
|
| bug-018 BUG-018-FIX-V1 2026-06-29 |
AI said "find the reschedule button" — no such button
Filed: 2026-05-30
|
嗱 我呢就 login as 一個 captain, 我嘗試呢就 reschedule 一個比賽, 但呢 根據個 AI 就話 你去個 match card 度 有個 reschedule button, 但我都搵唔到個 reschedule button.
EN: 嗱 [particle] I [particle] then login as one [measure word] captain, I try [particle] then reschedule one [measure word] match, but [particle] according-to [measure word] AI then say you go [measure word] match card [location particle] have one [measure word] reschedule button, but I also cannot find…
URL: /app.html
|
(no screenshot) | AI system prompt forbids referencing UI elements — the chat IS the action UX, emits ACTION_REQUEST + confirm button in-chat. |
Verify: /app.html
As role: captain
Sign in as captain → open AI assistant → type "I want to reschedule." Reply does NOT tell you to click a button; asks which match by team+date.
|
| bug-019 LANG-PIN-V1 2026-05-31 |
AI replied Chinese to English input
Filed: 2026-05-30
|
啦, captain page, 我用 AI assistant, 我都, swap player, 我明明打英文, 中文, consistent.
EN: [laa particle], captain page, I use AI assistant, I also, swap player, I clearly type English, Chinese, consistent.
URL: /app.html
|
(no screenshot) | lib/help-desk-agent.js — CJK vs Latin char ratio hard-pins reply language to the latest message. |
Verify: /app.html
As role: captain
Sign in as captain. Type English question → English reply. Type Chinese question → Chinese reply.
|
| bug-020 BUG-020-FIX-V1 2026-06-29 |
AI asked for "match number 406" — captains don't memorise IDs
Filed: 2026-05-30
|
我呢都 page 啦,佢有個 AI assistant 啦,有一個 reschedule。啦, reschedule, 佢話 reschedule 一個一個 match, 300 number 406, 問題 user 或者 captain 都知 406 number, 所以我覺得要要改,應該要 list out,你係想 reschedule match 啊,可以我,我對對 stack,我先知道嘛。
EN: I [particle] also page [particle], it has an AI assistant [particle], has a reschedule. [particle], reschedule, it says reschedule one one match, 300 number 406, problem user or captain all know 406 number, so I feel need need change, should need list out, you are want reschedule match [particle], c…
URL: /app.html
|
(no screenshot) | AI prompt: list matches as "{teamA} vs {teamB} on {date}" — never ask for a numeric ID. |
Verify: /app.html
As role: captain
Sign in as captain → AI assistant → "I want to reschedule" without naming a match. AI lists matches by team+date, not by number.
|
| bug-021 IN-APP-CONSENT-V1 2026-06 (prior) |
Captain "added" a player who never accepted
Filed: 2026-05-30
|
(none)
URL: /app.html
|
(no screenshot) | Captain POST /captain/players → creates TeamPlayer with is_active=false (PENDING). Player must accept via in-app banner. |
Verify: /app.html
As role: captain → then the added player
Captain adds a player. Player sees in-app pending banner. Until accepted, /club.html doesn't list them as active.
|
| bug-022 A4 + §3.1 guards 2026-06 (prior) |
Same player both slots + repeated MIX pairs
Filed: 2026-05-30
|
(none)
|
(no screenshot) | captain.js:1765 hard-errors if p1_dupr === p2_dupr same game. lib/match-format.repeatedMixedPairs catches reused MIX pairs. |
Verify: /scoresheet.html?match=<id>
As role: captain
Lineup wizard: set both slots of one game to the same player → hard error. Reuse a MIX pair → §3.1 error.
|
| bug-023 BUG-023-FIX-V1 2026-06-29 |
All-female team accepted on MIX game
Filed: 2026-05-30
|
我依家我都係 as a team captain,我就 嘗試 submit 個 score 啦。 咁 你知道啦,女雙男雙混雙㗎嘛。 咁對方呢, 我幫對方入埋,但對方呢係冇男仔嘅,我全部都有返 全部女師。 咁我發現呢其實個 logic 係有啲問題嘅,因為佢後面冇 check 到個其實,喂,你呢個係唔可以有 男仔嘅,因為呢個係混雙, 女雙或者係 男雙 for example。 咁後面係冇 check 到一樣嘢,但個 score 都係 submit 到成功嘅。
|
(no screenshot) | captain.js:1770 — partial lineups (one slot blank) now fail with incomplete_lineup before gender check is skipped. |
As role: captain
On lineup save, fill ONE slot of a MIX game and leave the other blank → 422 with "incomplete_lineup".
|
| bug-024 BUG-024-FIX-V1 2026-06-29 |
Co-sign button → bare "Failed: 403 Forbidden"
Filed: 2026-05-30
|
好,第三個問題呢,就我依家都係 team captain。咁明明我已經係 team captain 㗎嘛,咁我唔知點解下邊呢,submit score 嗰陣時呢,仲要我 sign in as a team captain。但撳掣呢,撳落去呢,有 error message 嘅,嗰個係 forbidden,但唔知係 forbidden 啲乜嘢。
EN: Okay, third one problem [particle], then I now still am team captain. So obviously I already am team captain [particle] [particle], so I don't know how why below [particle], submit score that time [particle], still need me sign in as a team captain. But press button [particle], press down-go [partic…
|
(no screenshot) | middleware/session.js requireAuth() returns detail + your_role + required_roles. signSide() prefers j.detail. |
As role: captain (of a different team)
Open a scoresheet for someone else's match → click "Sign as Captain" → get the helpful sentence, not "Failed: 403".
|
| bug-025 BUG-025-FIX-V1 2026-06-29 |
Score save intermittent — "didn't work, then worked"
Filed: 2026-05-30
|
好奇怪喎。頭先呢,我而家喺個 submit 個 score 度呢, as a captain 呢, save 唔到喎。唔知點解咗一咗下呢,跟住又 save 到,唔知係咪呢個係 bug。
EN: 好 [very] 奇怪 [strange] 喎 [particle]. 頭先 [just now] 呢 [particle], 我 [I] 而家 [now] 喺 [at] 個 [classifier] submit [submit] 個 [classifier] score [score] 度 [location] 呢 [particle], as a captain [as a captain] 呢 [particle], save [save] 唔 [not] 到 [able] 喎 [particle]. 唔知 [don't know] 點解 [how/why] 咗 [completed …
|
(no screenshot) | submitScores() catches network errors, auto-retries once on transient failure with toast feedback, surfaces j.detail on 4xx. |
Verify: /scoresheet.html?match=<id>
As role: captain
Submit a score. Transient blip → auto-retry toast. Hard failure → clear reason instead of silent.
|
| bug-028 CONTENT-STUDIO-HUB-V1 2026-05-31 |
Content Studio → Content Library button unresponsive
Filed: 2026-05-30
|
我 as admin, content studio,好多個, content library,反應。我 day one 都反應。
EN: I as admin, content studio, many [classifier], content library, reaction/responsive. I day one also reaction/responsive.
URL: /admin.html#super
|
(no screenshot) | 13 dead placeholder tiles replaced with 11 real working tools (Homepage Video, Gallery, Asset Library, Posts, Mission Cards, etc.). |
Verify: /admin.html#studio
As role: tenant admin
Open Content Studio. Every tile leads to a working tool.
|
| bug-029 BUG-029-FIX-V1 2026-06-29 |
Gallery editor missing upload button per row
Filed: 2026-05-30
|
我 login as admin, gallery page 嗰度呢, 要加返我 drop down menu. So that 係可以啲人可以揀返啲上去。 其他版又做,但呢版係留, 唔見咗個掣。
URL: /admin.html#super
|
(no screenshot) | Each gallery row has 📤 Upload button calling _inlineUpload helper. + Add Image Row uses the same template. |
Verify: /admin.html#gallery
As role: tenant admin
Open Gallery tab. Each row has 📤 Upload + Remove. Pick a file → URL fills in.
|
| bug-034 magic-link + H7 hide OAuth 2026-06 (prior) |
sign-in.html "no login method working"
Filed: 2026-06-09
|
i cannot test this page, as no login method is working.
EN: i cannot test this page, as no login method is working.
URL: /sign-in.html
|
(no screenshot) | Magic-link verified 200. OAuth hidden until providers set up (task #175 H7). |
Verify: /sign-in.html
As role: anonymous
Enter email → Send Magic Link → green confirmation. No misleading OAuth buttons.
|
| bug-035 AUTH-AWARE-SEARCH-V1 2026-06 (prior) |
/matchup — after remove, search returned nothing
Filed: 2026-06-09
|
i am doing the team comparison between team A and B, after i remove the players in team A, i should be able to choose back in the drop down list, but there is no this option. even i type the player name, nothing shown
|
(no screenshot) | Logged-out users now see clear sign-in CTA instead of silent "no results". Remove→re-pick flow works. |
Verify: /matchup
As role: signed-in (any role)
Fill 4 players, × to remove one. Click + Add Player → search returns 12+ results.
|
| bug-037 BUG-037-FIX-V1 2026-06-29 |
/players should default sort by Division
Filed: 2026-06-09
|
Players need to be sorted by Division first.
EN: [Cannot be generated as the input is not Cantonese.]
URL: /players
|
![]() |
Division dropdown auto-selects first division on page load. Page opens grouped by Division A. |
Verify: /players
As role: signed-in
Open /players. Division dropdown shows "Division A" pre-selected.
|
| bug-038 BUG-038-FIX-V1 2026-06-29 |
/clubs.html grammar: "We Are Clubs." + "Where the league plays"
Filed: 2026-06-09
|
"We Are Clubs." Poor Grammar.
Change to "We are Hong Kong Pickleball League"
"Where the league plays"
Change to "Find a Team:"
URL: /clubs.html
|
![]() |
Direct prisma update of clubs_hero_brand → "Hong Kong Pickleball League" + clubs_map_title → "Find a Team" across en/zh_Hant/zh_Hans. |
Verify: /clubs.html
As role: anonymous
Open /clubs.html. Hero reads "We are Hong Kong Pickleball League". Map title reads "Find a Team".
|
| bug-040 task #118 uat-cage 2026-06 (prior) |
Homepage showed "UAT SANDBOX" tab next to Div A/B/C
Filed: 2026-06-12
|
(none)
URL: /
|
![]() |
UAT-tenant cage cleanup removed all UAT sandbox leakage from live hkpl. Divisions API returns only A/B/C. |
Verify: /
As role: anonymous
Open homepage → Match Schedule section. Tabs are only Division A/B/C.
|
| bug-043 BUG-038-FIX-V1 2026-06-29 |
Same as bug-038 (clubs hero + map title)
Filed: 2026-06-12
|
(none)
URL: /clubs.html
|
![]() |
Duplicate of bug-038 — same fix. |
Verify: /clubs.html
As role: anonymous
See bug-038.
|
| bug-051 TOPTEN-DIVISION-TABS-V1 2026-06-24 |
/topten should split by division (A/B/C separately)
Filed: 2026-06-12
|
你 top 10 players, 麻煩你分 division A top 10 players, division B division C, 可以全部, 但有分 division.
EN: You top 10 players, please you divide division A top 10 players, division B division C, can all, but have division.
URL: /topten.html
|
![]() |
/topten.html grew 4 division pills (All / A / B / C). Each filters to that division's top-10. |
Verify: /topten.html
As role: anonymous
Open /topten.html. 4 pills present. Click Division A → only Div A players.
|
| bug-055 DIVISION-TABS-DYNAMIC-V1 2026-06-22 |
/results-history division filter dead
Filed: 2026-06-12
|
呢一版呢, 揀唔同 division 呢, 係出唔到唔同嘅球隊喎。 哎呀, 全部都係 All Season All Teams 㗎呢。 有 bug 喺度。
EN: This one page [呢], choose different division [呢], is come out not able different [嘅] teams [喎]. Aiyaa, all also is All Season All Teams [㗎] [呢]. Have bug at here.
|
![]() |
Division tabs re-wired to re-fetch with the chosen division. Each shows distinct teams (A=50, B=153, C=163). |
Verify: /results-history.html
As role: anonymous
Open /results-history.html. Click Div A, B, C — list visibly changes.
|
| bug-060 task #118 uat-cage 2026-06 (prior) |
Same as bug-040 (UAT SANDBOX tab on homepage)
Filed: 2026-06-12
|
(none)
URL: /#
|
![]() |
Duplicate of bug-040 — same fix. |
Verify: /
As role: anonymous
See bug-040.
|
🎥 Video-reported bug 1 ADDRESSED
| Video | Page | Original ask | Status | How to test |
|---|---|---|---|---|
| v0 video 0.mp4 |
/docs/rules |
A+ button should scale the rules CONTENT not just the TOC; every page should have a bug-report function |
FIXED — site-wide font-zoom widget shipped (public/js/font-zoom.js + font-zoom-pill.js), bug-report widget on every page |
Verify: /docs/rules
As role: anonymous
Open /docs/rules. Find the A+ font-zoom pill (top-right). Click + → text scales bigger. Find the bug-report widget (bottom-left) on every page.
|
🔧 Round 1–5 audit bugs — full history 37 fixedfrom the canonical docs/UAT_BUG_LOG.md
These were caught during deep audits before the committee batch — naive-user agents, role walks, security hats, multi-tenant integrity checks. Most have shipped fixes; a few are documented as "by design" (intentional choices) or "enhancement" (improvements queued for prioritisation).
| ID · reporter | Symptom | Root cause | How we fixed it | Status |
|---|---|---|---|---|
| A1 Player (QA) |
"Find your Team" went to the player database, not clubs | wrong link target | repointed `/players.html` → `/clubs.html` | FIXED |
| A2 Captain Alice |
Couldn't **sign** the scoresheet — "Forbidden" | frontend POSTed a non-existent route (`/sign`), swallowed by an admin-only catch-all → bare 403 | added a real **each-captain-signs-own-side** route (side derived from team membership) + shared finalizer | FIXED |
| A3 Captain Alice |
Could submit a **gender-illegal** lineup; "it let me save" | `?force=1` bypass was **un-gated** for captains; submit-score also dropped the (validated) lineup | `?force` now **admin-only**; submit-score **preserves the validated lineup** | FIXED |
| A4 Captain Alice |
Could add the **same player twice** | duplicate-in-one-game not checked on any path | gender/roster now enforced + non-bypassable; *same-person-both-slots still permitted* — see B-list | PARTIAL |
| A5 Captain Alice |
Score save "failed then worked" | rules engine correctly 400s **partial** (<5 games) submissions while filling the sheet | behaviour is correct; error now shows a clear reason; the wizard issue was the sign-403 (A2) | FIXED |
| A6 Captain Alice |
AI assistant replied in **Chinese to English** input | reply language was 100% delegated to the model; Chinese-heavy roster context biased it | reply language **pinned to the user's message** | FIXED |
| A7 Captain Alice |
Reschedule asks for a **match number**; "no reschedule button" | button shows only when there's an upcoming scheduled match (test data had none); AI help-desk asks for a match id | data-starvation (sandbox now fully seeded); AI-UX noted | ? |
| A8 Captain Alice |
Could add a player **without email verification** | no verified-contact gate on roster-add | intentional — HKPL identifies players by **DUPR id**, not email | BY DESIGN |
| A9 Captain Alice |
"Plan / request" function not needed | — | **kept** (industry-standard pre-list/post-game flow); admin fast-path noted for future | BY DESIGN |
| A10 Captain Alice |
No **division** shown on captain's team cards (confusing w/ multiple teams) | missing label | UX label | ENHANCEMENT |
| A11 Tenant-Admin Ursula |
**Content Studio / "content library" unresponsive** "since day one" | the 13 modules are a roadmap placeholder — never built, but styled clickable | tiles now clearly **"Coming soon"** + a working pointer to **Settings → Asset Library** (real uploader) | FIXED |
| A12 Tenant-Admin Ursula |
**Editorial: can view but can't change title/words** | saving a post with **no cover image** was rejected (`cover_url` required a URL) | blank cover/logo URLs now accepted; editing title/body **saves + persists** (verified) | FIXED |
| A13 Tenant-Admin Ursula |
Homepage **video** not editable in admin | homepage video was **hardcoded** (never-built TODO) | **Homepage Video editor** added (Settings → video URL + title + description; no schema change); also fixed relative `/cdn/` asset URLs being wrongly rejected as covers/logos | FIXED |
| A14 Tenant-Admin Ursula |
Gallery tab: dropdown / upload button missing | upload lives in Settings → Asset Library, not the gallery tab | pointer added via Content Studio (A11); gallery-tab pointer pending | PARTIAL |
| A15 Tenant-Admin Ursula |
**Create match**: teams not filtered by division → unclear error | UI lists all teams; server correctly rejects cross-division but the UI hid the detail | server error now honest; client division→team filter pending | ? |
| A16 Tenant-Admin Ursula |
Rulebook spacing | display formatting | formatting polish | ENHANCEMENT |
| A17 Captain (QA) |
`super/users-search` 403 ("search error") | the "sign in as any user" tool is **SUPER_ADMIN-only**; a captain session hit it | works as intended (admin-only); the QA test surface is now sandbox-only anyway | BY DESIGN |
| A18 Tenant-Admin (QA) |
"Add a Generate Invoice + Receipt button" | — | valid request | ENHANCEMENT |
| B1 Unknown `/api/*` routes returned a misleading **403** |
a last-mounted admin-only catch-all swallowed every typo | honest **404** + per-route admin gate (kills the wrong-URL bug class) | **FIXED** | FIXED |
| B2 Many handlers **500'd** on a blank field / stale id |
global error handler defaulted everything to 500 | maps **ZodError→400, Prisma P2025→404, P2002/3→409** | **FIXED** | FIXED |
| B3 | `findFirst({id:NaN})` threw outside a try → no response | bad-id guard → clean 400 (**caught by the test harness**) | **FIXED** | FIXED |
| B4 | frontend called the wrong path | → `/api/v1/admin/clubs` | **FIXED** | FIXED |
| B5 Referee "Start Live Scoring" dead |
wrong URL (`/live/start` vs `/matches/:id/live/start`) | corrected | **FIXED** | FIXED |
| B6 Judge 401 → `/login.html` (doesn't exist) |
wrong redirect | → `/sign-in.html` | **FIXED** | FIXED |
| B7 | **Cross-tenant** rule write (admin could target another tenant) | forced to own tenant | **FIXED** | FIXED |
| B8 **Cross-tenant** club join |
unscoped club lookup | scoped to request tenant | **FIXED** | FIXED |
| B9 Eligibility rules **skipped** on join-request approval |
engine wired only to the captain "add player" path | approval now enforces the same rules (registrar override = the exception) | **FIXED** | FIXED |
| B10 New divisions/tenants had **no DUPR-band / NR** rules |
rules seeded only for 3 hardcoded division ids | `scripts/sync-division-rules.js` derives them for every division | **FIXED** | FIXED |
| B11 Dead social-media links (`href="#"`) on 5 public pages |
populating script only on the homepage | shared `social-links.js` populates or hides them | **FIXED** | FIXED |
| WS1 Live updates dead on uat (naive-agent: `/ws` → 403) |
`WS_ALLOWED_ORIGINS` listed hkpl/szpb/macau/gba but **not `uat.silkvo.com`** → every WS upgrade from the uat host was origin-rejected | added `uat.silkvo.com` to the allowlist (`lib/ws-server.js`) | **FIXED** | FIXED |
| A4 Same player in both slots of one game |
no per-game duplicate check on any lineup path | hard, **non-bypassable** `same_player_both_slots` guard on **both** lineup paths (captain `my-lineup` + judge-console); even admin `?force` can't override (it would double-submit one DUPR id). Harness test added (red→green) | **FIXED** | FIXED |
| DISC1 | Admin couldn't find the homepage-video / post editors (naive-agent rated tenantadmin 1/10) | dashboard **Common tasks** now has "News, posts & announcements" + a "Homepage video" shortcut that jumps to and scrolls to the editor | **FIXED** | FIXED |
| A10 No division on captain team cards |
label only shown as a skewed badge | explicit "Division: … · season" subline under the team name | **FIXED** | FIXED |
| A14 Gallery tab had no upload affordance |
uploads live in Settings → Asset Library | added a "📤 Upload images (Asset Library)" button that jumps to and scrolls to the uploader | **FIXED** | FIXED |
| A15 | Create-match listed all teams → confusing cross-division reject + stale "endpoint not wired" note | team dropdowns now **filtered by the selected division**, default B≠A, client A=B guard, server `detail` surfaced, stale note removed | **FIXED** | FIXED |
| JUDGE1 Judge lands on an empty state (naive-agent rated 6/10) |
venue must be picked manually; bare "pick a venue" hint | empty state is now actionable ("no matches on this date — pick another"), and a date with exactly one venue **auto-selects + loads** so the judge lands on the match list | **FIXED** | FIXED |
| PLAYER1 | Player landing had no link to their own profile / DUPR (naive-agent rated 5/10) | added a "My profile & DUPR" shortcut (→ `/profile.html`) in all 3 languages | **FIXED** | FIXED |
| SEED1 | Registrar / division-rep cards showed a raw slug (`uat-pending-…`) in quotes | seed now writes a human-readable note (still unique → still idempotent); the two live uat rows were updated in place | **FIXED** | FIXED |
| A18 "Generate invoice + receipt" button |
New route `GET /api/v1/registrar/registrations/:id/invoice` renders a print-ready, self-contained **Invoice / Official Receipt** (browser Print → Save as PDF; no server Chrome dependency). Pulls real amount from `Tenant.registration_fee` + `currency`, presents as **PAID Official Receipt** once `rece… | New route `GET /api/v1/registrar/registrations/:id/invoice` renders a print-ready, self-contained **Invoice / Official Receipt** (browser Print → Save as PDF; no server Chrome dependency). Pulls real amount from `Tenant.registration_fee` + `currency`… | FIXED | |
| A16 Rulebook spacing |
Reader-facing rulebook is `/docs/rules` (docs.js). Each section now has a top divider + 40px top margin (clear separation) and the body reads at line-height 1.75 / 15px instead of the cramped 24px gap. `/docs/rules` 200 verified. **FIXED** | Reader-facing rulebook is `/docs/rules` (docs.js). Each section now has a top divider + 40px top margin (clear separation) and the body reads at line-height 1.75 / 15px instead of the cramped 24px gap. `/docs/rules` 200 verified. **FIXED** | FIXED | |
| RCPT1 Admin "View receipt" link was broken |
The link hrefed the raw `receipt:<file>` token (un-openable). Now points at the authenticated streaming route `/api/v1/registrar/receipt/:id`. **FIXED** (found while building A18) | The link hrefed the raw `receipt:<file>` token (un-openable). Now points at the authenticated streaming route `/api/v1/registrar/receipt/:id`. **FIXED** (found while building A18) | FIXED | |
| JUDGE2 "Referee Console" vs "Judge" perception |
No change warranted: the page says **Referee** consistently (HKPL's term for an on-court official); the "Judge" the agent saw top-right is the **QA persona's display name** ("Judge Jordan"), not a role label. Real users aren't named that. **NO CHANGE** | No change warranted: the page says **Referee** consistently (HKPL's term for an on-court official); the "Judge" the agent saw top-right is the **QA persona's display name** ("Judge Jordan"), not a role label. Real users aren't named that. **NO CHANGE… | NO CHANGE | |
| C1 | `GET /api/v1/public/teams?division=` (team-stats picker) had no route | built in public.js | **FIXED** | FIXED |
| C2 | `GET /api/v1/public/teams/:id/stats` (team-stats record) had no route | built (reuses computeStandings) | **FIXED** | FIXED |
| C3 | `GET /api/v1/results/history?division=` (Results History page) had no route | built in public.js; tier letter resolves off division NAME (slugs are `division-a`) | **FIXED** | FIXED |
| C4 admin-roles.html "Help" → `/help.html` (nonexistent) |
real dead link | → `/overview.html` | **FIXED** | FIXED |
| F1 Content Studio: 13 dead "coming soon" IG/Canva tiles |
real (placeholder) | rebuilt as a **working hub** launching the 9 real content tools (video, gallery, asset library, posts, mission cards, organization, sponsors, rulebook, translations). Zero dead tiles. | **FIXED** | FIXED |
| R1 Reports tab deferred "PDF/ledger/DUPR sync log to Phase 4" |
real (deferral) | built **two real CSV exports** — full-season standings + registration/payment ledger (`/admin/reports/standings.csv` + `registrations.csv`). Deferral language removed. | **FIXED** | FIXED |
| X1 League Insights "12 cards spin forever, no JS" |
**FALSE** — all 16 `/admin/analytics/*` endpoints exist and return real data; the agent saw the static "Loading…" placeholders and assumed they never populate | none | **WORKS** | FALSE |
| X2 | **FALSE** — it's in `EMBED_TABS`, iframes the real `admin-club-managers.html` | none | **WORKS** | FALSE |
| X3 disputes tab "is dead/TODO" |
**FALSE** — `renderDisputes` is a functional read view; the TODO was a dashboard *count* comment | none | **WORKS** | FALSE |
❓ Awaiting your input 3 items
→ Which specific page + which buttons / which table is the wrong size? A screenshot with the trouble area circled would let me fix it.
→ Please provide a short MP4 walkthrough (or a YouTube/Vimeo link). We'll embed it as the first section of /faq.html.
→ Naming + permission split is your call. Share the final naming and we'll rename + re-wire the permission gates.
📋 Deliberately not done accidental snaps + dev-smoke pings
Reviewed and intentionally skipped. If any are real problems we missed, please re-report with a one-line description.










































