Article

Zota: From YouChannel to Memory-Led Practice Surfaces

A code-led walkthrough of Zota as the successor to YouChannel: session memory design, context compaction, and model-generated frontend surfaces rendered by stable Next.js components.

10 min read

Zota is the project I built after YouChannel.

YouChannel treated an authentic YouTube video as the object of learning. The product had a concrete source, a transcript, derived study material, and a loop that turned media into practice. That was useful, but the media object also became the ceiling. The learner still had to translate a video lesson into the room they actually cared about.

Zota keeps the useful part of that architecture and changes the object. Instead of a YouTube video, the product starts from a real upcoming scene: an interview, client meeting, presentation, thesis defense, sales pitch, panel, or difficult conversation. The job is not to summarize content. The job is to remember the learner’s situation and keep generating practice surfaces around it.

Predecessor YouChannel
Primary object Real-world brief
Memory layer Session markdown + entries
Generated surfaces Cards, panels, logs, drills

From YouChannel to Zota

The product change is small in wording and large in architecture.

Design questionYouChannel answerZota answer
What is the learning object?A real video and its transcript.A real future communication scene.
What should the model preserve?Useful content, vocabulary, and lesson structure.Scenario facts, learner patterns, debrief evidence, live drill summaries, and deadlines.
What does the frontend render?Media-derived learning views.Model-generated training surfaces tied to session state.
What makes a return visit useful?The video context and derived material are still there.The advisor remembers the room, prior weakness, and next drill.

This is why Zota is not mainly a “language tutor chat.” The chat is only one surface. The durable product is the combination of scenario state, session memory, tool outputs, feedback rows, artifacts, and Live summaries.

Start from the real event

Zota opens in one advisor thread and asks for the actual moment the learner needs to survive: interview, client meeting, defense, pitch, panel, or Q&A.

Durable state
session row, advisor thread, empty session memory
Code evidence
createSessionWithAdvisorSeed, AdvisorPage
Code evidence: the app starts from a brief zota/app/(app)/app/page.tsx, components/session/scenario-composer.tsx, app/(app)/actions.ts

/app renders ScenarioComposer and QuickStartGrid. Submitting a text or voice seed calls createSessionAction, creates a session, initializes # Session Memory, optionally inserts the opening user message, and redirects to /s/[sid]/advisor.

Memory Is the Product Boundary

The biggest Zota design choice is that the model is not treated as the database.

There are two different memory surfaces:

Visible thread

The learner sees a conversation

agent_threads.messages stores UI messages, compact cards, Live summary cards, tool outputs, and the latest run pointer. It is the interface history.

Durable memory

The model reads selected state

session_memories.raw_markdown stores the session memory that should survive compaction, voice drills, and return visits. It is the coaching state.

That split matters because a long UI thread is not the same thing as useful context. A training product needs to preserve facts like “the interview is next Thursday,” “the learner softens ownership language under follow-up pressure,” and “the Live drill exposed weak metric answers.” It does not need to resend every old sentence forever.

Write pathWhat enters memoryWhy it exists
Session seed# Session Memory at session creation.Gives every workspace a memory document from the first turn.
save_memoryTyped entries: user, feedback, project.Lets the advisor preserve recurring patterns and deadlines.
Context compactExtracted memory entries from older UI turns.Keeps model input bounded without dropping coaching history.
Live endA ## Live Session ... markdown block.Converts spoken practice into future-readable evidence.
Code evidence: memory entries are explicit product state zota/lib/zota/advisor-tools.ts, lib/zota/advisor-memory-entries.ts, lib/zota/memory-repo.ts

The save_memory tool upserts typed entries into session_memories.raw_markdown. Entries are named, updated by name, and separated from ordinary UI turns.

Context Compaction

Zota uses a bounded model window and a durable memory document at the same time.

The current policy:

  • keep the last 32 UI messages for normal advisor input,
  • after a compact run, keep a smaller 16-message tail,
  • trigger compact when estimated full UI message JSON reaches 100,000 input tokens,
  • write older findings into session memory instead of replaying the whole thread.
