[{"data":1,"prerenderedAt":1070},["ShallowReactive",2],{"repo-tree":3,"repo-\u002Fapps\u002Flearning-api\u002Fagents":283},[4,7,10,13,16,19,22,25,28,31,34,37,40,43,46,49,52,55,58,61,64,67,69,72,75,78,81,84,86,88,90,93,96,99,102,105,108,111,114,117,120,123,125,127,129,131,133,135,138,141,143,146,149,152,155,158,161,164,167,169,172,175,178,180,183,186,189,192,195,198,201,203,206,209,212,215,218,221,224,227,230,233,236,239,242,245,248,251,254,257,260,263,266,269,272,275,278,281],{"path":5,"title":6},"\u002Fagents\u002Fbackend-code-style","Backend Conventions",{"path":8,"title":9},"\u002Fagents\u002Fdatabase","Database",{"path":11,"title":12},"\u002Fagents\u002Fportal-code-style","Portal Conventions",{"path":14,"title":15},"\u002Fagents\u002Ftranslation","Translation",{"path":17,"title":18},"\u002Fconventions\u002Fbackend-coding","Backend coding conventions",{"path":20,"title":21},"\u002Fconventions\u002Ffrontend-coding","Frontend coding conventions",{"path":23,"title":24},"\u002Fdevelopment-process","Development process",{"path":26,"title":27},"\u002Flearning-api-preview-hetzner-setup","Learning API Preview on Hetzner + Cloudflare",{"path":29,"title":30},"\u002Flearning-api-preview-vm-plan","Learning API Preview VM Plan",{"path":32,"title":33},"\u002Fmonorepo-structure","Monorepo structure",{"path":35,"title":36},"\u002Foperations","Operations — bugs and support",{"path":38,"title":39},"\u002Fpostmortems\u002F2026-03-16_onboarding-currency-regression","Onboarding Zod transform silently broken — web signups assigned wrong checkout currency",{"path":41,"title":42},"\u002Fpostmortems\u002Freadme","Postmortems",{"path":44,"title":45},"\u002Fpostmortems\u002F_template","TEMPLATE",{"path":47,"title":48},"\u002Fpostmortems\u002Fposthog-comparison","Postmortem practice — comparison with PostHog",{"path":50,"title":51},"\u002Fpreview-environment-plan","Preview Environment Plan",{"path":53,"title":54},"\u002Fprinciples","Engineering principles",{"path":56,"title":57},"\u002Fworking-with-ai","Working with AI",{"path":59,"title":60},"\u002F.claude\u002Fskills\u002Feval-playground\u002Fskill","Eval Playground — Co-development Skill",{"path":62,"title":63},"\u002F.claude\u002Fskills\u002Ffigma-diff-section\u002Fskill","Figma Diff Section Pipeline",{"path":65,"title":66},"\u002Fagents","AGENTS.md",{"path":68,"title":66},"\u002Fclaude",{"path":70,"title":71},"\u002Freadme","Studyflash",{"path":73,"title":74},"\u002Fapps\u002Fcore-api\u002Fagents","Core API (apps\u002Fcore-api)",{"path":76,"title":77},"\u002Fapps\u002Fcore-api\u002Freadme","README",{"path":79,"title":80},"\u002Fapps\u002Femail-previews\u002Fagents","Email Previews (apps\u002Femail-previews)",{"path":82,"title":83},"\u002Fapps\u002Flanding-page\u002Fagents","Landing Page (apps\u002Flanding-page)",{"path":85,"title":83},"\u002Fapps\u002Flanding-page\u002Fclaude",{"path":87,"title":66},"\u002Fapps\u002Flearning-api\u002Fagents",{"path":89,"title":77},"\u002Fapps\u002Flearning-api\u002Freadme",{"path":91,"title":92},"\u002Fapps\u002Flearning-api\u002Fevals-playground\u002Feval_metrics_design","Surface-Specific Eval Metrics Design",{"path":94,"title":95},"\u002Fapps\u002Flearning-api\u002Fevals-playground\u002Ftest_set","Quiz Eval Test Set",{"path":97,"title":98},"\u002Fapps\u002Flearning-api\u002Fevals-playground\u002Ffrontend\u002Freadme","React + TypeScript + Vite",{"path":100,"title":101},"\u002Fapps\u002Flearning-api\u002Fevals-playground\u002Fknown-issues\u002Fcontent-pillar-shallow-coverage\u002Freadme","Content pillar misses subtopics in dense documents",{"path":103,"title":104},"\u002Fapps\u002Flearning-api\u002Fevals-playground\u002Fknown-issues\u002Fdocling-empty-section-headers\u002Freadme","Empty section headers dropped by docling chunker",{"path":106,"title":107},"\u002Fapps\u002Flearning-api\u002Fevals-playground\u002Fknown-issues\u002Fdocling-table-reading-order\u002Freadme","Table\u002Fbox layout causes wrong reading order",{"path":109,"title":110},"\u002Fapps\u002Flearning-api\u002Fevals-playground\u002Fmetrics\u002Freadme","Quiz eval metrics — canonical rubrics",{"path":112,"title":113},"\u002Fapps\u002Flearning-api\u002Fevals-playground\u002Freports\u002F2026-04-12-quiz-summary-feedback-current-state","Quiz and Summary Feedback Current State",{"path":115,"title":116},"\u002Fapps\u002Flearning-api\u002Fevals-playground\u002Freports\u002F2026-04-24-quiz-eval-metrics","Quiz Evaluation Metrics",{"path":118,"title":119},"\u002Fapps\u002Flearning-api\u002Fevals-playground\u002Freports\u002F2026-05-01-quiz-eval-current-state","Quiz Eval Current State",{"path":121,"title":122},"\u002Fapps\u002Flearning-api\u002Fmonitoring\u002Freadme","Monitoring Stack",{"path":124,"title":77},"\u002Fapps\u002Flearning-api\u002Fshared\u002Freadme",{"path":126,"title":77},"\u002Fapps\u002Flearning-api\u002Fworkers\u002Flearning_agents\u002Fflashcard_agent\u002Freadme",{"path":128,"title":77},"\u002Fapps\u002Flearning-api\u002Fworkers\u002Flearning_agents\u002Fingestion_agent\u002Freadme",{"path":130,"title":77},"\u002Fapps\u002Flearning-api\u002Fworkers\u002Flearning_agents\u002Fquiz_agent\u002Freadme",{"path":132,"title":77},"\u002Fapps\u002Flearning-api\u002Fworkers\u002Flearning_agents\u002Fsummary_agent\u002Freadme",{"path":134,"title":77},"\u002Fapps\u002Flearning-api\u002Fworkers\u002Fparser\u002Freadme",{"path":136,"title":137},"\u002Fapps\u002Fmarketing-emails-preview\u002Fagents","Marketing Emails Preview (apps\u002Fmarketing-emails-preview)",{"path":139,"title":140},"\u002Fapps\u002Fmobile-app\u002Fagents","StudyFlash Mobile App - Claude Code Configuration",{"path":142,"title":140},"\u002Fapps\u002Fmobile-app\u002Fclaude",{"path":144,"title":145},"\u002Fapps\u002Fmountain-max\u002Fagents","Mountain Max (apps\u002Fmountain-max)",{"path":147,"title":148},"\u002Fapps\u002Fmountain-max\u002Fgame\u002Freadme","Mountain Max Game",{"path":150,"title":151},"\u002Fapps\u002Fportal\u002Fagents","Portal (apps\u002Fportal)",{"path":153,"title":154},"\u002Fapps\u002Fportal\u002Freadme","Nuxt Minimal Starter",{"path":156,"title":157},"\u002Fapps\u002Fportal\u002Fapp\u002Fcomposables\u002Ffiles\u002Freadme","File Upload Composables",{"path":159,"title":160},"\u002Fapps\u002Fportal\u002Fdocs\u002Flibrary-routing","Library Routing Documentation",{"path":162,"title":163},"\u002Fapps\u002Fsupabase\u002Fagents","Supabase (apps\u002Fsupabase)",{"path":165,"title":166},"\u002Fapps\u002Fwrapped\u002Fagents","Wrapped (apps\u002Fwrapped)",{"path":168,"title":98},"\u002Fapps\u002Fwrapped\u002Freadme",{"path":170,"title":171},"\u002Finfra\u002Freadme","infra\u002F",{"path":173,"title":174},"\u002Finfra\u002Fdns\u002Freadme","DNS Infrastructure",{"path":176,"title":177},"\u002Finfra\u002Fdokploy\u002Freadme","studyflash-dokploy",{"path":179,"title":77},"\u002Finfra\u002Fdokploy\u002Fsdk\u002Fnodejs\u002Freadme",{"path":181,"title":182},"\u002Finfra\u002Finfisical\u002Freadme","Infisical Infrastructure",{"path":184,"title":185},"\u002Finfra\u002Flearning-api\u002Freadme","Pulumi GCP TypeScript Template",{"path":187,"title":188},"\u002Finfra\u002Fopenreplay\u002Freadme","OpenReplay on Hetzner",{"path":190,"title":191},"\u002Finfra\u002Fscripts\u002Freadme","infra\u002Fscripts\u002F",{"path":193,"title":194},"\u002Finfra\u002Fturborepo-cache\u002Freadme","Turborepo Remote Cache Infrastructure",{"path":196,"title":197},"\u002Finternal\u002Fchatwoot\u002Freadme","Chatwoot Infrastructure",{"path":199,"title":200},"\u002Finternal\u002Fchatwoot\u002Fprovider\u002Freadme","studyflash-chatwoot-provider",{"path":202,"title":77},"\u002Finternal\u002Fchatwoot\u002Fprovider\u002Fsdk\u002Fnodejs\u002Freadme",{"path":204,"title":205},"\u002Finternal\u002Fdocs\u002Freadme","internal\u002Fdocs",{"path":207,"title":208},"\u002Finternal\u002Fsupport-bot\u002Fclaude","Support Bot (Maximilian)",{"path":210,"title":211},"\u002Finternal\u002Fsupport-bot\u002Freadme","Studyflash Customer Support Bot (Maximilian)",{"path":213,"title":214},"\u002Finternal\u002Fsupport-bot\u002Fkb\u002Faccount_issues","Account Issues",{"path":216,"title":217},"\u002Finternal\u002Fsupport-bot\u002Fkb\u002Fbilling_invoice","Billing Invoice",{"path":219,"title":220},"\u002Finternal\u002Fsupport-bot\u002Fkb\u002Fcontent_upload","Content Upload",{"path":222,"title":223},"\u002Finternal\u002Fsupport-bot\u002Fkb\u002Fdata_loss","Data Loss",{"path":225,"title":226},"\u002Finternal\u002Fsupport-bot\u002Fkb\u002Fflashcard_issues","Flashcard Issues",{"path":228,"title":229},"\u002Finternal\u002Fsupport-bot\u002Fkb\u002Fgarbage","Garbage",{"path":231,"title":232},"\u002Finternal\u002Fsupport-bot\u002Fkb\u002Fgeneral_how_to","General How To",{"path":234,"title":235},"\u002Finternal\u002Fsupport-bot\u002Fkb","Knowledge Base Index",{"path":237,"title":238},"\u002Finternal\u002Fsupport-bot\u002Fkb\u002Flanguage_issues","Language Issues",{"path":240,"title":241},"\u002Finternal\u002Fsupport-bot\u002Fkb\u002Fmindmap_issues","Mindmap Issues",{"path":243,"title":244},"\u002Finternal\u002Fsupport-bot\u002Fkb\u002Fmisunderstanding","Misunderstanding",{"path":246,"title":247},"\u002Finternal\u002Fsupport-bot\u002Fkb\u002Fmock_exam_issues","Mock Exam Issues",{"path":249,"title":250},"\u002Finternal\u002Fsupport-bot\u002Fkb\u002Fpodcast_issues","Podcast Issues",{"path":252,"title":253},"\u002Finternal\u002Fsupport-bot\u002Fkb\u002Fquiz_issues","Quiz Issues",{"path":255,"title":256},"\u002Finternal\u002Fsupport-bot\u002Fkb\u002Frefund_request","Refund Request",{"path":258,"title":259},"\u002Finternal\u002Fsupport-bot\u002Fkb\u002Fsubscription_cancellation","Subscription Cancellation",{"path":261,"title":262},"\u002Finternal\u002Fsupport-bot\u002Fkb\u002Fsubscription_info","Subscription Info",{"path":264,"title":265},"\u002Finternal\u002Fsupport-bot\u002Fkb\u002Fsummary_issues","Summary Issues",{"path":267,"title":268},"\u002Finternal\u002Fsupport-bot\u002Fkb\u002Ftechnical_errors","Technical Errors",{"path":270,"title":271},"\u002Finternal\u002Fsupport-bot\u002Fkb\u002Fvideo_issues","Video Issues",{"path":273,"title":274},"\u002Fpackages\u002Fcommon\u002Fdocs\u002Fearly-access-features","Declarative Early Access Features",{"path":276,"title":277},"\u002Fpackages\u002Fcommon\u002Fscripts\u002Freadme","Common Package Scripts",{"path":279,"title":280},"\u002Fpackages\u002Fdevtools\u002Ffigma-plugins\u002Freadme","Figma plugins",{"path":282,"title":77},"\u002Fpackages\u002Fpulumi-infisical\u002Freadme",{"id":284,"title":66,"body":285,"description":334,"extension":1064,"lastReviewed":1065,"meta":1066,"navigation":399,"owner":1065,"path":87,"seo":1067,"status":1065,"stem":1068,"tags":1065,"__hash__":1069},"repo\u002Fapps\u002Flearning-api\u002FAGENTS.md",{"type":286,"value":287,"toc":1054},"minimark",[288,292,297,301,324,328,550,554,639,643,660,664,714,718,721,925,929,936,993,999,1003,1050],[289,290,66],"h1",{"id":291},"agentsmd",[293,294,296],"h2",{"id":295},"project-overview","Project Overview",[298,299,300],"p",{},"LearningAPI V2 is a Python monorepo that generates educational content (flashcards, quizzes, summaries) from documents using AI. It consists of:",[302,303,304,312,318],"ul",{},[305,306,307,311],"li",{},[308,309,310],"strong",{},"api\u002F",": FastAPI web service providing REST endpoints",[305,313,314,317],{},[308,315,316],{},"workers\u002F",": Background processing with Celery (learning_agents for AI tasks, parser for document parsing)",[305,319,320,323],{},[308,321,322],{},"shared\u002F",": Common schemas and utilities used by all services",[293,325,327],{"id":326},"essential-commands","Essential Commands",[329,330,335],"pre",{"className":331,"code":332,"language":333,"meta":334,"style":334},"language-bash shiki shiki-themes github-light github-dark","# Development\npoe dev              # Start API + workers (no parser\u002FOCR)\npoe dev-docker       # Start all services with slim CPU parser\u002FOCR (requires TRITON_URL)\npoe dev-docker-parser-gpu   # Start legacy local GPU\u002FVLLM parser stack\npoe install          # Install dependencies for all services\n\n# Code Quality\npoe lint             # Run Ruff linter\npoe format           # Format code with Ruff\npoe typecheck        # Run Pyright type checking\npoe ci               # Run full CI pipeline (lint + typecheck)\n\n# Testing\npoe test             # Run all tests\npoe test-unit        # Run unit tests only\npoe test-integration # Run integration tests only\n\n# Testing with automatic service management\npoe test-with-services  # Run all tests (starts test Docker services)\n\n# Other\npoe update-types     # Generate TypeScript types from OpenAPI (requires poe dev running)\npoe set-secrets      # Pull secrets from Infisical\n","bash","",[336,337,338,347,361,372,383,394,401,407,418,429,440,451,456,462,473,484,495,500,506,517,522,528,539],"code",{"__ignoreMap":334},[339,340,343],"span",{"class":341,"line":342},"line",1,[339,344,346],{"class":345},"sJ8bj","# Development\n",[339,348,350,354,358],{"class":341,"line":349},2,[339,351,353],{"class":352},"sScJk","poe",[339,355,357],{"class":356},"sZZnC"," dev",[339,359,360],{"class":345},"              # Start API + workers (no parser\u002FOCR)\n",[339,362,364,366,369],{"class":341,"line":363},3,[339,365,353],{"class":352},[339,367,368],{"class":356}," dev-docker",[339,370,371],{"class":345},"       # Start all services with slim CPU parser\u002FOCR (requires TRITON_URL)\n",[339,373,375,377,380],{"class":341,"line":374},4,[339,376,353],{"class":352},[339,378,379],{"class":356}," dev-docker-parser-gpu",[339,381,382],{"class":345},"   # Start legacy local GPU\u002FVLLM parser stack\n",[339,384,386,388,391],{"class":341,"line":385},5,[339,387,353],{"class":352},[339,389,390],{"class":356}," install",[339,392,393],{"class":345},"          # Install dependencies for all services\n",[339,395,397],{"class":341,"line":396},6,[339,398,400],{"emptyLinePlaceholder":399},true,"\n",[339,402,404],{"class":341,"line":403},7,[339,405,406],{"class":345},"# Code Quality\n",[339,408,410,412,415],{"class":341,"line":409},8,[339,411,353],{"class":352},[339,413,414],{"class":356}," lint",[339,416,417],{"class":345},"             # Run Ruff linter\n",[339,419,421,423,426],{"class":341,"line":420},9,[339,422,353],{"class":352},[339,424,425],{"class":356}," format",[339,427,428],{"class":345},"           # Format code with Ruff\n",[339,430,432,434,437],{"class":341,"line":431},10,[339,433,353],{"class":352},[339,435,436],{"class":356}," typecheck",[339,438,439],{"class":345},"        # Run Pyright type checking\n",[339,441,443,445,448],{"class":341,"line":442},11,[339,444,353],{"class":352},[339,446,447],{"class":356}," ci",[339,449,450],{"class":345},"               # Run full CI pipeline (lint + typecheck)\n",[339,452,454],{"class":341,"line":453},12,[339,455,400],{"emptyLinePlaceholder":399},[339,457,459],{"class":341,"line":458},13,[339,460,461],{"class":345},"# Testing\n",[339,463,465,467,470],{"class":341,"line":464},14,[339,466,353],{"class":352},[339,468,469],{"class":356}," test",[339,471,472],{"class":345},"             # Run all tests\n",[339,474,476,478,481],{"class":341,"line":475},15,[339,477,353],{"class":352},[339,479,480],{"class":356}," test-unit",[339,482,483],{"class":345},"        # Run unit tests only\n",[339,485,487,489,492],{"class":341,"line":486},16,[339,488,353],{"class":352},[339,490,491],{"class":356}," test-integration",[339,493,494],{"class":345}," # Run integration tests only\n",[339,496,498],{"class":341,"line":497},17,[339,499,400],{"emptyLinePlaceholder":399},[339,501,503],{"class":341,"line":502},18,[339,504,505],{"class":345},"# Testing with automatic service management\n",[339,507,509,511,514],{"class":341,"line":508},19,[339,510,353],{"class":352},[339,512,513],{"class":356}," test-with-services",[339,515,516],{"class":345},"  # Run all tests (starts test Docker services)\n",[339,518,520],{"class":341,"line":519},20,[339,521,400],{"emptyLinePlaceholder":399},[339,523,525],{"class":341,"line":524},21,[339,526,527],{"class":345},"# Other\n",[339,529,531,533,536],{"class":341,"line":530},22,[339,532,353],{"class":352},[339,534,535],{"class":356}," update-types",[339,537,538],{"class":345},"     # Generate TypeScript types from OpenAPI (requires poe dev running)\n",[339,540,542,544,547],{"class":341,"line":541},23,[339,543,353],{"class":352},[339,545,546],{"class":356}," set-secrets",[339,548,549],{"class":345},"      # Pull secrets from Infisical\n",[293,551,553],{"id":552},"architecture","Architecture",[555,556,557,591,619],"ol",{},[305,558,559,562,563],{},[308,560,561],{},"API Service"," (api\u002F): FastAPI application handling HTTP requests",[302,564,565,581,588],{},[305,566,567,568,571,572,571,575,571,578],{},"Endpoints: ",[336,569,570],{},"\u002Fingest_document",", ",[336,573,574],{},"\u002Fgenerate_flashcards",[336,576,577],{},"\u002Fgenerate_quiz",[336,579,580],{},"\u002Fgenerate_summary",[305,582,583,584,587],{},"Authentication via ",[336,585,586],{},"X-API-Key"," header",[305,589,590],{},"Delegates heavy processing to Celery workers",[305,592,593,596,597],{},[308,594,595],{},"Worker Services"," (workers\u002F):",[302,598,599,613],{},[305,600,601,604,605],{},[308,602,603],{},"learning_agents\u002F",": AI-powered content generation using LangChain\u002FLangGraph\n",[302,606,607,610],{},[305,608,609],{},"Separate agents for flashcards, quizzes, summaries, and document ingestion",[305,611,612],{},"Uses Google Vertex AI and OpenAI models",[305,614,615,618],{},[308,616,617],{},"parser\u002F",": Document parsing with Docling (optional, GPU-capable)",[305,620,621,624,625],{},[308,622,623],{},"Shared Package"," (shared\u002F): Common Pydantic schemas and utilities",[302,626,627,630,633],{},[305,628,629],{},"All services depend on this package for type safety",[305,631,632],{},"Contains schemas for Chunks, Languages, Subjects, and API contracts",[305,634,635,638],{},[308,636,637],{},"IMPORTANT",": Since this is a monorepo with separate apps, shared code between API and Workers MUST be placed in the shared package. The API cannot import from workers and vice versa.",[293,640,642],{"id":641},"development-workflow","Development Workflow",[555,644,645,651,654,657],{},[305,646,647,648],{},"Each service is an independent UV project with its own ",[336,649,650],{},"pyproject.toml",[305,652,653],{},"Docker Compose with watch mode enables hot-reloading",[305,655,656],{},"Services communicate via Celery\u002FRedis task queue",[305,658,659],{},"Strong typing enforced with Pyright and Pydantic",[293,661,663],{"id":662},"key-technologies","Key Technologies",[302,665,666,672,678,684,690,696,702,708],{},[305,667,668,671],{},[308,669,670],{},"Python 3.12+"," (3.10+ for parser due to Docling requirements)",[305,673,674,677],{},[308,675,676],{},"UV"," for package management",[305,679,680,683],{},[308,681,682],{},"Poe the Poet"," for task running",[305,685,686,689],{},[308,687,688],{},"FastAPI"," for REST API",[305,691,692,695],{},[308,693,694],{},"Celery + Redis"," for task queue",[305,697,698,701],{},[308,699,700],{},"Docker Compose"," for local development",[305,703,704,707],{},[308,705,706],{},"Ruff"," for linting\u002Fformatting",[305,709,710,713],{},[308,711,712],{},"Pyright"," for type checking",[293,715,717],{"id":716},"test-structure","Test Structure",[298,719,720],{},"The project follows a distributed testing approach:",[555,722,723,749,779,810,896],{},[305,724,725,728,729],{},[308,726,727],{},"Unit Tests",": Located within each service directory",[302,730,731,737,743],{},[305,732,733,736],{},[336,734,735],{},"api\u002Ftests\u002F"," - API endpoint unit tests with mocked dependencies (13 tests)",[305,738,739,742],{},[336,740,741],{},"workers\u002Flearning_agents\u002Ftests\u002F"," - Worker task unit tests (currently empty)",[305,744,745,748],{},[336,746,747],{},"workers\u002Fparser\u002Ftests\u002F"," - Parser unit tests (currently empty)",[305,750,751,754,755,758,759],{},[308,752,753],{},"Integration Tests",": Located at the root level (",[336,756,757],{},"tests_integration\u002F",")",[302,760,761,767],{},[305,762,763,766],{},[336,764,765],{},"test_integration.py"," - All integration tests including connectivity checks and complete workflows (10 tests)",[305,768,769,770,571,773,571,776],{},"Tests use pytest markers: ",[336,771,772],{},"@pytest.mark.unit",[336,774,775],{},"@pytest.mark.integration",[336,777,778],{},"@pytest.mark.slow",[305,780,781,784,785],{},[308,782,783],{},"Test Configuration",":",[302,786,787,793,799],{},[305,788,789,792],{},[336,790,791],{},".env"," - Environment variables (uses same as development)",[305,794,795,798],{},[336,796,797],{},"pytest.ini"," - Pytest configuration with markers and paths",[305,800,801,802,805,806,809],{},"Test user: ",[336,803,804],{},"test@email.com"," \u002F ",[336,807,808],{},"password"," (from supabase seed)",[305,811,812,784,815],{},[308,813,814],{},"Running Tests",[302,816,817,863],{},[305,818,819,822,823],{},[308,820,821],{},"Option 1",": Manual setup\n",[302,824,825,851,857],{},[305,826,827,828,831],{},"Start Supabase and edge functions: ",[336,829,830],{},"cd ~\u002Fwork\u002Fstudyflash_v2\u002Fapps\u002Fsupabase && pnpm run dev",[302,832,833,840],{},[305,834,835,836,839],{},"This runs ",[336,837,838],{},"npx supabase start"," (predev hook) and serves edge functions",[305,841,842,843,846,847],{},"If test user doesn't exist, run: ",[336,844,845],{},"pnpm run seed"," to create ",[848,849,804],"a",{"href":850},"mailto:test@email.com",[305,852,853,854],{},"Start learning-api services: ",[336,855,856],{},"poe dev",[305,858,859,860],{},"Run tests: ",[336,861,862],{},"poe test",[305,864,865,868,869],{},[308,866,867],{},"Option 2",": Automatic setup (recommended)\n",[302,870,871,878,881],{},[305,872,873,874,877],{},"Use ",[336,875,876],{},"poe test-with-services"," - automatically stops existing Docker services and starts test-specific ones",[305,879,880],{},"The test script handles all service management",[305,882,883,885,886,891],{},[308,884,637],{},": Core-api Supabase must still be running separately at ",[848,887,888],{"href":888,"rel":889},"http:\u002F\u002Flocalhost:54321",[890],"nofollow",[302,892,893],{},[305,894,895],{},"Without Supabase, only smoke tests will pass (6\u002F7), integration tests will fail",[305,897,898,784,901],{},[308,899,900],{},"Test Commands",[302,902,903,908,914,920],{},[305,904,905,907],{},[336,906,862],{}," - Run all tests",[305,909,910,913],{},[336,911,912],{},"poe test-unit"," - Run unit tests only",[305,915,916,919],{},[336,917,918],{},"poe test-integration"," - Run integration tests only",[305,921,922,924],{},[336,923,876],{}," - Run all tests with automatic service management",[293,926,928],{"id":927},"code-quality-workflow","Code Quality Workflow",[298,930,931,932,935],{},"When making code changes, ",[308,933,934],{},"always"," follow this workflow to ensure code quality:",[555,937,938,967,973],{},[305,939,940,943,944],{},[308,941,942],{},"After Each Edit",": Run linting and type checking for the project you modified",[329,945,947],{"className":331,"code":946,"language":333,"meta":334,"style":334},"poe lint        # Check for linting issues\npoe typecheck   # Verify type safety\n",[336,948,949,958],{"__ignoreMap":334},[339,950,951,953,955],{"class":341,"line":342},[339,952,353],{"class":352},[339,954,414],{"class":356},[339,956,957],{"class":345},"        # Check for linting issues\n",[339,959,960,962,964],{"class":341,"line":349},[339,961,353],{"class":352},[339,963,436],{"class":356},[339,965,966],{"class":345},"   # Verify type safety\n",[305,968,969,972],{},[308,970,971],{},"Fix Any Issues",": Address all linting and type checking errors before proceeding",[305,974,975,978,979],{},[308,976,977],{},"Final Step",": Once all edits are complete and all checks pass, format the code",[329,980,982],{"className":331,"code":981,"language":333,"meta":334,"style":334},"poe format      # Auto-format all code with Ruff\n",[336,983,984],{"__ignoreMap":334},[339,985,986,988,990],{"class":341,"line":342},[339,987,353],{"class":352},[339,989,425],{"class":356},[339,991,992],{"class":345},"      # Auto-format all code with Ruff\n",[298,994,995,998],{},[308,996,997],{},"Important",": Always run these checks in this order. Format last to ensure consistent code style after all manual fixes.",[293,1000,1002],{"id":1001},"important-instructions","Important Instructions",[302,1004,1005,1019,1025,1031,1037],{},[305,1006,1007,1010,1011,1014,1015,1018],{},[308,1008,1009],{},"Package Management",": Always use ",[336,1012,1013],{},"uv add"," instead of ",[336,1016,1017],{},"uv pip install"," when adding dependencies in UV projects",[305,1020,1021,1024],{},[308,1022,1023],{},"File Management",": Never create files unless absolutely necessary. Always prefer editing existing files",[305,1026,1027,1030],{},[308,1028,1029],{},"Documentation",": Don't create documentation files (*.md) or README files unless explicitly requested",[305,1032,1033,1036],{},[308,1034,1035],{},"Validation",": Validate JSON\u002Fdict payloads with Pydantic models by default (except when an external library already validates\u002Fparses them)",[305,1038,1039,1042,1043,1046,1047,1049],{},[308,1040,1041],{},"Type Safety",": Avoid ",[336,1044,1045],{},"getattr"," for production code. Prefer typed models, explicit attributes, or typed fallbacks. Use ",[336,1048,1045],{}," only when interfacing with truly dynamic\u002Funknown objects and document why.",[1051,1052,1053],"style",{},"html pre.shiki code .sJ8bj, html code.shiki .sJ8bj{--shiki-default:#6A737D;--shiki-dark:#6A737D}html pre.shiki code .sScJk, html code.shiki .sScJk{--shiki-default:#6F42C1;--shiki-dark:#B392F0}html pre.shiki code .sZZnC, html code.shiki .sZZnC{--shiki-default:#032F62;--shiki-dark:#9ECBFF}html .default .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html.dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}",{"title":334,"searchDepth":349,"depth":349,"links":1055},[1056,1057,1058,1059,1060,1061,1062,1063],{"id":295,"depth":349,"text":296},{"id":326,"depth":349,"text":327},{"id":552,"depth":349,"text":553},{"id":641,"depth":349,"text":642},{"id":662,"depth":349,"text":663},{"id":716,"depth":349,"text":717},{"id":927,"depth":349,"text":928},{"id":1001,"depth":349,"text":1002},"md",null,{},{"title":66,"description":334},"apps\u002Flearning-api\u002FAGENTS","isGfmWr-W9D1cNg1TTlNsU2OvAc_ZOgHZh8TMHh-qeY",1779007962948]