<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: TAMSIV</title>
    <description>The latest articles on DEV Community by TAMSIV (@tamsiv).</description>
    <link>https://dev.to/tamsiv</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F3827379%2Fd9b80ef9-67e4-459a-bfc5-ac74e682badb.png</url>
      <title>DEV Community: TAMSIV</title>
      <link>https://dev.to/tamsiv</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/tamsiv"/>
    <language>en</language>
    <item>
      <title>From Idea to Production in 6 Months — A Solo Developer's Journey</title>
      <dc:creator>TAMSIV</dc:creator>
      <pubDate>Sat, 04 Apr 2026 07:04:27 +0000</pubDate>
      <link>https://dev.to/tamsiv/from-idea-to-production-in-6-months-a-solo-developers-journey-d2d</link>
      <guid>https://dev.to/tamsiv/from-idea-to-production-in-6-months-a-solo-developers-journey-d2d</guid>
      <description>&lt;p&gt;Six months ago, I had a sticky note problem.&lt;/p&gt;

&lt;p&gt;My wife would add "kid's shoes" to the grocery list while I was driving. I couldn't write anything down without stopping, unlocking my phone, opening an app, typing... So I opened VS Code. And didn't close it for 6 months.&lt;/p&gt;

&lt;p&gt;Today, &lt;strong&gt;TAMSIV is live on Google Play Store&lt;/strong&gt;. In production. For everyone.&lt;/p&gt;

&lt;h2&gt;
  
  
  What is TAMSIV?
&lt;/h2&gt;

&lt;p&gt;A voice-powered task and memo manager with conversational AI. Press the mic, speak naturally, and the AI creates tasks, memos, or calendar events — organized in the right folder, with the right priority, at the right time.&lt;/p&gt;

&lt;h2&gt;
  
  
  The stack
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Frontend&lt;/strong&gt;: React Native 0.81 (TypeScript, New Architecture / Fabric)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Backend&lt;/strong&gt;: Node.js/Express + WebSocket, port 3001&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Database&lt;/strong&gt;: Supabase PostgreSQL — 3 schemas (&lt;code&gt;privat&lt;/code&gt;, &lt;code&gt;collaborative&lt;/code&gt;, &lt;code&gt;gamification&lt;/code&gt;), 30+ tables, 40+ RLS policies&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;AI&lt;/strong&gt;: OpenRouter (400+ models), native on-device STT, OpenAI TTS (6 voices)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Website&lt;/strong&gt;: Next.js 16 + Tailwind CSS 4 on Vercel&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Payments&lt;/strong&gt;: RevenueCat (Free / Pro / Team)&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  750+ commits, 25 features shipped
&lt;/h2&gt;

&lt;p&gt;Here's what made it to production:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;🎙️ Conversational voice AI with 8 function tools&lt;/li&gt;
&lt;li&gt;📋 Tasks and memos with photos, videos, PDFs&lt;/li&gt;
&lt;li&gt;📅 Calendar with 5 views, recurrence, smart reminders&lt;/li&gt;
&lt;li&gt;👥 Real-time collaboration (groups, assignments, checklists)&lt;/li&gt;
&lt;li&gt;📁 Unlimited hierarchical folders&lt;/li&gt;
&lt;li&gt;🏆 Gamification — 12 levels, 10 badges, streaks up to 365 days&lt;/li&gt;
&lt;li&gt;🌍 6 languages (FR, EN, DE, ES, IT, PT)&lt;/li&gt;
&lt;li&gt;🖥️ Full web companion app at tamsiv.com&lt;/li&gt;
&lt;li&gt;🎨 AI-generated images (Runware HiDream)&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  The bugs that almost killed launch
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;6 hooks DDoSing my own database on startup.&lt;/strong&gt; Six independent React hooks calling &lt;code&gt;getGroups()&lt;/code&gt; concurrently. Six identical Supabase queries saturating the connection pool. Fix: one shared &lt;code&gt;GroupsContext&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;AI inventing impossible dates.&lt;/strong&gt; February 29 on non-leap years, "Monday the 15th" that was actually a Wednesday. Fix: server-side &lt;code&gt;validateAndFixDate()&lt;/code&gt; + pre-computed day tables in the prompt.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;WebSocket callback conflicts.&lt;/strong&gt; The AI setup screen and dictaphone fighting for the same channel. 3 hours to isolate.&lt;/p&gt;

&lt;h2&gt;
  
  
  i18n as an acquisition channel
&lt;/h2&gt;

&lt;p&gt;Before launch, I translated everything into 6 languages. Result: &lt;strong&gt;60% of website visitors don't speak French.&lt;/strong&gt; Germany is the 3rd largest market — without a single German social post. Every language is a door.&lt;/p&gt;

&lt;h2&gt;
  
  
  What's next
&lt;/h2&gt;

&lt;p&gt;TAMSIV is free — all core features work without paying. iOS is planned.&lt;/p&gt;

&lt;p&gt;If you're a developer, the technical decisions are real, the bugs are real, and the code is production code.&lt;/p&gt;

&lt;p&gt;If you need a voice-first task manager, give it a try and break things.&lt;/p&gt;

&lt;p&gt;📱 &lt;a href="https://play.google.com/store/apps/details?id=com.tamsiv" rel="noopener noreferrer"&gt;Download on Google Play&lt;/a&gt;&lt;br&gt;
🌐 &lt;a href="https://www.tamsiv.com" rel="noopener noreferrer"&gt;Website&lt;/a&gt;&lt;br&gt;
▶️ &lt;a href="https://www.youtube.com/watch?v=cHlnD54DBTI" rel="noopener noreferrer"&gt;Demo video&lt;/a&gt;&lt;/p&gt;




&lt;p&gt;&lt;em&gt;Built solo. 750+ commits. October 2025 → April 2026.&lt;/em&gt;&lt;/p&gt;

</description>
      <category>buildinpublic</category>
      <category>android</category>
      <category>productivity</category>
      <category>ai</category>
    </item>
    <item>
      <title>38 Commits, Zero New Features — How I Made My Web App Production-Ready</title>
      <dc:creator>TAMSIV</dc:creator>
      <pubDate>Wed, 01 Apr 2026 08:07:32 +0000</pubDate>
      <link>https://dev.to/tamsiv/38-commits-zero-new-features-how-i-made-my-web-app-production-ready-4gip</link>
      <guid>https://dev.to/tamsiv/38-commits-zero-new-features-how-i-made-my-web-app-production-ready-4gip</guid>
      <description>&lt;p&gt;Sometimes the most important sprints are the ones where nothing new gets shipped.&lt;/p&gt;

&lt;p&gt;38 commits in 10 days. Zero new features. But TAMSIV's web dashboard went from "it works" to "it's ready."&lt;/p&gt;

&lt;h2&gt;
  
  
  The Agenda Gets Real
&lt;/h2&gt;

&lt;p&gt;The web agenda had one view — week. Functional, but limiting. Now it has four: &lt;strong&gt;day, week, month, year&lt;/strong&gt;. Click any event, see its details. Click any task, same thing. Task and memo detail pages were completely redesigned to match the mobile app.&lt;/p&gt;

&lt;p&gt;The goal: make the transition between phone and desktop invisible. You create a task by voice on your phone, you find it on your computer with the same layout.&lt;/p&gt;

&lt;h2&gt;
  
  
  Safety Nets: Crashlytics + Sentry
&lt;/h2&gt;

&lt;p&gt;When your app is used by you and 12 testers, you can debug via Supabase logs and "works on my machine." When you're about to go public, that's not enough.&lt;/p&gt;

&lt;p&gt;Two monitoring systems were added:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Firebase Crashlytics&lt;/strong&gt; on the React Native frontend — catches crashes, ANRs, uncaught JS errors, with full stack traces&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Sentry&lt;/strong&gt; on the Node.js/Express backend — catches API errors, WebSocket timeouts, unhandled exceptions, with breadcrumbs and performance monitoring&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;When a bug happens in production, we know before the user complains.&lt;/p&gt;

&lt;h2&gt;
  
  
  The AI Learns Your Naming Habits
&lt;/h2&gt;

&lt;p&gt;One commit. But the kind that changes daily experience.&lt;/p&gt;

&lt;p&gt;The voice assistant now analyzes your existing folder names to detect &lt;strong&gt;naming patterns&lt;/strong&gt;. If all your grocery folders start with the store name ("Groceries Carrefour", "Groceries Leclerc"), the AI picks it up and suggests the same pattern for new folders.&lt;/p&gt;

&lt;p&gt;No user will ever ask for this. But everyone notices when it's there.&lt;/p&gt;

&lt;h2&gt;
  
  
  Performance &amp;amp; CRO
