[{"data":1,"prerenderedAt":1008},["ShallowReactive",2],{"repo-tree":3,"repo-\u002Fpostmortems\u002F2026-03-16_onboarding-currency-regression":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":39,"body":285,"description":1000,"extension":1001,"lastReviewed":1002,"meta":1003,"navigation":1004,"owner":1002,"path":38,"seo":1005,"status":1002,"stem":1006,"tags":1002,"__hash__":1007},"handbook\u002Fpostmortems\u002F2026-03-16_onboarding-currency-regression.md",{"type":286,"value":287,"toc":980},"minimark",[288,292,304,309,317,323,385,388,404,407,413,418,445,449,509,513,543,561,565,572,576,614,618,628,632,732,736,740,748,752,771,775,794,798,869,873,913,917,976],[289,290,39],"h1",{"id":291},"onboarding-zod-transform-silently-broken-web-signups-assigned-wrong-checkout-currency",[293,294,295,296,303],"p",{},"2026-03-16 | ",[297,298,302],"a",{"href":299,"rel":300},"https:\u002F\u002Fgithub.com\u002FRaj1v",[301],"nofollow","Rajiv"," | Resolved",[305,306,308],"h2",{"id":307},"tldr","TL;DR",[293,310,311,312,316],{},"During a refactor of the onboarding flow, an unobtrusive inconspicuous call to the ",[313,314,315],"code",{},"handleSubmit"," function of the Vee-Validate form validation library was removed. This function was vital for triggering the Zod transformations on onboarding data, including mapping countries to currencies. This caused all web-signups to get CHF currencies, and get wrong prices presented in the store. This ultimately led to a lower conversion rate for 2 weeks.",[293,318,319],{},[320,321,322],"strong",{},"Main Mistakes & Lessons",[324,325,326,339],"table",{},[327,328,329],"thead",{},[330,331,332,336],"tr",{},[333,334,335],"th",{},"Mistake",[333,337,338],{},"Lesson",[340,341,342,351,359,367,374],"tbody",{},[330,343,344,348],{},[345,346,347],"td",{},"PR author deleted existing function calls without understanding the underlying purpose. Red flags could have included that the function was imported from a library the author did not recognize.",[345,349,350],{},"Don't remove what you don't understand. Unfamiliar library imports are a signal to investigate, not delete.",[330,352,353,356],{},[345,354,355],{},"The original code author failed to recognize the importance of adding inline documentation to snippets using libraries that other team members might be unfamiliar with.",[345,357,358],{},"Document non-obvious library interactions — especially when a function does more than its name suggests.",[330,360,361,364],{},[345,362,363],{},"Mapping logic lived inside client-side form logic, tightly coupled to the onboarding carousel — a \"hot\" code path that is frequently touched and refactored.",[345,365,366],{},"Critical business logic (what currency a user pays in) should not live in a fragile, frequently-changed code path.",[330,368,369,372],{},[345,370,371],{},"Original author underestimated the criticality of this transformation was underestimated.",[345,373],{},[330,375,376,379],{},[345,377,378],{},"An integration test would not have realistically caught this, as the failure was silent.",[345,380,381,382,384],{},"Stricter AI code review would have flagged that removing ",[313,383,315],{}," disables the Zod transform chain.",[293,386,387],{},"Next steps:",[389,390,391,395],"ul",{},[392,393,394],"li",{},"Introduce postmortems as a standard practice",[392,396,397,398,403],{},"Take integration of ",[297,399,402],{"href":400,"rel":401},"https:\u002F\u002Flinear.app\u002Fstudyflash\u002Fproject\u002Fai-code-reviews-f11ff229b82b",[301],"AI code reviews"," more seriously",[405,406],"hr",{},[408,409,410],"blockquote",{},[293,411,412],{},"Machine-generated structured context below.",[414,415,417],"h3",{"id":416},"summary","Summary",[293,419,420,421,426,427,429,430,433,434,437,438,440,441,444],{},"PR ",[297,422,425],{"href":423,"rel":424},"https:\u002F\u002Fgithub.com\u002Fstudyflash-ai\u002Fstudyflash\u002Fpull\u002F1539",[301],"#1539"," (\"fix: onboarding data integrity\", merged March 3) removed ",[313,428,315],{}," from the portal onboarding form to implement per-slide validation. This inadvertently disabled the Zod ",[313,431,432],{},".transform()"," that derives ",[313,435,436],{},"currency"," (and other fields) from user input. Since the profile table defaults ",[313,439,436],{}," to ",[313,442,443],{},"CHF",", ~99% of web signups in DE\u002FNL (and likely all non-Swiss markets) have been assigned the wrong checkout currency for 13 days.",[414,446,448],{"id":447},"impact","Impact",[389,450,451,465,468,478,506],{},[392,452,453,456,457,460,461,464],{},[320,454,455],{},"4,248 confirmed"," DE\u002FNL web signups received ",[313,458,459],{},"currency = CHF"," instead of ",[313,462,463],{},"EUR"," since March 3.",[392,466,467],{},"These users were charged Swiss prices at Stripe checkout (e.g. 16 CHF\u002Fmonth instead of 12 EUR\u002Fmonth).",[392,469,470,473,474,477],{},[320,471,472],{},"Pricing experiment contaminated:"," 32 out of 186 conversions (17%) in the DE\u002FNL experiment period were charged in CHF. Their PostHog ",[313,475,476],{},"revenue_in_eur"," property records the EUR list price, not what they actually paid.",[392,479,480,483,484],{},[320,481,482],{},"All Zod transforms broken",", not just currency:\n",[389,485,486,500],{},[392,487,488,491,492,495,496,499],{},[313,489,490],{},"subject_of_studies"," normalization (",[313,493,494],{},"economics"," → ",[313,497,498],{},"business",") stopped running.",[392,501,502,505],{},[313,503,504],{},"newsletter_opt_in"," override for Apple\u002FGoogle OAuth users stopped running.",[392,507,508],{},"Scope extends beyond DE\u002FNL — any non-Swiss, non-USD web signup in this period got CHF.",[414,510,512],{"id":511},"root-causes","Root Causes",[293,514,515,516,518,519,521,522,525,526,528,529,531,532,535,536,538,539,542],{},"The onboarding form used vee-validate's ",[313,517,315],{}," to submit the form. ",[313,520,315],{}," internally runs Zod validation ",[320,523,524],{},"and"," the ",[313,527,432],{}," chain, then passes the transformed output to the callback. The PR replaced ",[313,530,315],{}," with a manual function that reads the reactive ",[313,533,534],{},"values"," object directly. In vee-validate, ",[313,537,534],{}," contains raw form input — not Zod-transformed output. The transform that computes ",[313,540,541],{},"currency = countryToCurrency(country)"," became dead code.",[293,544,545,546,549,550,553,554,557,558,560],{},"Because Supabase's ",[313,547,548],{},".update()"," ignores ",[313,551,552],{},"undefined"," fields, the ",[313,555,556],{},"currency: undefined"," in the payload simply didn't touch the column, leaving the DB default (",[313,559,443],{},") intact.",[414,562,564],{"id":563},"trigger","Trigger",[293,566,567,571],{},[297,568,570],{"href":423,"rel":569},[301],"PR #1539"," merged March 3, 2026 at 10:58 CET.",[414,573,575],{"id":574},"resolution","Resolution",[577,578,579,599],"ol",{},[392,580,581,584,585,590,591,594,595,598],{},[320,582,583],{},"Code fix:"," ",[297,586,589],{"href":587,"rel":588},"https:\u002F\u002Fgithub.com\u002Fstudyflash-ai\u002Fstudyflash\u002Fpull\u002F1590",[301],"PR #1590"," — calls ",[313,592,593],{},"schema.parse(values)"," before ",[313,596,597],{},"updateProfile"," so the Zod transform runs. Merged 2026-03-16 09:28 UTC.",[392,600,601,584,604,609,610,613],{},[320,602,603],{},"Currency mapping inverted:",[297,605,608],{"href":606,"rel":607},"https:\u002F\u002Fgithub.com\u002Fstudyflash-ai\u002Fstudyflash\u002Fpull\u002F1594",[301],"PR #1594"," — ",[313,611,612],{},"countryToCurrency()"," now lists CHF countries (Switzerland only) and defaults to EUR, instead of listing EUR countries and defaulting to CHF. Merged 2026-03-16 14:37 UTC.",[414,615,617],{"id":616},"detection","Detection",[293,619,620,621,624,625,627],{},"Discovered during a pricing experiment data integrity audit. Cross-referencing PostHog ",[313,622,623],{},"subscription:create"," events with Stripe transactions in Metabase revealed 57 DE\u002FNL conversions charged in CHF instead of EUR. Tracing back to the profile ",[313,626,436],{}," field exposed the regression.",[305,629,631],{"id":630},"action-items","Action Items",[324,633,634,650],{},[327,635,636],{},[330,637,638,641,644,647],{},[333,639,640],{},"Action",[333,642,643],{},"Type",[333,645,646],{},"Owner",[333,648,649],{},"Status",[340,651,652,670,685,697,709,721],{},[330,653,654,662,665,667],{},[345,655,656,657,661],{},"Restore Zod transform on final submit (",[297,658,660],{"href":587,"rel":659},[301],"#1590",")",[345,663,664],{},"fix",[345,666],{},[345,668,669],{},"Done",[330,671,672,679,681,683],{},[345,673,674,675,661],{},"Invert currency mapping to default EUR (",[297,676,678],{"href":606,"rel":677},[301],"#1594",[345,680,664],{},[345,682],{},[345,684,669],{},[330,686,687,690,692,694],{},[345,688,689],{},"Re-run Zod transforms for other broken fields (subject, newsletter)",[345,691,664],{},[345,693],{},[345,695,696],{},"TODO",[330,698,699,702,705,707],{},[345,700,701],{},"Add monitoring\u002Falert on currency distribution anomalies",[345,703,704],{},"prevent",[345,706],{},[345,708,696],{},[330,710,711,714,717,719],{},[345,712,713],{},"Assess financial impact and determine if price-difference refunds are needed",[345,715,716],{},"mitigate",[345,718],{},[345,720,696],{},[330,722,723,726,728,730],{},[345,724,725],{},"Clean up experiment data (exclude CHF-currency conversions or annotate)",[345,727,716],{},[345,729],{},[345,731,696],{},[305,733,735],{"id":734},"lessons-learned","Lessons Learned",[414,737,739],{"id":738},"what-went-well","What went well",[389,741,742,745],{},[392,743,744],{},"The pricing experiment audit caught this before experiment results were presented.",[392,746,747],{},"The Zod transform logic itself was correct — it just wasn't being called.",[414,749,751],{"id":750},"what-went-wrong","What went wrong",[389,753,754,759,762,768],{},[392,755,756,758],{},[313,757,315],{}," was removed without understanding it was the only thing triggering the Zod transform.",[392,760,761],{},"CodeRabbit's auto-generated summary (\"Currency now auto-selects based on chosen country\") was misleading — it described the existing transform as if it were a new feature, masking that it was actually broken.",[392,763,764,765,767],{},"This wasn't realistically preventable by a test — the failure was silent (no error, no crash). A test would only catch it if someone specifically asserted \"currency must not be undefined in the update payload\", which is an unlikely test to write. Stricter AI code review could have flagged that removing ",[313,766,315],{}," would break the transform chain.",[392,769,770],{},"No monitoring on the currency distribution of new signups. A simple alert on \"% of DE\u002FNL signups with CHF\" would have caught this on day one.",[414,772,774],{"id":773},"where-we-got-lucky","Where we got lucky",[389,776,777,780,787],{},[392,778,779],{},"The bug was discovered 13 days in, not months later.",[392,781,782,783,786],{},"The affected users can be precisely identified via ",[313,784,785],{},"_created >= '2026-03-03' AND signup_via = 'web'",".",[392,788,789,790,793],{},"The data migration to correct currency is straightforward since ",[313,791,792],{},"country"," was saved correctly.",[305,795,797],{"id":796},"timeline","Timeline",[324,799,800,810],{},[327,801,802],{},[330,803,804,807],{},[333,805,806],{},"Time (CET)",[333,808,809],{},"Event",[340,811,812,820,828,836,844,853,861],{},[330,813,814,817],{},[345,815,816],{},"2026-03-03 10:58",[345,818,819],{},"PR #1539 merged to main",[330,821,822,825],{},[345,823,824],{},"2026-03-03 ~11:00",[345,826,827],{},"Regression goes live — web onboarding stops computing currency from country",[330,829,830,833],{},[345,831,832],{},"2026-03-03 → 03-16",[345,834,835],{},"~4,248 DE\u002FNL web signups (and unknown number of other non-CH signups) receive CHF currency",[330,837,838,841],{},[345,839,840],{},"2026-03-16",[345,842,843],{},"Discovered during pricing experiment data audit — Stripe transactions in CHF for DE\u002FNL users flagged",[330,845,846,848],{},[345,847,840],{},[345,849,850,851],{},"Root cause traced to PR #1539 removing ",[313,852,315],{},[330,854,855,858],{},[345,856,857],{},"2026-03-16 09:28",[345,859,860],{},"Fix merged: PR #1590 restores Zod transform on submit",[330,862,863,866],{},[345,864,865],{},"2026-03-16 14:37",[345,867,868],{},"Fix merged: PR #1594 inverts currency mapping to default EUR",[305,870,872],{"id":871},"supporting-information","Supporting information",[389,874,875,881,887,893,903],{},[392,876,877,878],{},"PR: ",[297,879,423],{"href":423,"rel":880},[301],[392,882,883,884],{},"Commit: ",[313,885,886],{},"6a469a89a",[392,888,889,890],{},"Affected file: ",[313,891,892],{},"apps\u002Fportal\u002Fapp\u002Fpages\u002Fonboarding\u002Findex.vue",[392,894,895,896,899,900],{},"DB default: ",[313,897,898],{},"profiles.currency"," column defaults to ",[313,901,902],{},"'CHF'::text",[392,904,905,906,908,909,912],{},"The ",[313,907,612],{}," function in ",[313,910,911],{},"packages\u002Fcommon\u002Fsrc\u002Fmappers.ts"," is correct — it was just never invoked.",[414,914,916],{"id":915},"verification-query","Verification query",[918,919,924],"pre",{"className":920,"code":921,"language":922,"meta":923,"style":923},"language-sql shiki shiki-themes github-light github-dark","SELECT country, currency, count(*)\nFROM profiles\nWHERE country IN ('Germany', 'Netherlands')\n  AND _created >= '2026-03-03'\n  AND signup_via = 'web'\nGROUP BY country, currency;\n-- Before fix: ~99% CHF\n-- After fix: should show 0 CHF rows\n","sql","",[313,925,926,934,940,946,952,958,964,970],{"__ignoreMap":923},[927,928,931],"span",{"class":929,"line":930},"line",1,[927,932,933],{},"SELECT country, currency, count(*)\n",[927,935,937],{"class":929,"line":936},2,[927,938,939],{},"FROM profiles\n",[927,941,943],{"class":929,"line":942},3,[927,944,945],{},"WHERE country IN ('Germany', 'Netherlands')\n",[927,947,949],{"class":929,"line":948},4,[927,950,951],{},"  AND _created >= '2026-03-03'\n",[927,953,955],{"class":929,"line":954},5,[927,956,957],{},"  AND signup_via = 'web'\n",[927,959,961],{"class":929,"line":960},6,[927,962,963],{},"GROUP BY country, currency;\n",[927,965,967],{"class":929,"line":966},7,[927,968,969],{},"-- Before fix: ~99% CHF\n",[927,971,973],{"class":929,"line":972},8,[927,974,975],{},"-- After fix: should show 0 CHF rows\n",[977,978,979],"style",{},"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":923,"searchDepth":936,"depth":936,"links":981},[982,990,991,996,997],{"id":307,"depth":936,"text":308,"children":983},[984,985,986,987,988,989],{"id":416,"depth":942,"text":417},{"id":447,"depth":942,"text":448},{"id":511,"depth":942,"text":512},{"id":563,"depth":942,"text":564},{"id":574,"depth":942,"text":575},{"id":616,"depth":942,"text":617},{"id":630,"depth":936,"text":631},{"id":734,"depth":936,"text":735,"children":992},[993,994,995],{"id":738,"depth":942,"text":739},{"id":750,"depth":942,"text":751},{"id":773,"depth":942,"text":774},{"id":796,"depth":936,"text":797},{"id":871,"depth":936,"text":872,"children":998},[999],{"id":915,"depth":942,"text":916},"2026-03-16 | Rajiv | Resolved","md",null,{},true,{"title":39,"description":1000},"postmortems\u002F2026-03-16_onboarding-currency-regression","sGfiSUP4v8W6sphMZ7iNuvUqlATQkT8L9XvMz7UmxhE",1779007962946]