Case study · May 2026
I rewrote JobCannon solo
Here is the stack that made it possible
The setup
JobCannon is a B2C self-discovery platform. You take a test, you get a result, you can upgrade for a deeper read.
That description does not sound like much. Here is what it actually contains:
- 53 distinct assessments covering personality, career fit, intelligence, values, work style, relationship dynamics
- About 1,200 career pages programmatically generated from O*NET data plus a custom skills-to-careers graph
- Premium tier live since 21 April 2026 with multi-currency pricing in 14 currencies (USD, GBP, EUR, CHF, MXN, COP, ARS, BRL, EGP, SAR, TRY, INR, IDR, CLP, PEN, THB, PHP, VND)
- 3-layer billing pipeline (Stripe Payment Element + cron-driven drip + cancellation API)
- B2B 5-lane GTM (B2G + NPO, Hiring, L&D, Coaches, ATS)
It is a real product with real revenue, not a side bet.
The team
There is no team. There is me, and a Claude Code agent that I have been training with a 73-rule CLAUDE.md, 30+ custom skills, hook-enforced output discipline, and a 6-domain memory system I built specifically so it does not forget what we agreed three sessions ago.
That is the entire team for the build. There are people who help with QA passes and bug reports, but no co-developers and no engineering hires.
Why I rewrote it
The first version of JobCannon was a different shape. By early 2026 the rewrite was a forced choice, not a luxury one. Scaling to 53 tests and 1,200 career pages on the old architecture would have cost more in maintenance than it would have generated.
The rewrite ran solo over a few months. New code, new architecture, but I kept the brand, the audience commitments, and most of the assessment content.
The stack
The product stack is conventional. The interesting thing is what is not there.
- Framework: Next.js 16 (App Router), TypeScript strict, Tailwind CSS v4
- Backend: Supabase (Postgres + Auth + RLS + Realtime). No custom Node API server.
- Payments: Stripe Payment Element (native, no Checkout redirects), regional Prices for 14 currencies, cron-driven pre-charge drip
- Hosting: Vercel (Standard CPU functions, Edge for geo detection)
- CDN + geo: Cloudflare in front of jobcannon.io
- Email: Resend for transactional, daily digest to CEO via Telegram bot
- Analytics: Microsoft Clarity for behaviour, Cloudflare for traffic. No PostHog (removed 15 May 2026).
- Runtime AI: Google Gemini (for assessment scoring augmentation, career insight generation, drip content)
- Build-time AI: Claude Code (Opus 4.7) as the entire engineering team
Runtime AI and build-time AI are deliberately different. Gemini runs the product. Claude builds it. They never overlap.
The operator stack
This is the part that is actually unusual.
- CLAUDE.md — 73 rules, audited from 264 real sessions. Hook-enforced discipline against fabrication, against AI-fingerprint output formatting (no em-dash, banned vocabulary list), against ops I should be doing myself, against question batteries.
- 30+ custom skills —
walkfor live UX walkthroughs through Chrome DevTools MCP,supervisorfor end-of-turn self-review,deployfor lane-aware shipping,crofor funnel analysis,blog-articlefor batch content, and others. - Memory system — 6-domain file architecture on disk (JC, Litigation, People, Business, Projects, Obsidian) with around 200 individual memory files. The agent reads relevant memory before any session. It does not start cold.
- Hooks — pre-tool checks that block bash
cat/grep/find(Read/Grep/Edit tools instead), block memory-slug leaks into the Obsidian vault, block bloat in feedback files, and enforce supervisor pass before any chat closure. - Project tracker —
lib/admin/projects-tracker.tsis the single source of truth for in-flight work. Mandatory updates with every commit. - Visual diff CI — mobile (390×844) and desktop screenshots compared against baseline. Catches the regressions UI-only changes introduce.
Detailed write-up of the stack lives on the stack page.
A real day in the build
A representative example. 20 May 2026 — extend regional pricing from 4 currencies to 18.
This was not “a feature.” This was 14 new currencies, each with 3 Stripe Prices on a single Product (setup + monthly + annual), correct zero-decimal handling for CLP and VND, IDR being 2-decimal in Stripe despite having no real-world sub-units, FX-aligned to the canonical $0.95 / $19.95 / $99 anchors but rounded to memorable local figures.
It shipped in two waves on the same day:
- Wave 1 — MXN, COP, ARS, BRL, EGP, SAR, TRY
- Wave 2 — INR, IDR, CLP, PEN, THB, PHP, VND
Sanity-check pass ran before any Stripe write. Build-time gate validates the Supabase regional_pricing rows match the Stripe price IDs. The whole change reached production the same day.
The operator-Claude session did the data entry, the price-ID wiring, the Stripe API calls, the build guards. I did the decisions on which currencies to add and the rounding strategy for each. Division of labour that would not have been possible at this speed without the operator stack underneath.
Numbers
Real numbers from Stripe and Supabase, pulled before publish. Peter will approve specific figures before this section goes live.
- Revenue since premium launch: TBD (Peter to approve range)
- Premium conversion rate: TBD (Peter to approve)
- Test completions / month: TBD (Peter to approve)
- Signups / month: TBD (Peter to approve)
What is already public:
- Premium tier launched 21 April 2026
- 14 currencies live as of 20 May 2026
- 53 assessments across personality, career, IQ, values, relationships
- About 1,200 programmatically-generated career pages
- $760K total funding raised across the portfolio
Incidents
Five real ones from the recent build. Cost is monetary or time, depending.
Vercel OOM in production, 12 May 2026
Functions started running out of memory under load. Production was effectively frozen for tens of minutes. Root cause: a hot path was eager-loading a large dataset. Fix: lazy-load the path and bump function memory tier for the affected route. Lesson: standard CPU tier on Vercel has tighter memory than I had budgeted for.
Tripwire $0.95 dead-click
Premium $0.95 setup tripwire button was rendering but not wired to the right intent endpoint after a rename. Visitors clicked, nothing happened. Found by a live walkthrough via Chrome DevTools MCP, not by error logs (it was a silent UI bug). Lesson: type-checker green is not feature-works green.
Email captures table mis-named
Two tables, email_captures and anon_email_captures, with different schemas, drifted apart over months. A new email feature wrote to one but read from the other. Lesson: single source of truth on table names, enforced.
GitHub Actions billing dead, 6 May 2026
CI pipeline silently stopped running because the GH Actions billing limit hit. No alert. Found when a deploy verification did not show the expected check. Lesson: billing health check is part of the smoke pass.
Money-without-access, 18 May 2026
Stripe Payment Element was not requiring email in one variant. Customer paid, no account got created, no access granted. Lesson: email='always' on the Stripe Payment Element is non-negotiable.
The incidents page (planned) collects more.
What is next
- B2B 5-lane GTM in motion (B2G + NPO, Hiring, L&D, Coaches, ATS) with the new
/compare/*SEO grid - Free-flow Career Guide rebuild (linear 4-step, paywall on step 5) approved 14 May 2026
- Career explanation engine as Phase 2 candidate
- ATS marketplace white-label, B2B data licensing further out
Try it / read more
Three actions, ranked.
- Take a test on jobcannon.io to see the build from a user perspective. The career match test takes 2 minutes.
- Read the operator stack for the technical layer that made the build possible.
- Email peter@kolomiets.group with questions, opportunities, or to talk through anything in this case study.
Updated: 24 May 2026. This is a living document. Update log will appear here as the build progresses.