&lt;/h2&gt;

&lt;p&gt;The landing page hero used a JavaScript Canvas for its animated glow effect. Beautiful, but heavy — especially on mobile. Replaced with &lt;strong&gt;pure CSS&lt;/strong&gt;. Same visual, zero battery drain.&lt;/p&gt;

&lt;p&gt;Other landing page fixes: hero subtitle rewritten for clarity, pricing layout improved, header scroll spy fixed.&lt;/p&gt;

&lt;h2&gt;
  
  
  Smarter Tracking
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;UTM parameters&lt;/strong&gt; on every shared link — to know which post, channel, campaign drives traffic&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Server-side IP capture&lt;/strong&gt; — more reliable than client-side JavaScript&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Admin dashboard&lt;/strong&gt; — period selector (7d/30d/90d/all), config synced between mobile and web&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  versionCode 32
&lt;/h2&gt;

&lt;p&gt;Build 32 on the Play Store. 740+ commits. Production review pending at Google. We polish, we wait, we polish again.&lt;/p&gt;

&lt;p&gt;38 commits, zero features, and an app that went from "it works" to "it's ready."&lt;/p&gt;




&lt;p&gt;📖 Full article on the blog: &lt;a href="https://tamsiv.com/en/blog/web-app-agenda-monitoring-production" rel="noopener noreferrer"&gt;tamsiv.com/en/blog&lt;/a&gt;&lt;br&gt;
📱 Try it: &lt;a href="https://play.google.com/store/apps/details?id=com.tamsiv" rel="noopener noreferrer"&gt;Play Store&lt;/a&gt;&lt;/p&gt;

</description>
      <category>buildinpublic</category>
      <category>webdev</category>
      <category>reactnative</category>
      <category>productivity</category>
    </item>
    <item>
      <title>I Translated My App Into 6 Languages Before Launch. Here's What the Data Says.</title>
      <dc:creator>TAMSIV</dc:creator>
      <pubDate>Tue, 31 Mar 2026 08:58:54 +0000</pubDate>
      <link>https://dev.to/tamsiv/i-translated-my-app-into-6-languages-before-launch-heres-what-the-data-says-3lp7</link>
      <guid>https://dev.to/tamsiv/i-translated-my-app-into-6-languages-before-launch-heres-what-the-data-says-3lp7</guid>
      <description>&lt;p&gt;Everyone told me I was crazy.&lt;/p&gt;

&lt;p&gt;"You haven't even launched in France yet, and you're translating into German?"&lt;/p&gt;

&lt;p&gt;I did it anyway. Here's what happened.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Decision
&lt;/h2&gt;

&lt;p&gt;TAMSIV is a voice-powered AI task manager I've been building solo for 6 months (730+ commits). When I hit the i18n milestone, I had a choice: launch in French first and "see how it goes," or go all-in on 6 languages from day one.&lt;/p&gt;

&lt;p&gt;I chose the second option. I wrote an AI translation script that processes 1,993 keys across 5 target languages (English, German, Spanish, Italian, Portuguese) using OpenRouter. I localized the Play Store listing. The website. Even the voice assistant understands context in multiple languages.&lt;/p&gt;

&lt;p&gt;Total investment: about 2 days of work.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Data (41 days of tracking)
&lt;/h2&gt;

&lt;p&gt;Here's what tamsiv.com analytics show after 41 days:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;2,206 unique visitors&lt;/strong&gt; total&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;1,142 unique visitors&lt;/strong&gt; in the last 7 days alone&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;But the number that stopped me:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;60% of visitors don't speak French.&lt;/strong&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Language breakdown (last 7 days)
&lt;/h3&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Language&lt;/th&gt;
&lt;th&gt;% of traffic&lt;/th&gt;
&lt;th&gt;Unique visitors&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;🇫🇷 French&lt;/td&gt;
&lt;td&gt;40%&lt;/td&gt;
&lt;td&gt;440&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;🇬🇧 English&lt;/td&gt;
&lt;td&gt;20%&lt;/td&gt;
&lt;td&gt;226&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;🇩🇪 German&lt;/td&gt;
&lt;td&gt;13%&lt;/td&gt;
&lt;td&gt;150&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;🇪🇸 Spanish&lt;/td&gt;
&lt;td&gt;10%&lt;/td&gt;
&lt;td&gt;118&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;🇮🇹 Italian&lt;/td&gt;
&lt;td&gt;9%&lt;/td&gt;
&lt;td&gt;113&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;🇵🇹 Portuguese&lt;/td&gt;
&lt;td&gt;8%&lt;/td&gt;
&lt;td&gt;98&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;&lt;strong&gt;Germany is the third largest market.&lt;/strong&gt; I never wrote a single German social media post. Zero German marketing. These visitors found TAMSIV through:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Localized Play Store listings&lt;/strong&gt; — each language has its own title, description, and screenshots&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Google searches in their language&lt;/strong&gt; — the website auto-detects and serves the right locale&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Automatic language routing&lt;/strong&gt; — tamsiv.com detects browser language and redirects&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  The Technical Setup
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Translation script
&lt;/h3&gt;

&lt;p&gt;I use OpenRouter with a configurable model to translate JSON key files:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// Pseudo-code of the translation pipeline&lt;/span&gt;
&lt;span class="k"&gt;for &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;locale&lt;/span&gt; &lt;span class="k"&gt;of&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;en&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;de&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;es&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;it&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;pt&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;translated&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;openrouter&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;chat&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
    &lt;span class="na"&gt;model&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;process&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;env&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;OPENROUTER_MODEL&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;messages&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[{&lt;/span&gt;
      &lt;span class="na"&gt;role&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;system&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="na"&gt;content&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;`Translate to &lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;locale&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;. Keep placeholders like {name} intact. Adapt idioms naturally.`&lt;/span&gt;
    &lt;span class="p"&gt;},&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="na"&gt;role&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;user&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; 
      &lt;span class="na"&gt;content&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;JSON&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;stringify&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;frenchKeys&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;}]&lt;/span&gt;
  &lt;span class="p"&gt;});&lt;/span&gt;
  &lt;span class="nf"&gt;writeFile&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;`locales/&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;locale&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;.json`&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;translated&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Key decisions:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;French as source language&lt;/strong&gt; (not English) — my native language, so the source is always natural&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;AI translation with human review&lt;/strong&gt; — I review German/Spanish/Italian with native speakers when possible&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;9 passes per language&lt;/strong&gt; — the script runs multiple passes to catch context-dependent translations&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Placeholder preservation&lt;/strong&gt; — &lt;code&gt;{taskCount} tâches&lt;/code&gt; must become &lt;code&gt;{taskCount} tasks&lt;/code&gt;, not &lt;code&gt;{taskCount} Aufgaben tasks&lt;/code&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Play Store localization
&lt;/h3&gt;

&lt;p&gt;The Play Store supports per-language metadata. I localized:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;App title (30 chars)&lt;/li&gt;
&lt;li&gt;Short description (80 chars)
&lt;/li&gt;
&lt;li&gt;Full description (4000 chars)&lt;/li&gt;
&lt;li&gt;Release notes&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This is probably the highest-ROI i18n work. People search the Play Store in their language. If your listing isn't localized, you're invisible.&lt;/p&gt;

&lt;h3&gt;
  
  
  Website (Next.js + next-intl)
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;/fr/  → French (default)
/en/  → English
/de/  → German
/es/  → Spanish
/it/  → Italian
/pt/  → Portuguese
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Each locale has its own translated slugs, meta descriptions, and Open Graph tags. The &lt;code&gt;x-default&lt;/code&gt; hreflang points to &lt;code&gt;/fr/&lt;/code&gt; (primary market).&lt;/p&gt;

&lt;h2&gt;
  
  
  The Surprising Insight
&lt;/h2&gt;

&lt;p&gt;The most visited non-homepage pages are:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;/fr/politique-de-confidentialite&lt;/code&gt; (165 visits)&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;/fr/suppression-compte&lt;/code&gt; (92 visits)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;These are &lt;strong&gt;Play Store required pages&lt;/strong&gt; (privacy policy, account deletion). Their high traffic confirms that people are finding TAMSIV through the Play Store, clicking these links, and landing on the website. The Play Store localization is driving real traffic.&lt;/p&gt;

&lt;h2&gt;
  
  
  Another Data Point
&lt;/h2&gt;

&lt;p&gt;12 people clicked "Download for iOS." The app is Android-only.&lt;/p&gt;