Average turn payload

Full-thread estimate

72k Below compact threshold. The model still receives only the latest 32 messages.
Compact threshold 100k tokens
Normal model tail 32 messages
Post-compact model tail 16 messages + memory
Estimated request after gate 58k tokens

agent_threads.messages can still keep compact cards and folded rows for the UI. The advisor model does not need to see all of them. It should see the recent tail plus the session memory that was intentionally extracted.

Code evidence: compact gate runs before chat send zota/components/session/advisor/advisor-chat-transport.ts

DefaultChatTransport.prepareSendMessagesRequest estimates context size, calls /api/advisor/compact when the threshold is reached, folds older local messages, inserts a compact card, and sends the narrowed message list to /api/advisor/chat.

Code evidence: compact writes structured memory zota/app/api/advisor/compact/route.ts, lib/zota/advisor-memory-compact.ts

The compact route selects dropped messages, asks a model to extract memory entries, upserts those entries into session_memories.raw_markdown, and returns a compact result for the timeline card.

This is the YouChannel lesson in a different body. The model should read selected state, change selected state, and write back the parts that matter. It should not be asked to remember the whole product by having every old token pasted into every future request.

Model-Generated Frontend Surfaces

The second important design is how Zota lets the model create product UI without letting the model own the UI.

The model does not generate arbitrary React components. Instead, it generates structured objects with known schemas. The frontend owns rendering, spacing, permissions, empty states, and navigation. This keeps the product inspectable and avoids turning every model response into an unsafe mini application.

Model output becomes entry cards

Model contract
{ templates: [{ id, emoji, title, description, seedText }, ...six total] }
Frontend surface
QuickStartGrid renders locale-aware scenario cards on /app. Clicking one starts an advisor session with the generated seedText.
State boundary
Cached by unstable_cache. It is not a user record until the learner starts a session.
Code evidence
lib/zota/scenario-templates.ts, components/session/quick-start-grid.tsx

The pattern appears in several places:

SurfaceModel-generated contractFrontend-owned rendering
Quick-start cardsScenarioTemplate[] with id, emoji, title, description, seedText.QuickStartGrid renders six locale-aware starter cards on /app.
Scenario panelScenarioModel written by crystallize_scenario and patched by update_scenario.AdvisorScenarioPanel renders the current scene model.
Timeline tool cardsTool outputs such as save_memory, create_artifact, search_notes, start_drill.AdvisorToolCard switches by tool name and renders known components.
Training log pagesession_feedback rows and session_artifacts rows.TrainingLogPage renders stats, charts, debriefs, and artifact tabs.
Live drill sheetstart_drill brief with character, questions, pressure points, and partner instructions.The Live UI starts a constrained voice session and returns a summary card.
Code evidence: quick-start cards are generated as data zota/lib/zota/scenario-templates.ts, components/session/quick-start-grid.tsx

generateQuickStartTemplates calls generateObject with a Zod schema and returns exactly six scenario template cards. The app renders those cards; the model never writes the page component.

Code evidence: artifacts become a page, not loose chat zota/lib/zota/advisor-tools.ts, app/api/advisor/artifacts/route.ts, components/session/advisor/training-log-page.tsx

create_artifact stores phrase banks, drills, and cue cards in session_artifacts. The training log route reads those rows and renders them as tabs alongside feedback history.

This is the core frontend idea: the model generates page material, not page code. The component boundary stays deterministic. The generated material can be cached, persisted, searched, audited, and rendered in multiple places.

Orchestrator, Tools, and Pages

Zota still needs an agent loop, but the loop exists to protect product state.

An advisor turn is split into:

  1. A shared context build: session, scenario, recent messages, memory, simulation state, timezone.
  2. An orchestrator plan: the model returns an ExecutionPlan that chooses capabilities and tone.
  3. An executor prompt: selected capability files and context become the main advisor instructions.
  4. A tool loop: tool calls write scenarios, memory, feedback, artifacts, or drill briefs.
  5. A frontend render: stable React components turn those writes into cards, panels, and pages.
