<?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: Mad-jr</title>
    <description>The latest articles on DEV Community by Mad-jr (@tryandaasu).</description>
    <link>https://dev.to/tryandaasu</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.us-east-2.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F4024807%2Fe0192632-d73b-47b6-85d1-ab48b1b502db.jpg</url>
      <title>DEV Community: Mad-jr</title>
      <link>https://dev.to/tryandaasu</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/tryandaasu"/>
    <language>en</language>
    <item>
      <title>Inside Sonna AI: Building a Spec-Driven Multi-Modal AI Studio with NestJS, Turso DB, and Native Android (Kotlin)</title>
      <dc:creator>Mad-jr</dc:creator>
      <pubDate>Sat, 11 Jul 2026 07:02:56 +0000</pubDate>
      <link>https://dev.to/tryandaasu/building-sonna-ai-a-spec-driven-multi-modal-ai-studio-using-kiros-agentic-ide-54l0</link>
      <guid>https://dev.to/tryandaasu/building-sonna-ai-a-spec-driven-multi-modal-ai-studio-using-kiros-agentic-ide-54l0</guid>
      <description>&lt;p&gt;Hey developers! 👋&lt;/p&gt;

&lt;p&gt;We are excited to share &lt;strong&gt;Sonna AI&lt;/strong&gt;, a unified multi-modal AI Creative Studio built for creators and developers. In this post, we'll cover how we built and refactored Sonna AI using &lt;strong&gt;Kiro's Agentic IDE&lt;/strong&gt;—orchestrating spec-driven changes across our Next.js web client, NestJS backend, and native Android app.&lt;/p&gt;

&lt;p&gt;Web Dashboard: &lt;a href="https://sonnalabs.app" rel="noopener noreferrer"&gt;sonnalabs.app&lt;/a&gt;&lt;br&gt;&lt;br&gt;
Android App: &lt;a href="https://play.google.com/store/apps/details?id=com.sonna.studio" rel="noopener noreferrer"&gt;Google Play Store&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Flaluuexklqtckfm9da42.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Flaluuexklqtckfm9da42.png" alt="Sonna AI Dashboard and Mobile App Preview" width="800" height="450"&gt;&lt;/a&gt;&lt;/p&gt;


&lt;h2&gt;
  
  
  🛠️ The Tech Stack &amp;amp; Production Status
&lt;/h2&gt;

&lt;p&gt;Our infrastructure is fully live in production (deployed via custom PM2 configurations and automated shell deployment scripts) on a dedicated VPS stack:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;  &lt;strong&gt;Backend:&lt;/strong&gt; NestJS (Node.js framework running on PM2 as &lt;code&gt;sonnalabs-backend&lt;/code&gt;)&lt;/li&gt;
&lt;li&gt;  &lt;strong&gt;Database:&lt;/strong&gt; Turso DB (Distributed SQLite with 19 active tables)&lt;/li&gt;
&lt;li&gt;  &lt;strong&gt;Cache &amp;amp; Rate Limiting:&lt;/strong&gt; Redis&lt;/li&gt;
&lt;li&gt;  &lt;strong&gt;Storage:&lt;/strong&gt; Cloudflare R2 (S3-compatible bucket storage for media output)&lt;/li&gt;
&lt;li&gt;  &lt;strong&gt;Payments:&lt;/strong&gt; Midtrans (midtrans.com) for Web and Google Play Billing for Android&lt;/li&gt;
&lt;li&gt;  &lt;strong&gt;Web Client:&lt;/strong&gt; Next.js (React framework running on localhost/production)&lt;/li&gt;
&lt;li&gt;  &lt;strong&gt;Mobile Client:&lt;/strong&gt; Native Android built in Kotlin (integrated with Google Play Billing)&lt;/li&gt;
&lt;/ul&gt;


&lt;h2&gt;
  
  
  📋 Architectural Deep Dive: Resilient Multi-Platform Billing (SSOT)
&lt;/h2&gt;

&lt;p&gt;Handling subscriptions and credit allocations across different payment platforms (like &lt;strong&gt;Midtrans&lt;/strong&gt; for our web client and &lt;strong&gt;Google Play Billing&lt;/strong&gt; for Android) is notorious for logic drift and fraud. We built a strict &lt;strong&gt;Single Source of Truth (SSOT)&lt;/strong&gt; billing engine using atomic transactions and proration strategies to unify these payment gateways.&lt;/p&gt;
&lt;h3&gt;
  
  
  1. Google Play RTDN Safety Net
&lt;/h3&gt;

