Sports Club OS

Every player ready for the first practice

Web, Product Design, Agentic Coding • 2025–2026

A back office for youth sports clubs: registration, payments, waivers, groups and schedules, organised around one question. Which players can't start the season yet, and why?

  • Built solo
    Research, product, design and agentic coding.
  • December 2025 to January 2026
    200 of its 201 commits landed in the first eighteen days.

What's in it

  • Registration links with no parent login
  • A readiness dashboard for each season
  • Groups, coaches and courts
  • Weekly schedules from templates
  • Payments and instalments
  • Waivers and emergency contacts
  • Search across every record
  • A demo club that resets every night

Built with

Agent
Cursor
Stack
Next.js · React · TypeScript · Prisma · Postgres · Tailwind · Supabase · Render

Why it exists

I built it to learn agentic coding hands-on. I wanted to find out, on something bigger than a toy, what an AI coding agent could and couldn't do with me as the product manager and designer, and the agent writing the code. That needed a real problem, and youth tennis clubs had one.

Twice a year a club turns a pile of sign-up forms into groups on court. Several of the clubs I interviewed named making the groups as the hardest job of the season: matching players by age, level and availability against the courts and coaches they have, on paper and in spreadsheets. That was the way in. Use what parents say at registration, and last season's groups, and let an algorithm or an AI propose the groups.

The decision that shaped it

Parents register without an account. The obvious design is a parent portal with a login up front, which keeps the data clean. But parents register once or twice a year, from a link the club sends, often on a phone. Asking for a password at that moment loses sign-ups.

So the club's link opens a form and nothing else. Whatever is missing afterwards, such as the payment, a waiver or an emergency contact, becomes a checklist instead of a blocker, enforced at the first practice. That is the job of the readiness view. A player counts as ready only when they are paid, cleared, placed in a group and have confirmed the schedule.

Where the AI needed a human

Words drifted. The agent named things as it went. A field called registrationStatus was really tracking whether a player could start, and buttons said New, Add and Create for the same action. I renamed the field readinessStatus and wrote the naming down in a principles file the agent worked from.

Done wasn't done. The project's rules told the agent to run the type checker and the tests before calling a task finished. Tasks came back finished anyway, and I didn't check. When I put it online in October, the type checker found 181 errors, and the registration form had been broken since a merge on 3 January: the page came from one branch and the API from another.

What I'd do differently

Interview first. Because I was building to learn, I built and interviewed at the same time. Without that second goal, I'd have spent the first weeks understanding how clubs actually make their groups, and only then written code.

Go AI-native on the groups. What I built asks an admin to type in courts, coach hours, programs and players before anything useful happens. I planned the grouping engine in detail and stopped before building it. Today I'd start there: the club uploads what it already has, such as its documents, court and coach availability, this season's registrations and preferences, and last season's groups, and an agent proposes the groups for the admin to adjust.

Automate the messages. The other half of running a season is telling families things: a payment is due, a form is incomplete, the groups are out, a group has changed, practice is cancelled or moved. In the prototype an admin writes each reminder from a template, one at a time. They should go out on their own as the season changes.