&lt;p&gt;12 strangers, somewhere in the world, want TAMSIV on iPhone. That's not a metric — that's a roadmap.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Lesson
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;i18n is not polish you add after launch. It's an acquisition channel.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Every language is a door. I spent 2 days opening 5 extra doors, and 60% of my traffic walks through them.&lt;/p&gt;

&lt;p&gt;If you're building a side project and thinking "I'll translate later" — consider translating now. The Play Store alone makes it worth it. And with AI translation tools, the cost is nearly zero.&lt;/p&gt;

&lt;h2&gt;
  
  
  What's Next
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Production release is pending (waiting for Google Play review)&lt;/li&gt;
&lt;li&gt;iOS version is now on the roadmap (thanks to those 12 clicks)&lt;/li&gt;
&lt;li&gt;German-language content might be worth creating (13% is significant)&lt;/li&gt;
&lt;/ul&gt;




&lt;p&gt;TAMSIV is a voice-powered AI task manager — speak your tasks, memos, and calendar events instead of typing them. Free on Android.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;🌐 &lt;a href="https://www.tamsiv.com" rel="noopener noreferrer"&gt;tamsiv.com&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;📱 &lt;a href="https://play.google.com/store/apps/details?id=com.tamsiv" rel="noopener noreferrer"&gt;Play Store&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>i18n</category>
      <category>buildinpublic</category>
      <category>android</category>
      <category>productivity</category>
    </item>
    <item>
      <title>How I Added Voice-Based AI Personalization Two Days Before Launch</title>
      <dc:creator>TAMSIV</dc:creator>
      <pubDate>Sun, 29 Mar 2026 08:47:35 +0000</pubDate>
      <link>https://dev.to/tamsiv/how-i-added-voice-based-ai-personalization-two-days-before-launch-20af</link>
      <guid>https://dev.to/tamsiv/how-i-added-voice-based-ai-personalization-two-days-before-launch-20af</guid>
      <description>&lt;p&gt;Since October, TAMSIV's voice AI understood commands perfectly. "Create a task", "add a memo", "check my schedule" — all worked. But the AI didn't &lt;em&gt;know&lt;/em&gt; the user.&lt;/p&gt;

&lt;p&gt;Say "remind me about the thing for the kids" and it had no idea you had kids.&lt;/p&gt;

&lt;p&gt;Two days before public launch, I shipped voice-based AI personalization.&lt;/p&gt;

&lt;h2&gt;
  
  
  Configure Your AI by Talking to It
&lt;/h2&gt;

&lt;p&gt;A new screen: "Configure my AI". You tap, you talk:&lt;/p&gt;

&lt;p&gt;&lt;em&gt;"I'm a parent of 3, I manage a cleaning crew of 4, and I forget everything after 30 seconds."&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;The AI listens, summarizes your context, stores it. From that point on, every response is tailored. "The little one" means your son. "Tuesday's site" means your field intervention.&lt;/p&gt;

&lt;p&gt;The implementation: the custom context is sent as part of the system prompt to OpenRouter. The LLM sees it on every conversation turn. Simple, but the UX around it — the onboarding flow, the voice recording, the save/edit/delete — that's where the 1,800 lines went.&lt;/p&gt;

&lt;h2&gt;
  
  
  6 TTS Voices with Live Preview
&lt;/h2&gt;

&lt;p&gt;Voice is personal. The same robotic voice 50 times a day gets old.&lt;/p&gt;

&lt;p&gt;I added a voice selector: 6 OpenAI TTS voices, each with a live preview button. Tap → hear "Hello, I'm your TAMSIV assistant" in that voice → choose. Saved to the user profile, used for all responses.&lt;/p&gt;

&lt;p&gt;The tricky part: the preview uses the same WebSocket pipeline as the main dictaphone. Which led to...&lt;/p&gt;

&lt;h2&gt;
  
  
  The 3-Hour WebSocket Bug
&lt;/h2&gt;

&lt;p&gt;The AI setup screen and the dictaphone share one WebSocket connection. When you preview a voice in setup, the dictaphone's callbacks also fired — processing audio meant for the setup screen.&lt;/p&gt;

&lt;p&gt;Fix: an "active owner" pattern. When setup opens, it claims exclusive WebSocket ownership. The dictaphone's callbacks become no-ops until setup closes. Clean in theory, 3 hours of debugging because the conflict only appeared on specific Android models with aggressive garbage collection.&lt;/p&gt;

&lt;h2&gt;
  
  
  Personalization as a Feature Gate
&lt;/h2&gt;

&lt;p&gt;AI personalization is now part of the Pro/Team plans in the pricing. Free users get basic context, paid users get full customization + voice selection. Added to the pricing page on the website too.&lt;/p&gt;

&lt;h2&gt;
  
  
  730+ Commits, Monday Is Launch Day
&lt;/h2&gt;

&lt;p&gt;This was the last feature. The app now knows who it's talking to.&lt;/p&gt;

&lt;p&gt;Monday, TAMSIV goes public on the Play Store. 6 months of solo development, and the AI finally has context.&lt;/p&gt;




&lt;p&gt;&lt;em&gt;TAMSIV is a voice-powered task and memo manager with AI. &lt;a href="https://play.google.com/store/apps/details?id=com.tamsiv" rel="noopener noreferrer"&gt;Try it on Android&lt;/a&gt; or visit &lt;a href="https://www.tamsiv.com" rel="noopener noreferrer"&gt;tamsiv.com&lt;/a&gt;.&lt;/em&gt;&lt;/p&gt;

</description>
      <category>ai</category>
      <category>reactnative</category>
      <category>android</category>
      <category>buildinpublic</category>
    </item>
    <item>
      <title>Vanishing Reminders and Missing Emails — The Never-Ending Quality Sprint</title>
      <dc:creator>TAMSIV</dc:creator>
      <pubDate>Thu, 26 Mar 2026 09:16:49 +0000</pubDate>
      <link>https://dev.to/tamsiv/vanishing-reminders-and-missing-emails-the-never-ending-quality-sprint-c2i</link>
      <guid>https://dev.to/tamsiv/vanishing-reminders-and-missing-emails-the-never-ending-quality-sprint-c2i</guid>
      <description>&lt;p&gt;When you're building an app solo, some weeks you ship shiny new features. Other weeks, you spend your evenings hunting bugs nobody reported — because you found them yourself while dogfooding your own app. This was the second kind of week on &lt;a href="https://tamsiv.com" rel="noopener noreferrer"&gt;TAMSIV&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Here's a rundown of four fixes that look small on their own. But stacked together, they genuinely improve the daily experience.&lt;/p&gt;

&lt;h2&gt;
  
  
  Reminders That Vanished Into Thin Air
&lt;/h2&gt;

&lt;p&gt;The scenario: you create a task with three reminders — one in 10 minutes, one tomorrow morning, one on Friday. But the first reminder is already in the past (you took too long to confirm). TAMSIV would display a warning: "This reminder is in the past." Fair enough.&lt;/p&gt;

&lt;p&gt;The problem? Dismissing that warning &lt;strong&gt;wiped all reminders&lt;/strong&gt;. The two perfectly valid future reminders were gone too. An overzealous cleanup in the validation logic.&lt;/p&gt;

&lt;p&gt;The fix now clearly separates past reminders from future ones. The warning only targets what's actually expired, and upcoming reminders stay untouched.&lt;/p&gt;

&lt;h2&gt;
  
  
  Email Reminders That Never Arrived
&lt;/h2&gt;

&lt;p&gt;TAMSIV supports two notification channels for reminders: push and email. In practice, when a user created a reminder, only the push channel was enabled by default. Email? Silently disabled.&lt;/p&gt;

&lt;p&gt;Now both channels are enabled by default. You get a push &lt;strong&gt;and&lt;/strong&gt; an email.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Ghost Error on Cold Start
&lt;/h2&gt;

&lt;p&gt;You open the app after a few hours of inactivity, and for a split second an "Invalid Refresh Token" error flashes on screen. Then everything works fine.&lt;/p&gt;

&lt;p&gt;On cold start, Supabase tried to refresh the auth token. If it had expired, the SDK threw an error before the automatic reconnection mechanism had time to kick in. The error bubbled up to the UI when it had no business being there.&lt;/p&gt;

&lt;p&gt;The fix intercepts this specific error at the right level and suppresses it from the display.&lt;/p&gt;

&lt;h2&gt;
  
  
  Badge Guide Translated Into 6 Languages
&lt;/h2&gt;

&lt;p&gt;TAMSIV is available in French, English, German, Spanish, Italian, and Portuguese. But the badge explainer guide only existed in French and English. Fixed — all 10 badges are now translated into all 6 languages.&lt;/p&gt;

&lt;h2&gt;
  
  
  What's Next?
&lt;/h2&gt;

