What's New in Version 3
Version 3 introduces Supabase as a read-first data layer, clarifies the early runner dual-role rule, and confirms the Wrong Hcp DOS-parity formula is active.
supabase-mirror.js. No manual action needed.
System Overview
The BAC Handicap App manages 24 weeks of cross-country handicap racing. Runners receive staggered start times based on their handicap. Placings are determined by clock finish time within each distance ร gender ร age group.
Key terms
| Term | Meaning |
|---|---|
| Handicap | Your personal start time advantage. Updated after each race based on how your run time compared to the target finish time. |
| Clock finish | The actual time you crossed the line from the stopwatch. Used to determine your placing. |
| Run time | Clock finish minus your handicap. Used to compute your next handicap adjustment. |
| Placing | Your rank within your distance ร gender ร age group for the week. Based on clock finish time โ earliest finisher wins. |
| Season | 24-week series, split into 6 monthly blocks of 4 weeks each. |
Data storage
lrfhvesylfwbabzxbqhr. Read-first source for results, handicaps, and result-weeks. Results are mirrored here after every save. Handicaps are derived from the latest results.handicap per (name, distance) โ there is no separate handicaps table.
Target finish times
| Distance | Target finish time |
|---|---|
| 1K | 26:00 |
| 3K | 38:00 |
| 6K | 44:00 |
Before Race Day
Complete these steps before the season opens. Most are one-off tasks โ once done, weekly processing follows the Race Day workflow.
- Set up the Google Sheet Confirm the BAC Handicap 2026 sheet is in place. Run the Setup script if starting fresh โ it creates all required tabs and seeds the config.
- Assign bib numbers Check that all registered runners have bib numbers. Use Admin โ Assign Bibs for any PENDING runners who registered without an auto-assigned bib.
- Set opening handicaps Load each runner's opening handicap from the DOS master list. Handicaps are set per runner per distance.
- Apply the season reset Subtract 2 minutes from every runner's handicap at season start. This levels the field relative to the prior season's finishing handicaps.
- Test mode verification Run the full race-day workflow against the test sheet before the first live race. Confirm standings, handicap updates, and monthly prize computation all work correctly.
- Distribute the App Guide Share the App Guide link (/guide) with the committee and regular volunteers so they know how to use the stopwatch and volunteer portal.
Race Day Processing
Follow these steps in order every race week. Each step builds on the previous โ don't skip ahead to Confirm & Save before reviewing.
Step 1 โ Enter the stopwatch times
The finish line volunteer uses /stopwatch to record a lap time for each finisher. After the race, they tap Sync to Sheet, which pushes the laps to the Week N Stopwatch tab in Google Sheets. In the main app (Timesheet), click Import Stopwatch to pull those times in.
Step 2 โ Enter Early Runners
Early runners started ahead of the main field with a watch timer. Enter each one manually on the Timesheet: enter their bib, start time (their handicap), clock finish time, distance, and mark the entry as Early runner.
Step 3 โ Mark Wrong Hcp and Time Trial entries
Step 4 โ Enter DNFs
Mark any runner who did not finish as DNF. Their handicap is NOT adjusted โ the stored value is kept unchanged. A start time of 0:00 and a finish time of 99:00 are placeholder values stored for DNF entries; they are not real times and are not used in any calculation.
Step 5 โ Review Results
Click Review Results. The app runs sanity checks covering duplicates, suspicious times, missing entries, and age group issues. Fix anything flagged before proceeding to save.
Step 6 โ Confirm & Save
Click Confirm & Save. The system:
- Writes to Google Sheets Results go to the current week tab and the main Results tab.
- Updates each runner's handicap Applies the standard adjustment formula (or DOS-parity formula for Wrong Hcp entries).
- Recalculates standings Season standings are rebuilt from all saved weeks.
- Creates per-week backup tabs Week N Results and Week N Handicaps tabs are created automatically for audit purposes.
- Mirrors results to Supabase All results are written to the Supabase database so the public results page can serve them quickly.
Handicaps
A runner's handicap is their personal start time advantage. It is updated after every race they complete (excluding DNF). There is no separate handicaps table โ handicaps are derived from the most recent saved result per (name, distance).
Where to see handicaps
Standard adjustment rules
| How you ran vs target | Handicap change |
|---|---|
| Finished early (faster than target) | +seconds early, rounded UP to nearest 10s |
| 0โ60 seconds late | No change |
| 61โ120 seconds late | โ10 seconds |
| More than 120 seconds late | โ20 seconds |
Wrong Hcp formula
When a runner starts at the wrong time, the system computes an effective finish time as: storedHandicap + (clockFinish โ actualStart). The standard adjustment formula is then applied to that effective finish time. Both early-start and late-start Wrong Hcp runners have their handicap adjusted this way.
DNF
No handicap adjustment is made for DNF entries. The stored handicap is kept unchanged regardless of what time appears on the Timesheet.
Managing Runners & Registrations
Runners register via the Google Form and appear in the Runners tab as PENDING until a bib is assigned. Walk-up entrants need to be added manually before their results can be saved.
- New registrations via Google Form Runners who complete the registration form appear in the Runners tab with PENDING status. Sync the form responses using Admin โ Sync Registrations to pull in the latest entries.
- Assign bib numbers Go to Admin โ Assign Bibs, or use the Register tab. Bib numbers are permanent within a season โ do not reassign a bib that has already been used in a result.
- Walk-up entrants Use Admin โ Register a Runner to add anyone who didn't fill in the form before race day. Assign them a bib before entering their result on the Timesheet.
-
Name resolution
Long hyphenated names may truncate in source data. If a runner's name doesn't match, add the variant to
data/name-aliases.csvrather than editing result rows directly.
Using Test Mode
Test mode lets you run the full processing workflow against a test dataset without affecting any live data. Use it before the first race week and whenever you want to verify a change.
Enabling test mode
Add ?test=1 to the main app URL. The app switches to the test Google Sheet and displays a TEST MODE banner at the top of the page.
Pre-season checklist in test mode
- Set up the test sheet Populate the test sheet with sample runners and opening handicaps.
- Import a sample stopwatch CSV Use a CSV with realistic lap times to simulate a full race day import.
- Process a full week Run through the complete workflow โ import, mark specials, review, save. Verify placings, handicap changes, and standings all look correct.
- Verify monthly prize calculation Use Admin โ Monthly Prizes in test mode to confirm the computation and tie-break logic works as expected.
-
Return to live
Remove
?test=1from the URL to return to the live sheet. Confirm the TEST MODE banner is gone before processing any live race.
Volunteer Management
Volunteers sign up via /volunteer (no PIN required). Their signup is written directly to the Google Sheet roster. Viewing the roster and cancelling signups is also handled from that page.
Points
Volunteers who don't run earn 8 points for that week, assigned to their preferred distance. If a runner does an early start AND volunteers in the same week, they earn the higher of their position points or 8 โ whichever is greater.
Viewing the roster
The volunteer page shows current week assignments and the next three upcoming weeks. Use this to see where help is still needed before race day.
Volunteer roles
| Role | Description |
|---|---|
| Finish | Record finish order and confirm runners through the line. |
| Marshal | Guide runners safely around the course and key turn points. |
| Numbers / Tokens | Hand out tokens or record bib numbers at the finish. |
| General | Flexible help โ setup, pack-up, or wherever needed on the day. |
Monthly Prizes
The season is split into 6 monthly blocks of 4 weeks each. A prize winner is determined per age group ร gender ร distance for each block.
Prize rules
How to compute prizes
Go to Admin panel โ Monthly Prizes โ select the block weeks (e.g. weeks 1โ4) โ click Compute. Review the results before publishing. The system enforces the one-win-per-season rule automatically.
Season Rollover
At the end of the 24-week season, follow these steps in order. The season reset must happen after final processing and before new handicaps are distributed.
- Run final monthly prize computation Compute prizes for the last block (weeks 21โ24). Publish and record results.
- Export standings and results Download season standings and results as PDF/CSV for club records before archiving.
- Apply the season reset Subtract 2 minutes from every runner's handicap. This is their opening handicap for the next season. Use Admin โ Season Rollover or the setup script.
- Archive the Google Sheet Rename the current sheet to "BAC Handicap 2026 Archive" so it's preserved but not used for live processing.
- Set up the new season sheet Run the setup script to create a fresh sheet with all required tabs and config for the new season.
-
Update season constants in code
Update the
SEASONconstant insrc/lib/supabase-reads.jsandsrc/lib/sheets.jsto the new season year/config.
PDF Reports
The main app can generate PDF reports for weekly results, full season standings, and monthly prize summaries. These are suitable for printing or distributing to committee members.
Available reports
๐ Weekly results
Placings, times, points, and handicap changes for one race week. Grouped by distance, gender, and age group.
๐ Season standings
Full accumulated standings table, broken down by distance ร gender ร age group. Shows all runners and total points.
๐ Monthly prize summary
Prize winners for each block, with points totals and the weeks run used for tie-breaking.
To generate: go to the Results or Standings tab in the main app โ click Download PDF. The report opens in a new browser tab โ print or save from there.
Other Tools
Admin Panel
Available at /admin (separate admin password, not the main app PIN). Use for coordinator-level corrections and maintenance outside the normal weekly workflow.
โ๏ธ Correct results
Fix an individual result after a week has been saved โ change a time, swap a bib, or adjust a flag. Always run Recalculate Standings after any correction.
๐ Recalculate standings
Rebuild the full season standings from scratch after corrections. Standings do not update automatically from admin edits.
๐ Register a runner
Add a walk-up entrant who didn't fill in the Google Form before race day. Assign a bib before entering their result.
๐ท๏ธ Assign bibs
Assign bib numbers to PENDING runners who registered but didn't receive an auto-assigned bib.
๐งน Clean blank rows
Remove empty rows from the Google Sheet that can appear after bulk operations.
๐ Monthly prizes
Compute and publish monthly prize winners for a given block of weeks.
Archive tab
Historical results from the 2025 season are available in the Archive tab of the main app. For reference only โ not used in 2026 processing.
Legacy Verify
Available at /legacy-verification. Checks 2025 season data against the DOS scoring engine for consistency. Not used in normal weekly processing.
Troubleshooting
| Problem | Solution |
|---|---|
| Results showing stale data | The app reads from Supabase first. If Supabase data looks stale, check whether the mirror ran after the last save. The app falls back to Google Sheets automatically if Supabase is unavailable. |
| Wrong week shown | The main app derives the current week from the season start date. Check the seasonStart config in the Google Sheet config tab. |
| Runner missing from results | Check the bib is registered. Walk-up runners need to be added in Admin first before their bib will be recognised on the Timesheet. |
| Duplicate bib flagged | Two entries in the same week share a bib number. Remove the duplicate from the Timesheet before saving. |
| Standings not updated | After any admin correction to a result, run Admin โ Recalculate Standings. Standings don't update automatically from admin edits. |
| Handicap looks wrong | Check whether the runner was flagged as Wrong Hcp or DNF. DNF entries keep the stored handicap unchanged. Wrong Hcp applies the DOS-parity formula โ confirm the actual start time entered is correct. |
| OCR extracted wrong value | Review the OCR output against the original paper sheet. Correct on the Timesheet before importing. Ambiguous handwriting is the most common cause. |
| Can't sync stopwatch | No internet at the course. Use Download CSV or Copy to Clipboard as backup, then import from the main app later using the single-file CSV option. |
Quick Reference Card
| Item | Value |
|---|---|
| Season weeks | 24 (6 monthly blocks of 4) |
| Target finish โ 1K | 26:00 |
| Target finish โ 3K | 38:00 |
| Target finish โ 6K | 44:00 |
| Volunteer points | 8 (assigned to preferred distance) |
| DNF handicap change | None (stored value unchanged) |
| Early finish adjustment | +seconds early, rounded UP to nearest 10s |
| Late 61โ120s | โ10s |
| Late >120s | โ20s |
| Season reset | โ2 minutes from all handicaps |
| Data storage | Google Sheets (primary write) + Supabase (read-first, mirrored) |
| Main app PIN | Shared โ ask the committee secretary |
| Admin password | Separate from PIN โ ask the committee secretary |