The Workshop
Before building the product, build the tools to build the product.
A physics researcher walks away from academia and decides to build software. Not by taking a bootcamp or reading the canonical textbooks in order, but by doing what physicists do: building the same system over and over, varying one parameter at a time, until the underlying structure reveals itself.
The first project is staticweb — a static site generator. Not because the world needs another one, but because building a static site generator forces you to understand file systems, templating, asset pipelines, and build processes. The second is standard_islands, an island hydration framework. The question it answers: how do you ship zero JavaScript by default and hydrate only the parts of a page that need interactivity? The third is kameo_es, an event sourcing prototype built on the Kameo actor framework. The question there: how do you model state as a sequence of events rather than a snapshot?
None of these ship. That's the point. They're research instruments. Each one produces a specific piece of understanding that gets filed away — not in documentation, but in muscle memory. When Astro is evaluated six months later, the island hydration model is immediately recognizable because it's already been built from scratch. When NATS JetStream is adopted for event streaming, the event sourcing patterns from kameo_es map directly to stream subjects and consumer groups.
Then comes the turning point. A suggestion from family: build a queue management system for a dental office. It sounds small. A kiosk where patients check in, a screen where staff see the queue, some routing logic for which provider handles which patient.
Five iterations in three months. The first uses plain HTML and server-rendered templates. The second tries React. The third moves to SolidJS and proves that fine-grained reactivity without a virtual DOM is the right model. The fourth adds real-time updates. The fifth integrates with the office's existing workflow — check-in, triage, provider assignment, room tracking, patient status.
The kiosk itself is modest. What it produces is not. Five iterations of building queue management for a dental office means five iterations of learning how a dental office actually works. The check-in flow. The triage decisions. The provider routing logic. The difference between a scheduled appointment and a walk-in. The way a front desk handles three phone calls while checking someone in. This is stumbling upon a gold mine — not the software, but the domain expertise. Every detail of dental office workflow, learned from the inside by building systems around it.
The workshop phase also produces isoastra-mainweb, the company's public site. Built with Astro and SolidJS, it's the first production deployment of the patterns developed during the research phase. It works. The abstractions hold. Fourteen months of building tools that never shipped have produced something real: a set of instincts about how software should be structured, tested by rebuilding the same ideas until they're right.
The Receptionist
Iterate relentlessly. Kill fast. The tenth attempt isn't the first attempt done ten times — it's a different thing entirely.
The thesis is simple: if you understand how a dental office handles phone calls, you can build an AI that does it. The domain expertise from QMS is the unfair advantage — knowing when to ask for insurance information, when to offer the next available slot versus the patient's preferred provider, when a call needs a human.
Ten named iterations. Each one built, tested, and killed for a specific reason. The kill reasons are the story:
| Iteration | Lesson | Kill Reason |
|---|---|---|
| Alpha | First end-to-end attempt | Monolithic — everything coupled to everything |
| Bravo | State machine approach | Too rigid — real conversations don't follow state diagrams |
| Charlie | SolidJS frontend works | Backend still wrong — but frontend architecture validated |
| Delta | Booking flow proven | Booking worked, but voice integration was bolted on |
| Echo | WebSocket streaming | Streaming worked, but latency was unacceptable |
| Foxtrot | Latency optimization | Fast enough, but couldn't handle interruptions |
| Golf | Vapi gets to production | Works but you don't own the pipeline — debugging is guessing |
| Hotel | Pure third-party AI | Black box — can't tune what you can't see |
| India | Custom pipeline architecture | Architecture right, implementation incomplete |
| voice_stream | Deepgram + Cartesia + Fireworks | Shipped. The one that works. |
The voice pipeline that emerges is Deepgram for speech-to-text, Cartesia for text-to-speech, and Fireworks for the LLM. This stack wasn't planned. It was discovered by elimination. Deepgram because it handles dental terminology and background noise better than alternatives. Cartesia because the voice quality is natural and the latency is low. Fireworks because the models are fast enough for real-time conversation and the API is reliable.
By February 2026, the receptionist works. A caller dials the dental office number. An AI picks up, understands the request, checks availability, books the appointment, and confirms the details. It handles insurance questions, provider preferences, schedule conflicts, and the dozen edge cases that make phone-based booking hard.
But the infrastructure beneath it is held together with duct tape. SQLite in production. Auth is three different systems stitched together with session cookies that break across domains. Deployment is SSH into the server, pull the latest code, restart the process, hope nothing crashes. The product works. The platform doesn't exist yet.
The knowledge retrieval system deserves its own mention. The receptionist doesn't hallucinate office hours or make up provider names because it doesn't generate that information — it retrieves it. A Qdrant vector store holds the office's knowledge base: providers, schedules, procedures, insurance accepted, FAQs. Every answer is grounded in retrieved context, not LLM generation. This pattern — retrieve then generate — becomes a core architectural principle for Assist.
The Agent
If the product is an AI that acts autonomously on behalf of a business, you'd better understand what autonomous action actually means.
Ten days. Five iterations. The Yulier agent framework.
The receptionist works for phone calls, but the vision is larger: an AI assistant that can handle any business communication channel — phone, chat, email, SMS. That requires an agent that can reason about tasks, manage context across conversations, decide when to act and when to ask, and calibrate its confidence against the stakes of the action it's about to take.
The first Yulier iteration is a scaffold. It takes a task, breaks it into steps, executes them in order. It's too deterministic — real-world tasks don't decompose neatly. The second iteration adds branching: the agent can choose between approaches based on context. Better, but it doesn't know when it's out of its depth.
The third iteration introduces trust calibration. Every action has a confidence score and a consequence weight. Booking a routine cleaning? High confidence, low consequence — proceed. Canceling a series of appointments? Lower confidence, high consequence — escalate to a human. The trust model isn't a simple threshold; it's a function of the agent's track record with similar tasks, the specificity of the operator's instructions, and the reversibility of the action.
The fourth iteration adds intent clarification. Instead of guessing what ambiguous requests mean, the agent asks targeted questions. "You'd like to reschedule — do you want the same provider, or the next available appointment with any provider?" The questions are generated from the gap between what the agent knows and what it needs to know to act confidently.
The fifth iteration brings operator steering. The business owner configures how the agent behaves: which actions it can take autonomously, which require confirmation, which should always go to a human. This isn't a settings page — it's a policy language that the agent interprets at runtime. A dental office might say "book any cleaning autonomously, but always confirm extractions with the front desk." The agent understands this and routes accordingly.
A 12-model evaluation runs across all five iterations. GPT-4, Claude, Llama variants, Mistral, Fireworks-hosted models. The evaluation isn't "which model is smartest" — it's "which model follows operator policies most reliably while maintaining natural conversation." The answer varies by task type, which leads to a routing architecture where different models handle different kinds of interactions.
The Yulier line validates the architecture. Trust calibration, intent clarification, and operator steering work. But it also reveals a hard truth: the infrastructure can't support it. The agent needs persistent state across sessions, reliable event streaming, service-to-service authentication, and deployment that doesn't involve SSH. The agent framework is ready. Everything underneath it is not.
The Yulier iterations are archived. Not abandoned — their concepts are extracted and queued for the platform build. The agent work is R&D that produced architectural specifications, not a product. That distinction matters.
The Platform
You can move this fast when you've already solved every problem once.
Eleven days. The entire production infrastructure, from bare metal to deployed services.
It starts with the archive day. Every project that's served its purpose gets formally archived with a post-mortem: what it taught, what it produced, why it's done. The QMS iterations. The receptionist iterations. The Yulier agent framework. The early infrastructure experiments. Archiving isn't deleting — it's acknowledging that the value has been extracted and the code can stop being maintained.
Then the cluster audit. Five physical nodes. Inventory every service, every port, every certificate, every DNS record. Map the dependency graph. Identify what's running that shouldn't be, what's missing that should exist, what's configured by hand that should be automated.
March 12 — Kubernetes launches. Not a multi-day migration. A single-day cutover. The preparation happened over the preceding months: every service was already containerized, every database schema was managed by migrations, every secret was documented. The cluster comes up with Traefik as the ingress controller, CNPG for PostgreSQL, NATS for messaging, SeaweedFS for object storage, Harbor for the container registry.
March 13–15 — Service migration. The custom IDP deploys first because everything else depends on authentication. Then the core platform services: session management via NATS KV, the deploy CLI's provisioning endpoints, the CDN proxy. Each service deploys with health checks, readiness probes, and resource limits. NetworkPolicies restrict which services can talk to which.
The auth migration chain deserves a closer look. Five identity systems in six months sounds like thrashing. It isn't. It's earning the right to build your own.
SuperTokens — Easy to start, too opinionated about session management. Couldn't customize the token format or the session storage backend.
Ory Kratos + Hydra — Powerful and modular, but operational complexity was enormous. Two services, a database each, configuration that required deep OIDC expertise to get right.
Authentik — Full-featured but heavy. Python-based, slow to start, resource-hungry. Overkill for what amounted to OIDC + user management.
Zitadel — Closest to right. Go-based, fast, good OIDC support. But the multi-tenancy model assumed organizational structures that didn't match. Account hierarchy was wrong for the use case.
Custom IDP — Rust, Axum, PKCS#8 JWT signing, OIDC with PKCE, sessions in NATS KV. Does exactly what's needed. Each previous system taught a specific lesson that's now baked into this one.
March 16–18 — The hardening sprint. Seventy commits in three days. SealedSecrets replace plain secrets in the repo. NetworkPolicies lock down namespace-to-namespace communication. The deploy CLI gets health check verification — it won't report success until the service is actually responding. VictoriaMetrics and Grafana deploy for metrics. Loki for logs.
March 19–22 — The receptionist becomes Assist. Not a rename — a reconception. The dental receptionist is one integration on a multi-channel AI communication platform. Assist handles voice, chat, and is architected for email and SMS. The episode/session/artifact model gives every interaction a structured lifecycle. Episodes group related sessions. Sessions track individual conversations. Artifacts capture the outputs: bookings made, questions answered, escalations triggered.
Every pattern from the workshop clicks into place during these eleven days. The island hydration understanding makes Astro frontends fast. The event sourcing research makes NATS stream design intuitive. The QMS domain expertise shapes the Assist conversation model. The agent architecture defines the trust and escalation systems. Fourteen months of research and ten iterations of product development compress into less than two weeks of platform construction.
What's Next
The hard part is done. Now it's about execution.
The platform is live. Not "live" in the sense of a landing page with a waitlist. Live in the sense of: services are deployed on Kubernetes, health checks pass, observability is real, the deploy CLI goes from code to production with automated provisioning and verification.
The infrastructure inventory as of March 2026:
Assist is deployed with its core services running: the voice pipeline, the booking engine, the knowledge retrieval system, the session manager. The agent architecture from the Yulier R&D is being integrated — trust calibration and operator steering are moving from research prototypes to production code.
The observability overhaul is underway. OpenTelemetry instrumentation across all Rust services, traces flowing to Tempo, logs aggregated in Loki, the Pulse service providing real-time system health. The goal: when something goes wrong, the answer is in the data, not in someone's memory of how the system works.
What comes next is execution against a known architecture. The i18n platform for multi-language support. The real-time WebSocket system for live dashboards. More Assist channels — email, SMS, chat widgets. Deeper agent capabilities. The dental receptionist becomes the first customer integration on a platform that can serve any business that communicates with its customers.
Twenty-five months from first line of code to production platform. Not a straight line — a workshop phase, ten product iterations, an agent R&D sprint, and an eleven-day platform build. Every detour was research. Every killed iteration was a lesson. Every migration was education.
The foundation is set. The tools are built. The platform is running. Now it's about what gets built on top of it.