&lt;p&gt;These four fixes represent roughly a day's work. None of them would make for an exciting changelog headline. But together, they eliminate real friction.&lt;/p&gt;

&lt;p&gt;The quality sprint continues. 720+ commits and counting.&lt;/p&gt;




&lt;p&gt;📖 &lt;a href="https://tamsiv.com/en/blog/vanishing-reminders-missing-emails-quality-sprint" rel="noopener noreferrer"&gt;Full article on the blog&lt;/a&gt;&lt;br&gt;
📖 &lt;a href="https://tamsiv.com/en/blog/productivity-app-fatigue-voice-first-cure" rel="noopener noreferrer"&gt;New: Productivity App Fatigue&lt;/a&gt;&lt;br&gt;
📲 &lt;a href="https://play.google.com/store/apps/details?id=com.tamsiv" rel="noopener noreferrer"&gt;Try TAMSIV on Android&lt;/a&gt;&lt;/p&gt;

</description>
      <category>buildinpublic</category>
      <category>reactnativeandroidproductivity</category>
    </item>
    <item>
      <title>TAMSIV Has a Face — First Demo Video + Quality Sprint</title>
      <dc:creator>TAMSIV</dc:creator>
      <pubDate>Wed, 25 Mar 2026 13:16:07 +0000</pubDate>
      <link>https://dev.to/tamsiv/tamsiv-has-a-face-first-demo-video-quality-sprint-m9e</link>
      <guid>https://dev.to/tamsiv/tamsiv-has-a-face-first-demo-video-quality-sprint-m9e</guid>
      <description>&lt;p&gt;For 6 months, TAMSIV existed only as text and screenshots. Today, for the first time, the app has a face: a full demo video.&lt;/p&gt;

&lt;p&gt;  &lt;iframe src="https://www.youtube.com/embed/cHlnD54DBTI"&gt;
  &lt;/iframe&gt;
&lt;/p&gt;

&lt;h2&gt;
  
  
  Why a video now?
&lt;/h2&gt;

&lt;p&gt;A voice app needs to be &lt;em&gt;seen&lt;/em&gt;. Reading "speak, AI understands and organizes" isn't enough — you need to see the mic activating, the real-time transcription, the task being created automatically in the right folder.&lt;/p&gt;

&lt;p&gt;The video shows all of that in 2 minutes. It's now embedded directly in the &lt;a href="https://tamsiv.com/en" rel="noopener noreferrer"&gt;website's hero section&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Quality sprint: 7 commits, 0 new features
&lt;/h2&gt;

&lt;p&gt;Alongside the video, I spent a week on a 100% quality sprint. No new features — only polish, reliability, and silent fixes.&lt;/p&gt;

&lt;h3&gt;
  
  
  Dictaphone: 100% reliable stop button
&lt;/h3&gt;

&lt;p&gt;The most frustrating bug: sometimes the "stop" button wouldn't respond. The mic kept recording, forcing users to kill the app.&lt;/p&gt;

&lt;p&gt;The cause? A timing issue between native STT initialization and React state. The fix:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;A "standby" mode for native STT — ready instantly without blocking the UI&lt;/li&gt;
&lt;li&gt;Recording start 2x faster (no callback wait)&lt;/li&gt;
&lt;li&gt;A stop button that works 100% of the time&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Multi-day voice agenda queries
&lt;/h3&gt;

&lt;p&gt;Previously, asking "what do I have this week?" only worked for a single day. Now the assistant understands ranges: "Monday to Friday", "the next 3 days", "this week".&lt;/p&gt;

&lt;h3&gt;
  
  
  AI images: switch to HiDream-I1-Fast
&lt;/h3&gt;

&lt;p&gt;Folder cover images are AI-generated. SDXL 0.9 poorly understood complex prompts. HiDream-I1-Fast: better comprehension, more consistent results, ~$0.003 per image.&lt;/p&gt;

&lt;h3&gt;
  
  
  Folders: instant display
&lt;/h3&gt;

&lt;p&gt;In collaborative groups, the folder tree waited for all content to load. Now it shows instantly — content loads in the background.&lt;/p&gt;

&lt;h3&gt;
  
  
  Security: data leakage fix
&lt;/h3&gt;

&lt;p&gt;Critical bug: on shared devices, previous user's data could briefly appear when switching accounts. The singleton cache wasn't cleared on logout. Fixed with complete state cleanup.&lt;/p&gt;

&lt;h2&gt;
  
  
  What I take away
&lt;/h2&gt;

&lt;p&gt;A quality sprint isn't glamorous. No new feature, no before/after screenshot. But it's what separates an app people &lt;em&gt;try&lt;/em&gt; from an app people &lt;em&gt;keep&lt;/em&gt;.&lt;/p&gt;

&lt;p&gt;The video is the opposite: the moment the project becomes tangible for someone who's never touched it. Both complement each other — the storefront and the foundation.&lt;/p&gt;




&lt;p&gt;📲 &lt;a href="https://play.google.com/store/apps/details?id=com.tamsiv" rel="noopener noreferrer"&gt;Try TAMSIV on Google Play&lt;/a&gt;&lt;br&gt;
🌐 &lt;a href="https://tamsiv.com" rel="noopener noreferrer"&gt;Website&lt;/a&gt;&lt;br&gt;
📖 &lt;a href="https://tamsiv.com/en/blog/first-demo-video-quality-sprint" rel="noopener noreferrer"&gt;Full blog post&lt;/a&gt;&lt;/p&gt;

</description>
      <category>buildinpublic</category>
      <category>reactnative</category>
      <category>android</category>
      <category>productivity</category>
    </item>
    <item>
      <title>Why Productivity Apps Fail with ADHD — and the 3 Principles That Actually Work</title>
      <dc:creator>TAMSIV</dc:creator>
      <pubDate>Mon, 23 Mar 2026 11:40:01 +0000</pubDate>
      <link>https://dev.to/tamsiv/why-productivity-apps-fail-with-adhd-and-the-3-principles-that-actually-work-5gm3</link>
      <guid>https://dev.to/tamsiv/why-productivity-apps-fail-with-adhd-and-the-3-principles-that-actually-work-5gm3</guid>
      <description>&lt;p&gt;On Reddit, the same post shows up every week. Someone writes: &lt;em&gt;"I downloaded a new productivity app. The first three days were amazing. Now it's collecting dust on my phone."&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;If you have ADHD, you know this cycle by heart. Download → get excited → forget → feel guilty → avoid → uninstall. And start over with the next "miracle" app.&lt;/p&gt;

&lt;p&gt;The problem isn't you. It's that 95% of productivity apps are designed for neurotypical brains.&lt;/p&gt;

&lt;h2&gt;
  
  
  The real problem: friction
&lt;/h2&gt;

&lt;p&gt;Open the app. Find the right list. Tap "+". Type the task. Pick a date. Select a category. Six steps. For an ADHD brain, that's five too many.&lt;/p&gt;

&lt;p&gt;Each step is a chance to get distracted, lose your train of thought, or tell yourself "I'll do it later" — which means never.&lt;/p&gt;

&lt;p&gt;Apps like Todoist, TickTick, or Notion are great for people who enjoy organizing. But with ADHD, the problem isn't organizing — it's &lt;strong&gt;capturing&lt;/strong&gt; before the thought disappears.&lt;/p&gt;

&lt;h2&gt;
  
  
  Principle 1: Reduce friction to zero
&lt;/h2&gt;

&lt;p&gt;Capturing should take less than 5 seconds. Period.&lt;/p&gt;

&lt;p&gt;The fastest method: voice. You think something, you say it out loud, it's recorded. No screen to navigate, no text to type, no category to choose.&lt;/p&gt;

&lt;p&gt;Apps like TAMSIV or even Google Tasks' voice input allow this. The goal isn't to find the perfect app — it's to find the one that puts the fewest barriers between your thought and its capture.&lt;/p&gt;

&lt;p&gt;The test is simple: if you have to think about HOW to note something down, the app has already failed.&lt;/p&gt;

&lt;h2&gt;
  
  
  Principle 2: Make progress visible
&lt;/h2&gt;

&lt;p&gt;The ADHD brain needs immediate feedback. A shrinking task list isn't enough — we need to SEE that we're making progress.&lt;/p&gt;

&lt;p&gt;That's why gamification works surprisingly well:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Streaks&lt;/strong&gt;: "Day 27 without breaking the chain" creates a loss aversion that's more motivating than any to-do list.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Levels&lt;/strong&gt;: Going from "Beginner" to "Organizer" to "Expert" — the brain loves progression narratives.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Badges&lt;/strong&gt;: Visual rewards for specific actions. It sounds childish, but it activates the reward circuit exactly the right way.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Habitica does this with an RPG angle. TAMSIV does it with levels and daily challenges. Even a simple counter of completed tasks on a sticky note can work. What matters is that progress is VISIBLE.&lt;/p&gt;