&lt;p&gt;To prevent wrongful account downgrades due to delayed Google Play RTDN (Real-Time Developer Notifications) webhooks, we used Kiro to design a pre-downgrade validator. Before executing a lazy downgrade on an expired subscription, our backend calls &lt;code&gt;verifyAndDowngradeUserIfExpired(userId)&lt;/code&gt; in &lt;code&gt;backend/sonna/src/services/billing/api-utils.ts&lt;/code&gt;.&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="k"&gt;export&lt;/span&gt; &lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;verifyAndDowngradeUserIfExpired&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;boolean&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="c1"&gt;// 1. Fetch latest completed purchase token&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;tokenRow&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;db&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;execute&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
    &lt;span class="na"&gt;sql&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;SELECT purchase_token FROM purchases WHERE user_id = ? AND purchase_kind = 'subscription' AND status = 'completed' AND platform IN ('android', 'google_play') ORDER BY verified_at DESC LIMIT 1&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;args&lt;/span&gt;&lt;span class="p"&gt;:&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="p"&gt;});&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;token&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;tokenRow&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;rows&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;]?.&lt;/span&gt;&lt;span class="nx"&gt;purchase_token&lt;/span&gt; &lt;span class="p"&gt;?&lt;/span&gt; &lt;span class="nc"&gt;String&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;tokenRow&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;rows&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;].&lt;/span&gt;&lt;span class="nx"&gt;purchase_token&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;null&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

  &lt;span class="c1"&gt;// 2. Perform live check against Google Play API if token exists&lt;/span&gt;
  &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;token&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;verify&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;verifyGooglePlaySubscriptionV2&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;SUBSCRIPTION_PRODUCT_ID&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;token&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;verify&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;valid&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="nx"&gt;verify&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;expiryMs&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="nx"&gt;verify&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;expiryMs&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="nb"&gt;Date&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;now&lt;/span&gt;&lt;span class="p"&gt;())&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
       &lt;span class="c1"&gt;// Extend subscription expiry and prevent downgrade&lt;/span&gt;
       &lt;span class="k"&gt;return&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;span class="c1"&gt;// 3. Fallback to downgrade if verification fails&lt;/span&gt;
  &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;downgradeUserToFree&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="k"&gt;return&lt;/span&gt; &lt;span class="kc"&gt;true&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;If Google Play confirms the automatic renewal is active, the downgrade is canceled, and database expiry timestamps are extended.&lt;/p&gt;

&lt;h3&gt;
  
  
  2. Transaction Safety: Upfront Wallet Deductions &amp;amp; Ledger Auditing
&lt;/h3&gt;

&lt;p&gt;To prevent race conditions where two concurrent requests on different PM2 instances could double-spend credits, we implemented an atomic transaction read-modify-write pattern in &lt;code&gt;deductCredits()&lt;/code&gt;:&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="k"&gt;export&lt;/span&gt; &lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;deductCredits&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="nx"&gt;amount&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;number&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;meta&lt;/span&gt;&lt;span class="p"&gt;?:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;jobId&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="nl"&gt;feature&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="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;tx&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;db&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;transaction&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;write&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="k"&gt;try&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;read&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;tx&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;execute&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
      &lt;span class="na"&gt;sql&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;SELECT subscription_credits, payg_credits, credits, plan_type FROM users WHERE id = ?&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="na"&gt;args&lt;/span&gt;&lt;span class="p"&gt;:&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="p"&gt;});&lt;/span&gt;
    &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;read&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;rows&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;length&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;tx&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;rollback&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;success&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="na"&gt;error&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;User not found&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;r&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;read&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;rows&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;0&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;sub&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;Number&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;r&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;subscription_credits&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="mi"&gt;0&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;payg&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;Number&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;r&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;payg_credits&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="mi"&gt;0&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;free&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;Number&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;r&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;credits&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

    &lt;span class="c1"&gt;// Calculate split waterfall: Subscription Credits -&amp;gt; PAYG -&amp;gt; Free Credits&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;breakdown&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;splitWaterfall&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;amount&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;sub&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;payg&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;free&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;newSub&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;sub&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="nx"&gt;breakdown&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;fromSubscription&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;newPayg&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;payg&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="nx"&gt;breakdown&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;fromPayg&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;newFree&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;free&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="nx"&gt;breakdown&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;fromFree&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

    &lt;span class="c1"&gt;// Perform atomic deduction&lt;/span&gt;
    &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;tx&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;execute&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
      &lt;span class="na"&gt;sql&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;UPDATE users SET subscription_credits = ?, payg_credits = ?, credits = ? WHERE id = ?&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="na"&gt;args&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;newSub&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;newPayg&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;newFree&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="p"&gt;});&lt;/span&gt;

    &lt;span class="c1"&gt;// Log to audit credit_ledger in the same transaction&lt;/span&gt;
    &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;tx&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;execute&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
      &lt;span class="na"&gt;sql&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;INSERT INTO credit_ledger (id, user_id, kind, job_id, feature, amount, from_subscription, from_payg, from_free) VALUES (?, ?, 'deduct', ?, ?, ?, ?, ?, ?)&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="na"&gt;args&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nf"&gt;randomUUID&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="nx"&gt;meta&lt;/span&gt;&lt;span class="p"&gt;?.&lt;/span&gt;&lt;span class="nx"&gt;jobId&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;meta&lt;/span&gt;&lt;span class="p"&gt;?.&lt;/span&gt;&lt;span class="nx"&gt;feature&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;amount&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;breakdown&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;fromSubscription&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;breakdown&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;fromPayg&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;breakdown&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;fromFree&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
    &lt;span class="p"&gt;});&lt;/span&gt;
    &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;tx&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;commit&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;catch &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;error&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;tx&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;rollback&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;If the downstream generation fails (e.g., the model provider returns a transient error), &lt;code&gt;refundCredits()&lt;/code&gt; parses the exact ledger breakdown and restores the credits to their original buckets with zero-ambiguity.&lt;/p&gt;

