I Built an Entire AI Academy With AI.
Here’s What That Actually Looked Like.
This morning at 8:21 AM, I got a text from my buddy Justin: “It’s working.”
The TSV AI Academy was live. A full learning management system with authentication, payment processing, video delivery, anti-cheat gating, a 40-question proctored exam, a live check ride pipeline, and verifiable digital certificates. All of it custom-built. None of it is outsourced. And every single line of code was written by an AI agent while I sat in the pilot’s seat, telling it what to build.
I’m not a software engineer. I’m a 13-year combat veteran with three deployments to Iraq and one to Afghanistan. I don’t write JavaScript. I didn’t go to a coding bootcamp. But I just shipped a production SaaS platform in under 90 days.
This is the full story of how that happened, and more importantly, what broke along the way.
The Day I Walked Away From a “Real” Developer
Back in late March, I had the curriculum mostly written. Four modules. A military-style Task/Conditions/Standards framework for every lesson. Two specialized tracks (Career and Entrepreneur) so the content actually fits whether you’re job hunting or building a business. I knew what I wanted to teach. I just didn’t have the platform to teach it on.
So I found a technical partner. A developer. Someone who could build the LMS while I focused on content.
He wanted equity.
I said no. We parted ways.
And sitting in my office that night, I made a decision that changed everything: I’d build it myself. Using the exact AI tools I was teaching in the course. If I couldn’t use AI to build the platform that teaches people how to use AI, then the whole premise was a lie.
That was March 25th.
The Stack (For the Nerds)
Here’s what I used:
Claude Code running inside Cursor as my IDE and terminal. Claude Code is an AI coding agent. You describe what you want, it writes the code, you review it, iterate, and ship. That’s the entire workflow.
Next.js (App Router) for the frontend
Supabase for the database, authentication, and row-level security
Stripe for payments and webhooks
Vercel for hosting and deployment
YouTube (unlisted videos) via react-player for video delivery
TidyCal + Make.com for check ride scheduling automation
No Teachable. No Kajabi. No Thinkific. No drag-and-drop course builder. A real codebase. A real database. Real infrastructure.
My build workflow was stupidly simple: write a spec, save it as SPEC.md, open Cursor’s terminal, type “claude,” and say “Read SPEC.md and let’s build this.” Then iterate until it’s right. Push to GitHub. Vercel auto-deploys.
That’s it. That’s the whole process.
Phase 1: The Foundation (Late March - April 29)
I initialized the GitHub repo on March 30th. First commit: “init: Next.js scaffold with folder structure and dependencies.”
Claude Code built out the core learning mechanics over the next few weeks. I’d describe what I wanted, and it would scaffold the components, write the database queries, and handle the routing. The big pieces:
Video gating. Every lesson video has to be watched to at least 80% completion before the student can proceed. No scrubbing forward. No skipping. The system tracks your actual watch time against the video duration.
Quiz gating. Module quizzes require a 70% passing score. Fail it, and you go back and study.
Progress tracking. A full database schema across 14 tables tracking every student’s journey through the platform.
The Phase 1 commit was massive. LMS build, brand, layout, admin panel, slug seeding. All of it landing in one push.
And then the bugs started.
The Bugs (This Is the Real Part)
Let me be clear: building with AI is not magic. It’s not “I said the thing and the thing appeared perfectly.” Here’s what actually happened.
The Localhost Disaster. A friend tried to sign up via the waitlist and accidentally triggered a Supabase authentication modal I didn’t know was exposed. She got an unbranded confirmation email pointing to localhost:3000. On a production site. I had to rip out the self-registration flow entirely and enforce that accounts could only be created through a successful Stripe payment webhook.
The Stripe Production Mystery. Stripe worked perfectly on my local machine. Deployed to Vercel? Broke silently. The checkout API route was swallowing the real error behind a generic message. I had to patch it to surface the actual Stripe error parameters (type, code, message) before I could even see what was wrong.
The Video That Wouldn’t Stop Pausing. This one almost broke me. I had a React-Player component for the lesson videos, and every time the progress-gating logic ran (which was roughly every second to track watch percentage), the video would pause itself. Just stop. Mid-sentence. Over and over.
The root cause? I’d hardcoded playing={false} as a prop. React-player v3 evaluates that prop on every re-render. So every time my gating logic updated the watch fraction (triggering a state change and re-render), the component would read that hardcoded false and force-pause the video. It was fighting the user’s playback on every single tick.
The fix was embarrassingly simple: delete the prop. Let the browser’s native controls handle playback. But finding that? Hours of debugging with Claude Opus walking through the component lifecycle.
The Z-Index War. I built a picture-in-picture feature so students could scroll through the written lesson content while the video kept playing in a sticky corner overlay. Worked great until my onboarding modal (z-index: 10000) buried the PiP tile (z-index: 50) underneath it. Layers on layers on layers.
Every single one of these bugs was a lesson. And every single one was solved by me describing the problem to an AI agent and working through it together.
The Feature That Changes Everything: The Check Ride
Most online courses hand you a certificate for watching videos. Maybe you click through a quiz. Congratulations, you’re “certified.”
That’s not how this works.
When you pass the 40-question written exam (70% minimum, questions shuffled every attempt, correct answers hidden on failure), you don’t get a certificate. You get a prompt to book a live, 30-minute Check Ride with me.
The name is intentional. In aviation, a check ride is the final practical test where an examiner puts you in the seat and watches you fly. You demonstrate competency or you don’t pass.
I built an Examiner Dashboard that shows me exactly which questions each student got wrong on their written exam. That’s my clipboard. When we get on the call, I know exactly where to push. You either prove you know this material, or you come back and study.
If you pass, the system mints a verifiable certificate with a unique ID (like TSV-FND-0047) and a public verification URL. That certificate is your key to the alumni community.
If you fail, I will send you notes on what to study, and you reschedule.
There are no participation trophies here.
The Day I Killed My Own Launch
By May 15th, everything was technically working. Stripe was processing. Auth was solid. The check ride pipeline was wired up. I had a launch date. I had 19 people on a waitlist.
And I killed it.
The backend was glitching on some content uploads. The UI had rough edges I couldn’t unsee. I could’ve shipped it and fixed things on the fly, the way most people launch online courses. But I’m building something that’s supposed to represent a standard. If the platform isn’t tight, the standard means nothing.
So I sent my Substack list an email titled “The Standard is the Standard: Why I Killed Today’s Launch.” I told them the truth. I told them I’d rather be late than be wrong.
Then I spent the next five days doing what I should’ve done from the start: a ruthless pre-launch audit. Deleted dead code. Dropped unused components. Ran TypeScript strict mode until there were zero errors. Fixed the video-pausing bug the night before launch. Cleaned up database constraints. Killed technical debt.
By May 18th, the codebase was launch-clean.
Launch Morning
May 20th. This morning.
At 7:57 AM, I sent the waitlist email. Twenty people. Founding member pricing for the ones who committed before anything was built.
At 8:14 AM, Justin texted me back: white screen. The site wasn’t loading. Some email tracking I’d set up in the DNS records was conflicting with the deployment. I fixed it.
At 8:21 AM: “It’s working.”
At 8:27 AM: I found another bug. Fixed that too.
By 9:00 AM, the public LinkedIn announcement was up. The site was live. Stripe was ready for the dings.
That’s how launches actually go. Not champagne and confetti. DNS conflicts and last-minute patches at 8 AM.
The Part That Actually Matters
Here’s what I need you to understand about this story.
I am not a developer. I did not mass-produce a Kajabi course. I built a custom, production-grade learning management system from a blank terminal to a live product, using the same AI tools I’m teaching in the course.
I am the proof of concept.
If a 41-year-old combat veteran with zero engineering background can build a full-stack SaaS platform using Claude Code and a SPEC.md file, then you can learn to use AI to transform your career. Or your business. Or both.
That’s the whole point of the TSV AI Academy. Not to give you another certificate to hang on your LinkedIn wall. To put you in the seat. Make you prove it. And send you out the door with skills that actually work.
You’ve been here for this build. You read the post when I killed my own launch because the standard wasn’t met. You watched this thing go from an idea to a live platform in real time. That means something to me.
So here’s what I’ve got for you: the first 15 Substack subscribers to enroll get $50 off the $300 price. Use code SUBSTACK at checkout. Once 15 people use it, it’s gone.
The Academy is live: academy.thestrategicveteran.com
My name is Adam Peters, and I’m here to unfuck the transition.