&lt;h2&gt;
  
  
  Principle 3: Flexibility without guilt
&lt;/h2&gt;

&lt;p&gt;Apps that punish you for missing a day are toxic for ADHD. You miss Monday, feel guilty Tuesday, avoid the app Wednesday, and it's over.&lt;/p&gt;

&lt;p&gt;The app that works for ADHD lets you pick up where you left off. No passive-aggressive "You missed 3 days!" message. No streak reset without a safety net.&lt;/p&gt;

&lt;p&gt;Moving a task to tomorrow isn't failure — it's realistic planning.&lt;/p&gt;

&lt;h2&gt;
  
  
  Capture first, organize later
&lt;/h2&gt;

&lt;p&gt;The classic trap: trying to organize AT THE MOMENT you capture. "Which folder does this task go in? What priority? What date?"&lt;/p&gt;

&lt;p&gt;Stop. Capture first. Organize during a dedicated moment (5 minutes in the morning or evening). These are two different mental modes, and mixing them is exhausting for an ADHD brain.&lt;/p&gt;

&lt;p&gt;With voice capture, it's natural: you speak, it's captured. You organize later, with a clear head.&lt;/p&gt;

&lt;h2&gt;
  
  
  What really matters
&lt;/h2&gt;

&lt;p&gt;The perfect app doesn't exist. But apps that work with ADHD always have these three things: minimal friction, visible progress, flexibility without judgment.&lt;/p&gt;

&lt;p&gt;If your current app makes you feel guilty when you open it, it's not the right one. Switch. Test. The right app is the one you still open 30 days from now.&lt;/p&gt;

</description>
      <category>adhd</category>
      <category>productivity</category>
      <category>android</category>
      <category>apps</category>
    </item>
    <item>
      <title>How I Gave My Voice AI the Ability to Read Your Schedule (Not Just Write To It)</title>
      <dc:creator>TAMSIV</dc:creator>
      <pubDate>Mon, 23 Mar 2026 08:26:38 +0000</pubDate>
      <link>https://dev.to/tamsiv/how-i-gave-my-voice-ai-the-ability-to-read-your-schedule-not-just-write-to-it-4ecb</link>
      <guid>https://dev.to/tamsiv/how-i-gave-my-voice-ai-the-ability-to-read-your-schedule-not-just-write-to-it-4ecb</guid>
      <description>&lt;p&gt;For five months, my voice AI assistant could create tasks, memos, and calendar events. It could write. But it couldn't read.&lt;/p&gt;

&lt;p&gt;If you asked "What do I have tomorrow?", it had no idea. It only knew write commands — create, update, delete. Your actual schedule? Completely opaque.&lt;/p&gt;

&lt;p&gt;This week I changed that. Here's how.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Problem
&lt;/h2&gt;

&lt;p&gt;My app TAMSIV uses a voice pipeline: audio → Deepgram STT → OpenRouter LLM → function calling → OpenAI TTS. The LLM has 7 function tools: &lt;code&gt;create_task&lt;/code&gt;, &lt;code&gt;update_task&lt;/code&gt;, &lt;code&gt;create_memo&lt;/code&gt;, &lt;code&gt;update_memo&lt;/code&gt;, &lt;code&gt;create_calendar_event&lt;/code&gt;, &lt;code&gt;ask_clarification&lt;/code&gt;, &lt;code&gt;end_conversation&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;All write operations. The AI could create things in your schedule, but couldn't tell you what was already there.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Solution: An 8th Tool
&lt;/h2&gt;

&lt;p&gt;I added &lt;code&gt;query_agenda(startDate, endDate)&lt;/code&gt; — a read tool.&lt;/p&gt;

&lt;p&gt;When you ask "What's on my plate this week?", the LLM:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Parses your intent and calls &lt;code&gt;query_agenda&lt;/code&gt; with the right date range&lt;/li&gt;
&lt;li&gt;A Supabase RPC function queries three tables: &lt;code&gt;privat.tasks&lt;/code&gt;, &lt;code&gt;privat.calendar_events&lt;/code&gt;, and &lt;code&gt;privat.memos&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Returns structured data: tasks with due dates, events with times, memos with timestamps&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;But raw data isn't useful as a voice response. Nobody wants to hear "Task ID 47, title grocery shopping, due date 2026-03-24T10:00:00Z."&lt;/p&gt;

&lt;h2&gt;
  
  
  The 2nd LLM Call
&lt;/h2&gt;

&lt;p&gt;This is where it gets interesting. The orchestrator makes a &lt;strong&gt;second LLM call&lt;/strong&gt; — taking the raw agenda data and generating a natural vocal summary:&lt;/p&gt;

&lt;p&gt;"Tomorrow you have three things. In the morning, grocery shopping. At 2pm, a dentist appointment. And you still have that memo about calling the insurance company."&lt;/p&gt;

&lt;p&gt;Natural. Conversational. Adapted for voice.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Architecture Change That Surprised Me
&lt;/h2&gt;

&lt;p&gt;The &lt;code&gt;FunctionExecutor&lt;/code&gt; — the component that runs tools — was synchronous and had no database access. It only returned instructions that the frontend would execute.&lt;/p&gt;

&lt;p&gt;For &lt;code&gt;query_agenda&lt;/code&gt;, the backend needed to query Supabase directly. So the FunctionExecutor became async, with access to the Supabase client and userId.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// Before: synchronous, no DB access&lt;/span&gt;
&lt;span class="nf"&gt;executeTool&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;args&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;object&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt; &lt;span class="nx"&gt;ToolResult&lt;/span&gt;

&lt;span class="c1"&gt;// After: async with DB context&lt;/span&gt;
&lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="nf"&gt;executeTool&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
  &lt;span class="nx"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; 
  &lt;span class="nx"&gt;args&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;object&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; 
  &lt;span class="nx"&gt;supabase&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;SupabaseClient&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; 
  &lt;span class="nx"&gt;userId&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt;
&lt;span class="p"&gt;):&lt;/span&gt; &lt;span class="nb"&gt;Promise&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;ToolResult&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This opened the door for future read tools — the AI can now query anything server-side.&lt;/p&gt;

&lt;h2&gt;
  
  
  The RPC Function
&lt;/h2&gt;

&lt;p&gt;The Supabase RPC crosses three tables in a single query:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="k"&gt;CREATE&lt;/span&gt; &lt;span class="k"&gt;OR&lt;/span&gt; &lt;span class="k"&gt;REPLACE&lt;/span&gt; &lt;span class="k"&gt;FUNCTION&lt;/span&gt; &lt;span class="n"&gt;get_user_agenda&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
  &lt;span class="n"&gt;p_user_id&lt;/span&gt; &lt;span class="n"&gt;UUID&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="n"&gt;p_start_date&lt;/span&gt; &lt;span class="n"&gt;TIMESTAMPTZ&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="n"&gt;p_end_date&lt;/span&gt; &lt;span class="n"&gt;TIMESTAMPTZ&lt;/span&gt;
&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;RETURNS&lt;/span&gt; &lt;span class="n"&gt;JSON&lt;/span&gt; &lt;span class="k"&gt;AS&lt;/span&gt; &lt;span class="err"&gt;$$&lt;/span&gt;
  &lt;span class="k"&gt;SELECT&lt;/span&gt; &lt;span class="n"&gt;json_build_object&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="s1"&gt;'tasks'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;SELECT&lt;/span&gt; &lt;span class="n"&gt;json_agg&lt;/span&gt;&lt;span class="p"&gt;(...)&lt;/span&gt; &lt;span class="k"&gt;FROM&lt;/span&gt; &lt;span class="n"&gt;privat&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;tasks&lt;/span&gt; &lt;span class="k"&gt;WHERE&lt;/span&gt; &lt;span class="p"&gt;...),&lt;/span&gt;
    &lt;span class="s1"&gt;'events'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;SELECT&lt;/span&gt; &lt;span class="n"&gt;json_agg&lt;/span&gt;&lt;span class="p"&gt;(...)&lt;/span&gt; &lt;span class="k"&gt;FROM&lt;/span&gt; &lt;span class="n"&gt;privat&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;calendar_events&lt;/span&gt; &lt;span class="k"&gt;WHERE&lt;/span&gt; &lt;span class="p"&gt;...),&lt;/span&gt;
    &lt;span class="s1"&gt;'memos'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;SELECT&lt;/span&gt; &lt;span class="n"&gt;json_agg&lt;/span&gt;&lt;span class="p"&gt;(...)&lt;/span&gt; &lt;span class="k"&gt;FROM&lt;/span&gt; &lt;span class="n"&gt;privat&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;memos&lt;/span&gt; &lt;span class="k"&gt;WHERE&lt;/span&gt; &lt;span class="p"&gt;...)&lt;/span&gt;
  &lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="err"&gt;$$&lt;/span&gt; &lt;span class="k"&gt;LANGUAGE&lt;/span&gt; &lt;span class="k"&gt;sql&lt;/span&gt; &lt;span class="k"&gt;SECURITY&lt;/span&gt; &lt;span class="k"&gt;INVOKER&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Using &lt;code&gt;SECURITY INVOKER&lt;/code&gt; means RLS policies are enforced — users only see their own data, even when the backend calls this function.&lt;/p&gt;

