Sunday Riders · build notes · 10 September 2026
Tooling to run the Sunday Riders for the long haul: fairer, more open, easier on the people who run it, and able to host more kinds of golf than one standing game.
It makes the pairings fair and shows why. It keeps a record everyone can see, and gives the league's stats somewhere to go: awards, recognition, side bets. It takes the week of email off the commissioners and out of everyone's inbox. And it lets any commissioner put up any format on any day, so the league can run a wider variety of games. That's how it grows, and how players with different strengths get their turn to shine.
This isn't a mockup. Everything described here is built and running at sundayridersgolf.com right now, including the scorecard screen further down, which you can click around in. What's left is tuning: loading the real course numbers, fixing the names that came across wrong, and turning the email on.
The reason it isn't live yet is that we should launch with the vision settled rather than change the rules on people three weeks in. So this is going around first. Tell me what we want, and just as usefully, what we don't.
Technical detail sits behind the Under the hood toggles. Feel free to skip them if you don't care about the technical details and just want to read about the impact on our golf. The last section is about how this gets better, which is mostly you telling us what's wrong with it.
The commissioners run the league by hand. Every game means writing to the whole list, tracking who answered in an inbox, chasing the ones who haven't, and building the foursomes on paper. It works. It also costs a few people several hours a week, and it leaves nothing behind that anyone can look at later.
Four goals:
It doesn't assume our schedule. Any commissioner can put up a game of any type on any day: the weekly game, a quota points game, a nine-hole Tuesday thing. Signups, reminders, groups, scoring and the record all work the same way.
It doesn't assume how much anyone wants to hear from us. You decide: every signup as it happens, only the messages that need an answer from you, or nothing. Snowbirding, or out of town for two weeks? Set the dates and we leave you alone and mark you out.
And it doesn't assume it's the last system the league will ever use. Everything it records is plain, inspectable and exportable, so whatever comes next can start from the full history instead of from zero.
The commissioners send a lot of mail because it's the only tool they have. A mailing list can't tell who has already answered, so a reminder meant for the four people who haven't replied goes to all sixty-one. The guy who said yes on Monday gets it anyway, learns that most of what we send doesn't concern him, and stops reading, including the ones that did matter. The goal is to cut the noise so what's left is worth opening.
Reminders aren't the problem. Spots don't fill on their own and the pressure has to rise as the deadline gets closer. What changes is who each one goes to. Every message is addressed to a group defined by where things stand: people who haven't answered, people who are in, people on the waiting list. Say yes and you're done hearing about that game, unless your settings say you want to watch the field fill up, in which case you get that and nothing else.
There's no mailing list stored anywhere. Each send works out its recipients at the moment it goes out, from the current state of the game and each person's settings. Someone who answers thirty seconds before a reminder fires is already excluded.
Every send and every skip is recorded with the reason: didn't answer yet, on vacation, muted this game, unsubscribed, address bounced. The console shows those reasons, so "why didn't Bob get this" has an answer.
Reminder timing belongs to the game rather than to a weekly clock. It tightens as the cutoff approaches while the field is still short. Some sends are triggered by events instead of the calendar: someone drops out of a full field and the people who never answered are told right away that a spot opened, while everyone who already declined hears nothing.
When groups are put together by hand, two things come up. Sometimes the teams feel lopsided, and you tend to end up with the same guys week after week. Neither one means anybody did anything wrong. It's just that when a person builds the foursomes, there's no way to show that they didn't lean one way, and no way to catch it when chance leans for them.
Building them by computer fixes both. The rules are written down, they run the same way every time, the round page explains what shaped that week's groups, and the whole history is there to look at. That makes it demonstrably even-handed instead of just claimed to be. And because the system can see the whole history, it can correct drift that random chance produces on its own: pairs who keep landing together, players who keep getting the same draw.
The point of all that is participation. The two-man game is optional, and people opt in when they believe they have a real chance. The more obviously fair the groups are, the more guys play. Further out, if the league trusts the draw, we could loosen the rule that teams have to come from your own foursome, and pick teams across the whole field instead.
The rule is simple: with four groups, the four best players go in four different groups. Nobody's foursome gets two of them. The same thing happens at any size, one of the top players per group.
Inside each group the system recommends the two-man split that comes out closest to even on the Rider Rating, and shows the numbers behind it. A commissioner can override the recommendation, and the override is recorded like everything else.
Random alone doesn't get you there. Left to chance, some pairs land together several times a season and others never once. So the system counts how often each pair has actually been grouped and compares it to how often chance would have done it. Pairs running ahead of their share get pushed apart in the next draw, and pairs who've never played together get nudged closer.
Over a season that turns a random draw into everyone getting around to everyone. The same count is where the end-of-year stats come from: who you played with most, who you somehow never drew.
Later on, form could feed into this too. The system already knows who's playing better than usual and who's cold, so for a bigger event the groups could be built around that instead. It's a setting, not a rewrite.
Our own number, built for this league. It measures how you score against the difficulty of the course you actually played, weighted so recent rounds count for more than old ones.
It isn't tied to anyone else's system, and that matters. People in this group score themselves inconsistently, rolling it, taking gimmes, the occasional generous six, and the only way to balance that out is to measure each player against his own tendencies. What the number rewards is improvement. Get better, win more. That's the whole idea.
It does three jobs. It balances two-man teams, it sets each player's number in a quota points game, and it tells you whether you're playing better or worse than usual. A new player starts with an estimate from whoever brought him, and real rounds replace it as he plays them.
Each round gets adjusted for the course, so a 90 on a hard track and an 86 on an easy one can be compared:
round_rating = (gross − course_rating) × 113 / slope
Then the rounds are averaged with a fade: each round counts half as much as the one six rounds after it. Last week counts fully, six weeks ago half, twelve weeks ago a quarter. This is a weighted moving average with a six-round half-life.
Why not a plain rolling average of the last ten? Because a rolling window has a cliff. Round ten counts the same as round one, and then round eleven drops off the edge entirely, so your rating can jump the week an old blow-up ages out, for no golf-related reason. The fade has no cliff. It also gives "hot or cold" something to measure against: a short-window average runs alongside the long one, and the gap between them is the form signal.
Two guards on it. A round more than 8 strokes from your current baseline is pulled in to 8 before it's averaged, so one disaster in the wind doesn't move a number built from thirty rounds. And a nine-hole round counts at half weight, because it's half the evidence.
It's an average rather than a best-of-N on purpose. Best-of measures what you're capable of. The average measures what to expect. Balancing teams and setting quotas both want the expected number.
Before the draw, the top players by Rider Rating are marked as seeds, one per group, and the draw fills in around them. That's the fairness rule above, applied mechanically instead of by someone's judgment.
The repeat-pairing pressure works from a count. For each pair, the number of times they've been grouped together is compared to the number chance would predict from the actual group sizes played. The ratio is smoothed with a +1 on both sides so a single overlap in a short season doesn't look like a pattern. A pair at 1.0 is exactly where chance would put them. Above that, the draw pushes them apart. Below, it nudges them together.
The seed for the draw is committed as a hash before the field is known, and the field itself is hashed into the draw. A redraw is added to the record rather than replacing what was there, so it's visible that one happened and why.
In each foursome there are exactly three ways to split into two pairs. The system scores each split by weighting the stronger player at 65% and the weaker at 35%, because in a two-man game the better ball is carried more often by the better player, and recommends the split where the two sides come out closest.
Scoring stays with the commissioners, and nobody has to keep score on a phone. Whoever is running the game photographs each group's card as it comes in, and the numbers are read off the picture.
Live scoring from the course, hole by hole on a phone, is possible and not hard to add. I haven't, because I don't think enough of us would do it consistently, and a system where half the cards are live and half are photographed is worse than one that's all paper. If the league decides it wants it, say so and it goes in.
AI and computer vision are still new and still make mistakes. So the machine never has the last word on anybody's score. When the commissioner uploads a card, the numbers it read come up on screen next to the photo, and he reads down the two and confirms they match. It takes a few seconds a card, and it's what keeps a bad read from ever reaching the results.
Below is that confirm screen, working, loaded with a card that was read badly on purpose: two rows have the wrong names on them and one row doesn't add up to the total on the card. Fix it the way a commissioner would.
| Player | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | Sum | Card says |
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| Par | ||||||||||||||||||||
| Stroke idx |
Confirm is a deliberate action on every card, clean ones included. He's attesting, not dismissing a dialog.
Tap a name to reassign a row; tap a number to correct it. Amber is the model saying it isn't sure — it clears when a person touches the cell.
The photo is kept permanently and linked from every result it produced, and every correction is recorded with a name and a reason instead of being made quietly. That isn't about catching anyone. It's about the record being complete enough to rebuild into whatever comes next. This system doesn't assume it's the right one or the last one, so everything it holds is cataloged, inspectable and exportable.
Only commissioners enter scores, but anyone can check them. Every player can open a round, see his own row beside the card, and flag something that looks wrong.
If a photo comes back unusable, the same screen falls back to typing the numbers in. That's tedious, but it's never blocked.
The image goes to a vision model along with what's already known: the four names in that group from the draw, the pars, the stroke indexes, the tee set. It's never asked "who are these people." It's asked to match four handwritten rows to four known names. That's a much easier problem than open-ended handwriting recognition, and it's why the confidence numbers in the demo are per-row assignments instead of per-character guesses.
Does each row add up to the total written on the card. Is any hole more than double par. Are there blanks. Does a row disagree with its written total in the way two swapped rows would. Anything that fails is called out in words above the grid, and low-confidence cells stay amber until somebody touches them.
Cards arrive as groups finish and the round page shows which are in. Nothing is announced until a commissioner has reviewed everything and pressed publish, because greenies can't be settled until the last group is off the course. Four to six cards a week at a couple of cents each.
There's no app. The site runs in whatever web browser is already on your phone, tablet or computer, and it's deliberately plain-looking so it behaves the same on all of them: big text, few buttons, and no clever layouts that fall apart on an older phone.
Answering an invitation takes no sign-in at all. The buttons in the email, I'm in, Me + guests and I'm out, work by themselves. For most of the league that's the entire interaction.
Looking at scores and history does need to know who you are, and that's still not a password. Type your email address, we send you a link, you click it, you're in. You stay signed in on that device for a month. Nothing to remember, nothing to reset.
Two kinds of link, shaped differently because these emails get forwarded and that has to be safe.
An RSVP link is tied to one game and one player, and carries no login. Forward it and your buddy can answer for you on that game, which the round page shows and a commissioner can fix. He can't do anything else with it.
A sign-in link is a random token the server remembers. It works once and dies after fifteen minutes. What it leaves behind is a signed cookie on your device with your player id and an expiry, never put in a URL and never emailed, so there's no forward that carries a login.
The login form shows the same screen for every address, so it can't be used to find out who's in the league. Commissioner rights are a flag on a player, so granting and revoking are ordinary recorded events like everything else.
Everything lives in a cloud account that belongs to the league and nothing else. Every fact, that a game was scheduled, that a player said yes, that a card was confirmed, is a small plain-text file. Readable in any editor, no database, no proprietary format.
That choice does four things:
Routine upkeep, fixing a name or loading a course or adjusting a setting, is being set up so an AI assistant can do it from a plain-English request, with a person approving the change. The league shouldn't depend on one person remembering how it works.
Every event is an immutable JSON object in S3, named by a sortable ID so the files list in the order they happened:
events/2026/09/09/01K4Q8ZB7N3XJ6VYD2M9F0TRWA-rsvp.recorded.json { "id": "01K4Q8ZB7N3XJ6VYD2M9F0TRWA", "type": "rsvp.recorded", "v": 1, "at": "2026-09-09T14:02:11.417Z", "actor": { "kind": "player", "id": "p14" }, "subject": { "event": "evt_0f2a", "player": "p14" }, "data": { "response": "in", "source": "link", "guests": 0 } }
Pages read from a snapshot that's rebuilt by folding the log. Writes never conflict, because every event is its own file with its own unique name. Two people clicking I'm in at the same instant produce two files rather than a collision, and there's no lock anywhere in the system.
Hole scores are stored. Totals, skins, greenies, ratings and standings are computed from them on rebuild. So a scoring fix corrects the whole history, and a format invented later can be applied to rounds played earlier. Loading a course rating after the fact is ordinary data entry and never a repair job.
The rebuild is deterministic, taking its timestamp from the last event rather than the clock, and there's a test that a replay produces byte-identical output. That test is what makes the claim above about moving the data real.
All storage goes through one small interface, and the S3 implementation is the only code that knows S3 exists. Pointing it at a database later means replaying the log, with no schema to convert.
Things that need a call, with the detail behind each so you can make it quickly.
What does someone get who never opens their settings? Everything, minus reminders they've already answered, which is roughly what they get today. Or the quieter "only when you need an answer" setting, with the play-by-play as an opt-in. The first is less of a change and the second is less mail.
Either way, every message should carry a link that says exactly what it does, "You email me too much", and take the person straight to their settings. Ask someone how much mail they want at the moment they're annoyed by it and you'll get a real answer, which makes the default matter a lot less.
Right now two-man teams are picked inside your own foursome, which is the rule we play by. Balancing across all sixteen instead of within four would produce fairer teams than any within-group split can, but it changes how the game feels. Not for launch, but worth raising once people trust the draw.
Skins, greenies and team results are recorded now. Pot splits and who owes whom are not, because the rules for those aren't written down anywhere yet. When they are, the results already recorded can be priced retroactively.
The Rider Rating produces a quota per player already. What's not decided is the day, the field size, the point values per score, and whether it runs alongside skins or instead of them.
Guys already play 1-on-1 for $5 front, $5 back, $5 overall, sometimes with strokes given, and most of it gets arranged in the parking lot. That should keep working exactly as it does. The question is whether the system should also let a player offer a bet after signing up, something like "5/5/5, I'll give two strokes, first two takers", so others can claim it before the day. Longer-running bets across a season are the same question. The pieces fit: a bet is an offer on a round, claimed by a set number of people, settled from the confirmed cards. Probably not in the first version, but worth deciding before the money layer, since they're the same layer.
Ratings, slopes and stroke indexes for the courses we rotate through are placeholders until a card from each is photographed or typed in. Gross formats work without them. The Rider Rating needs them.
This is a first version of something the league will use for years. Some of it will be wrong, and some of it will be right but confusing. The only way it gets close to good is if the people using it say so, especially about the things that aren't easy, the things that don't make sense, and the calls you'd have made differently.
The same goes for what it doesn't do yet. Formats we've never tried, side games, a better way to settle something, a stat worth tracking. If the league only ever runs the games one person thought of, it'll stay the size it is.
Reply to the email. Every message the system sends can be answered like any other email, and the reply lands with the commissioners. If something looks wrong, hit reply and type.
Or use the link. Every page and every email carries one: Something off? Tell us. It's a short form, no sign-in needed, and it asks one question first. Is this something that was hard or confusing, something you don't agree with, or an idea? It already knows what page or email you came from, so you don't have to explain where you were. What you write goes into the same record as everything else and shows up for the commissioners with a way to reply, and ideas go on a list the whole league can see so they don't disappear.
Two things to know before you do. Changes are usually a lot easier than they look from the outside. The whole thing took a few evenings, and it was built to be extended, so don't hold back because something seems like a big ask. And none of it is taken personally. A lot of this is me guessing at what will work for the group. The way I build anything is to start with the smallest version that's useful, put it in front of people, and change it fast based on what they say. That only works if people say things.
Running now at sundayridersgolf.com. Tell me what's wrong with it before we turn it on for everyone.