&lt;h3&gt;
  
  
  3. Verification Security &amp;amp; Replay Prevention
&lt;/h3&gt;

&lt;p&gt;When refactoring &lt;code&gt;billing.controller.ts&lt;/code&gt;, we prompted Kiro to prioritize security. Kiro helped us implement a &lt;code&gt;verifyPurchase()&lt;/code&gt; endpoint that enforces strict checks:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;  &lt;strong&gt;Replay Prevention:&lt;/strong&gt; Purchase tokens are hashed and checked against the database (&lt;code&gt;iap_${purchaseToken}&lt;/code&gt; or &lt;code&gt;restore_${purchaseToken}&lt;/code&gt;) to block duplicate claims.&lt;/li&gt;
&lt;li&gt;  &lt;strong&gt;Cross-Account Token Guard:&lt;/strong&gt; Prevent users from claiming tokens purchased by another account (&lt;code&gt;tokenOwner.user_id !== userId&lt;/code&gt;).&lt;/li&gt;
&lt;li&gt;  &lt;strong&gt;Accumulation &amp;amp; Zeroing Policy:&lt;/strong&gt; A paid subscription zeroes the free &lt;code&gt;credits&lt;/code&gt; bucket to prevent stacking free and paid allocations. Upgrades dynamically add credits (&lt;code&gt;subscription_credits = subscription_credits + new_allocation&lt;/code&gt;) without wiping old balances.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  4. Client-Side Android Billing Flows
&lt;/h3&gt;

&lt;p&gt;Writing the Android client logic manually can be error-prone, but Kiro mapped the Google Play replacement modes flawlessly. In &lt;code&gt;BillingViewModel.kt&lt;/code&gt;, we handle Google Play subscriptions by calculating the difference between plans to assign the correct replacement mode:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight kotlin"&gt;&lt;code&gt;&lt;span class="kd"&gt;val&lt;/span&gt; &lt;span class="py"&gt;replacementMode&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;targetRank&lt;/span&gt; &lt;span class="p"&gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;currentRank&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="c1"&gt;// Upgrade (Pro -&amp;gt; Max)&lt;/span&gt;
    &lt;span class="nc"&gt;BillingFlowParams&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;SubscriptionUpdateParams&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;ReplacementMode&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;CHARGE_FULL_PRICE&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;else&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="c1"&gt;// Downgrade (Max -&amp;gt; Pro)&lt;/span&gt;
    &lt;span class="nc"&gt;BillingFlowParams&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;SubscriptionUpdateParams&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;ReplacementMode&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;WITHOUT_PRORATION&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This ensures Google Play charges the full price immediately for upgrades (carrying over remaining time), while downgrades only apply at the next renewal, preserving the user's current tier for the active billing cycle.&lt;/p&gt;




&lt;h2&gt;
  
  
  ⚡ The Platform Configuration Layer (Zero-Redeploy Architecture)
&lt;/h2&gt;