&lt;h2&gt;
  
  
  Frontend: No PendingCreation
&lt;/h2&gt;

&lt;p&gt;Every other tool creates a &lt;code&gt;PendingCreation&lt;/code&gt; — a preview the user validates before it's saved to the database. But &lt;code&gt;query_agenda&lt;/code&gt; doesn't create anything. The frontend just needs to handle the &lt;code&gt;agenda_queried&lt;/code&gt; action by... doing nothing. The voice response is the result.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Bigger Picture
&lt;/h2&gt;

&lt;p&gt;This changes the nature of the voice assistant fundamentally. Before, it was an input device — you talk, it writes. Now it's a conversational partner — you ask, it answers.&lt;/p&gt;

&lt;p&gt;"What do I have this week?" → vocal summary&lt;br&gt;
"Create a task: buy flowers for Friday" → creates task&lt;br&gt;
"Actually, what else is on Friday?" → checks and responds&lt;br&gt;
"Add a reminder for 5pm" → creates event&lt;/p&gt;

&lt;p&gt;The conversation flows naturally because the AI can both read and write.&lt;/p&gt;

&lt;p&gt;700+ commits. Solo dev. Production in 5 days.&lt;/p&gt;

&lt;p&gt;If you're building voice AI features, the key insight is: &lt;strong&gt;read tools change everything&lt;/strong&gt;. An AI that can only write feels like a command interface. An AI that can read and write feels like an assistant.&lt;/p&gt;

</description>
      <category>ai</category>
      <category>reactnative</category>
      <category>supabase</category>
      <category>productivity</category>
    </item>
    <item>
      <title>I Built a Blog with 31 Articles and AI-Generated Cover Images in One Day — Here's How</title>
      <dc:creator>TAMSIV</dc:creator>
      <pubDate>Wed, 18 Mar 2026 09:06:30 +0000</pubDate>
      <link>https://dev.to/tamsiv/i-built-a-blog-with-31-articles-and-ai-generated-cover-images-in-one-day-heres-how-3kle</link>
      <guid>https://dev.to/tamsiv/i-built-a-blog-with-31-articles-and-ai-generated-cover-images-in-one-day-heres-how-3kle</guid>
      <description>&lt;p&gt;I'm building &lt;a href="https://tamsiv.com" rel="noopener noreferrer"&gt;TAMSIV&lt;/a&gt;, a voice-powered task manager, solo. After 670+ commits over 6 months, I had a landing page, a web dashboard, and... zero blog. Google didn't know I existed.&lt;/p&gt;

&lt;p&gt;So I decided to fix that. In one day.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Problem
&lt;/h2&gt;

&lt;p&gt;My site had no indexable content. No articles. No FAQ. No user guide worth the name. Every piece of marketing I'd done (50+ posts across Discord, LinkedIn, Facebook, dev.to) lived on other people's platforms. Nothing on my own domain.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Solution: 31 Articles, AI Cover Images, 6 Languages
&lt;/h2&gt;

&lt;p&gt;Here's what I built:&lt;/p&gt;

&lt;h3&gt;
  
  
  Blog Architecture (Next.js)
&lt;/h3&gt;

&lt;p&gt;The blog lives in &lt;code&gt;src/lib/blog.ts&lt;/code&gt; — all content is inline HTML, no database. Each article has metadata (title, slug, date, tag, excerpt, cover image) and content in HTML.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// blog.ts structure&lt;/span&gt;
&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;articles&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;BlogArticle&lt;/span&gt;&lt;span class="p"&gt;[]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
  &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="na"&gt;slug&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;voice-pipeline-architecture&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;tag&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Engineering&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;image&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;/blog/voice-pipeline.png&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;content&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="na"&gt;fr&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;title&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;...&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;excerpt&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;...&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;body&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;...&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt;
      &lt;span class="na"&gt;en&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;title&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;...&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;excerpt&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;...&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;body&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;...&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt;
      &lt;span class="c1"&gt;// + de, es, it, pt&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;]&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The listing page uses a Hero (latest article, full width) + Grid (2 columns) layout. Each article gets its own page with &lt;code&gt;generateStaticParams&lt;/code&gt; for SSG.&lt;/p&gt;

&lt;h3&gt;
  
  
  AI-Generated Cover Images (Gemini 2.5 Flash)
&lt;/h3&gt;

