Case study · Built by Brian Gonzalez
Taking a one‑teacher yoga school off manual work.
A client engagement with a yoga teacher in Rishikesh. Four systems that now run without anyone at a keyboard, the places where a person deliberately stays in the loop, what broke once they ran unattended, and the membership platform they feed.
One click, no card, no email. Opens the full library for a day.
31
class write-ups drafted from their own transcripts
18×
smaller per class, 4.0GB to 220MB
$0.015
to draft each class's notes
15
teachers listed across 6 countries
38
days of TikTok posts scheduled from 326 ranked reels
13 / 13
lifecycle checks passing on production
The client
Parveen Nair teaches embodied yoga from Rishikesh to a paying audience on Patreon, with a small volunteer team. Every weekly class meant pulling a 4GB file off the camera, compressing it, writing a title and description, numbering it into the right series, uploading it and telling the team. Teachers asking to join the directory arrived by email. Social posting was by hand. None of it was difficult; all of it was recurring, and it competed with teaching.
What runs on its own now
The class pipeline
- Starts when
- A recording lands in a Google Drive folder.
- Does
- Runs every hour on GitHub Actions. Pulls the file through the Drive API, compresses it with ffmpeg, transcribes it with Whisper, and has Claude draft the title, focus and sequence from what was said. Works out the next number in the series, files it, attaches the description, and emails the team.
- Human
- A person posts the drafted class to Patreon from the email.
- Costs
- About $0.015 a class to draft, a few pence to transcribe.
The teacher directory
- Starts when
- A teacher applies through a form on the client's site.
- Does
- Screens each application for profanity, spam patterns, missing training dates and daily volume, then emails one reviewer signed approve and decline links. Approving commits the teacher's entry and photograph to the site's repository and the site rebuilds.
- Human
- One click from the reviewer, because it publishes a stranger's details on the client's site.
- Costs
- Free tier throughout.
Social distribution
- Starts when
- A one-off run, then a schedule.
- Does
- Ranked 326 Instagram reels by views, captioned the strongest 38 and scheduled them as a 38-day TikTok run through Publer's API, then rebuilt the run for YouTube Shorts alongside five free full-length classes described from their own transcripts.
- Human
- None once scheduled.
- Costs
- The client's existing Publer plan.
Kula, the platform
- Starts when
- A member joins a school.
- Does
- Schools get their own page, price and colour. Members subscribe through Stripe, video is authorized on every request, membership is written only by Stripe's signed webhook, and members can search the library by what was said in each class.
- Human
- None. A browser coming back from checkout is never trusted on its own.
- Costs
- Cloudflare free tiers, including Workers AI; Stripe's fee per payment.
Where a person stays in the loop
The rule is that autonomy follows reversibility. Filing a class is private and easy to undo, so nobody checks it. Publishing a stranger's photograph and biography on the client's site is public and hard to take back, so it waits for one click from one person. Posting to the paying audience waits for a person too. Payment is the strictest: the only thing that can grant a membership is an event signed by Stripe, because a browser arriving at a success page proves it came back, not that anyone paid.
What broke once it ran unattended
Connecting the services was the easy part. The work was in the failures that only appear when nobody is watching, and each one got a guard rather than a patch.
| What happened | Why | What changed |
|---|---|---|
| Uploads failed with a storage quota error | Google service accounts have no storage quota, so the robot could read the Drive and make folders but not write a single file. | Replace the contents of an existing file instead of creating one. Ownership never changes, so the quota never applies. It removed a second storage service from the design. |
| Compression quietly did nothing | Google Drive reports a file's full size before it has downloaded any of it, so the file looked ready when it was not. | Wait for readable bytes, not for the size to look right. |
| Three scheduled runs died on their last line | Moving from a Mac to a Linux runner left a macOS-only notification command behind. The failure was in the part that reports failures, so nothing said so. | Announcing is now non-fatal, and anything stuck for six hours raises its own alert. |
| Runs broke halfway when the laptop lost wifi | The first version ran on a laptop. | Check Google and Anthropic are both reachable before starting, then move the whole job to GitHub Actions so no laptop is involved. |
| Email sending returned 403, which read like a bad key | The email provider sits behind Cloudflare, which rejects requests with no User-Agent. | Send one. It is a one-line fix that took the longest to find. |
| The contact form had never sent a message | The form service's free tier refuses server-side calls, and it had been failing since launch. | Replaced with direct sending through Resend on a verified domain. |
Why build Kula
Patreon keeps around a tenth of every payment before processing fees, owns the viewing experience, and has no way to represent a school with several teachers. Kula is the alternative: each school lives at its own address with its own price and colour, members subscribe to a school rather than to a platform, and a membership at one school opens nothing at another. Video streams through the server rather than from a shareable link, so access is checked on every request, including every time someone skips ahead.
It is checked the way it would be in production. A script walks the whole membership lifecycle against the live site through real systems: a real checkout session, a real test subscription, a webhook that rejects a forged signature and accepts a real one, a real sign-in email read back from the mail provider, the paid class streaming for the member and refused for a stranger, then a cancellation that Stripe itself signs and delivers, revoking access. All 13 checks pass. Stripe's hosted checkout was also driven by hand with the test card to confirm the payer comes back signed in.
Ask the library
Members can describe what they want to practise in their own words, such as humming bee breath, or tight hips from sitting all day, and get the three classes that fit best. Each class is cut into passages, its notes plus overlapping windows of what the teacher said in the recording, 634 in all, embedded with Workers AI into a Vectorize index. The best passage per class decides the ranking. A small model then writes one line per class on why it fits, using only that class's notes and the passage that matched.
The model explains a ranking it did not make, so a weak sentence costs a sentence, not a wrong class. Its output is tidied in code rather than trusted to the prompt. Transcripts never leave the server whole: members see a 28-word snippet of the passage that matched, visitors see none. And a daily ceiling counted in the database bounds what a public demo can spend on models, whatever the account's plan.
How I work
I direct coding agents rather than typing most of the code myself. Here that was Claude Code, with me owning the client conversations, the architecture, which failures were worth a guard, and the call on every trade-off. The working rule is that nothing is done until it has been checked against the running system, which is why the figures on this page are measured rather than estimated.
See it
31 real classes with their real titles and notes. Video in this demo is short excerpts, and payments run in Stripe test mode.