Every change we ship to katura1999.com — features, fixes, security patches, the lot. Pulled straight from our private GitHub repository so you can see exactly what was built and when.
By the numbers
Lines of code
850,259
Web platform — TypeScript, React, Prisma, CSS
iOS
79,607
Swift + SwiftUI lines
Android
3,117
Kotlin + Jetpack Compose lines
All platforms
932,983
Web + iOS + Android combined (45.8× the King James Bible)
Characters written
36.05M
36,053,358 total characters
Updates pushed
2,087
exact commit count on main
Current version
v1.20.87
build 2087 · 40f34e0
Database models
411
across 47 schema files — most SaaS platforms have 20–50
API endpoints
988
individually routed — Stripe's public API has ~400
Translated strings
82,512
every string, in 24 languages
System permutations
10^297
2^988 endpoint combinations — more than atoms in the observable universe (10^80)
Project age
8mo
since Dec 14, 2025
Pre-AI dev hours
31.1K hrs
932,983 lines ÷ 30 LOC/hr — equivalent to 15.0 years (senior engineer, no AI)
With-AI dev hours
7.8K hrs
4× AI productivity multiplier (2024–2026 studies) — equivalent to 972 days
Equivalent firm cost
$8,119,136
live ticker · Katura rate: $60 USD/hr
Hours estimated from source line count at 30 LOC/hr (industry benchmark for production-quality TypeScript/React without AI assistance), with a 4× multiplier for AI-assisted development per published 2024–2026 enterprise studies. Equivalent Firm Cost uses a $250/hr loaded billable rate reflecting a premium engineering firm building enterprise-grade SaaS — and ticks up live, because the project is still being actively built.
Commit history
2,087 updates pushed
Showing page 1 of 40 · 1–50 of 2,000 fetched
Tuesday, August 11, 2026
12 updates pushed
Docs9:26 PM · ZRosserMcIntosh
add session log for meetings Classic vs Railgun-beta toggle (2026-08-11)
add Classic vs Railgun-beta engine toggle on the meetings list page
Toggle lives on /admin/meetings list (localStorage-persisted), never inside the live room, so switching engines never re-initializes an active LiveKit session.
Engine choice threaded via ?engine=railgun through list -> prep -> room.
meeting-room-client.tsx reads the flag via useSearchParams and gates the knock-to-enter lobby/admission panel + its polling effect behind Railgun-beta only; Classic mode is untouched from before.
Small 'Railgun Beta' badge shown in-room when active.
multilingual ON by default for everyone; relabel language selector
bilingualMode now defaults to true in both host and guest room clients (state + ref), so every participant can speak any mix of languages out of the box. Still toggleable off (pins a single language) in settings.
Guest pre-join selector relabeled 'My preferred spoken language is' with copy explaining multilingual is on and this is just a primary-language hint.
Friday, August 7, 2026
3 updates pushed
4:51 PM · ZRosserMcIntosh
capture verifiable proof in discovery + upgrade to gpt-5.4 reasoning
Evidence rows now require a verbatim sourceExcerpt proving the claim; rows that can't be quoted are omitted
Advertising rows only created when a public ad library was actually checked (Meta Ad Library / Google Ads Transparency Center URL + dated phrasing); a company website can never prove ad absence
Social/local rows require structured metrics (counts, date ranges, ratings)
Insert now persists sourceExcerpt, sourceTitle, structuredMetrics (previously dropped), so captured proof actually reaches the validator
validate / write / audit now default to gpt-5.4 (PROSPECTING_MODEL) instead of gpt-4o — stronger judgment for weighing evidence, choosing one diagnosis, and auditing claims
Optional PROSPECTING_REASONING_EFFORT applied to writer+audit, guarded so it's only sent to gpt-5 reasoning models (never to gpt-4o)
Discovery stays on gpt-4o (needs the web_search tool), documented
usage.ts: added gpt-5.4/mini/nano pricing; longest-prefix match so gpt-5.4-mini no longer mis-prices as gpt-5.4
New consultation.ts: buildConsultationBrief() turns collected evidence into a meeting agenda where EVERY talking point traces to a real source (URL/excerpt) the analyst can pull up live. Contradicted/unsupported/estimated evidence and anything without a source is filtered OUT (counted as excluded) so nothing unverifiable is raised on the call
Thursday, August 6, 2026
4 updates pushed
9:21 PM · ZRosserMcIntosh
discovery crash fix, pipeline UX, and deal management
Fixed discovery JSON parsing: web-search model now coerces 'Not specified'/ 'N/A'/messy numeric strings (e.g. '~120 reviews') to proper null/Int/Float values; Instagram handles get @ stripped; dedup now per-campaign (name+city or domain within same campaign, allowing re-approach under different offers)
Pipeline UX: grouped list view (By stage / By location toggle), clear dividers and stage labels, yellow NEW badge for prospects <48h old, close % display
Deal page (/admin/saas/prospecting/[id]): anticipated close probability (slider+input), est. monthly value, weighted forecast, next-step tracking with due dates
Meeting notes timeline: log calls/meetings/emails, snapshot close % at each touch so we can track confidence movement and coach close-rate improvement
Deterministic coaching prompts (missing email, stage-specific advice, % probability band recommendations)
Schema: added closeProbability, estMonthlyValue, nextStep, nextStepDueAt, lastContactedAt to JewelerProspect; new ProspectNote model; migration 20260806_prospecting_deal_notes.sql (additive)
PDF: cover logo now white text (Playfair) on dark navy (was invisible dark logo on navy); media-spend copy reworded (you fund and control, billed to you, not 'up to $15k' implying our spend)
Screen shares now render in a dedicated 'watch together' focus view in both the host room and guest room. Controls:
Fit/Fill toggle (object-contain letterbox vs object-cover zoom)
Full screen (theater) — maximizes the share to fill the video area while KEEPING the subtitle overlay on top (browser-native fullscreen would hide captions, so this is an in-app maximize instead) Camera participants become a filmstrip; local self-view stays as a PiP. Filmstrip + PiP hide in theater mode so nothing covers the movie.
in-meeting invoice payment never rendered embedded checkout
embedded (ui_mode:'embedded', redirect_on_completion:'never') -> client_secret for in-meeting payment that completes in place (onComplete -> paid state)
hosted -> url kept as the fallback 'open in new tab' link
content-collection.ts: full bilingual LandingContent for the 'Market the collection before you manufacture it' pre-launch demand-test offer. Reuses the same verified KATURA case study; no invented pre-sale metrics.
/collection-launch and /pt/collection-launch pages (offer=collection_launch), canonical + hreflang alternates; routed automatically by the existing proxy rewrite. Landing component now threads an offer prop into the lead form so these leads are attributed as collection_launch end-to-end.
Sitemap extended with both collection-launch URLs and their alternates.
POST /api/marketing/qualification: appends answers to the original SaasFormSubmission and adds a CRM activity; never fires a second Meta Lead; promotes ready/high-budget leads to 'qualified'. Accepts unknown IDs quietly.
qualification-form.tsx: client component that renders ONLY when the just- submitted lead ID is present in sessionStorage; non-blocking; bilingual. Embedded on /thank-you (en) and /pt/obrigado (pt).
New toggle in guest settings (OFF by default) switches their Deepgram STT to Nova-3 language=multi code-switching; per-utterance detected language now drives sourceLanguage on interims, finals, transcript persistence, and the translation source — so a guest speaking mixed languages is captioned in the language actually spoken and translated correctly.
translateSegment gains a realtime fast-path that SKIPS the Postgres cache read (up to 500ms) and the cache write for live/interim captions — LRU + DeepL only. Interim text mutates constantly and never recurs, so the DB round-trip was pure latency. Threaded realtime:true through both admin and guest /translate routes and their interim callers.
fix STT 1011 timeout when muted + guest Yen toggle knob
STT dropped when muted: while the mic is off, the worklet sends zero audio frames to Deepgram, which closes the socket with code 1011 after its ~10s idle timeout ('did not receive audio data'). Added a 5s KeepAlive heartbeat ({"type":"KeepAlive"}) sent only while muted, in both host and guest room clients; cleared on stop and on close. Connection now survives long mutes.
Guest 'Hey Virgil' toggle knob rendered detached/outside the pill: the knob span had no horizontal anchor (translate-x-6 with no left-*). Standardised to the same top-0.5 left-0.5 w-5 h-5 / translate-x-5 pattern as the other toggles.
Root cause: transcript ordered by startMs, which is RELATIVE to each participant's own STT session start (and 0 for guest/remote captions), so speakers didn't interleave — all of Stella's lines jumped to the end. Fixed: order by absolute createdAt (then startMs) in minutes page, end, regenerate-minutes, [id] detail, and minutes-translation routes so the AI and the UI both see true chronology.
New TranscriptView: per-line language chip (EN/PT) identifying what each speaker actually spoke, plus 'Show translation' that pulls the persisted translation package (zero extra tokens) and renders the translated line highlighted beside/below the original; lines already in target skipped.
Bilingual beta mode: Deepgram Nova-3 language=multi code-switching; per-segment detected language feeds transcript persistence + live translation source
MeetingMinutesTranslation: minutes+labels+full transcript translated ONCE via batched DeepL (50 strings/request) then persisted per (meetingId, targetLanguage)
New API: GET/POST /api/admin/meetings/[id]/minutes-translation (idempotent)
Points ranked verified > observed > likely; each carries why-it-matters, a non-leading discussion prompt, and the source to show on screen
Includes suggested agenda, discovery/qualification questions, de-duped complimentary quick wins to give away free, and a next-step recommendation tied to the proposed offer
briefToMarkdown() for one-click copy into notes
Deal page: Consultation brief panel with strength badges, live source links, captured excerpts, agenda, discovery questions, free-value, and next step
New lead-score.ts: 0-100 conversion score + A/B/C/D tier from 7 weighted factors that predict a paid first retainer — reachability (25), decision-maker access (15), ability to pay (20), fixable pain (15), speed to decide (10), commercial fit (10), engagement (5)
Pipeline list: tier badge + score per row; each stage/location section now sorts by conversion likelihood (highest first); ranks by manual close % when set, else the computed score
Deal page: full Conversion likelihood panel — per-factor bars, points, detail, and a 'biggest lever' callout for the highest-impact next action
List route returns lightweight evidence + meetingNotes count for scoring
6-page luxury PDF (Playfair+Geist embedded, real logo, 7.77 imagery); shared buildProposalPdfData() for preview + attachment; controlled image sources only
SEO assessment framework (seo-audit.ts) + complimentary specific quick wins rendered per audit category (free client value in every area)
Expanded launch metros grouped by region (~80, West Coast first)
fix malformed-JSON crash in discovery + AI token cost tracking
discover.ts: robust JSON extraction — repairs trailing commas, smart quotes, stray markdown citations, control chars; falls back to element-by-element salvage so one bad store no longer kills the run (fixes 'Expected comma or ] after array element' error)
fix: remove duplicated '} from "lucide-react"' line in meeting-room-client.tsx that broke the Vercel build
src/lib/prospecting/discover.ts MANUAL-trigger jeweler discovery (no crons anywhere): OpenAI Responses API w/ web_search tool, small-company priority, dedupes, creates prospects as RESEARCH with sourced evidence rows; falls back to knowledge-only clearly labeled 'estimated' if web search unavailable
/api/admin/prospecting/discover POST { count, metro, campaign }
emails/[id] route: new 'mark-sent' action for the manual workflow (Zack copies subject+body, downloads PDF, sends from his own inbox, then records the send)
UI: 'Find jewelers' modal (count/metro/campaign), Copy subject / Copy body buttons, 'Mark sent manually' button; Brevo send kept as optional 'Send via Brevo'
config: new 'design-bench' offer (Design & Bench Support) so proposals can pitch CAD + design + bench work
email: auto-emails now identify as Virgil (not Yen) — sender name, sign-off, subject, signature block, logo alt, HTML title, persona prompts. yen@katura1999.com mailbox + all code identifiers unchanged.
meeting-cad-viewer: both render modes stop pointer events bubbling to the room auto-hide control bar (metal-select/orbit/zoom no longer pops the overlay). Shared component -> guest room benefits too.
meeting-chat-panel: ChatContent root stops propagation so typing/ tapping in chat no longer re-triggers the control bar.
meeting-room-client: mobile settings menu now has tap-outside backdrop + larger 'Done' close button.
auto-translation, mobile fixes, overlay styles, Virgil rename
Auto-enable translation (host+guest): when a caption arrives in a different language and the viewer has no translation set, switch it on automatically (translating to the viewer's spoken language); explicit 'Off' is respected
Mute fix: STT audio frames are dropped while muted (guest micOnRef guard; host belt-and-suspenders isMicrophoneEnabled guard — iOS suspend() fallback); guest mic toggle now verifies actual track state (cellular sync bug)
Mobile subtitles: XS font size added (both rooms), XS default on phones; drag-resize handles enlarged + touch-none so they work on touch screens; overlay corner handle enlarged for fingers
No scrolling in meetings: body scroll lock + overscroll none (both rooms)
iPhone edge-to-edge: viewportFit=cover + themeColor #07101e painted into notch/Dynamic Island safe areas; control bar respects safe-area-inset-bottom
Subtitle overlay styles: 'Box' (darkened semi-transparent blur, default) or 'Plain' (text directly on video with strong text shadow) — both rooms
Assistant renamed Yen -> Virgil (all user-facing strings, wake words with pt/es/fr-friendly variants incl. virgílio/vergilio, transcript normalization 'Hey Virgil'); internal identifiers/routes unchanged for stability
Fix #1: Remove translationLang broadcast override translationLang is a per-viewer preference, not a room-wide setting. Removed the subtitles_broadcast packet that carried 'lang', which was causing one participant's language pick to clobber everyone else's translation target. This is why the host lost translations when Stella/guests selected their own language mid-meeting.
Fix #2: Use translationLangRef in broadcast callback broadcastCaption was reading stale sttLang from React state dependency, causing captions sent after a language switch to be tagged with the old source language. Now reads sttLangRef so receivers always see the current spoken language and can translate correctly.
Fix #3: Deduplicate participants in minutes view Guests get fresh random LiveKit identities on every rejoin (security rule B.7), creating multiple MeetingParticipant rows per person. The minutes query now groups by (userId, email, displayName) and aggregates sessions, fixing the '7 participants for 3 people' bug. Also surfaced reconnect count: '(3 sessions — reconnected 2×)'.
/marketing-site/robots.txt + /marketing-site/sitemap.xml route handlers matching the proxy rewrites (thank-you pages excluded/disallowed; hreflang alternates for / and /pt in the sitemap).
Branded not-found page with EN/PT recovery links.
JSON-LD in the marketing layout: Organization (Katura Marketing, child of Katura), ProfessionalService (30-Day Paid Growth Pilot, en + pt-BR, US/BR), Person (Stella Barbosa). Facts only — no invented ratings or reviews.
marketing site: Phase 3+4 — lead API, thank-you pages, CAPI Lead helper
POST /api/marketing/lead: Zod validation, honeypot, 5/hr IP rate limit, dedupe by eventId (idempotent retry) + same-email 10-min window, transaction (SaasFormSubmission + CrmContact upsert + CrmActivity + high-priority follow-up CrmTask), response { success, processed, leadId, eventId, duplicate }. Side effects run in after(): server-side Meta Lead (CAPI, deduped by the shared event ID), internal notification to MARKETING_LEAD_NOTIFICATION_EMAIL (default marketing@katura1999.com), bilingual acknowledgement email to the lead.
meta-capi.ts: META_GRAPH_API_VERSION now configurable via env; new trackCapiLead helper (not gated on Meta traffic — fires for every processed lead; browser/server dedup via event_id; honors META_TEST_EVENT_CODE).
/thank-you and /pt/obrigado: noindex confirmation pages with env-gated scheduler + WhatsApp CTAs (NEXT_PUBLIC_MARKETING_SCHEDULER_URL / NEXT_PUBLIC_MARKETING_WHATSAPP_NUMBER, hidden when unset).
content.ts / content-pt.ts: typed bilingual content model, copy verbatim from the brief; KPI figures derived from the case-study module only.
layout.tsx: dedicated marketing layout (navy/ivory/gold direction), metadataBase on the marketing subdomain so /marketing-site never leaks.
paid-growth-landing.tsx: all 11 sections server-rendered (hero, verified result + mandatory qualifier, conversion problem, 4-week pilot, fit cards, Stella work gallery [scroll-snap, no carousel], case study incl. 'what it did not prove', Stella bio [no unverified portrait], risk reversal, server-rendered FAQ, lead form section).
lead-form.tsx: exactly four fields + honeypot; event ID generated pre-POST; browser Lead fires only on confirmed processed non-duplicate response with the server-confirmed event ID; inputs preserved on failure.
page.tsx / pt/page.tsx: EN root + /pt with canonical + hreflang alternates.
K99: subdomain-specific favicon + add marketing site implementation brief
Added /brand/k99-icon.png and wired it as the K99 marketing layout's icon/apple-touch icon, overriding the Katura store favicon on k99.katura1999.com pages.
Checked in docs/marketing/marketing-katura1999-fable5-implementation-brief.md (the marketing.katura1999.com paid-growth funnel brief) for reference.