&lt;p&gt;Every article has a cinematic cover image generated by &lt;strong&gt;Gemini 2.5 Flash Image&lt;/strong&gt; via OpenRouter:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;POST https://openrouter.ai/api/v1/chat/completions
Model: google/gemini-2.5-flash-image
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The prompt formula:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Subject&lt;/strong&gt;: Description of the article topic&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Style&lt;/strong&gt;: Photorealistic, cinematic, shallow depth of field&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Mood&lt;/strong&gt;: Dark background (#101922), blue/cyan accent lighting&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Constraint&lt;/strong&gt;: NEVER include text in the image&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The result? Images that look like they belong in a tech magazine editorial, not a solo dev's side project.&lt;/p&gt;

&lt;h3&gt;
  
  
  SEO Audit
&lt;/h3&gt;

&lt;p&gt;While I was at it, I ran a full SEO audit:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;JSON-LD&lt;/strong&gt; structured data (SoftwareApplication + Organization)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;OpenGraph + Twitter cards&lt;/strong&gt; for every page&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Meta descriptions&lt;/strong&gt; per page and per article&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Sitemap&lt;/strong&gt; auto-generated (listing + individual articles)&lt;/li&gt;
&lt;li&gt;Fixed &lt;strong&gt;OG image 404&lt;/strong&gt; that had been broken since launch&lt;/li&gt;
&lt;li&gt;Added &lt;code&gt;x-default&lt;/code&gt; hreflang for international SEO&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  User Guide Overhaul
&lt;/h3&gt;

&lt;p&gt;The existing guide was a single page. I split it into sub-pages:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Voice assistant guide&lt;/li&gt;
&lt;li&gt;Folders &amp;amp; organization guide&lt;/li&gt;
&lt;li&gt;Gamification guide&lt;/li&gt;
&lt;li&gt;FAQ (expanded from 6 to 18 questions)&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Other Stuff That Shipped
&lt;/h2&gt;

&lt;p&gt;Since I was in "ship everything" mode:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;In-app updates&lt;/strong&gt; via Google Play (flexible update flow)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Admin dashboard&lt;/strong&gt; rebuilt from scratch: 5 tabs (stats, services, infrastructure, logs, credits)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Infrastructure monitoring&lt;/strong&gt;: real-time Supabase, Railway, and Vercel metrics&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;API credit tracking&lt;/strong&gt;: OpenRouter, Deepgram, Runware, OpenAI usage dashboards&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  The Question
&lt;/h2&gt;

&lt;p&gt;Is launching a blog with 31 articles at once a good idea? The SEO argument says yes — Google rewards content-rich domains. The UX argument says maybe not — who reads 31 articles from a brand they've never heard of?&lt;/p&gt;

&lt;p&gt;My bet: the articles aren't for reading today. They're for Google to index, for people to find when they search "voice task manager" or "AI productivity app" six months from now. They're planting seeds.&lt;/p&gt;

&lt;p&gt;670 commits in. The site is becoming a content hub. The app is becoming a product.&lt;/p&gt;




&lt;p&gt;&lt;strong&gt;Links:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Blog: &lt;a href="https://tamsiv.com/en/blog" rel="noopener noreferrer"&gt;tamsiv.com/en/blog&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Previous article: &lt;a href="https://dev.to/tamsiv/how-i-built-a-full-referral-system-in-react-native-in-one-day-deep-links-stacking-rewards-push-46c4"&gt;How I Built a Referral System in React Native in One Day&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;What's your approach to blogging for side projects? One article at a time, or go all-in from day one?&lt;/p&gt;

</description>
      <category>nextjs</category>
      <category>ai</category>
      <category>seo</category>
      <category>webdev</category>
    </item>
    <item>
      <title>How I Built a Full Referral System in React Native in One Day (Deep Links, Stacking Rewards, Push Notifications)</title>
      <dc:creator>TAMSIV</dc:creator>
      <pubDate>Tue, 17 Mar 2026 09:03:38 +0000</pubDate>
      <link>https://dev.to/tamsiv/how-i-built-a-full-referral-system-in-react-native-in-one-day-deep-links-stacking-rewards-push-46c4</link>
      <guid>https://dev.to/tamsiv/how-i-built-a-full-referral-system-in-react-native-in-one-day-deep-links-stacking-rewards-push-46c4</guid>
      <description>&lt;p&gt;Yesterday I spent 8 hours building something that has nothing to do with my product's core features. No new UI. No bug fix. No optimization. A referral system.&lt;/p&gt;

&lt;p&gt;I'm building &lt;a href="https://www.tamsiv.com" rel="noopener noreferrer"&gt;TAMSIV&lt;/a&gt;, a voice-powered task manager for Android. Solo dev, 660+ commits, 6 months in. I have 12 alpha testers on the Play Store and I need that number to grow — organically.&lt;/p&gt;

&lt;p&gt;So I built the full referral loop. Here's how it works, what I learned, and the gotchas you'll hit if you try this in React Native.&lt;/p&gt;

&lt;h2&gt;
  
  
  The concept
&lt;/h2&gt;

&lt;p&gt;Simple: every user gets a unique referral code. Share it. When someone signs up with it, &lt;strong&gt;both&lt;/strong&gt; get 1 free month of Pro. And it stacks — 10 referrals = 10 months free, queued one after another.&lt;/p&gt;

&lt;h2&gt;
  
  
  The 3 capture sources (this is where it gets tricky)
&lt;/h2&gt;

&lt;p&gt;The hardest part isn't generating codes. It's &lt;strong&gt;capturing&lt;/strong&gt; them reliably across all the ways someone might arrive at your app.&lt;/p&gt;

&lt;h3&gt;
  
  
  1. Deep links via the website
&lt;/h3&gt;

&lt;p&gt;When someone visits &lt;code&gt;tamsiv.com/invite/CODE&lt;/code&gt;, Next.js redirects to the Play Store with the referral code embedded:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// next.config.ts&lt;/span&gt;
&lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="nf"&gt;redirects&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;[{&lt;/span&gt;
    &lt;span class="na"&gt;source&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;/invite/:code&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;destination&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;https://play.google.com/store/apps/details?id=com.tamsiv&amp;amp;referrer=:code&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;permanent&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="p"&gt;}]&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  2. Android App Links (direct open)
&lt;/h3&gt;

&lt;p&gt;If the app is already installed, &lt;code&gt;tamsiv://invite/CODE&lt;/code&gt; opens it directly. This requires:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;AndroidManifest.xml:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight xml"&gt;&lt;code&gt;&lt;span class="nt"&gt;&amp;lt;intent-filter&lt;/span&gt; &lt;span class="na"&gt;android:autoVerify=&lt;/span&gt;&lt;span class="s"&gt;"true"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;action&lt;/span&gt; &lt;span class="na"&gt;android:name=&lt;/span&gt;&lt;span class="s"&gt;"android.intent.action.VIEW"&lt;/span&gt; &lt;span class="nt"&gt;/&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;category&lt;/span&gt; &lt;span class="na"&gt;android:name=&lt;/span&gt;&lt;span class="s"&gt;"android.intent.category.DEFAULT"&lt;/span&gt; &lt;span class="nt"&gt;/&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;category&lt;/span&gt; &lt;span class="na"&gt;android:name=&lt;/span&gt;&lt;span class="s"&gt;"android.intent.category.BROWSABLE"&lt;/span&gt; &lt;span class="nt"&gt;/&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;data&lt;/span&gt; &lt;span class="na"&gt;android:scheme=&lt;/span&gt;&lt;span class="s"&gt;"https"&lt;/span&gt; &lt;span class="na"&gt;android:host=&lt;/span&gt;&lt;span class="s"&gt;"tamsiv.com"&lt;/span&gt;
        &lt;span class="na"&gt;android:pathPrefix=&lt;/span&gt;&lt;span class="s"&gt;"/invite"&lt;/span&gt; &lt;span class="nt"&gt;/&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/intent-filter&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Plus an &lt;code&gt;assetlinks.json&lt;/code&gt; on your website for verification:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;[{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"relation"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;"delegate_permission/common.handle_all_urls"&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"target"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"namespace"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"android_app"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"package_name"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"com.tamsiv"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"sha256_cert_fingerprints"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;"YOUR_SHA256"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}]&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;React Navigation linking config:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;linking&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="na"&gt;prefixes&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;tamsiv://&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;https://tamsiv.com&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
  &lt;span class="na"&gt;config&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="na"&gt;screens&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="na"&gt;Signup&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;invite/:code&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="p"&gt;},&lt;/span&gt;
  &lt;span class="p"&gt;},&lt;/span&gt;
&lt;span class="p"&gt;};&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  3. Play Store Install Referrer
&lt;/h3&gt;

&lt;p&gt;This is the sneaky-good one. When someone clicks a Play Store link with &lt;code&gt;&amp;amp;referrer=CODE&lt;/code&gt;, Android stores that referrer string. Even if they install the app &lt;strong&gt;3 days later&lt;/strong&gt;, you can still read it.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;npm &lt;span class="nb"&gt;install &lt;/span&gt;react-native-play-install-referrer
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;getInstallReferrer&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;react-native-play-install-referrer&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;referrer&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;getInstallReferrer&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="c1"&gt;// referrer.installReferrer contains your code&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This covers the "click link → browse Play Store → install later → open" flow that deep links can't handle.&lt;/p&gt;

&lt;h2&gt;
  
  
  Stacking rewards
&lt;/h2&gt;

&lt;p&gt;Most referral systems give you one reward and that's it. I wanted rewards to &lt;strong&gt;accumulate&lt;/strong&gt;. Each referral adds 30 days of Pro, queued after the previous one expires.&lt;/p&gt;

&lt;p&gt;The logic: when a referral is validated, the backend checks if the user already has an active reward. If yes, the new reward's start date = previous reward's end date. If no, it starts now.&lt;/p&gt;

&lt;p&gt;This required rethinking my subscription logic — RevenueCat handles paid subscriptions, but free months from referrals are managed separately in Supabase.&lt;/p&gt;

&lt;h2&gt;
  
  
  Push notifications in 6 languages
&lt;/h2&gt;

&lt;p&gt;When someone uses your referral code, you get a push notification. In your language. Because TAMSIV supports 6 languages (FR, EN, DE, ES, IT, PT), the backend checks the referrer's language preference before sending via FCM:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// Backend endpoint: POST /api/push/referral-reward&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;referrerProfile&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;getUserProfile&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;referrerId&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;lang&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;referrerProfile&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;language&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;en&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;messages&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="na"&gt;fr&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;`&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;referredName&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt; a utilisé votre code ! 1 mois Pro offert 🎉`&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;en&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;`&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;referredName&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt; used your code! 1 free month of Pro 🎉`&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="c1"&gt;// ... 4 more languages&lt;/span&gt;
&lt;span class="p"&gt;};&lt;/span&gt;
&lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;sendPushNotification&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;referrerProfile&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;fcm_token&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;messages&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;lang&lt;/span&gt;&lt;span class="p"&gt;]);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  The Invite tab
&lt;/h2&gt;

&lt;p&gt;The frontend has a dedicated Invite section in the Social screen: your unique code, a native share button, referral stats (total invites, pending, rewarded), and reward history. All managed by a &lt;code&gt;ReferralService&lt;/code&gt; singleton with caching.&lt;/p&gt;

&lt;h2&gt;
  
  
  By the numbers
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;22 files&lt;/strong&gt; changed across backend, frontend, website, and Android manifest&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;1,324 lines&lt;/strong&gt; added&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;1 day&lt;/strong&gt; of focused work&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;6 languages&lt;/strong&gt; supported&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;3 capture sources&lt;/strong&gt; for maximum coverage&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  What I learned
&lt;/h2&gt;