&lt;p&gt;To minimize downtime, we asked Kiro to help architect a database-driven provider orchestration layer. This allows us to scale rate limits, switch providers, and monitor errors without redeploying code:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;  &lt;strong&gt;Visual Generation Routing:&lt;/strong&gt; All image and video generations route through a multi-key API key pool (&lt;code&gt;API_KEY_0&lt;/code&gt; and &lt;code&gt;API_KEY_1&lt;/code&gt;) targeting our serverless model generation endpoints (FLUX, LTX-Video, and WanVideo).&lt;/li&gt;
&lt;li&gt;  &lt;strong&gt;DB-Driven Settings:&lt;/strong&gt; Rate limits and feature flags are loaded from Turso DB (&lt;code&gt;provider_config&lt;/code&gt; and &lt;code&gt;feature_flags&lt;/code&gt; tables) and cached in-memory with a 5-minute TTL. Toggling an engine or updating a rate window (e.g., changing a provider's limit of 18/10s to 50/10s) requires a single SQL &lt;code&gt;UPDATE&lt;/code&gt; statement.&lt;/li&gt;
&lt;li&gt;  &lt;strong&gt;Universal Logging &amp;amp; Slack/Telegram Alerts:&lt;/strong&gt; Errors across our generation engines, ElevenLabs, Google TTS, and Gemini TTS are logged in &lt;code&gt;provider_error_logs&lt;/code&gt;. If errors cross a threshold (&amp;gt;10 errors/hour per provider), our backend automatically dispatches a Telegram alert. We can export full error reports as CSVs using &lt;code&gt;/api/admin/provider-error-report.csv&lt;/code&gt; to forward logs directly to support tickets.&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  🤖 Kiro-Driven Development: Our Agentic IDE Experience
&lt;/h2&gt;

&lt;p&gt;As a developer showcase entry, the real hero of Sonna AI's cross-platform journey is &lt;strong&gt;Kiro's Agentic IDE&lt;/strong&gt;. Rather than using Kiro as a simple autocomplete helper, we integrated its plan-first methodology directly into our development lifecycle to coordinate changes between our Next.js web application, NestJS backend, and native Android app.&lt;/p&gt;

&lt;p&gt;Here is a look at what it actually felt like to build and refactor an enterprise-scale SaaS with Kiro:&lt;/p&gt;

&lt;h3&gt;
  
  
  1. Spec-Driven Planning &amp;amp; EARS Notation
&lt;/h3&gt;

&lt;p&gt;Instead of jumping straight into coding and ending up with out-of-sync API models, we initiated our development with Kiro's &lt;strong&gt;Spec-driven workflow&lt;/strong&gt;. Before modifying the billing controllers, we created structured specifications in the &lt;code&gt;/spec&lt;/code&gt; folder:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;  &lt;code&gt;requirements.md&lt;/code&gt;: Mapped business rules (e.g., distinguishing one-time prepaid web passes from recurring Android subscriptions).&lt;/li&gt;
&lt;li&gt;  &lt;code&gt;design.md&lt;/code&gt;: Defined exact API schemas and DB migration templates using formal &lt;strong&gt;EARS (Easy Approach to Requirements Syntax)&lt;/strong&gt; notation.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Kiro processed these specs, generated a detailed roadmap in &lt;code&gt;tasks.md&lt;/code&gt;, and proceeded to coordinate file creations across the codebase—guaranteeing that the Kotlin clients and NestJS server endpoints stayed completely aligned.&lt;/p&gt;

&lt;h3&gt;
  
  
  2. Guarding Architecture Boundaries with Steering Rules
&lt;/h3&gt;

&lt;p&gt;In a large multi-platform workspace, it is easy for AI assistants to mix up layers—like importing Node modules into Android code or writing raw queries inside HTTP controllers. To enforce strict system boundaries, we defined custom steering parameters in &lt;code&gt;.kiro/steering/&lt;/code&gt;:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;  We instructed Kiro to isolate native Android business logic inside dedicated ViewModels and cleanly split DB access using our Repository pattern.&lt;/li&gt;
&lt;li&gt;  We standardized on in-memory caching with a 5-minute TTL for configurations.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Because Kiro respects steering configurations, the generated code felt like it was written by our lead engineer, maintaining zero structural drift.&lt;/p&gt;

&lt;h3&gt;
  
  
  3. Solving the Double-Spend Race Condition: Iterative Refactoring
&lt;/h3&gt;

&lt;p&gt;One of Kiro's most impressive feats was helping us refactor the critical credit deduction race condition. Originally, our NestJS backend did a standard &lt;code&gt;SELECT&lt;/code&gt; then &lt;code&gt;UPDATE&lt;/code&gt; sequence. Under high concurrency across multiple PM2 clustering instances, two requests could check balances simultaneously, bypassing limits.&lt;/p&gt;

&lt;p&gt;We prompted Kiro to refactor the logic in &lt;code&gt;api-utils.ts&lt;/code&gt; to be fully thread-safe. Kiro analyzed the Turso DB SQLite constraints and immediately generated:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt; A database-level &lt;strong&gt;write transaction&lt;/strong&gt; to lock the user row.&lt;/li&gt;
&lt;li&gt; A pure waterfall calculator (&lt;code&gt;splitWaterfall()&lt;/code&gt;) to dynamically subtract from &lt;code&gt;subscription_credits&lt;/code&gt; first, then &lt;code&gt;payg_credits&lt;/code&gt;, and finally &lt;code&gt;credits&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt; An append-only &lt;code&gt;credit_ledger&lt;/code&gt; entry executed inside the exact same database transaction for auditable tracebacks.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Kiro generated the transaction block, verified rollback exceptions, and automatically added concurrency verification suites to our Vitest test suite to ensure the fix was robust.&lt;/p&gt;

&lt;h3&gt;
  
  
  4. Orchestrating Parallel Agents
&lt;/h3&gt;

&lt;p&gt;For the billing launch, we needed to build the Google Play RTDN webhook handler (&lt;code&gt;play-rtdn.service.ts&lt;/code&gt;) while simultaneously updating the Android client's payment state and generating integration tests. &lt;/p&gt;

&lt;p&gt;Kiro orchestrated this by spawning parallel sub-agents:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;  &lt;strong&gt;Agent A&lt;/strong&gt; constructed the NestJS RTDN verification service.&lt;/li&gt;
&lt;li&gt;  &lt;strong&gt;Agent B&lt;/strong&gt; generated the Kotlin client endpoints and mapped Google Play's proration/replacement billing parameters.&lt;/li&gt;
&lt;li&gt;  &lt;strong&gt;Agent C&lt;/strong&gt; wrote our Vitest billing integration tests.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Having parallel agent orchestration eliminated manual API contract negotiation, cutting our development cycle in half.&lt;/p&gt;




&lt;h2&gt;
  
  
  📱 Android Kotlin Client UI Showcase
&lt;/h2&gt;

&lt;p&gt;Here is a quick look at our native mobile client interface built in Kotlin:&lt;/p&gt;

&lt;h3&gt;
  
  
  Home &amp;amp; New Generation
&lt;/h3&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fcvae75xc4l1llceaonde.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fcvae75xc4l1llceaonde.png" alt="Home Screen" width="575" height="1280"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F9w5j98wcfdl51hwkdafi.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F9w5j98wcfdl51hwkdafi.png" alt="New Generation" width="575" height="1280"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Explore Music &amp;amp; Media &amp;amp; Library
&lt;/h3&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fl3ywqweequ9aaz77t5th.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fl3ywqweequ9aaz77t5th.png" alt="Explore Music" width="575" height="1280"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fg4rpvre94eyiwvbwykep.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fg4rpvre94eyiwvbwykep.png" alt="Media Section" width="575" height="1280"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F4s6dhygkiefev65zkqhu.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F4s6dhygkiefev65zkqhu.png" alt="Library Screen" width="575" height="1280"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Creator Space
&lt;/h3&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fkrcs7rofzz5frw6zlx2w.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fkrcs7rofzz5frw6zlx2w.png" alt="Creator Space" width="575" height="1280"&gt;&lt;/a&gt;&lt;/p&gt;




&lt;h2&gt;
  
  
  🚀 Live Demo &amp;amp; Feedback
&lt;/h2&gt;

&lt;p&gt;Sonna AI is now live in production! &lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;  &lt;strong&gt;Web Dashboard:&lt;/strong&gt; Try it out at &lt;a href="https://sonnalabs.app" rel="noopener noreferrer"&gt;sonnalabs.app&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;  &lt;strong&gt;Android App:&lt;/strong&gt; Download the native Kotlin application on the &lt;a href="https://play.google.com/store/apps/details?id=com.sonna.studio" rel="noopener noreferrer"&gt;Google Play Store&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;blockquote&gt;
&lt;p&gt;💡 &lt;strong&gt;Access Policy Note:&lt;/strong&gt; Anyone can download the app and sign up. The free tier gives you access to Google TTS. Generation features for Music, Images, Videos, as well as advanced TTS engines (ElevenLabs and Gemini TTS) require subscription credits or PAYG credits.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;We would love to hear your thoughts on our DB-driven platform layer design, our Google Play billing proration setup, or how you utilize agentic steering files in your own workflows. Any feedback on our Web or Android client UI is highly appreciated!&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Happy coding!&lt;/em&gt;&lt;/p&gt;

</description>
      <category>webdev</category>
      <category>showdev</category>
      <category>ai</category>
      <category>kiro</category>
    </item>
  </channel>
</rss>