Code evidence: plan and execution are separated zota/lib/zota/orchestrator/types.ts, lib/zota/advisor-agent.ts, lib/zota/orchestrator/build-executor-prompt.ts

createAdvisorChatAgent builds shared context, calls the orchestrator, builds the executor prompt from selected capability files, creates a traced tool-loop agent, and records the orchestrator plan in agent_runs.input_data.

ToolDurable effectSurface it unlocks
crystallize_scenarioWrites sessions.scenario_json and session title.Scenario panel and session workspace identity.
record_debriefInserts session_feedback.Training log stats, chart, and debrief rows.
create_artifactInserts session_artifacts.Artifact tabs and timeline artifact card.
save_memoryUpserts structured memory entries.Future advisor context and memory card.
start_drillReturns a voice-drill brief.Live drill sheet and spoken practice surface.
search_notesSearches session memories.Search result card inside the advisor thread.

Live Voice as Memory Input

Live voice is where the memory design gets tested. A spoken drill is useful only if the next advisor turn knows what happened.

The handoff is intentionally narrow:

  1. The advisor creates a drill brief, or the server generates a thread brief from recent context.
  2. The server mints a short-lived Gemini Live token with constrained instructions.
  3. The browser runs the voice session.
  4. On end, transcript segments are summarized into a Live Session markdown block.
  5. That block is appended to session_memories.raw_markdown and shown as a card in the advisor thread.

Advisor prepares the drill

You will speak with a skeptical hiring manager. I will ask about ownership, tradeoffs, and the moment your project failed.
Durable state
start_drill tool output with briefForDrillPartner
Code evidence: Live end becomes session memory zota/app/api/advisor/live-end/route.ts, lib/zota/advisor-live-summary.ts

The Live end route converts transcript segments into a structured summary, formats a markdown block with findings, hypothesis updates, next strategy, optional drill questions, pressure points, and transcript, then appends it to session memory.

The voice call is transient. The memory result is durable. That is the product boundary.

Deployment and Documentation

I also organized the Zota repo so the implementation can be handed off without relying on old notes.

Document or surfacePurpose
README.mdShort project entry, docs links, local run summary.
docs/Zota-Docs-Index.mdCurrent docs index and maintenance rules.
docs/Zota-Flow.mdRoute, advisor, memory, tools, Live, generated surfaces, and data model.
docs/Zota-Usage.mdUser path, developer commands, env vars, current boundaries.
docs/Zota-Deployment.mdLocal, Supabase, Vercel, and Cloudflare Pages deployment.
static-site/Public static explanation page for the project.

The app runtime is a normal Next.js deployment with Supabase and model provider secrets. The static explanation page is separate, so the project can have a public readable page even when the real product is authenticated.

What Is Still Fragile

Planner quality

The orchestrator plan is still model output

The plan has a JSON contract and fallbacks, but capability selection still affects product behavior.

Generated surfaces

Schemas need to stay tight

The frontend is stable only if model output remains constrained and validated before rendering.

Voice evidence

Live summaries depend on transcript quality

A sparse transcript cannot support a rich memory update without inventing facts.

Memory hygiene

Saved observations need pruning rules

Typed memory entries are useful, but a real product still needs merge, delete, and user-visible review flows.

These are product risks worth naming because they point to the next work: stricter schemas, better plan validation, memory review UI, trace inspection, and cost controls.

The Useful Lesson

Zota is not interesting because it is another AI language-learning chat.

It is interesting because it treats memory and frontend generation as product primitives. The model remembers by writing selected state, not by owning every old turn. The model generates surfaces by emitting structured data, not by owning the React tree.

That is the line I wanted to draw after YouChannel: keep the real object, keep the practice loop, but make the learner’s upcoming room the center of the product.

The code is public at github.com/larry-xue/zota. The static explanation page is packaged in static-site/, and the canonical project reference on this site is Zota AI Communication Practice Console. The earlier YouChannel static archive is at youchannel-product.pages.dev.