&lt;ol&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Android App Links verification is fragile.&lt;/strong&gt; The &lt;code&gt;assetlinks.json&lt;/code&gt; must be served at &lt;code&gt;/.well-known/assetlinks.json&lt;/code&gt; with the exact SHA256 of your signing key. Miss one character and it silently fails.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Play Store Install Referrer is underrated.&lt;/strong&gt; Most tutorials skip it, but it's the only way to capture referrals through delayed installs.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Reward stacking adds real complexity.&lt;/strong&gt; A simple "give 1 month free" is easy. Queuing multiple rewards requires tracking start dates, end dates, and the relationship between RevenueCat subscriptions and your own reward system.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;i18n multiplies everything.&lt;/strong&gt; 1 push notification message × 6 languages × 2 recipients (referrer + referred) = 12 translation keys. For every user-facing string.&lt;/p&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  Will it work?
&lt;/h2&gt;

&lt;p&gt;Honestly? No idea. The code is ready, the circuit is complete. Now it's up to the users.&lt;/p&gt;

&lt;p&gt;But here's the thing — building the referral system forced me to think about growth for the first time. After 660 commits of pure product work, spending one day on distribution felt uncomfortable. But necessary.&lt;/p&gt;

&lt;p&gt;If you're a solo dev reading this: don't wait as long as I did. Build the growth loop early.&lt;/p&gt;




&lt;p&gt;&lt;em&gt;TAMSIV is a voice-powered task manager for Android. Try it on the &lt;a href="https://play.google.com/store/apps/details?id=com.tamsiv" rel="noopener noreferrer"&gt;Play Store&lt;/a&gt; or visit &lt;a href="https://www.tamsiv.com" rel="noopener noreferrer"&gt;tamsiv.com&lt;/a&gt;.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Follow the journey: &lt;a href="https://www.tamsiv.com" rel="noopener noreferrer"&gt;GitHub commits in public&lt;/a&gt; | &lt;a href="https://discord.gg/4wAwyECSsP" rel="noopener noreferrer"&gt;Discord&lt;/a&gt;&lt;/em&gt;&lt;/p&gt;

</description>
      <category>reactnative</category>
      <category>android</category>
      <category>mobile</category>
      <category>tutorial</category>
    </item>
    <item>
      <title>I Built a Voice-Powered Task Manager with AI in 650 Commits — Here's What I Learned</title>
      <dc:creator>TAMSIV</dc:creator>
      <pubDate>Mon, 16 Mar 2026 13:26:02 +0000</pubDate>
      <link>https://dev.to/tamsiv/i-built-a-voice-powered-task-manager-with-ai-in-650-commits-heres-what-i-learned-56dk</link>
      <guid>https://dev.to/tamsiv/i-built-a-voice-powered-task-manager-with-ai-in-650-commits-heres-what-i-learned-56dk</guid>
      <description>&lt;p&gt;Five months ago, I had a simple problem: my family of four couldn't keep track of groceries, appointments, and who's picking up the kids. Post-it notes on the fridge weren't cutting it.&lt;/p&gt;

&lt;p&gt;So I built &lt;strong&gt;TAMSIV&lt;/strong&gt; — a voice-powered task and memo manager with AI. You speak, the AI understands, and everything gets organized automatically. No typing, no tapping through forms. Just talk.&lt;/p&gt;

&lt;p&gt;650 commits later, here's what I learned building it solo.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Stack
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Frontend&lt;/strong&gt;: React Native 0.81 + TypeScript (New Architecture enabled)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Backend&lt;/strong&gt;: Node.js/Express with WebSocket for real-time voice streaming&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Database&lt;/strong&gt;: Supabase (PostgreSQL) with Row Level Security&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;AI&lt;/strong&gt;: OpenRouter (400+ LLM models), Deepgram (STT), OpenAI (TTS)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Hosting&lt;/strong&gt;: Railway (backend) + Vercel (website)&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  The Voice Pipeline
&lt;/h2&gt;

&lt;p&gt;This is the core of TAMSIV. The flow looks like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;User speaks → PCM audio chunks via WebSocket
  → Deepgram (real-time STT with VAD)
  → LLM via OpenRouter (context-aware)
  → Function calling (create_task, create_memo, create_event)
  → OpenAI TTS → Audio response back to user
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The tricky part? &lt;strong&gt;Voice Activity Detection (VAD)&lt;/strong&gt;. You need to know when the user stopped talking before you send the audio to the LLM. Too early and you cut them off. Too late and the app feels sluggish.&lt;/p&gt;

&lt;p&gt;I ended up with a dual-mode system:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Push-to-talk&lt;/strong&gt;: User controls when they start/stop. More reliable.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;VAD mode&lt;/strong&gt;: Deepgram detects silence automatically. More natural but harder to get right.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  What the AI Actually Does
&lt;/h2&gt;

&lt;p&gt;The LLM doesn't just transcribe — it &lt;strong&gt;understands intent&lt;/strong&gt;. Say:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;"Remind me to buy milk tomorrow at 5pm, it's urgent"&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;The AI extracts:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Type&lt;/strong&gt;: Task&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Title&lt;/strong&gt;: "Buy milk"&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Due date&lt;/strong&gt;: Tomorrow 5pm&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Priority&lt;/strong&gt;: High&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Category&lt;/strong&gt;: Auto-detected based on context&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;It uses function calling to create structured data, not free text. The backend returns a &lt;code&gt;function_result&lt;/code&gt; message, and the frontend handles the actual database operations.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Hardest Bugs
&lt;/h2&gt;

&lt;h3&gt;
  
  
  1. LLMs Can't Do Dates
&lt;/h3&gt;

&lt;p&gt;Ask an LLM "what's next Tuesday?" and you'll get wrong answers 30% of the time. My fix: inject a &lt;strong&gt;date lookup table&lt;/strong&gt; into the system prompt with the current date, day of week, and the next 14 days mapped out. Accuracy went from ~70% to ~99%.&lt;/p&gt;

&lt;h3&gt;
  
  
  2. The Singleton Cache Leak
&lt;/h3&gt;

&lt;p&gt;After logout, the next user would see the previous user's data. The singleton services (ConversationService, ContentCacheService) kept stale data in memory. Fix: proper cleanup on auth state change.&lt;/p&gt;

&lt;h3&gt;
  
  
  3. Six Hooks DDoSing Supabase
&lt;/h3&gt;

&lt;p&gt;Six different React hooks were all calling &lt;code&gt;getGroups()&lt;/code&gt; independently on mount. That's 6 concurrent identical requests per screen load. Fix: centralize in a GroupsContext with a single fetch.&lt;/p&gt;

&lt;h2&gt;
  
  
  Features That Surprised Me
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Gamification works.&lt;/strong&gt; I added levels, badges, streaks, and daily challenges almost as an afterthought. Turns out, getting a "5-day streak!" notification makes you actually want to use the app. 12 levels, 5 database tables, full schema — overkill? Maybe. But engagement went up.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Hierarchical folders are underrated.&lt;/strong&gt; Most task apps give you flat lists or one level of categories. TAMSIV supports unlimited depth: &lt;code&gt;Work &amp;gt; Project Alpha &amp;gt; Sprint 3 &amp;gt; Backend&lt;/code&gt;. It sounds simple but the recursive navigation was a real challenge in React Native.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;AI-generated images add personality.&lt;/strong&gt; I integrated Runware (FLUX.1) for automatic image generation on tasks and memos. But the real trick: a LLM first analyzes your content and generates an optimized prompt before sending it to the image model. Way better results than raw text.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Numbers
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;650+ commits&lt;/strong&gt; in 5 months (solo dev)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;6 languages&lt;/strong&gt; (FR, EN, DE, ES, IT, PT)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;400+ AI models&lt;/strong&gt; available via OpenRouter&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;12 gamification levels&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Real-time collaboration&lt;/strong&gt; with groups, assignments, comments, reactions&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;3 voice modes&lt;/strong&gt;: Standard, Live Streaming, Push-to-talk&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  What's Next
&lt;/h2&gt;

&lt;p&gt;TAMSIV is currently in &lt;strong&gt;closed alpha on Google Play&lt;/strong&gt; with 12 testers. Production launch is planned for late March 2026.&lt;/p&gt;

&lt;p&gt;If you're interested in:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Testing the app (Android)&lt;/li&gt;
&lt;li&gt;The technical architecture&lt;/li&gt;
&lt;li&gt;How I handle voice streaming in React Native&lt;/li&gt;
&lt;li&gt;Supabase RLS patterns for multi-tenant apps&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Drop a comment or check out &lt;a href="https://tamsiv.com" rel="noopener noreferrer"&gt;tamsiv.com&lt;/a&gt;.&lt;/p&gt;




&lt;p&gt;&lt;em&gt;Building in public, one commit at a time. Follow along for more technical deep dives.&lt;/em&gt;&lt;/p&gt;

</description>
      <category>react</category>
      <category>ai</category>
      <category>productivity</category>
      <category>android</category>
    </item>
  </channel>
</rss>
