<?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>Forem: kirodotdev</title>
    <description>The latest articles on Forem by kirodotdev (@kirodotdev).</description>
    <link>https://forem.com/kirodotdev</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%2Forganization%2Fprofile_image%2F11225%2F064034a6-e76b-4eb6-a41e-f07febd795b0.png</url>
      <title>Forem: kirodotdev</title>
      <link>https://forem.com/kirodotdev</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://forem.com/feed/kirodotdev"/>
    <language>en</language>
    <item>
      <title>I Built an Interactive Learning Engine Inside an AI Chat App — Here's Every Technical Decision</title>
      <dc:creator>Asad marcus</dc:creator>
      <pubDate>Fri, 24 Apr 2026 15:41:05 +0000</pubDate>
      <link>https://forem.com/kirodotdev/i-built-an-interactive-learning-engine-inside-an-ai-chat-app-heres-every-technical-decision-4jo4</link>
      <guid>https://forem.com/kirodotdev/i-built-an-interactive-learning-engine-inside-an-ai-chat-app-heres-every-technical-decision-4jo4</guid>
      <description>&lt;p&gt;There's a problem that every AI chat app eventually runs into that nobody really talks about openly: text is a terrible medium for teaching certain things. Not because the AI gives bad answers, but because the format itself gets in the way.&lt;/p&gt;

&lt;p&gt;Ask an AI to explain how a sine wave changes with frequency and it'll give you a mathematically correct, well-worded paragraph. You'll read it, nod, and still not &lt;em&gt;feel&lt;/em&gt; what happens when frequency doubles. You need to see the wave. You need to drag a slider and watch it compress in real time. You need the concept to be &lt;em&gt;live&lt;/em&gt; in front of you.&lt;/p&gt;

&lt;p&gt;I built AimiChat as my AI web app, and this problem was bothering me enough that I spent a few weeks building a full interactive learning visualization engine directly into the chat interface. The AI doesn't just answer questions anymore. It generates live, interactive widgets embedded inside its responses. Adjustable math graphs, step-through code walkthroughs, physics simulations, inline quizzes, the whole thing.&lt;/p&gt;

&lt;p&gt;I built this using Kiro as my AI IDE, and honestly the experience taught me a lot about what that kind of tooling is actually good for at a deeper level. This post covers the full technical architecture, the specific problems I ran into and how I solved them, and honest detail about where Kiro genuinely changed how I worked versus where I still had to drive things myself.&lt;/p&gt;

&lt;p&gt;If you're building anything with streaming AI responses, interactive DOM components, or you're just curious about Kiro and what it actually means to use an agentic IDE in practice, I hope this is useful.&lt;/p&gt;




&lt;h2&gt;
  
  
  Understanding the Problem Space First
&lt;/h2&gt;

&lt;p&gt;Before getting into implementation, it's worth understanding &lt;em&gt;why&lt;/em&gt; this is hard. The difficulty isn't in building a graph renderer. Canvas graphs are straightforward. The difficulty is the intersection of three things happening at the same time.&lt;/p&gt;

&lt;p&gt;The AI returns text. Every AI model, regardless of provider, returns a stream of tokens that your frontend assembles into a string. Your markdown parser turns that string into HTML. There's no native concept of "and also render a live React component here."&lt;/p&gt;

&lt;p&gt;Responses stream in progressively. The response doesn't arrive all at once. Tokens come in over one to ten seconds and you're updating the DOM incrementally so the user sees text appearing in real time. This means your parser runs many times on the same message, each time on a slightly longer version of the text.&lt;/p&gt;

&lt;p&gt;Interactive widgets have lifecycle requirements. A Canvas graph needs to mount once, attach event listeners once, and stay mounted. If anything resets &lt;code&gt;innerHTML&lt;/code&gt; on its parent, which streaming forces you to do constantly, the widget is destroyed and its listeners are orphaned.&lt;/p&gt;

&lt;p&gt;These three constraints create a genuine architectural puzzle. You can't mount widgets during streaming. You can't wait until streaming is done to parse because the user would see a blank screen. And you need the widget configs to survive DOM resets. Every approach that seems obvious breaks on one of these three.&lt;/p&gt;

&lt;p&gt;The solution I arrived at is a two-phase registry architecture. But understanding why simpler approaches fail is important before understanding why this one works.&lt;/p&gt;




&lt;h2&gt;
  
  
  Kiro: What It Actually Is and How I Used It
&lt;/h2&gt;

&lt;p&gt;Since this is a community post about building with Kiro, I want to give you an honest picture of what it is rather than just repeating the marketing summary.&lt;/p&gt;

&lt;p&gt;Kiro is an AI-powered IDE built on VS Code, developed by AWS. The surface-level description is "it has AI assistance built in" but that really undersells what makes it meaningfully different from GitHub Copilot or cursor-style tab completion.&lt;/p&gt;

&lt;p&gt;The key concept in Kiro is specs. When you start a significant piece of work, Kiro generates a spec document: a structured breakdown of what you're building, broken into requirements, then tasks, then implementation steps. This spec lives in your project and Kiro uses it to maintain context across an entire feature's development, not just the current file.&lt;/p&gt;

&lt;p&gt;For this project I wrote a prompt describing the interactive learning system. What types of visualizations I wanted, how they'd integrate with the chat streaming system, the security constraints around eval, the JSON contract between the AI model and the renderer. Kiro turned that into a spec with tasks like "implement recursive descent math parser," "build two-phase streaming architecture," "implement graph renderer with parameter sliders," and "add JSON repair pipeline for AI output." Each task had acceptance criteria.&lt;/p&gt;

&lt;p&gt;What this gave me practically: when I was three widget types into building eleven, and I asked Kiro to help me implement the simulation renderer, it understood the full contract from the spec without me re-explaining it. That's the real value. Not tab completion. Persistent project-level context that makes each individual task faster because you're not starting from zero each time.&lt;/p&gt;

&lt;p&gt;Kiro also has agent hooks, which are automated actions that trigger on file save, test run, or other events. I set up a hook that ran a basic smoke test on the widget registry whenever I added a new renderer type, catching mismatches between the type string I registered and the function I exported. Small thing, but it caught two bugs before I found them manually.&lt;/p&gt;

&lt;p&gt;The other Kiro concept worth knowing is steering documents. These are markdown files you put in your project that give Kiro persistent instructions about your codebase conventions. Things like "always use the &lt;code&gt;escapeHtml()&lt;/code&gt; utility instead of setting innerHTML directly with user data," or "responsive canvas sizing always uses &lt;code&gt;devicePixelRatio&lt;/code&gt; for retina support." Kiro reads these before generating code. It meant I stopped seeing the same category of mistake in generated code after I documented my patterns once.&lt;/p&gt;




&lt;h2&gt;
  
  
  Designing the Widget Protocol
&lt;/h2&gt;

&lt;p&gt;The first real design decision was how the AI model communicates what widget to render and with what data. I settled on custom fenced code blocks with an &lt;code&gt;interactive-&lt;/code&gt; prefix as the language identifier.&lt;/p&gt;

&lt;p&gt;A normal markdown code block looks like this in the raw text:&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;// Simplified streaming loop&lt;/span&gt;
&lt;span class="nx"&gt;eventSource&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;onmessage&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;event&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;accumulatedText&lt;/span&gt; &lt;span class="o"&gt;+=&lt;/span&gt; &lt;span class="nx"&gt;event&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;data&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;html&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;markdownToHtml&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;accumulatedText&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="nx"&gt;messageDiv&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;innerHTML&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;html&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="c1"&gt;// This runs 50-200 times per message&lt;/span&gt;
&lt;span class="p"&gt;};&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Every time &lt;code&gt;innerHTML&lt;/code&gt; is assigned, the entire subtree is torn down and rebuilt from scratch. Any Canvas elements lose their contexts. Any event listeners are orphaned. Any widget state is gone. If you mounted a graph on the 30th token, it's destroyed by the 31st.&lt;/p&gt;

&lt;p&gt;The instinct is to try to be smart about partial updates, only re-rendering the parts that changed and preserving existing widgets. This sounds reasonable but breaks quickly because streaming text can change earlier parts of the message and tracking which DOM nodes correspond to which parts of the markdown is genuinely complex.&lt;/p&gt;

&lt;h3&gt;
  
  
  Phase 1: Register, don't render
&lt;/h3&gt;

&lt;p&gt;The solution is to make the markdown parsing phase completely inert with respect to widgets. When the regex finds an &lt;code&gt;interactive-*&lt;/code&gt; code block, it does two things and only two things.&lt;/p&gt;

&lt;p&gt;First, it stores the config in an external Map keyed by a stable hash:&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;// The registry lives outside the DOM, so innerHTML resets can't touch it&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;_ilBlockRegistry&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;Map&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

&lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;_hashCode&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;str&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;hash&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="k"&gt;for &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;i&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="nx"&gt;i&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="nx"&gt;str&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;length&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="nx"&gt;i&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="nx"&gt;hash&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="nx"&gt;hash&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&amp;lt;&lt;/span&gt; &lt;span class="mi"&gt;5&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="nx"&gt;hash&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="nx"&gt;str&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;charCodeAt&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;i&lt;/span&gt;&lt;span class="p"&gt;))&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;return&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;il-&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="nb"&gt;Math&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;abs&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;hash&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;toString&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;36&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 hash takes both the widget type and the JSON string as input, so the same widget config always generates the same ID regardless of how many times streaming re-parses the message. This is the key insight: content-addressed IDs survive DOM resets because they're deterministic.&lt;/p&gt;

&lt;p&gt;Second, it returns a placeholder div instead of any real widget markup:&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;function&lt;/span&gt; &lt;span class="nf"&gt;processInteractiveBlocks&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;html&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="nx"&gt;html&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;replace&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
        &lt;span class="sr"&gt;/&amp;lt;pre&amp;gt;&amp;lt;code&lt;/span&gt;&lt;span class="se"&gt;[^&lt;/span&gt;&lt;span class="sr"&gt;&amp;gt;&lt;/span&gt;&lt;span class="se"&gt;]&lt;/span&gt;&lt;span class="sr"&gt;*class="&lt;/span&gt;&lt;span class="se"&gt;[^&lt;/span&gt;&lt;span class="sr"&gt;"&lt;/span&gt;&lt;span class="se"&gt;]&lt;/span&gt;&lt;span class="sr"&gt;*language-&lt;/span&gt;&lt;span class="se"&gt;(&lt;/span&gt;&lt;span class="sr"&gt;interactive-&lt;/span&gt;&lt;span class="se"&gt;[\w][\w&lt;/span&gt;&lt;span class="sr"&gt;-&lt;/span&gt;&lt;span class="se"&gt;]&lt;/span&gt;&lt;span class="sr"&gt;*&lt;/span&gt;&lt;span class="se"&gt;)[^&lt;/span&gt;&lt;span class="sr"&gt;"&lt;/span&gt;&lt;span class="se"&gt;]&lt;/span&gt;&lt;span class="sr"&gt;*"&lt;/span&gt;&lt;span class="se"&gt;[^&lt;/span&gt;&lt;span class="sr"&gt;&amp;gt;&lt;/span&gt;&lt;span class="se"&gt;]&lt;/span&gt;&lt;span class="sr"&gt;*&amp;gt;&lt;/span&gt;&lt;span class="se"&gt;([\s\S]&lt;/span&gt;&lt;span class="sr"&gt;*&lt;/span&gt;&lt;span class="se"&gt;?)&lt;/span&gt;&lt;span class="sr"&gt;&amp;lt;&lt;/span&gt;&lt;span class="se"&gt;\/&lt;/span&gt;&lt;span class="sr"&gt;code&amp;gt;&amp;lt;&lt;/span&gt;&lt;span class="se"&gt;\/&lt;/span&gt;&lt;span class="sr"&gt;pre&amp;gt;/g&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;match&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;type&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;encodedJson&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;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="o"&gt;!&lt;/span&gt;&lt;span class="nx"&gt;INTERACTIVE_TYPES&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;type&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt; &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;match&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

            &lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;jsonStr&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;encodedJson&lt;/span&gt;
                &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;replace&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sr"&gt;/&amp;lt;&lt;/span&gt;&lt;span class="se"&gt;[^&lt;/span&gt;&lt;span class="sr"&gt;&amp;gt;&lt;/span&gt;&lt;span class="se"&gt;]&lt;/span&gt;&lt;span class="sr"&gt;+&amp;gt;/g&lt;/span&gt;&lt;span class="p"&gt;,&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="nf"&gt;replace&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sr"&gt;/&amp;amp;lt;/g&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;&amp;lt;&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="nf"&gt;replace&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sr"&gt;/&amp;amp;gt;/g&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;&amp;gt;&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="nf"&gt;replace&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sr"&gt;/&amp;amp;amp;/g&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;&amp;amp;&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="nf"&gt;replace&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sr"&gt;/&amp;amp;quot;/g&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="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;replace&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sr"&gt;/&amp;amp;#39;/g&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;'&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;id&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;_hashCode&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;type&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="nx"&gt;jsonStr&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
            &lt;span class="nx"&gt;_ilBlockRegistry&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;set&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;type&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;jsonStr&lt;/span&gt; &lt;span class="p"&gt;});&lt;/span&gt;

            &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="s2"&gt;`&amp;lt;div id="&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;id&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;" class="il-placeholder"&amp;gt;
                &amp;lt;div class="il-loading-spinner"&amp;gt;&amp;lt;/div&amp;gt;
                Loading visualization...
            &amp;lt;/div&amp;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;p&gt;The placeholder is just a div with an id and a loading spinner. Recreating it on every stream tick is fine because it has no state, no listeners, no canvas context. It's just markup.&lt;/p&gt;

&lt;h3&gt;
  
  
  Phase 2: Mount once, after streaming ends
&lt;/h3&gt;

&lt;p&gt;The rendering phase runs exactly once, triggered by your streaming completion event:&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="nx"&gt;eventSource&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;addEventListener&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;done&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="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;messageDiv&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;innerHTML&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;markdownToHtml&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;accumulatedText&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="nf"&gt;renderInteractiveWidgets&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;messageDiv&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;

&lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;renderInteractiveWidgets&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;container&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;placeholders&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;container&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;querySelectorAll&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;.il-placeholder&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

    &lt;span class="nx"&gt;placeholders&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;forEach&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;el&lt;/span&gt; &lt;span class="o"&gt;=&amp;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;config&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;_ilBlockRegistry&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;el&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;id&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="o"&gt;!&lt;/span&gt;&lt;span class="nx"&gt;config&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="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;parsed&lt;/span&gt; &lt;span class="o"&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;parse&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;config&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;jsonStr&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
            &lt;span class="nx"&gt;el&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;innerHTML&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;''&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
            &lt;span class="nx"&gt;el&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;classList&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;replace&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;il-placeholder&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;il-rendered&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
            &lt;span class="nx"&gt;INTERACTIVE_TYPES&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;config&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;type&lt;/span&gt;&lt;span class="p"&gt;](&lt;/span&gt;&lt;span class="nx"&gt;el&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;parsed&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;err&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="nx"&gt;el&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;innerHTML&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;`
                &amp;lt;div class="il-error"&amp;gt;
                    &amp;lt;div class="il-error-title"&amp;gt;Could not render visualization&amp;lt;/div&amp;gt;
                    &amp;lt;details&amp;gt;
                        &amp;lt;summary&amp;gt;Show raw data&amp;lt;/summary&amp;gt;
                        &amp;lt;pre&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nf"&gt;escapeHtml&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;config&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;jsonStr&lt;/span&gt;&lt;span class="p"&gt;)}&lt;/span&gt;&lt;span class="s2"&gt;&amp;lt;/pre&amp;gt;
                    &amp;lt;/details&amp;gt;
                &amp;lt;/div&amp;gt;
            `&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
            &lt;span class="nx"&gt;el&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;classList&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;remove&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;il-placeholder&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;p&gt;The renderer gets a clean, stable div that nothing will ever reset. It can mount canvas elements, attach resize observers, start animation loops, all of it safe.&lt;/p&gt;




&lt;h2&gt;
  
  
  Building a Safe Math Expression Evaluator
&lt;/h2&gt;

&lt;p&gt;The graph renderer needs to evaluate user-parameterized math expressions like &lt;code&gt;sin(a * x) + b * cos(x / 2)&lt;/code&gt; in real time as sliders move. The obvious approach is &lt;code&gt;eval()&lt;/code&gt;. The obvious approach is also a serious security risk in a web app where the expression comes from an AI model responding to arbitrary user input.&lt;/p&gt;

&lt;p&gt;The correct approach is a recursive descent parser. This is a well-studied technique in compiler design and it's worth understanding even outside this specific context.&lt;/p&gt;

&lt;h3&gt;
  
  
  How recursive descent parsing works
&lt;/h3&gt;

&lt;p&gt;A recursive descent parser is built around the grammar of your expression language. Every grammar rule becomes a function. The functions call each other recursively, and the call depth at any point mirrors the nesting depth of the expression.&lt;/p&gt;

&lt;p&gt;For a math expression language, the grammar 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;expression  := term (('+' | '-') term)*
term        := power (('*' | '/') power)*
power       := unary ('^' unary)?
unary       := '-' primary | '+' primary | primary
primary     := number | constant | function_call | '(' expression ')'
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The ordering here encodes operator precedence. Addition and subtraction are at the top level, so they're evaluated last. Multiplication and division are nested one level deeper, so they bind more tightly. Exponentiation is deeper still. This is why &lt;code&gt;2 + 3 * 4&lt;/code&gt; evaluates to 14 and not 20. The &lt;code&gt;term()&lt;/code&gt; call for &lt;code&gt;3 * 4&lt;/code&gt; completes before the addition at the &lt;code&gt;expression()&lt;/code&gt; level sees either operand.&lt;/p&gt;

&lt;p&gt;Here's the tokenizer first:&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="nf"&gt;tokenize&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;expr&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;tokens&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[];&lt;/span&gt;
    &lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;i&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="k"&gt;while &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;i&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="nx"&gt;expr&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;length&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="sr"&gt;/&lt;/span&gt;&lt;span class="se"&gt;\s&lt;/span&gt;&lt;span class="sr"&gt;/&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;test&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;expr&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;i&lt;/span&gt;&lt;span class="p"&gt;]))&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;i&lt;/span&gt;&lt;span class="o"&gt;++&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="k"&gt;continue&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="sr"&gt;/&lt;/span&gt;&lt;span class="se"&gt;[&lt;/span&gt;&lt;span class="sr"&gt;0-9.&lt;/span&gt;&lt;span class="se"&gt;]&lt;/span&gt;&lt;span class="sr"&gt;/&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;test&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;expr&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;i&lt;/span&gt;&lt;span class="p"&gt;]))&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;num&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;''&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
            &lt;span class="k"&gt;while &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;i&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="nx"&gt;expr&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;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="sr"&gt;/&lt;/span&gt;&lt;span class="se"&gt;[&lt;/span&gt;&lt;span class="sr"&gt;0-9.eE&lt;/span&gt;&lt;span class="se"&gt;]&lt;/span&gt;&lt;span class="sr"&gt;/&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;test&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;expr&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;i&lt;/span&gt;&lt;span class="p"&gt;]))&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
                &lt;span class="nx"&gt;num&lt;/span&gt; &lt;span class="o"&gt;+=&lt;/span&gt; &lt;span class="nx"&gt;expr&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;i&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="nx"&gt;tokens&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;push&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="na"&gt;type&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;num&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;val&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nf"&gt;parseFloat&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;num&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="k"&gt;else&lt;/span&gt; &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sr"&gt;/&lt;/span&gt;&lt;span class="se"&gt;[&lt;/span&gt;&lt;span class="sr"&gt;a-zA-Z_&lt;/span&gt;&lt;span class="se"&gt;]&lt;/span&gt;&lt;span class="sr"&gt;/&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;test&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;expr&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;i&lt;/span&gt;&lt;span class="p"&gt;]))&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;id&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;''&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
            &lt;span class="k"&gt;while &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;i&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="nx"&gt;expr&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;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="sr"&gt;/&lt;/span&gt;&lt;span class="se"&gt;[&lt;/span&gt;&lt;span class="sr"&gt;a-zA-Z0-9_&lt;/span&gt;&lt;span class="se"&gt;]&lt;/span&gt;&lt;span class="sr"&gt;/&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;test&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;expr&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;i&lt;/span&gt;&lt;span class="p"&gt;]))&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
                &lt;span class="nx"&gt;id&lt;/span&gt; &lt;span class="o"&gt;+=&lt;/span&gt; &lt;span class="nx"&gt;expr&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;i&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="nx"&gt;tokens&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;push&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="na"&gt;type&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;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;val&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;id&lt;/span&gt; &lt;span class="p"&gt;});&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="nx"&gt;tokens&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;push&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="na"&gt;type&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;op&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;val&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;expr&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;i&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="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="nx"&gt;tokens&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;And the parser, where each grammar rule is a function:&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="nf"&gt;parse&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;tokens&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;vars&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="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;pos&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;peek&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;tokens&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;pos&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;next&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;tokens&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;pos&lt;/span&gt;&lt;span class="o"&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;expression&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;left&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;term&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
        &lt;span class="k"&gt;while &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;peek&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;peek&lt;/span&gt;&lt;span class="p"&gt;().&lt;/span&gt;&lt;span class="nx"&gt;val&lt;/span&gt; &lt;span class="o"&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="o"&gt;||&lt;/span&gt; &lt;span class="nf"&gt;peek&lt;/span&gt;&lt;span class="p"&gt;().&lt;/span&gt;&lt;span class="nx"&gt;val&lt;/span&gt; &lt;span class="o"&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="p"&gt;{&lt;/span&gt;
            &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;op&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;next&lt;/span&gt;&lt;span class="p"&gt;().&lt;/span&gt;&lt;span class="nx"&gt;val&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;right&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;term&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
            &lt;span class="nx"&gt;left&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;op&lt;/span&gt; &lt;span class="o"&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="nx"&gt;left&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="nx"&gt;right&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;left&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="nx"&gt;right&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="nx"&gt;left&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;term&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;left&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;power&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
        &lt;span class="k"&gt;while &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;peek&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;peek&lt;/span&gt;&lt;span class="p"&gt;().&lt;/span&gt;&lt;span class="nx"&gt;val&lt;/span&gt; &lt;span class="o"&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="o"&gt;||&lt;/span&gt; &lt;span class="nf"&gt;peek&lt;/span&gt;&lt;span class="p"&gt;().&lt;/span&gt;&lt;span class="nx"&gt;val&lt;/span&gt; &lt;span class="o"&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="p"&gt;{&lt;/span&gt;
            &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;op&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;next&lt;/span&gt;&lt;span class="p"&gt;().&lt;/span&gt;&lt;span class="nx"&gt;val&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;right&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;power&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
            &lt;span class="nx"&gt;left&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;op&lt;/span&gt; &lt;span class="o"&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="nx"&gt;left&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="nx"&gt;right&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;left&lt;/span&gt; &lt;span class="o"&gt;/&lt;/span&gt; &lt;span class="nx"&gt;right&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="nx"&gt;left&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;power&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;base&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;unary&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="nf"&gt;peek&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="nf"&gt;peek&lt;/span&gt;&lt;span class="p"&gt;().&lt;/span&gt;&lt;span class="nx"&gt;val&lt;/span&gt; &lt;span class="o"&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="p"&gt;{&lt;/span&gt;
            &lt;span class="nf"&gt;next&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
            &lt;span class="nx"&gt;base&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;Math&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;pow&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;base&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nf"&gt;power&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="nx"&gt;base&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;unary&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;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="nf"&gt;peek&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="nf"&gt;peek&lt;/span&gt;&lt;span class="p"&gt;().&lt;/span&gt;&lt;span class="nx"&gt;val&lt;/span&gt; &lt;span class="o"&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="p"&gt;{&lt;/span&gt; &lt;span class="nf"&gt;next&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt; &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="nf"&gt;primary&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="nf"&gt;peek&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="nf"&gt;peek&lt;/span&gt;&lt;span class="p"&gt;().&lt;/span&gt;&lt;span class="nx"&gt;val&lt;/span&gt; &lt;span class="o"&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="p"&gt;{&lt;/span&gt; &lt;span class="nf"&gt;next&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt; &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nf"&gt;primary&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="nf"&gt;primary&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;primary&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;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;t&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;peek&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="o"&gt;!&lt;/span&gt;&lt;span class="nx"&gt;t&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="mi"&gt;0&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;t&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;type&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;num&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="nf"&gt;next&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt; &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;t&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;val&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;t&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;type&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;id&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="nf"&gt;next&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;MathEval&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;CONSTS&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;t&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;val&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;!==&lt;/span&gt; &lt;span class="kc"&gt;undefined&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;MathEval&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;CONSTS&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;t&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;val&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;MathEval&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;FUNCS&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;t&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;val&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="nf"&gt;peek&lt;/span&gt;&lt;span class="p"&gt;()?.&lt;/span&gt;&lt;span class="nx"&gt;val&lt;/span&gt; &lt;span class="o"&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="p"&gt;{&lt;/span&gt;
                &lt;span class="nf"&gt;next&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;args&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nf"&gt;expression&lt;/span&gt;&lt;span class="p"&gt;()];&lt;/span&gt;
                &lt;span class="k"&gt;while &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;peek&lt;/span&gt;&lt;span class="p"&gt;()?.&lt;/span&gt;&lt;span class="nx"&gt;val&lt;/span&gt; &lt;span class="o"&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="p"&gt;{&lt;/span&gt; &lt;span class="nf"&gt;next&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="nf"&gt;push&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;expression&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="nf"&gt;peek&lt;/span&gt;&lt;span class="p"&gt;()?.&lt;/span&gt;&lt;span class="nx"&gt;val&lt;/span&gt; &lt;span class="o"&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="nf"&gt;next&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
                &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;MathEval&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;FUNCS&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;t&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;val&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="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;vars&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;t&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;val&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;!==&lt;/span&gt; &lt;span class="kc"&gt;undefined&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;vars&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;t&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;val&lt;/span&gt;&lt;span class="p"&gt;];&lt;/span&gt;
            &lt;span class="k"&gt;return&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;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;t&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;val&lt;/span&gt; &lt;span class="o"&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="p"&gt;{&lt;/span&gt;
            &lt;span class="nf"&gt;next&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;v&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;expression&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="nf"&gt;peek&lt;/span&gt;&lt;span class="p"&gt;()?.&lt;/span&gt;&lt;span class="nx"&gt;val&lt;/span&gt; &lt;span class="o"&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="nf"&gt;next&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
            &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;v&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;

        &lt;span class="nf"&gt;next&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
        &lt;span class="k"&gt;return&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;return&lt;/span&gt; &lt;span class="nf"&gt;expression&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 security guarantee here is total. This code only ever calls functions from the &lt;code&gt;FUNCS&lt;/code&gt; whitelist and reads values from the &lt;code&gt;CONSTS&lt;/code&gt; and &lt;code&gt;vars&lt;/code&gt; objects. There's no code path that executes arbitrary JavaScript.&lt;/p&gt;

&lt;p&gt;Kiro helped me get the recursive structure scaffolded quickly. What I had to work through myself was the right-associativity for exponentiation, which is easy to get wrong. If you write &lt;code&gt;power()&lt;/code&gt; as left-recursive like &lt;code&gt;term()&lt;/code&gt;, you get left-associativity and &lt;code&gt;2^3^2&lt;/code&gt; gives the wrong answer. I also had to sort out the ordering of checks in &lt;code&gt;primary()&lt;/code&gt; because you need to check &lt;code&gt;CONSTS&lt;/code&gt; before checking &lt;code&gt;FUNCS&lt;/code&gt; since &lt;code&gt;E&lt;/code&gt; as a constant and &lt;code&gt;exp&lt;/code&gt; as a function share a namespace, and a bug here gives silent wrong answers with no error thrown.&lt;/p&gt;




&lt;h2&gt;
  
  
  Handling AI-Generated JSON That Isn't Quite Valid
&lt;/h2&gt;

&lt;p&gt;Here's a real-world problem that doesn't appear in blog posts about AI systems often enough. The model's output is almost right, but not quite, in ways that vary by model version, temperature setting, and prompt phrasing.&lt;/p&gt;

&lt;p&gt;The specific failure modes I ran into:&lt;/p&gt;

&lt;p&gt;Trailing commas. The model sometimes emits &lt;code&gt;{ "a": 1, "b": 2, }&lt;/code&gt; which is valid JavaScript but not JSON. &lt;code&gt;JSON.parse&lt;/code&gt; throws.&lt;/p&gt;

&lt;p&gt;Single quotes. Occasionally &lt;code&gt;{ 'title': 'Sine Wave' }&lt;/code&gt; instead of double quotes.&lt;/p&gt;

&lt;p&gt;Colons corrupted to &lt;code&gt;&amp;gt;&lt;/code&gt;. This one is subtle. When the markdown parser applies HTML encoding, &lt;code&gt;:&lt;/code&gt; can become &lt;code&gt;&amp;gt;&lt;/code&gt; in certain highlight span wrappers, giving you &lt;code&gt;"title"&amp;gt;"Sine Wave"&lt;/code&gt; instead of &lt;code&gt;"title":"Sine Wave"&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Highlight spans inside the JSON. Syntax highlighters wrap parts of code blocks in &lt;code&gt;&amp;lt;span&amp;gt;&lt;/code&gt; elements for coloring. The JSON content has spans injected into it before you ever see it.&lt;/p&gt;

&lt;p&gt;The repair pipeline addresses these in a specific order that matters:&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;// Strip syntax highlight spans BEFORE any entity decoding.&lt;/span&gt;
&lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;jsonStr&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;encodedJson&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;replace&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sr"&gt;/&amp;lt;&lt;/span&gt;&lt;span class="se"&gt;[^&lt;/span&gt;&lt;span class="sr"&gt;&amp;gt;&lt;/span&gt;&lt;span class="se"&gt;]&lt;/span&gt;&lt;span class="sr"&gt;+&amp;gt;/g&lt;/span&gt;&lt;span class="p"&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;// Decode HTML entities&lt;/span&gt;
&lt;span class="nx"&gt;jsonStr&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;jsonStr&lt;/span&gt;
    &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;replace&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sr"&gt;/&amp;amp;lt;/g&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;&amp;lt;&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;replace&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sr"&gt;/&amp;amp;gt;/g&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;&amp;gt;&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="nf"&gt;replace&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sr"&gt;/&amp;amp;amp;/g&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;&amp;amp;&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;replace&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sr"&gt;/&amp;amp;quot;/g&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="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;replace&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sr"&gt;/&amp;amp;#39;/g&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&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;// Collapse whitespace for non-canvas types&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;type&lt;/span&gt; &lt;span class="o"&gt;!==&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;interactive-canvas&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="nx"&gt;jsonStr&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;jsonStr&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;trim&lt;/span&gt;&lt;span class="p"&gt;().&lt;/span&gt;&lt;span class="nf"&gt;replace&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sr"&gt;/&lt;/span&gt;&lt;span class="se"&gt;\n\s&lt;/span&gt;&lt;span class="sr"&gt;*/g&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="p"&gt;}&lt;/span&gt;

&lt;span class="c1"&gt;// Remove trailing commas&lt;/span&gt;
&lt;span class="nx"&gt;jsonStr&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;jsonStr&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;replace&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sr"&gt;/,&lt;/span&gt;&lt;span class="se"&gt;\s&lt;/span&gt;&lt;span class="sr"&gt;*&lt;/span&gt;&lt;span class="se"&gt;([&lt;/span&gt;&lt;span class="sr"&gt;}&lt;/span&gt;&lt;span class="se"&gt;\]])&lt;/span&gt;&lt;span class="sr"&gt;/g&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;$1&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="c1"&gt;// Fix &amp;gt; corruption&lt;/span&gt;
&lt;span class="nx"&gt;jsonStr&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;jsonStr&lt;/span&gt;
    &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;replace&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sr"&gt;/"&lt;/span&gt;&lt;span class="se"&gt;(\w&lt;/span&gt;&lt;span class="sr"&gt;+&lt;/span&gt;&lt;span class="se"&gt;)&lt;/span&gt;&lt;span class="sr"&gt;"&amp;gt;&lt;/span&gt;&lt;span class="se"&gt;([\d&lt;/span&gt;&lt;span class="sr"&gt;.&lt;/span&gt;&lt;span class="se"&gt;]&lt;/span&gt;&lt;span class="sr"&gt;+&lt;/span&gt;&lt;span class="se"&gt;)&lt;/span&gt;&lt;span class="sr"&gt;/g&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;"$1":$2&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="nf"&gt;replace&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sr"&gt;/"&lt;/span&gt;&lt;span class="se"&gt;(\w&lt;/span&gt;&lt;span class="sr"&gt;+&lt;/span&gt;&lt;span class="se"&gt;)&lt;/span&gt;&lt;span class="sr"&gt;"&amp;gt;"&lt;/span&gt;&lt;span class="se"&gt;([^&lt;/span&gt;&lt;span class="sr"&gt;"&lt;/span&gt;&lt;span class="se"&gt;]&lt;/span&gt;&lt;span class="sr"&gt;*&lt;/span&gt;&lt;span class="se"&gt;?)&lt;/span&gt;&lt;span class="sr"&gt;"/g&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;"$1":"$2"&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="c1"&gt;// If still invalid, try single-to-double quote conversion&lt;/span&gt;
&lt;span class="k"&gt;try&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;parse&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;jsonStr&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="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;singleQuoteFix&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;jsonStr&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;replace&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sr"&gt;/'/g&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="k"&gt;try&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;parse&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;singleQuoteFix&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
        &lt;span class="nx"&gt;jsonStr&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;singleQuoteFix&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="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;combined&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;singleQuoteFix&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;replace&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sr"&gt;/"&lt;/span&gt;&lt;span class="se"&gt;([^&lt;/span&gt;&lt;span class="sr"&gt;"&lt;/span&gt;&lt;span class="se"&gt;]&lt;/span&gt;&lt;span class="sr"&gt;+&lt;/span&gt;&lt;span class="se"&gt;)&lt;/span&gt;&lt;span class="sr"&gt;"&amp;gt;&lt;/span&gt;&lt;span class="se"&gt;\s&lt;/span&gt;&lt;span class="sr"&gt;*/g&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;"$1":&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="nx"&gt;JSON&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;parse&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;combined&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
            &lt;span class="nx"&gt;jsonStr&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;combined&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="c1"&gt;// All repairs failed, store as-is and surface error at render time&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 ordering of the first two steps is the non-obvious part that Kiro helped me reason through. If you decode HTML entities before stripping tags, then &lt;code&gt;&amp;amp;lt;span&amp;amp;gt;&lt;/code&gt; becomes &lt;code&gt;&amp;lt;span&amp;gt;&lt;/code&gt; before the tag stripper runs, which is fine. But &lt;code&gt;&amp;amp;lt;10&lt;/code&gt; becomes &lt;code&gt;&amp;lt;10&lt;/code&gt; which looks like a malformed tag to the stripper and gets incorrectly removed. Strip tags first on the encoded string, then decode the entities on the cleaned result.&lt;/p&gt;




&lt;h2&gt;
  
  
  Canvas Rendering: DPI, Coordinates, and Performance
&lt;/h2&gt;

&lt;p&gt;The graph renderer draws to an HTML Canvas element. Canvas has a subtle gotcha that causes blurry output on retina displays if you don't handle it. There are two separate size concepts.&lt;/p&gt;

&lt;p&gt;The CSS size is what you set with &lt;code&gt;canvas.style.width&lt;/code&gt; and &lt;code&gt;canvas.style.height&lt;/code&gt;. This controls how much space the element takes up in the layout.&lt;/p&gt;

&lt;p&gt;The buffer size is what you set with &lt;code&gt;canvas.width&lt;/code&gt; and &lt;code&gt;canvas.height&lt;/code&gt;. This is the actual resolution of the pixel buffer the browser renders into.&lt;/p&gt;

&lt;p&gt;On a retina display, &lt;code&gt;window.devicePixelRatio&lt;/code&gt; is 2 or 3 on some phones. If your CSS size is 700px wide but your buffer is also 700px, the browser has to upscale the buffer 2x to fill the CSS space and the result is visibly blurry. The fix is to make the buffer 2x the CSS size and then scale the drawing context by the same factor:&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;function&lt;/span&gt; &lt;span class="nf"&gt;resizeCanvas&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;containerWidth&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;wrapper&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getBoundingClientRect&lt;/span&gt;&lt;span class="p"&gt;().&lt;/span&gt;&lt;span class="nx"&gt;width&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="mi"&gt;32&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;dpr&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;window&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;devicePixelRatio&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

    &lt;span class="nx"&gt;canvas&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;width&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;containerWidth&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="nx"&gt;dpr&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="nx"&gt;canvas&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;height&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;containerWidth&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="mf"&gt;0.55&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="nx"&gt;dpr&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

    &lt;span class="nx"&gt;canvas&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;style&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;width&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;containerWidth&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;px&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="nx"&gt;canvas&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;style&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;height&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;containerWidth&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="mf"&gt;0.55&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;px&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

    &lt;span class="nx"&gt;ctx&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;setTransform&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;dpr&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="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;dpr&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="mi"&gt;0&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;After this, all your drawing code works in CSS pixel coordinates. &lt;code&gt;ctx.fillRect(0, 0, 100, 100)&lt;/code&gt; draws a 100 CSS pixel square that looks sharp on any display density.&lt;/p&gt;

&lt;p&gt;The coordinate transform from math space to canvas space is then layered on top:&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;function&lt;/span&gt; &lt;span class="nf"&gt;toCanvasX&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;x&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="nx"&gt;pad&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;left&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="nx"&gt;x&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="nx"&gt;xRange&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="o"&gt;/&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;xRange&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="nx"&gt;xRange&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="o"&gt;*&lt;/span&gt; &lt;span class="nx"&gt;plotWidth&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;toCanvasY&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;y&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="nx"&gt;pad&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;top&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="nx"&gt;yRange&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="nx"&gt;y&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="nx"&gt;yRange&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="nx"&gt;yRange&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="o"&gt;*&lt;/span&gt; &lt;span class="nx"&gt;plotHeight&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 y-inversion is important. In math, y increases upward. In canvas, y increases downward. So &lt;code&gt;yRange[1] - y&lt;/code&gt; gives you the correct mapping.&lt;/p&gt;

&lt;p&gt;For plotting the actual curve, I sample 400 points across the x range and handle discontinuities explicitly. Functions like &lt;code&gt;tan(x)&lt;/code&gt; have vertical asymptotes where the value jumps from large positive to large negative. Without handling this you'd get a visible vertical line at the asymptote:&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;points&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[];&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;let&lt;/span&gt; &lt;span class="nx"&gt;i&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="nx"&gt;i&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;=&lt;/span&gt; &lt;span class="nx"&gt;numPoints&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="nx"&gt;i&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="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;x&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;xRange&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="o"&gt;+&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;i&lt;/span&gt; &lt;span class="o"&gt;/&lt;/span&gt; &lt;span class="nx"&gt;numPoints&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="nx"&gt;xRange&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="nx"&gt;xRange&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;y&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;MathEval&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;evaluate&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;f&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;expr&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;x&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;...&lt;/span&gt;&lt;span class="nx"&gt;paramValues&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="nf"&gt;isNaN&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;y&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="nf"&gt;isFinite&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;y&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="nx"&gt;y&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="nx"&gt;yRange&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="o"&gt;-&lt;/span&gt; &lt;span class="mi"&gt;50&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="nx"&gt;y&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;yRange&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="mi"&gt;50&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="nx"&gt;points&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;push&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="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="nx"&gt;points&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;push&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="na"&gt;cx&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nf"&gt;toCanvasX&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;x&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="na"&gt;cy&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nf"&gt;toCanvasY&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;y&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;span class="nx"&gt;ctx&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;beginPath&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;penDown&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="nx"&gt;points&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;forEach&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;p&lt;/span&gt; &lt;span class="o"&gt;=&amp;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="o"&gt;!&lt;/span&gt;&lt;span class="nx"&gt;p&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;penDown&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kc"&gt;false&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="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="o"&gt;!&lt;/span&gt;&lt;span class="nx"&gt;penDown&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;ctx&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;moveTo&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;p&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;cx&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;p&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;cy&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt; &lt;span class="nx"&gt;penDown&lt;/span&gt; &lt;span class="o"&gt;=&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;span class="k"&gt;else&lt;/span&gt; &lt;span class="nx"&gt;ctx&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;lineTo&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;p&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;cx&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;p&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;cy&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;
&lt;span class="nx"&gt;ctx&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;stroke&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h2&gt;
  
  
  Performance: IntersectionObserver for Animation Loops
&lt;/h2&gt;

&lt;p&gt;The &lt;code&gt;interactive-canvas&lt;/code&gt; widget type runs a continuous &lt;code&gt;requestAnimationFrame&lt;/code&gt; loop for animated visualizations like oscillating waves or particle simulations. On a page with several of these in a long conversation, running all of them all the time is expensive even when they're off-screen.&lt;/p&gt;

&lt;p&gt;The fix is &lt;code&gt;IntersectionObserver&lt;/code&gt;, which fires a callback whenever an element enters or leaves the viewport:&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;let&lt;/span&gt; &lt;span class="nx"&gt;active&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;animFrame&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kc"&gt;null&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;loop&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="o"&gt;!&lt;/span&gt;&lt;span class="nx"&gt;active&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="nx"&gt;animTime&lt;/span&gt; &lt;span class="o"&gt;+=&lt;/span&gt; &lt;span class="mf"&gt;0.016&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="nf"&gt;draw&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
    &lt;span class="nx"&gt;animFrame&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;requestAnimationFrame&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;loop&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;observer&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;IntersectionObserver&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;entries&lt;/span&gt; &lt;span class="o"&gt;=&amp;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;isVisible&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;entries&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;isIntersecting&lt;/span&gt; &lt;span class="o"&gt;??&lt;/span&gt; &lt;span class="kc"&gt;false&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;isVisible&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="nx"&gt;active&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="nx"&gt;active&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
        &lt;span class="nf"&gt;loop&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;else&lt;/span&gt; &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="nx"&gt;isVisible&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="nx"&gt;active&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="nx"&gt;active&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kc"&gt;false&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;animFrame&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="nf"&gt;cancelAnimationFrame&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;animFrame&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;span class="na"&gt;threshold&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mf"&gt;0.1&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;

&lt;span class="nx"&gt;observer&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;observe&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;widgetElement&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This means only the widgets currently visible on screen are running their animation loops. With five physics simulations in a long conversation, this is the difference between 10ms/frame and 50ms/frame.&lt;/p&gt;




&lt;h2&gt;
  
  
  LaTeX Rendering with KaTeX
&lt;/h2&gt;

&lt;p&gt;Many of the widget titles and descriptions contain mathematical notation. I integrated KaTeX rather than MathJax because KaTeX renders synchronously. MathJax is more complete but uses an async API that adds complexity when you're updating the DOM incrementally during streaming.&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;function&lt;/span&gt; &lt;span class="nf"&gt;renderKaTeX&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;text&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="o"&gt;!&lt;/span&gt;&lt;span class="nx"&gt;text&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="k"&gt;typeof&lt;/span&gt; &lt;span class="nx"&gt;katex&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;undefined&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nf"&gt;escapeHtml&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;text&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

    &lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;result&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;escapeHtml&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;text&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

    &lt;span class="nx"&gt;result&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;result&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;replace&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sr"&gt;/&lt;/span&gt;&lt;span class="se"&gt;\$\$([\s\S]&lt;/span&gt;&lt;span class="sr"&gt;*&lt;/span&gt;&lt;span class="se"&gt;?)\$\$&lt;/span&gt;&lt;span class="sr"&gt;/g&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;_&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;tex&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;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="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;katex&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;renderToString&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;tex&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;displayMode&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;throwOnError&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="k"&gt;catch&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nf"&gt;escapeHtml&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;tex&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="nx"&gt;result&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;result&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;replace&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sr"&gt;/&lt;/span&gt;&lt;span class="se"&gt;\$([^\$]&lt;/span&gt;&lt;span class="sr"&gt;+&lt;/span&gt;&lt;span class="se"&gt;?)\$&lt;/span&gt;&lt;span class="sr"&gt;/g&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;_&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;tex&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;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="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;katex&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;renderToString&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;tex&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;displayMode&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;throwOnError&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="k"&gt;catch&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nf"&gt;escapeHtml&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;tex&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="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;result&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 &lt;code&gt;throwOnError: false&lt;/code&gt; option is important for production. If the AI generates slightly malformed LaTeX, you want graceful degradation to raw text rather than an uncaught exception that kills the widget render.&lt;/p&gt;

&lt;p&gt;Display math gets processed first. If you process inline &lt;code&gt;$...$&lt;/code&gt; first, the regex will match the first &lt;code&gt;$&lt;/code&gt; of a &lt;code&gt;$$...$$&lt;/code&gt; block and corrupt it.&lt;/p&gt;




&lt;h2&gt;
  
  
  How Kiro Actually Changed My Day-to-Day
&lt;/h2&gt;

&lt;p&gt;I want to be specific here because "AI-powered IDE" covers a lot of ground and most descriptions stay vague.&lt;/p&gt;

&lt;p&gt;Adding new widget types is where Kiro made me genuinely faster. Once I had the graph and code-trace renderers working, I had a clear pattern. Kiro could read my existing renderers, understand the contract, and produce a solid first draft of the timeline or flowchart renderer that already matched my conventions. I'd typically spend maybe 30-40% of the time I would have spent writing it from scratch, with the rest going toward refinement rather than structure.&lt;/p&gt;

&lt;p&gt;The spec-based context also caught bugs I'd have missed. When I refactored the hash function from random IDs to content-based IDs, Kiro flagged three places where I was still generating IDs the old way. Each would have been a silent bug with no error, just widgets that occasionally failed to render.&lt;/p&gt;

&lt;p&gt;Visual quality is still something I had to drive manually. The rendering math, the gradient fills, the glow effects on plotted curves, the tooltip positioning, all of that required iteration with eyes on the actual output. Kiro could implement a line renderer. Whether it looked right was something I had to evaluate myself. That's honestly the right boundary. Code structure is something an AI IDE can carry a lot of. Aesthetic judgment isn't.&lt;/p&gt;

&lt;p&gt;The steering documents were underrated honestly. I put in conventions like "always use &lt;code&gt;devicePixelRatio&lt;/code&gt; for canvas sizing" and "never set innerHTML directly with user data." Kiro followed these consistently in generated code and I stopped seeing the same categories of mistake repeatedly.&lt;/p&gt;




&lt;h2&gt;
  
  
  What I'd Build Differently
&lt;/h2&gt;

&lt;p&gt;I'd go schema-first. Defining TypeScript interfaces for every widget config before writing any renderer would have saved a lot of pain. Right now the JSON repair pipeline and the renderers' defensive defaults are doing work that a proper schema validator could do more clearly. Something like:&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="kr"&gt;interface&lt;/span&gt; &lt;span class="nx"&gt;GraphConfig&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nl"&gt;title&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;description&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;functions&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;Array&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="na"&gt;expr&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;label&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;color&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="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="nl"&gt;parameters&lt;/span&gt;&lt;span class="p"&gt;?:&lt;/span&gt; &lt;span class="nb"&gt;Array&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="na"&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="nl"&gt;label&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;min&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="nl"&gt;max&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="nl"&gt;default&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="nl"&gt;step&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="p"&gt;}&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="nl"&gt;xRange&lt;/span&gt;&lt;span class="p"&gt;:&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="kr"&gt;number&lt;/span&gt;&lt;span class="p"&gt;];&lt;/span&gt;
    &lt;span class="nl"&gt;yRange&lt;/span&gt;&lt;span class="p"&gt;:&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="kr"&gt;number&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;I'd also separate the streaming buffer from the display layer. Right now they're more coupled than I'd like. The cleaner approach would be an intermediate representation where you parse markdown into a tree of content nodes and widget placeholder nodes, then render the tree to DOM once at the end rather than rebuilding innerHTML incrementally. More complex upfront, cleaner long-term.&lt;/p&gt;

&lt;p&gt;And for the canvas widget type specifically, I'd add server-side static analysis of the generated draw functions before shipping at scale. For now the attack surface is limited, but it's something that should be handled properly before this gets in front of a lot of users.&lt;/p&gt;




&lt;h2&gt;
  
  
  The Result
&lt;/h2&gt;

&lt;p&gt;When a user asks "show me how frequency and amplitude interact in a sine wave," they get a graph with two sliders and a crosshair tooltip. When they ask "walk me through merge sort," they get a step-by-step code trace with variable state visible at each step. When they ask about quantum mechanics, they get an animated canvas simulation they can pause and scrub.&lt;/p&gt;

&lt;p&gt;This is what AI-assisted learning should look like. Not longer text answers. Active answers that respond to interaction.&lt;/p&gt;

&lt;p&gt;The full app is at &lt;a href="https://aimichat.app" rel="noopener noreferrer"&gt;aimichat.app&lt;/a&gt; while still in beta. Every interactive learning widget is available on all plans, and the feature works on any topic the AI decides warrants a visual explanation.&lt;/p&gt;

&lt;p&gt;If any part of this architecture is something you're working on, whether that's streaming widget systems, safe expression evaluation, canvas rendering, or the two-phase DOM approach, I'm happy to go deeper in the comments.&lt;/p&gt;




</description>
      <category>ai</category>
      <category>programming</category>
      <category>kiro</category>
      <category>aws</category>
    </item>
    <item>
      <title>Analyzing React Best Practices with Kiro Powers</title>
      <dc:creator>Salih Guler </dc:creator>
      <pubDate>Wed, 21 Jan 2026 08:59:21 +0000</pubDate>
      <link>https://forem.com/kirodotdev/analyzing-react-best-practices-with-kiro-powers-4i1f</link>
      <guid>https://forem.com/kirodotdev/analyzing-react-best-practices-with-kiro-powers-4i1f</guid>
      <description>&lt;p&gt;Developers pair-program with AI assistants to build software outside your expert areas. One big example I have observed is, backend and cloud engineers building frontends. However, they all have the same concern: how do you verify the frontend code quality and be sure you follow the best practices? &lt;/p&gt;

&lt;p&gt;On last Wednesday (Jan 14th), Vercel &lt;a href="https://vercel.com/blog/introducing-react-best-practices" rel="noopener noreferrer"&gt;announced&lt;/a&gt; the set of AI skills to follow the best practices for React and Next.js applications, addressing this exact need. Therefore, I decided to bring these best practices to Kiro as well by building a Kiro Power. &lt;/p&gt;

&lt;p&gt;Before we move forward, a disclaimer: This is not an official version, it is just me using Kiro to build a Kiro power :) &lt;/p&gt;

&lt;h2&gt;
  
  
  Power Builder
&lt;/h2&gt;

&lt;p&gt;We have been talking about Kiro Powers but what are they? Kiro Powers inject specialized context and tools into Kiro agents on-demand, providing focused knowledge exactly when needed. Unlike traditional context injection that loads everything upfront, Powers activate specific capabilities dynamically. &lt;/p&gt;

&lt;p&gt;You might be wondering, why not just use MCP? Even though MCP is a great solution for many problems, it comes with trade-offs. To find an answer or a content, it still needs to go through information and it has an impact on the context window. However with powers, you can focus and bring only what you need and save some context window.&lt;/p&gt;

&lt;p&gt;To build Kiro Powers you can use the &lt;a href="https://github.com/kirodotdev/powers/tree/main/power-builder" rel="noopener noreferrer"&gt;Power Builder&lt;/a&gt;. You can add it with a single click to your project.&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.amazonaws.com%2Fuploads%2Farticles%2Fdmscuy6td7j54iep3wzl.jpg" 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.amazonaws.com%2Fuploads%2Farticles%2Fdmscuy6td7j54iep3wzl.jpg" alt="Kiro IDE screenshot with a highlight of showcasing Power Builder installed." width="800" height="497"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Once you add that, now Kiro has the resources and context to build new Kiro Powers. &lt;/p&gt;

&lt;h2&gt;
  
  
  Prompt that bringing all together
&lt;/h2&gt;

&lt;p&gt;Writing a proper prompt is one of the most fun but also challenging parts of building with AI nowadays. We have to have crisp, well written instructions on what we want to build and how we want to build. We can't say "build me a sign up" page and expect AI to build everything without missing a point with the design that we are imagining. &lt;/p&gt;

&lt;p&gt;There are great resources such as &lt;a href="https://help.openai.com/en/articles/6654000-best-practices-for-prompt-engineering-with-the-openai-api" rel="noopener noreferrer"&gt;this FAQ section&lt;/a&gt; from Open AI on how to write prompts but I highly recommend the the &lt;a href="https://dev.to/aws/how-i-used-kiro-to-optimize-its-own-mcp-configuration-4mdg"&gt;great article&lt;/a&gt; that my colleague Danilo Poccia wrote on how you can optimize your AI assistant to optimize itself and it helped me a lot to optimize my prompt as well. &lt;/p&gt;

&lt;p&gt;Each step in this prompt serves a purpose: Step 1 ensures complete feature coverage, Step 2 guarantees proper Power structure, Step 3 defines clear specifications, and Step 4 creates a review checkpoint before execution. This structure helps you adapt the prompt for your own Power creation.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight markdown"&gt;&lt;code&gt;&lt;span class="p"&gt;1.&lt;/span&gt; I want to create a Kiro Power from the Vercel agent-skills repository. Clone https://github.com/vercel-labs/agent-skills and analyze all files including subfolders to understand the complete feature set.
&lt;span class="p"&gt;
2.&lt;/span&gt; Research the official Kiro Powers documentation online to understand the correct folder structure, POWER.md frontmatter syntax (name, displayName, description, keywords, mcpServers), and steering file best practices.
&lt;span class="p"&gt;
3.&lt;/span&gt; Create a Power called "react-best-practices" with:
a. Publisher: "Salih Güler"
b. All agent skills converted to steering files (exclude Vercel deployment content)
c. Proper keywords that match how developers naturally talk about React/Next.js optimization
d. A README explaining how to install and use the Power
&lt;span class="p"&gt;
4.&lt;/span&gt; Prepare a detailed plan first and propose it to me before creating any files. Show me:
a. Which agent skill files you found and what they cover
b. How you'll organize them into steering files
c. The proposed keywords for activation
d. The POWER.md frontmatter you'll use
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Once I ran the prompt, Kiro activated the "Power Builder", went through the files, came up with a plan to map &lt;strong&gt;rules&lt;/strong&gt; to &lt;strong&gt;steering files&lt;/strong&gt; and created a plan to create a &lt;strong&gt;POWER.md&lt;/strong&gt; file and asked me if I want to execute it. After I said yes, all was done in a few seconds:&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.amazonaws.com%2Fuploads%2Farticles%2F4n3j65p9o9r28o5ap24j.jpg" 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.amazonaws.com%2Fuploads%2Farticles%2F4n3j65p9o9r28o5ap24j.jpg" alt="Kiro created the Kiro Power for React Best Practices." width="800" height="497"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Using Kiro Powers
&lt;/h2&gt;

&lt;p&gt;You can use Kiro Powers in three different ways. You can use the official ones with a click and install. You can use them from GitHub repositories and from your local folders. &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.amazonaws.com%2Fuploads%2Farticles%2Fg7djet422hyl05r1thp8.jpg" 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.amazonaws.com%2Fuploads%2Farticles%2Fg7djet422hyl05r1thp8.jpg" alt="Import window for importing Kiro Powers. Options are GitHub repository or local file." width="800" height="497"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;You can use the GitHub repository &lt;a href="https://github.com/salihgueler/react-best-practices" rel="noopener noreferrer"&gt;reference&lt;/a&gt; or the local folder to install the power.&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.amazonaws.com%2Fuploads%2Farticles%2F3y24xbyyhfopcdw3hf2c.jpg" 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.amazonaws.com%2Fuploads%2Farticles%2F3y24xbyyhfopcdw3hf2c.jpg" alt="Showcasing the new Kiro Powers successfully installed for React Best Practices." width="800" height="497"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Running the React Best Practices on an Actual Project
&lt;/h2&gt;

&lt;p&gt;I built a &lt;a href="https://github.com/salihgueler/library-recommendation-system" rel="noopener noreferrer"&gt;project&lt;/a&gt; to teach some students about how to build a full-stack application with AWS. &lt;/p&gt;

&lt;p&gt;When I built it, I was more prototyping and building to create a starting point for my students. With this I will check how much improvement that we can make on the project. &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.amazonaws.com%2Fuploads%2Farticles%2Fssaoqlb8fkdah86r0ju6.jpg" 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.amazonaws.com%2Fuploads%2Farticles%2Fssaoqlb8fkdah86r0ju6.jpg" alt="React best practices power is starting to go through the project." width="800" height="497"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Once you run the project, it analyzes your codebase and identifies improvements. In my case, it found:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Missing error boundaries in React components&lt;/li&gt;
&lt;li&gt;Unoptimized image loading without Next.js Image component&lt;/li&gt;
&lt;li&gt;Client-side data fetching that could move to server components&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Here's the full analysis:&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.amazonaws.com%2Fuploads%2Farticles%2F3284qcs4sl94tmss4f14.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.amazonaws.com%2Fuploads%2Farticles%2F3284qcs4sl94tmss4f14.png" alt="Kiro Power Analysis for React Best Practices" width="800" height="1251"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;After you are good with all, you can tell it to implement everything for your project. Once it is done, you can check the project on how many files changed and what changes happened. &lt;/p&gt;

&lt;p&gt;You can also define a global steering file rule to commit the changes by splitting them logically. &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.amazonaws.com%2Fuploads%2Farticles%2Fvg3ehy9nae3fzeld1lk3.jpg" 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.amazonaws.com%2Fuploads%2Farticles%2Fvg3ehy9nae3fzeld1lk3.jpg" alt="All of the suggestions implemented and split in to logical commits. User is given this feedback by the IDE." width="800" height="497"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Conclusion
&lt;/h2&gt;

&lt;p&gt;AI transforms how we build software daily. Kiro Powers offer a focused approach to code quality—activating expert knowledge when you need it without overwhelming your context window.&lt;/p&gt;

&lt;p&gt;What you learned:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;How to convert existing agent skills into Kiro Powers&lt;/li&gt;
&lt;li&gt;The difference between Powers and MCP servers&lt;/li&gt;
&lt;li&gt;A practical workflow for code quality checks&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Considerations:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Powers work best with well-defined rule sets&lt;/li&gt;
&lt;li&gt;Initial setup requires understanding Power structure&lt;/li&gt;
&lt;li&gt;Results depend on your project's complexity&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This Power is part of &lt;a href="https://www.promptz.dev/powers/promptz-power-react-best-practices" rel="noopener noreferrer"&gt;promptz.dev&lt;/a&gt; (a website that you can find great prompts and context files to enhance your development workflow) for broader availability (and you can check the source code and create issues on the on the &lt;a href="https://github.com/cremich/promptz.lib" rel="noopener noreferrer"&gt;promptz.dev repository&lt;/a&gt;. Try it on your React projects and share feedback—your input helps improve the Power for everyone.&lt;/p&gt;

</description>
      <category>kiro</category>
      <category>agents</category>
      <category>ai</category>
      <category>webdev</category>
    </item>
    <item>
      <title>Running Kiro CLI from a Raspberry Pi 400</title>
      <dc:creator>Alvaro Llamojha</dc:creator>
      <pubDate>Thu, 15 Jan 2026 14:32:35 +0000</pubDate>
      <link>https://forem.com/kirodotdev/running-kiro-cli-from-a-raspberry-pi-400-4d2h</link>
      <guid>https://forem.com/kirodotdev/running-kiro-cli-from-a-raspberry-pi-400-4d2h</guid>
      <description>&lt;p&gt;A few years ago, I got a Raspberry Pi 400 as a birthday present. I looked up projects, ideas, cool builds, but nothing really grabbed me. So it went back in the box.&lt;/p&gt;

&lt;p&gt;Recently, I’ve been building with Kiro for a couple of months now, and one day I found the Raspberry Pi and the idea came to me. &lt;/p&gt;

&lt;p&gt;&lt;strong&gt;What if I could run Kiro CLI from the Raspberry Pi?&lt;/strong&gt;&lt;/p&gt;




&lt;h2&gt;
  
  
  Raspberry Pi setup
&lt;/h2&gt;

&lt;p&gt;I had to buy a microSD and a power cord first before I could get hands on. For the OS on the Micro SD, I used &lt;a href="https://www.raspberrypi.com/software/" rel="noopener noreferrer"&gt;Raspberry Pi Imager&lt;/a&gt; selected the default Raspberry Pi OS (Debian-based), went through the configuration, plugged in the Micro SD and connected the Pi. After a couple of minutes, I was able to SSH in. &lt;/p&gt;

&lt;p&gt;SSH in (if it's in your local network):&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;ssh user@&amp;lt;your-pi-hostname-or-ip&amp;gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Then the usual housekeeping + tools I knew I’d need for the Kiro install:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# update the Pi&lt;/span&gt;
&lt;span class="nb"&gt;sudo &lt;/span&gt;apt update &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="nb"&gt;sudo &lt;/span&gt;apt upgrade &lt;span class="nt"&gt;-y&lt;/span&gt;

&lt;span class="c"&gt;# dev box basics (I wanted git, and I installed Node because I plan to build a small wrapper later)&lt;/span&gt;
curl &lt;span class="nt"&gt;-fsSL&lt;/span&gt; https://deb.nodesource.com/setup_20.x | &lt;span class="nb"&gt;sudo&lt;/span&gt; &lt;span class="nt"&gt;-E&lt;/span&gt; bash -
&lt;span class="nb"&gt;sudo &lt;/span&gt;apt &lt;span class="nb"&gt;install&lt;/span&gt; &lt;span class="nt"&gt;-y&lt;/span&gt; nodejs
&lt;span class="nb"&gt;sudo &lt;/span&gt;apt &lt;span class="nb"&gt;install&lt;/span&gt; &lt;span class="nt"&gt;-y&lt;/span&gt; git
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;One thing that becomes important later is that this machine is ARM64:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;uname&lt;/span&gt; &lt;span class="nt"&gt;-m&lt;/span&gt;
&lt;span class="c"&gt;# aarch64 - you can also see this when doing SSH &lt;/span&gt;
ldd &lt;span class="nt"&gt;--version&lt;/span&gt;
&lt;span class="c"&gt;# ldd (Debian GLIBC 2.41-12+rpt1) 2.41&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h2&gt;
  
  
  Installing Kiro CLI and hitting SIGILL
&lt;/h2&gt;

&lt;p&gt;Once everything was ready, I went straight to the official Kiro CLI install docs and followed the Linux ARM (aarch64) instructions:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://kiro.dev/docs/cli/installation/#linux-arm-aarch64" rel="noopener noreferrer"&gt;Kiro CLI — Linux ARM (aarch64)&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;I downloaded it and ran the installer:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;curl &lt;span class="nt"&gt;--proto&lt;/span&gt; &lt;span class="s1"&gt;'=https'&lt;/span&gt; &lt;span class="nt"&gt;--tlsv1&lt;/span&gt;.2 &lt;span class="nt"&gt;-sSf&lt;/span&gt; &lt;span class="s1"&gt;'https://desktop-release.q.us-east-1.amazonaws.com/latest/kirocli-aarch64-linux.zip'&lt;/span&gt; &lt;span class="nt"&gt;-o&lt;/span&gt; &lt;span class="s1"&gt;'kirocli.zip'&lt;/span&gt;
unzip kirocli.zip
bash ./kirocli/install.sh
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The install itself looked fine. The binary was in place. Edit the Path in the .bashrc to include the path for the kiro-cli bin:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s1"&gt;'export PATH="$HOME/.local/bin:$PATH"'&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&amp;gt;&lt;/span&gt; ~/.bashrc
&lt;span class="nb"&gt;source&lt;/span&gt; ~/.bashrc
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;And then I ran:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;kiro-cli
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;…and got:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;Illegal instruction &lt;span class="o"&gt;(&lt;/span&gt;SIGILL&lt;span class="o"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;I knew then that this may not be straightforward, and I may had to refresh my processor architecture knowledge. Just in case, I've tried re-installing, rebooting, trying multiple times but still was the same issue.&lt;/p&gt;

&lt;h2&gt;
  
  
  The fix: use the ARM64 musl build
&lt;/h2&gt;

&lt;p&gt;After going back to the docs (again), I noticed the musl option:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://kiro.dev/docs/cli/installation/#linux-arm-aarch64-with-musl" rel="noopener noreferrer"&gt;Kiro CLI — Linux ARM (aarch64) with musl&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The docs frame it mostly around &lt;code&gt;glibc&lt;/code&gt; version. My &lt;code&gt;glibc&lt;/code&gt; was new enough (2.41), so I assumed the standard build was the right one.&lt;/p&gt;

&lt;p&gt;But at that point, I’d already tried the standard route and it was consistently crashing with SIGILL, so I figured: let’s just try the musl build.&lt;/p&gt;

&lt;p&gt;I installed the aarch64 musl version… and that was it. &lt;code&gt;kiro-cli&lt;/code&gt; ran successfully.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;curl &lt;span class="nt"&gt;--proto&lt;/span&gt; &lt;span class="s1"&gt;'=https'&lt;/span&gt; &lt;span class="nt"&gt;--tlsv1&lt;/span&gt;.2 &lt;span class="nt"&gt;-sSf&lt;/span&gt; &lt;span class="s1"&gt;'https://desktop-release.q.us-east-1.amazonaws.com/latest/kirocli-aarch64-linux-musl.zip'&lt;/span&gt; &lt;span class="nt"&gt;-o&lt;/span&gt; &lt;span class="s1"&gt;'kirocli.zip'&lt;/span&gt;
unzip kirocli.zip 
./kirocli/install.sh
kiro-cli
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&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.amazonaws.com%2Fuploads%2Farticles%2Fu55f445ae92ji2xf6f20.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.amazonaws.com%2Fuploads%2Farticles%2Fu55f445ae92ji2xf6f20.png" alt="Kiro CLI" width="800" height="297"&gt;&lt;/a&gt;&lt;/p&gt;




&lt;h2&gt;
  
  
  Login (headless, over SSH)
&lt;/h2&gt;

&lt;p&gt;Once the CLI actually ran, the login flow was straightforward.&lt;br&gt;
It printed a login URL in the terminal. I copied it, pasted it into my laptop browser, logged in (I used GitHub), went back to the SSH session, and Kiro CLI was authenticated on the Pi.&lt;/p&gt;




&lt;h2&gt;
  
  
  Wrap-up
&lt;/h2&gt;

&lt;p&gt;So yes: you can run Kiro CLI on a Raspberry Pi 400. I've tried running simple and complex tasks, and both worked fine. &lt;/p&gt;

&lt;p&gt;It took me about an hour end-to-end, including the time lost to the SIGILL crash. The key detail was simply using the &lt;strong&gt;ARM64 musl&lt;/strong&gt; build from the docs instead of the standard one.&lt;/p&gt;




&lt;h2&gt;
  
  
  What I’m doing next
&lt;/h2&gt;

&lt;p&gt;Now that it’s running, I want to push this a bit further.&lt;/p&gt;

&lt;p&gt;I want that PM approach for working with Kiro. A workflows that looks like: I create and curate tickets like a PM, and Kiro acts as the dev taking care of the code implementation (running from the Raspberry Pi). &lt;/p&gt;




&lt;p&gt;Do you have a Raspberry Pi and want to try this? What has been your experience with Kiro-CLI? Share it with us in the comments. &lt;/p&gt;

</description>
      <category>raspberrypi</category>
      <category>kiro</category>
    </item>
    <item>
      <title>The Paradigm Shift from Reactive to Proactive AI in Software Development: A Comparative Analysis of AI IDEs</title>
      <dc:creator>Asad marcus</dc:creator>
      <pubDate>Sun, 11 Jan 2026 15:04:06 +0000</pubDate>
      <link>https://forem.com/kirodotdev/the-paradigm-shift-from-reactive-to-proactive-ai-in-software-development-a-comparative-analysis-of-148p</link>
      <guid>https://forem.com/kirodotdev/the-paradigm-shift-from-reactive-to-proactive-ai-in-software-development-a-comparative-analysis-of-148p</guid>
      <description>&lt;h1&gt;
  
  
  A Comparative Analysis of Agentic IDE Architectures: AWS Kiro vs Cursor, Claude Code, GitHub Copilot, and Codeium
&lt;/h1&gt;

&lt;h2&gt;
  
  
  Executive Summary
&lt;/h2&gt;

&lt;p&gt;This analysis compares AWS Kiro, a spec driven agentic IDE released in July 2025, against four incumbent AI coding assistants: Cursor, Claude Code, GitHub Copilot, and Codeium (Windsurf). The core tension examined is the architectural shift from reactive autocomplete to proactive specification based generation.&lt;/p&gt;

&lt;h3&gt;
  
  
  Key Findings
&lt;/h3&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Finding&lt;/th&gt;
&lt;th&gt;Assessment&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Paradigm Shift&lt;/td&gt;
&lt;td&gt;Kiro's mandatory Spec First workflow (User Story → Design → Code) is a distinct architectural choice that empirically reduces logic errors by preventing hallucinated objects common in reactive chat interfaces&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Context Reality&lt;/td&gt;
&lt;td&gt;While Kiro claims superior context persistence via graph based indexing, independent benchmarks indicate all tools still face significant reasoning degradation beyond ~32k tokens. The advantage lies in retrieval strategy, not raw memory&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Enterprise Readiness&lt;/td&gt;
&lt;td&gt;Kiro dominates in compliance inheritance, leveraging AWS's existing SOC/HIPAA posture. However, it lacks the friction free developer experience and plugin maturity of Cursor or VS Code native Copilot&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Developer Autonomy&lt;/td&gt;
&lt;td&gt;Contrary to the automation trend, Kiro's approach succeeds by restoring control. By allowing developers to edit specs rather than just code, it aligns better with 2025 research on professional developer psychology&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;&lt;strong&gt;Overall Verdict:&lt;/strong&gt; Kiro represents a genuine architectural innovation for complex, greenfield enterprise development. However, for rapid iteration and maintenance of existing legacy codebases, reactive tools like Cursor and Copilot likely remain superior due to lower friction.&lt;/p&gt;




&lt;h2&gt;
  
  
  Table of Contents
&lt;/h2&gt;

&lt;ol&gt;
&lt;li&gt;Introduction
&lt;/li&gt;
&lt;li&gt;Subject A: AWS Kiro Deep Dive
&lt;/li&gt;
&lt;li&gt;Subject B: Competitor Analysis
&lt;/li&gt;
&lt;li&gt;Point by Point Comparison
&lt;/li&gt;
&lt;li&gt;Analysis of Similarities and Differences
&lt;/li&gt;
&lt;li&gt;Conclusions and Recommendations
&lt;/li&gt;
&lt;/ol&gt;




&lt;h2&gt;
  
  
  1. Introduction
&lt;/h2&gt;

&lt;h3&gt;
  
  
  1.1 The Evolution of AI Assisted Development
&lt;/h3&gt;

&lt;p&gt;Between 2021 and 2024, the industry standard for AI coding was reactive: autocomplete (Copilot) and chat (ChatGPT/Claude). The interaction model was simple:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Developer writes code → AI suggests completions
&lt;/li&gt;
&lt;li&gt;Developer asks question → AI responds
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;By late 2024, agentic loops emerged. Tools like Cursor Composer and Windsurf Cascade began automating multi file edits, introducing a new paradigm:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Developer describes intent → AI plans changes → AI executes across files
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;As of January 2026, AWS Kiro attempts to formalize this into a fully proactive paradigm, one where the AI doesn't just respond to requests but actively structures the development process itself.&lt;/p&gt;

&lt;h3&gt;
  
  
  1.2 Research Questions
&lt;/h3&gt;

&lt;p&gt;This analysis investigates four core questions:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Architectural Validity: Does the shift from Chat to Spec Driven constitute a genuine paradigm shift, or is it workflow theater?
&lt;/li&gt;
&lt;li&gt;Context Persistence: How do Kiro's context mechanisms compare to RAG based competitors in real world scenarios?
&lt;/li&gt;
&lt;li&gt;Developer Autonomy: Does the agentic model enhance or diminish developer control over their codebase?
&lt;/li&gt;
&lt;li&gt;Enterprise Readiness: Which tool is best positioned for regulated, large scale enterprise deployment?
&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  1.3 Scope
&lt;/h3&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Dimension&lt;/th&gt;
&lt;th&gt;Coverage&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Subject A&lt;/td&gt;
&lt;td&gt;AWS Kiro (Spec Driven Agent)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Subject B&lt;/td&gt;
&lt;td&gt;Cursor, GitHub Copilot, Claude Code, Codeium/Windsurf&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Analysis Dimensions&lt;/td&gt;
&lt;td&gt;Architecture, Context Persistence, Developer Autonomy, Enterprise Readiness&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Time Frame&lt;/td&gt;
&lt;td&gt;Data available as of January 2026&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;




&lt;h2&gt;
  
  
  2. Subject A Overview: AWS Kiro
&lt;/h2&gt;

&lt;h3&gt;
  
  
  2.1 Background
&lt;/h3&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Attribute&lt;/th&gt;
&lt;th&gt;Detail&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Release&lt;/td&gt;
&lt;td&gt;July 2025 (Preview)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Core Engine&lt;/td&gt;
&lt;td&gt;Amazon Bedrock AgentCore / Claude 4 Sonnet family (Sonnet 4.0, 4.5, Opus 4.5)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Architecture&lt;/td&gt;
&lt;td&gt;Code OSS fork with graph based state engine&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Primary Differentiator&lt;/td&gt;
&lt;td&gt;Mandatory spec driven workflow&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;h3&gt;
  
  
  2.2 The Spec Driven Workflow
&lt;/h3&gt;

&lt;p&gt;Unlike chat interfaces where a prompt immediately triggers code generation, Kiro enforces a waterfall like agentic loop:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Step&lt;/th&gt;
&lt;th&gt;Description&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;1. Ingestion&lt;/td&gt;
&lt;td&gt;Developer defines a high level goal. This ensures clarity before any design or code is generated.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;2. Structuring&lt;/td&gt;
&lt;td&gt;Agent produces User Stories and Technical Design documents, creating a formal blueprint for implementation.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;3. Review (Human Gate)&lt;/td&gt;
&lt;td&gt;Developer reviews, edits, and approves all artifacts, restoring control and ensuring correctness.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;4. Execution&lt;/td&gt;
&lt;td&gt;Agent generates production ready code and automated tests based on the approved specifications.&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;h3&gt;
  
  
  2.3 Core Features
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;Specs System&lt;/strong&gt;  &lt;/p&gt;

&lt;p&gt;Kiro's specs are structured documents that capture:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;requirements.md&lt;/code&gt; – User stories and acceptance criteria
&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;design.md&lt;/code&gt; – Technical architecture and implementation plan
&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;tasks.md&lt;/code&gt; – Generated tasks and code that trace back to spec items
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Steering Files&lt;/strong&gt;  &lt;/p&gt;

&lt;p&gt;Persistent instructions in &lt;code&gt;.kiro/steering/*.md&lt;/code&gt; that guide AI behavior across all interactions:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Team coding standards
&lt;/li&gt;
&lt;li&gt;Project specific conventions
&lt;/li&gt;
&lt;li&gt;Always included or conditionally included based on file patterns
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Agent Hooks&lt;/strong&gt;  &lt;/p&gt;

&lt;p&gt;Event-driven automation that triggers AI actions:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;fileEdited&lt;/code&gt; → Run linting
&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;promptSubmit&lt;/code&gt; → Execute pre checks
&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;agentStop&lt;/code&gt; → Generate documentation
&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;contextualHooks&lt;/code&gt; → Trigger actions based on code context, file type, or spec state
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;MCP Integration&lt;/strong&gt;  &lt;/p&gt;

&lt;p&gt;Native Model Context Protocol support for extensibility without vendor lock in.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Kiro Powers&lt;/strong&gt;  &lt;/p&gt;

&lt;p&gt;Reusable, declarative capability bundles that constrain and standardize agent behavior:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Encode allowed actions, guardrails, and expected outputs
&lt;/li&gt;
&lt;li&gt;Enable consistent API creation, refactoring, migrations, and reviews
&lt;/li&gt;
&lt;li&gt;Reduce hallucinations by limiting the agent’s action space
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Sub Agents&lt;/strong&gt;  &lt;/p&gt;

&lt;p&gt;Modular AI agents that can be delegated tasks by the main agent for specialized execution, enabling more scalable and compartmentalized workflows.&lt;/p&gt;

&lt;h3&gt;
  
  
  2.4 Value Proposition
&lt;/h3&gt;

&lt;p&gt;Kiro's thesis: Vibe Coding creates technical debt.  &lt;/p&gt;

&lt;p&gt;When developers use chat based AI to generate zode without explicit design, they get:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Code that looks correct but lacks cohesive architecture
&lt;/li&gt;
&lt;li&gt;Hallucinated objects and inconsistent patterns
&lt;/li&gt;
&lt;li&gt;Difficulty maintaining or extending the codebase
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;By forcing an intermediate design state, Kiro claims to solve this at the source.  &lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Reminder:&lt;/strong&gt; This post evaluates Kiro's Spec-First workflow, not Vibe Mode. Vibe Mode may yield faster output but at higher risk of errors.&lt;/p&gt;




&lt;h2&gt;
  
  
  3. Subject B Overview: Competitors
&lt;/h2&gt;

&lt;h3&gt;
  
  
  3.1 Competitive Landscape
&lt;/h3&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Tool&lt;/th&gt;
&lt;th&gt;Type&lt;/th&gt;
&lt;th&gt;Philosophy&lt;/th&gt;
&lt;th&gt;Primary Interaction&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Cursor&lt;/td&gt;
&lt;td&gt;AI Native IDE&lt;/td&gt;
&lt;td&gt;Flow State&lt;/td&gt;
&lt;td&gt;Fluid mix of inline edits, chat, and agentic Composer mode. Optimizes for speed&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;GitHub Copilot&lt;/td&gt;
&lt;td&gt;Extension + Platform&lt;/td&gt;
&lt;td&gt;Integration&lt;/td&gt;
&lt;td&gt;Deep GitHub ecosystem integration. Workspace offers agentic plans, but primarily reactive&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Claude Code&lt;/td&gt;
&lt;td&gt;CLI / Agent&lt;/td&gt;
&lt;td&gt;Autonomous Logic&lt;/td&gt;
&lt;td&gt;Terminal first agent. Strengths in complex reasoning loops and tool use&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Codeium (Windsurf)&lt;/td&gt;
&lt;td&gt;AI Native IDE&lt;/td&gt;
&lt;td&gt;Deep Context&lt;/td&gt;
&lt;td&gt;Cascade engine focuses on deep awareness of current repo state&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;h3&gt;
  
  
  3.2 Cursor
&lt;/h3&gt;

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

&lt;ul&gt;
&lt;li&gt;Exceptional developer experience (DX)
&lt;/li&gt;
&lt;li&gt;Composer mode for multi file agentic edits
&lt;/li&gt;
&lt;li&gt;Rules for AI for persistent instructions
&lt;/li&gt;
&lt;li&gt;Shadow workspace for safe code testing
&lt;/li&gt;
&lt;li&gt;Rapid iteration speed
&lt;/li&gt;
&lt;/ul&gt;

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

&lt;ul&gt;
&lt;li&gt;Context is largely ephemeral (session based)
&lt;/li&gt;
&lt;li&gt;Less structured approach to complex projects
&lt;/li&gt;
&lt;li&gt;Enterprise compliance requires additional configuration
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Best For:&lt;/strong&gt; Startups, rapid prototyping, developers who prioritize flow state  &lt;/p&gt;

&lt;h3&gt;
  
  
  3.3 GitHub Copilot
&lt;/h3&gt;

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

&lt;ul&gt;
&lt;li&gt;Deepest integration with GitHub ecosystem
&lt;/li&gt;
&lt;li&gt;Copilot Workspace for agentic planning
&lt;/li&gt;
&lt;li&gt;Enterprise tier with strong compliance
&lt;/li&gt;
&lt;li&gt;Familiar VS Code experience
&lt;/li&gt;
&lt;li&gt;CI/CD pipeline integration
&lt;/li&gt;
&lt;/ul&gt;

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

&lt;ul&gt;
&lt;li&gt;Primarily reactive (ghost text suggestions)
&lt;/li&gt;
&lt;li&gt;Agentic features still maturing
&lt;/li&gt;
&lt;li&gt;Less flexible than dedicated AI IDEs
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Best For:&lt;/strong&gt; GitHub native teams, enterprise standardization, CI/CD heavy workflows  &lt;/p&gt;

&lt;h3&gt;
  
  
  3.4 Claude Code
&lt;/h3&gt;

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

&lt;ul&gt;
&lt;li&gt;Superior complex reasoning capabilities
&lt;/li&gt;
&lt;li&gt;Terminal first, scriptable interface
&lt;/li&gt;
&lt;li&gt;Excellent tool use and multi step planning
&lt;/li&gt;
&lt;li&gt;Strong Project Memory via CLAUDE.md
&lt;/li&gt;
&lt;li&gt;Anthropic's safety focused approach
&lt;/li&gt;
&lt;/ul&gt;

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

&lt;ul&gt;
&lt;li&gt;Less polished UI/UX
&lt;/li&gt;
&lt;li&gt;Requires comfort with CLI
&lt;/li&gt;
&lt;li&gt;Context limited by session
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Best For:&lt;/strong&gt; Complex reasoning tasks, terminal native developers, autonomous workflows  &lt;/p&gt;

&lt;h3&gt;
  
  
  3.5 Codeium / Windsurf
&lt;/h3&gt;

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

&lt;ul&gt;
&lt;li&gt;Cascade engine for deep repo awareness
&lt;/li&gt;
&lt;li&gt;Predictive editing based on codebase patterns
&lt;/li&gt;
&lt;li&gt;Strong free tier
&lt;/li&gt;
&lt;li&gt;Good context retrieval
&lt;/li&gt;
&lt;/ul&gt;

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

&lt;ul&gt;
&lt;li&gt;Less mature than Cursor
&lt;/li&gt;
&lt;li&gt;Enterprise features still developing
&lt;/li&gt;
&lt;li&gt;Smaller ecosystem
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Best For:&lt;/strong&gt; Cost conscious teams, deep codebase context needs  &lt;/p&gt;




&lt;h2&gt;
  
  
  4. Point by Point Comparison
&lt;/h2&gt;

&lt;h3&gt;
  
  
  4.1 Architectural Philosophy: Reactive vs. Proactive
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;Kiro (Proactive/Structured)&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Kiro treats code as a downstream artifact of specifications. It is structurally impossible to generate code without a plan.
&lt;/li&gt;
&lt;li&gt;Evidence: OSVBench (April 2025) data shows Specification Driven Approaches reduce logic errors by 23 to 37 percent compared to direct generation.
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;The mechanism:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Without Specs: "Build a user auth system" → [LLM generates code] → Hallucinated patterns
&lt;/li&gt;
&lt;li&gt;With Specs: "Build a user auth system" → [LLM generates spec] → [Human reviews] → [LLM generates code matching spec]
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Reminder:&lt;/strong&gt; Vibe Mode shortcuts this process, generating code directly without specs, which can increase risk of logical or architectural errors.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Competitors (Reactive/Flexible)&lt;/strong&gt;&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Tool&lt;/th&gt;
&lt;th&gt;Approach&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Cursor/Windsurf&lt;/td&gt;
&lt;td&gt;Mixed initiative: user can ask for a plan, but tool defaults to immediate execution&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Copilot&lt;/td&gt;
&lt;td&gt;Primarily reactive suggestions based on cursor position&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Claude Code&lt;/td&gt;
&lt;td&gt;Can plan when asked, but doesn't enforce it&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;




&lt;h2&gt;
  
  
  5. Analysis of Similarities and Differences
&lt;/h2&gt;

&lt;p&gt;Kiro's rigidity is a double edged sword:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Aspect&lt;/th&gt;
&lt;th&gt;Kiro&lt;/th&gt;
&lt;th&gt;Competitors&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Bug reduction&lt;/td&gt;
&lt;td&gt;✅ Supported by research&lt;/td&gt;
&lt;td&gt;⚠️ Depends on user discipline&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Time to first token&lt;/td&gt;
&lt;td&gt;❌ Slower (spec generation required)&lt;/td&gt;
&lt;td&gt;✅ Immediate&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Simple tasks&lt;/td&gt;
&lt;td&gt;❌ Overhead may frustrate&lt;/td&gt;
&lt;td&gt;✅ Frictionless&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Complex tasks&lt;/td&gt;
&lt;td&gt;✅ Architectural integrity&lt;/td&gt;
&lt;td&gt;⚠️ Risk of vibe coding&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;&lt;strong&gt;Verdict:&lt;/strong&gt; The paradigm shift is real regarding capability. However, labeling it a paradigm shift may be marketing hyperbole. It's technically an evolution of tool use capabilities rather than a fundamental change in software theory.&lt;/p&gt;




&lt;h2&gt;
  
  
  6. Conclusions
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Reminder: Throughout this analysis, Kiro's Spec-First workflow is evaluated, not Vibe Mode. Vibe Mode may produce faster results but at higher risk of logic or architectural inconsistencies.
&lt;/li&gt;
&lt;li&gt;AWS Kiro is not merely another IDE. It is an attempt to enforce software engineering best practices through tooling.
&lt;/li&gt;
&lt;li&gt;Its Spec Driven Architecture is scientifically sound, backed by 2025 research showing that separating design from implementation significantly reduces hallucination rates.
&lt;/li&gt;
&lt;li&gt;However, its success depends on the Developer Experience (DX) trade off: Will developers accept the friction of generating specs for the sake of robustness?
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Disclaimer:&lt;/strong&gt; This analysis reflects the state of AWS Kiro and competitor AI coding tools as of Late 2025. It was generated in a AI researcher created by Kiro, leveraging public benchmarks, vendor documentation, and early reports. Some claims may be outdated as tools evolve rapidly, and future research or updates may conflict with findings presented here.  &lt;/p&gt;

</description>
      <category>aws</category>
      <category>ai</category>
      <category>devtools</category>
      <category>kiro</category>
    </item>
    <item>
      <title>Building my first Kiro Power: PostHog Observability</title>
      <dc:creator>Alvaro Llamojha</dc:creator>
      <pubDate>Thu, 18 Dec 2025 01:14:24 +0000</pubDate>
      <link>https://forem.com/kirodotdev/building-my-first-kiro-power-posthog-observability-fik</link>
      <guid>https://forem.com/kirodotdev/building-my-first-kiro-power-posthog-observability-fik</guid>
      <description>&lt;p&gt;When I first explored &lt;strong&gt;Kiro Powers&lt;/strong&gt;, the &lt;strong&gt;Datadog Power&lt;/strong&gt; immediately stood out. I'm a DevOps Engineer focused on Observability and Monitoring. I have experience with Datadog, New Relic and PostHog, so this piqued my attention. I was also participating in a hackathon that used Datadog, so I got to try that Power in a real workflow—not just as a demo. That experience triggered a simple thought: I also use &lt;strong&gt;PostHog&lt;/strong&gt; heavily in another project, and I couldn’t find a PostHog Power. Since PostHog already has an &lt;strong&gt;MCP server&lt;/strong&gt;, it felt like a perfect candidate for my first Power project.&lt;/p&gt;

&lt;p&gt;TLDR; You can try my first Kiro Power for Posthog: &lt;a href="https://github.com/llamojha/posthog-kiro-power" rel="noopener noreferrer"&gt;posthog-kiro-power&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  What are Kiro Powers?
&lt;/h2&gt;

&lt;p&gt;Kiro Powers are &lt;strong&gt;on-demand capability bundles&lt;/strong&gt; for your AI agent. Instead of always loading every tool and every set of best practices into context, a Power is activated when it’s relevant—so the agent can load the right context and tools at the right time.&lt;/p&gt;

&lt;p&gt;A Power typically packages:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;MCP servers / tool connections&lt;/strong&gt; (so the agent can interact with real systems)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Steering / guidance files&lt;/strong&gt; (best practices and workflow patterns)&lt;/li&gt;
&lt;li&gt;Optional &lt;strong&gt;hooks&lt;/strong&gt; (validation or automation depending on the Power)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;That “load on demand” part is the key idea for me. Powers are meant to keep the agent focused without overwhelming it with irrelevant context.&lt;/p&gt;

&lt;p&gt;If you want the official docs: &lt;a href="https://kiro.dev/docs/powers" rel="noopener noreferrer"&gt;Kiro Powers&lt;/a&gt;&lt;/p&gt;




&lt;h2&gt;
  
  
  Building the Power with Kiro's Build a Power
&lt;/h2&gt;

&lt;p&gt;The first step for me was using the Power that helps you &lt;a href="https://github.com/kirodotdev/powers/tree/main/power-builder" rel="noopener noreferrer"&gt;Build a Power&lt;/a&gt;. This power guides you through the structure and basic setup.&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.amazonaws.com%2Fuploads%2Farticles%2Flgsrlahexb372ikzdssa.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.amazonaws.com%2Fuploads%2Farticles%2Flgsrlahexb372ikzdssa.png" alt="Build a Power" width="800" height="408"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;My approach was simple:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;I told Kiro I wanted to create a Power for &lt;strong&gt;PostHog MCP&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;I provided the PostHog MCP URL/docs as context (link below)&lt;/li&gt;
&lt;li&gt;I also mentioned I already had the Datadog Observability Power installed, and asked if we could use it as a template since both are observability-oriented&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Normally, I use spec-driven development with Kiro. But in this case, I didn’t. This was small and specific, and I knew exactly what I wanted—so I treated it more like a guided conversation than a formal spec.&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.amazonaws.com%2Fuploads%2Farticles%2Fea16mux531pk0zb7tir2.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.amazonaws.com%2Fuploads%2Farticles%2Fea16mux531pk0zb7tir2.png" alt="Power Folder Structure" width="306" height="222"&gt;&lt;/a&gt;&lt;/p&gt;




&lt;h2&gt;
  
  
  “An unexpected error occurred, please retry.”
&lt;/h2&gt;

&lt;p&gt;There’s a common Kiro error that people mention a lot:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;“An unexpected error occurred, please retry.”&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;I’d seen it discussed in the Discord community, but I’d never hit it myself until I installed my own Power.&lt;/p&gt;

&lt;p&gt;Because this was my first Power, I initially assumed I’d made a mistake in the implementation or missed a required configuration step. I ended up going down a rabbit hole trying to figure out what was wrong.&lt;/p&gt;

&lt;p&gt;In the end, it was much simpler: it was a &lt;strong&gt;misconfiguration / bad state in the Power setup&lt;/strong&gt;. What fixed it was:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Uninstalling a couple of Powers I wasn’t using&lt;/li&gt;
&lt;li&gt;Reinstalling cleanly&lt;/li&gt;
&lt;li&gt;Re-trying the install&lt;/li&gt;
&lt;li&gt;Click on 'Try this Power'&lt;/li&gt;
&lt;/ul&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.amazonaws.com%2Fuploads%2Farticles%2F2pu682hvzgon2ameso4b.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.amazonaws.com%2Fuploads%2Farticles%2F2pu682hvzgon2ameso4b.png" alt="Try This Power" width="490" height="262"&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.amazonaws.com%2Fuploads%2Farticles%2Fosnvwaos82eka4tjv6ms.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.amazonaws.com%2Fuploads%2Farticles%2Fosnvwaos82eka4tjv6ms.png" alt="Try this power chat" width="800" height="651"&gt;&lt;/a&gt;&lt;/p&gt;




&lt;h2&gt;
  
  
  Installing it, testing it, and open sourcing it
&lt;/h2&gt;

&lt;p&gt;For a testing installation, I decided to import power from a folder and it worked fine. I just have to add my &lt;strong&gt;PostHog API key&lt;/strong&gt; and it was ready to go.&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.amazonaws.com%2Fuploads%2Farticles%2F9brywl6cbas52anmhrxx.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.amazonaws.com%2Fuploads%2Farticles%2F9brywl6cbas52anmhrxx.png" alt="Add Power" width="542" height="132"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Testing was straightforward because my application was already wired up with PostHog. Once the Power was installed, I could ask Kiro to inspect what I already had in PostHog dashboards, existing insights, and the general shape of my data. It actually came back and propose to use Feature Flags with PostHog, which is a really good suggestion, as I am using feature flags in a very basic way.&lt;/p&gt;

&lt;p&gt;I also asked Kiro to look at my PostHog setup and propose a plan for adding &lt;strong&gt;LLM monitoring&lt;/strong&gt;, because I’m currently missing that. It gave me a concrete direction, and implementing that is the next feature I want to add after writing this blog post.&lt;/p&gt;

&lt;p&gt;This Kiro Power is key as I can focus my development to be more &lt;strong&gt;data-driven&lt;/strong&gt; by just asking Kiro to look into my data on PostHog and use it as context or new features.&lt;/p&gt;




&lt;h2&gt;
  
  
  How to try it?
&lt;/h2&gt;

&lt;p&gt;In order to install it, you just need the GitHub URL &lt;code&gt;https://github.com/llamojha/posthog-kiro-power/tree/main/powers/posthog&lt;/code&gt;, plus your PostHog API key in the MCP config.&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.amazonaws.com%2Fuploads%2Farticles%2Fcf06vuu88l1fkm4t07du.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.amazonaws.com%2Fuploads%2Farticles%2Fcf06vuu88l1fkm4t07du.png" alt="Add power via Github URL" width="800" height="143"&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.amazonaws.com%2Fuploads%2Farticles%2F25szorr9yq2oi8rtzwq7.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.amazonaws.com%2Fuploads%2Farticles%2F25szorr9yq2oi8rtzwq7.png" alt="My Power URL" width="800" height="97"&gt;&lt;/a&gt;&lt;/p&gt;




&lt;h2&gt;
  
  
  Lessons learned and what’s next
&lt;/h2&gt;

&lt;p&gt;Initially, it felt a bit intimidating having to 'build a power'. This all changed once I tried. Having the 'Build a Power' power to help me and guide me building helped a lot. It felt like a conversation with Kiro and it was very straightforward. I fully recommend you try to build a Power even if it's just to find out how it works. &lt;/p&gt;

&lt;p&gt;If you already know the tool you want to wrap—an MCP server, a CLI, or a workflow you use often—then creating a Power is mostly about packaging it cleanly and making it easy for the agent to load when relevant.&lt;/p&gt;

&lt;h3&gt;
  
  
  Next steps
&lt;/h3&gt;

&lt;p&gt;This was a fun and smooth experience so I'm eager to keep building. I have a couple of ideas on what to do next:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;I’ve used &lt;strong&gt;New Relic&lt;/strong&gt; as well, so I’m looking into the New Relic MCP side and creating a &lt;strong&gt;New Relic Power&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;I also want to build a more complex Power. Bundle up Hooks and Steerings and test how far the Power concept can go in a larger workflow.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;If someone from PostHog is reading this, please feel free to copy my work and make an official PostHog Kiro Power &amp;lt;3. &lt;/p&gt;

&lt;p&gt;What was your experience with Kiro Powers? Have you built your own? Share your experience in the comments or join the &lt;a href="https://discord.gg/kirodotdev" rel="noopener noreferrer"&gt;Kiro Discord&lt;/a&gt;&lt;/p&gt;




&lt;h2&gt;
  
  
  Links
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;My first Kiro Power for PostHog repository: &lt;a href="https://github.com/llamojha/posthog-kiro-power" rel="noopener noreferrer"&gt;Link&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Kiro Powers docs: &lt;a href="https://kiro.dev/docs/powers" rel="noopener noreferrer"&gt;Link&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;PostHog MCP docs: &lt;a href="https://posthog.com/docs/model-context-protocol" rel="noopener noreferrer"&gt;Link&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>kiro</category>
      <category>posthog</category>
    </item>
    <item>
      <title>I Resurrected 1980s Teletext to Cure My Doomscrolling (Built with Kiro)</title>
      <dc:creator>Md. Rafi</dc:creator>
      <pubDate>Fri, 05 Dec 2025 19:57:22 +0000</pubDate>
      <link>https://forem.com/kirodotdev/i-resurrected-1980s-teletext-to-cure-my-doomscrolling-built-with-kiro-37kb</link>
      <guid>https://forem.com/kirodotdev/i-resurrected-1980s-teletext-to-cure-my-doomscrolling-built-with-kiro-37kb</guid>
      <description>&lt;p&gt;The modern web is exhausted. It’s infinite, noisy, and designed to keep us scrolling forever. I missed the "finish line"—that feeling when you’ve read the news, checked the weather, and you are &lt;em&gt;done&lt;/em&gt;.&lt;/p&gt;

&lt;p&gt;So, for the &lt;strong&gt;Kiroween Hackathon&lt;/strong&gt;, I decided to build &lt;strong&gt;Teletext Zero&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;It’s a "Slow Web" browser that forces the chaotic internet into a strict 1980s TV format: 40 columns, 24 rows, 8 colors, and absolutely no scrolling.&lt;/p&gt;

&lt;p&gt;Building a retro-constraint engine like this usually takes weeks of tedious CSS math and state management. I built it in a weekend using &lt;strong&gt;Kiro&lt;/strong&gt;, an AI-native IDE.&lt;/p&gt;

&lt;p&gt;Here is how I used Kiro’s &lt;strong&gt;"Hybrid Workflow"&lt;/strong&gt; to switch between being a rigorous Architect and a creative Artist.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Problem: Logic vs. Vibes
&lt;/h2&gt;

&lt;p&gt;Every developer knows the struggle.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt; &lt;strong&gt;The Architect Brain&lt;/strong&gt; wants strict rules, type safety, and robust architecture.&lt;/li&gt;
&lt;li&gt; &lt;strong&gt;The Artist Brain&lt;/strong&gt; wants to mess around with shaders, animations, and "feel."&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Usually, you have to compromise. With Kiro, I didn't have to.&lt;/p&gt;

&lt;h3&gt;
  
  
  Phase 1: The Architect (Spec-to-Code)
&lt;/h3&gt;

&lt;p&gt;To make Teletext Zero feel real, I couldn't fake the constraints. If the text wrapped wrong or the grid broke on a mobile screen, the illusion would die.&lt;/p&gt;

&lt;p&gt;Instead of writing React components manually, I used Kiro's &lt;strong&gt;Spec&lt;/strong&gt; mode. I wrote a &lt;code&gt;requirements.md&lt;/code&gt; file that defined the laws of my universe:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;  &lt;strong&gt;The Grid:&lt;/strong&gt; Must be exactly 40x24 characters.&lt;/li&gt;
&lt;li&gt;  &lt;strong&gt;The Input:&lt;/strong&gt; Keyboard only (3-digit page dialing).&lt;/li&gt;
&lt;li&gt;  &lt;strong&gt;The Palette:&lt;/strong&gt; Only the standard 8 Teletext colors.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Kiro took this spec and generated the entire engine. But here is the kicker: it didn't just write the code; it wrote &lt;strong&gt;Property-Based Tests&lt;/strong&gt; (using &lt;code&gt;fast-check&lt;/code&gt;). It mathematically proved that no matter what text I threw at the engine, the layout would &lt;em&gt;never&lt;/em&gt; break or scroll.&lt;/p&gt;

&lt;p&gt;I had a bulletproof foundation in minutes.&lt;/p&gt;

&lt;h3&gt;
  
  
  Phase 2: The Artist (Vibe Coding)
&lt;/h3&gt;

&lt;p&gt;Once the logic was solid, I needed it to &lt;em&gt;look&lt;/em&gt; like a haunted TV found in an attic.&lt;/p&gt;

&lt;p&gt;I switched Kiro to &lt;strong&gt;Vibe&lt;/strong&gt; mode (conversational coding). This is where the magic happened. I didn't write CSS classes; I just described the aesthetic:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;em&gt;"Make it look like a 1980s Trinitron. Add scanlines, a phosphor bloom on the text, chromatic aberration on the edges, and a subtle screen curvature vignette."&lt;/em&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Kiro generated complex CSS shaders and keyframe animations instantly. It tweaked the text shadows to simulate that fuzzy, glowing look of old cathode ray tubes. It was like pair-programming with a retro-graphics expert.&lt;/p&gt;

&lt;h3&gt;
  
  
  Phase 3: The "Kiroween" Twist
&lt;/h3&gt;

&lt;p&gt;Since this was for a Halloween hackathon, I added a ghost in the machine.&lt;/p&gt;

&lt;p&gt;Using Vibe mode, I instructed Kiro to build &lt;strong&gt;Page 666&lt;/strong&gt;.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;  &lt;strong&gt;The Logic:&lt;/strong&gt; If the user dials 666, the system degrades.&lt;/li&gt;
&lt;li&gt;  &lt;strong&gt;The Visuals:&lt;/strong&gt; Random character corruption and background red flashing.&lt;/li&gt;
&lt;li&gt;  &lt;strong&gt;The Audio:&lt;/strong&gt; Kiro helped me wire up the Web Audio API to play static hiss that syncs with the visual glitches.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  The Result
&lt;/h3&gt;

&lt;p&gt;Teletext Zero isn't just a toy; it works.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;  &lt;strong&gt;Page 200&lt;/strong&gt; pulls live BBC News via RSS.&lt;/li&gt;
&lt;li&gt;  &lt;strong&gt;Page 300&lt;/strong&gt; pulls tech news from The Verge.&lt;/li&gt;
&lt;li&gt;  &lt;strong&gt;Page 400&lt;/strong&gt; uses the browser's Geolocation to fetch live weather from Open-Meteo.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;And because of the Spec-driven foundation, the Kiro-generated "Word Wrap" logic takes those long web articles and perfectly truncates them into 40-character summaries. It respects the grid.&lt;/p&gt;

&lt;h3&gt;
  
  
  Conclusion
&lt;/h3&gt;

&lt;p&gt;Kiro changed my mental model of development. I used &lt;strong&gt;Spec&lt;/strong&gt; to enforce rigor where it mattered (the engine) and &lt;strong&gt;Vibe&lt;/strong&gt; to embrace chaos where it was fun (the aesthetics).&lt;/p&gt;

&lt;p&gt;I didn't just write code; I directed a vision.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;🔗 Try Teletext Zero here:&lt;/strong&gt; &lt;a href="https://rafi.is-a.dev/Teletext-Zero/" rel="noopener noreferrer"&gt;https://rafi.is-a.dev/Teletext-Zero/&lt;/a&gt;&lt;br&gt;
&lt;strong&gt;💻 Check the Repo:&lt;/strong&gt; &lt;a href="https://github.com/MdRaf1/Teletext-Zero" rel="noopener noreferrer"&gt;https://github.com/MdRaf1/Teletext-Zero&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Have you tried AI-native IDEs yet? Let me know in the comments!&lt;/em&gt;&lt;/p&gt;




&lt;p&gt;&lt;em&gt;Built with #kiro&lt;/em&gt;&lt;/p&gt;

</description>
      <category>kiro</category>
      <category>webdev</category>
      <category>react</category>
      <category>ai</category>
    </item>
    <item>
      <title>Bringing Yahoo Chat Rooms Back to Life: How I Built Lingo with Kiro</title>
      <dc:creator>Deep</dc:creator>
      <pubDate>Fri, 05 Dec 2025 18:33:58 +0000</pubDate>
      <link>https://forem.com/kirodotdev/bringing-yahoo-chat-rooms-back-to-life-how-i-built-lingo-with-kiro-3jdl</link>
      <guid>https://forem.com/kirodotdev/bringing-yahoo-chat-rooms-back-to-life-how-i-built-lingo-with-kiro-3jdl</guid>
      <description>&lt;h1&gt;
  
  
  🟣 Lingo — Bringing Yahoo Chat Rooms Back to Life for Language Learning
&lt;/h1&gt;

&lt;p&gt;Remember Yahoo chat rooms? Those chaotic, wonderful digital hangouts where strangers became friends, ideas flowed freely, and the internet felt &lt;em&gt;alive&lt;/em&gt;?&lt;br&gt;&lt;br&gt;
For me, they were more than a pastime—they were formative. I learned expressions, cultures, and even bits of other languages simply by talking to real people.&lt;/p&gt;

&lt;p&gt;So when Kiroween announced the &lt;strong&gt;“Resurrection”&lt;/strong&gt; theme, I immediately knew what I wanted to build.&lt;/p&gt;




&lt;h2&gt;
  
  
  🧩 The Problem I Wanted to Solve
&lt;/h2&gt;

&lt;p&gt;I’ve always wanted to learn new languages, and I’ve tried everything—apps, textbooks, structured courses. They helped, but only to a point.&lt;br&gt;&lt;br&gt;
Whenever I had to actually &lt;em&gt;speak&lt;/em&gt;, my mind froze.&lt;/p&gt;

&lt;p&gt;But when I started talking to real people—native speakers, language exchange partners—everything changed:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Vocabulary became tools I could actually use
&lt;/li&gt;
&lt;li&gt;Grammar rules made sense in real conversation
&lt;/li&gt;
&lt;li&gt;Learning felt natural and fun
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Languages aren’t learned through instruction alone. They’re learned through interaction.&lt;/strong&gt;&lt;br&gt;&lt;br&gt;
That idea became the heart behind &lt;strong&gt;Lingo&lt;/strong&gt;.&lt;/p&gt;




&lt;h1&gt;
  
  
  🌍 What Lingo Does
&lt;/h1&gt;

&lt;p&gt;Lingo resurrects the magic of old-school chat rooms and combines it with powerful AI to create a modern language-learning playground.&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.amazonaws.com%2Fuploads%2Farticles%2F0kzxy8bfsn9m8qf4pg05.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.amazonaws.com%2Fuploads%2Farticles%2F0kzxy8bfsn9m8qf4pg05.png" alt=" " width="800" height="451"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  🏠 Global Language Rooms
&lt;/h2&gt;

&lt;p&gt;Choose a language—Spanish, French, Japanese, Korean, German, Italian, Portuguese, Chinese—and jump into:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Live group chat rooms
&lt;/li&gt;
&lt;li&gt;Private conversations
&lt;/li&gt;
&lt;li&gt;Text chat with formatting
&lt;/li&gt;
&lt;li&gt;Voice message support
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;It’s social, real-time, and authentic.&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.amazonaws.com%2Fuploads%2Farticles%2Fylrao7c0i9eamhtxov6q.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.amazonaws.com%2Fuploads%2Farticles%2Fylrao7c0i9eamhtxov6q.png" alt=" " width="800" height="449"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  🤖 AI Language Buddy (7 Modes of Learning)
&lt;/h2&gt;

&lt;p&gt;Your AI partner lives directly inside the chat. It’s not just a translator—it’s a coach.&lt;/p&gt;

&lt;h3&gt;
  
  
  The 7 Modes:
&lt;/h3&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Learn Mode&lt;/strong&gt; — Step-by-step lessons
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Chat Mode&lt;/strong&gt; — Natural conversation practice
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Translate Mode&lt;/strong&gt; — Real-time translation with cultural notes
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Grammar Mode&lt;/strong&gt; — Instant corrections with explanations
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Pronunciation Mode&lt;/strong&gt; — Voice input + feedback
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Practice Mode&lt;/strong&gt; — Role plays (e.g., ordering food)
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Vocab Mode&lt;/strong&gt; — Contextual vocabulary building
&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Every response is bilingual.&lt;br&gt;&lt;br&gt;
There's even &lt;strong&gt;Simple Mode&lt;/strong&gt; for beginners—with emojis, slow explanations, and encouragement.&lt;/p&gt;




&lt;h2&gt;
  
  
  🎮 Mini Games (Halloween Edition)
&lt;/h2&gt;

&lt;p&gt;Learning should be fun, so Lingo includes:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Hangman&lt;/strong&gt; with spooky animations and sounds
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Word Scramble Battle&lt;/strong&gt; with difficulty levels and leaderboards
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;It turns vocabulary into play.&lt;/p&gt;




&lt;h2&gt;
  
  
  🖥️ Retro Desktop Interface (Yahoo Messenger Vibes)
&lt;/h2&gt;

&lt;p&gt;Lingo lives inside a nostalgic Windows XP-style UI:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Draggable windows
&lt;/li&gt;
&lt;li&gt;A taskbar with a start menu
&lt;/li&gt;
&lt;li&gt;Changeable themes: Retro Yahoo, Halloween, Cyberpunk, Minimal
&lt;/li&gt;
&lt;li&gt;Keyboard shortcuts
&lt;/li&gt;
&lt;li&gt;Smooth animations
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;It feels familiar, playful, and surprisingly modern.&lt;/p&gt;




&lt;h1&gt;
  
  
  🛠️ How I Built It with Kiro
&lt;/h1&gt;

&lt;p&gt;This project was only possible because &lt;strong&gt;Kiro&lt;/strong&gt; acted like a full dev team by my side.&lt;/p&gt;




&lt;h2&gt;
  
  
  1️⃣ Vibe Coding
&lt;/h2&gt;

&lt;p&gt;I started with a simple description:&lt;br&gt;&lt;br&gt;
&lt;strong&gt;“Yahoo chat rooms for language learning.”&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Kiro instantly generated:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;React components
&lt;/li&gt;
&lt;li&gt;Retro CSS
&lt;/li&gt;
&lt;li&gt;Draggable windows
&lt;/li&gt;
&lt;li&gt;Taskbar system
&lt;/li&gt;
&lt;li&gt;Window manager logic
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Within minutes I had a working UI skeleton.&lt;/p&gt;




&lt;h2&gt;
  
  
  2️⃣ Spec-Driven Development
&lt;/h2&gt;

&lt;p&gt;For each feature, I wrote what I &lt;em&gt;wanted&lt;/em&gt;, and Kiro figured out the &lt;em&gt;how&lt;/em&gt;:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;AI Buddy with 7 modes
&lt;/li&gt;
&lt;li&gt;Voice input + bilingual formatting
&lt;/li&gt;
&lt;li&gt;Hangman + Word Scramble
&lt;/li&gt;
&lt;li&gt;Theme system
&lt;/li&gt;
&lt;li&gt;Text formatting toolbar
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Kiro built the logic, and I iterated.&lt;/p&gt;




&lt;h2&gt;
  
  
  3️⃣ Agent Hooks
&lt;/h2&gt;

&lt;p&gt;Kiro’s automation made the project production-ready:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;test-component-on-save&lt;/code&gt; — runs tests automatically
&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;firebase-security-audit&lt;/code&gt; — audits my database rules
&lt;/li&gt;
&lt;li&gt;Auto-formatting and dependency checks
&lt;/li&gt;
&lt;li&gt;Build optimization hooks
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;It felt like having a senior engineer reviewing every commit.&lt;/p&gt;




&lt;h2&gt;
  
  
  4️⃣ Steering Documents
&lt;/h2&gt;

&lt;p&gt;These controlled the AI’s teaching style:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;How to correct without discouraging
&lt;/li&gt;
&lt;li&gt;When to translate vs. when to challenge
&lt;/li&gt;
&lt;li&gt;Tone, personality, cultural sensitivity
&lt;/li&gt;
&lt;li&gt;Lesson sequencing logic
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;With steering docs, Gemini became a &lt;em&gt;real tutor&lt;/em&gt;, not a random assistant.&lt;/p&gt;




&lt;h1&gt;
  
  
  ⚙️ Tech Stack
&lt;/h1&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;React 19&lt;/strong&gt; — UI
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Vite 5&lt;/strong&gt; — Fast builds
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Firebase&lt;/strong&gt; — Real-time chat, auth, presence
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Google Gemini&lt;/strong&gt; — AI Buddy
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Web Speech API&lt;/strong&gt; — Voice recognition
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Vercel&lt;/strong&gt; — Deployment
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Everything is real-time and client-side.&lt;/p&gt;




&lt;h1&gt;
  
  
  🧠 Challenges I Overcame
&lt;/h1&gt;

&lt;h3&gt;
  
  
  🔄 Real-Time Chat Sync
&lt;/h3&gt;

&lt;p&gt;Managing message order, timestamps, and presence required a smart Firebase data model.&lt;/p&gt;

&lt;h3&gt;
  
  
  🤖 AI Personality Consistency
&lt;/h3&gt;

&lt;p&gt;Steering documents solved early issues with tone and inconsistency.&lt;/p&gt;

&lt;h3&gt;
  
  
  ⚡ Retro UI Performance
&lt;/h3&gt;

&lt;p&gt;Balancing animations (ghosts, bats, draggable windows) while keeping everything smooth took optimization.&lt;/p&gt;

&lt;h3&gt;
  
  
  🔐 Firebase Security Rules
&lt;/h3&gt;

&lt;p&gt;Ensuring public rooms were open but private chats were locked-down required detailed rule writing.&lt;/p&gt;




&lt;h1&gt;
  
  
  🌟 What I’m Proud Of
&lt;/h1&gt;

&lt;ul&gt;
&lt;li&gt;I resurrected Yahoo Chat with &lt;em&gt;purpose&lt;/em&gt;, not nostalgia alone
&lt;/li&gt;
&lt;li&gt;The AI Buddy genuinely &lt;em&gt;teaches&lt;/em&gt;, not just translates
&lt;/li&gt;
&lt;li&gt;Real-time chat + AI coaching feels magical
&lt;/li&gt;
&lt;li&gt;I built an entire multi-feature platform &lt;strong&gt;solo in days&lt;/strong&gt;, thanks to Kiro
&lt;/li&gt;
&lt;li&gt;Lingo helps people learn languages the way humans naturally learn—through conversation
&lt;/li&gt;
&lt;/ul&gt;




</description>
      <category>kiro</category>
      <category>webdev</category>
      <category>ai</category>
      <category>javascript</category>
    </item>
    <item>
      <title>🔍 The Potential of Perspective-Driven Code Reviews - Gaining New Insights</title>
      <dc:creator>snickerjp</dc:creator>
      <pubDate>Fri, 05 Dec 2025 17:58:47 +0000</pubDate>
      <link>https://forem.com/kirodotdev/the-potential-of-perspective-driven-code-reviews-gaining-new-insights-53gd</link>
      <guid>https://forem.com/kirodotdev/the-potential-of-perspective-driven-code-reviews-gaining-new-insights-53gd</guid>
      <description>&lt;h2&gt;
  
  
  Introduction
&lt;/h2&gt;

&lt;h3&gt;
  
  
  The Importance of Perspective in Code Reviews
&lt;/h3&gt;

&lt;p&gt;Code reviews are a crucial process that serves as the cornerstone of quality assurance, but the content and priorities of feedback can vary significantly depending on the reviewer's perspective. This report analyzes &lt;strong&gt;reviews from two different perspectives - operational and architectural - by AI "kiro"&lt;/strong&gt; using actual Terraform code reviews as a case study, and examines the new insights gained by changing perspectives.&lt;/p&gt;

&lt;h3&gt;
  
  
  Two-Perspective Reviews by kiro
&lt;/h3&gt;

&lt;p&gt;In this report, we requested AI "kiro" to conduct reviews from the following two perspectives:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;🔧 Operational Perspective&lt;/strong&gt;: Emphasizing safety, maintainability, and practicality in team development for daily operations&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;🏗️ Architectural Perspective&lt;/strong&gt;: Emphasizing code design elegance, scalability, and architectural soundness&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;While these perspectives are not necessarily opposing, they differ in priorities and focus points. By requesting the same AI to review from different perspectives, we can objectively analyze how perspective differences affect feedback content.&lt;/p&gt;

&lt;h2&gt;
  
  
  🔧 Operational Perspective Review by kiro
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Review Overview
&lt;/h3&gt;

&lt;p&gt;The operational perspective review was analyzed using the following four evaluation criteria:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Item&lt;/th&gt;
&lt;th&gt;Rating&lt;/th&gt;
&lt;th&gt;Details&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Security&lt;/td&gt;
&lt;td&gt;🔴 Needs Improvement&lt;/td&gt;
&lt;td&gt;Hardcoded sensitive information&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;State Management&lt;/td&gt;
&lt;td&gt;🔴 Needs Improvement&lt;/td&gt;
&lt;td&gt;Using local state&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Code Quality&lt;/td&gt;
&lt;td&gt;🟡 Good&lt;/td&gt;
&lt;td&gt;Some room for improvement&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Operability&lt;/td&gt;
&lt;td&gt;🟡 Good&lt;/td&gt;
&lt;td&gt;CI/CD not implemented&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;h3&gt;
  
  
  Major Issues Identified
&lt;/h3&gt;

&lt;h4&gt;
  
  
  1. State File Management (Most Critical)
&lt;/h4&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;❌ Problem&lt;/strong&gt;: Managing state files locally&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;⚡ Risk&lt;/strong&gt;: Conflicts in team development, loss risk, no backup&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;✅ Solution&lt;/strong&gt;: Implement S3 backend + DynamoDB locking
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight hcl"&gt;&lt;code&gt;&lt;span class="nx"&gt;terraform&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;backend&lt;/span&gt; &lt;span class="s2"&gt;"s3"&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;bucket&lt;/span&gt;         &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"your-terraform-state-bucket"&lt;/span&gt;
    &lt;span class="nx"&gt;key&lt;/span&gt;            &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"alb-rules/terraform.tfstate"&lt;/span&gt;
    &lt;span class="nx"&gt;region&lt;/span&gt;         &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"ap-northeast-1"&lt;/span&gt;
    &lt;span class="nx"&gt;encrypt&lt;/span&gt;        &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;
    &lt;span class="nx"&gt;dynamodb_table&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"terraform-state-lock"&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;h4&gt;
  
  
  2. Security Concerns
&lt;/h4&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;❌ Problem&lt;/strong&gt;: ARNs hardcoded in tfvars files&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;⚡ Risk&lt;/strong&gt;: Sensitive information leakage&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;✅ Solution&lt;/strong&gt;: Environment variables, Parameter Store utilization, .gitignore configuration&lt;/li&gt;
&lt;/ul&gt;

&lt;h4&gt;
  
  
  3. Stricter Version Management
&lt;/h4&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;❌ Problem&lt;/strong&gt;: Provider version range too broad (&lt;code&gt;~&amp;gt; 5.0&lt;/code&gt;)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;✅ Solution&lt;/strong&gt;: More specific version specification (&lt;code&gt;~&amp;gt; 5.30.0&lt;/code&gt;)&lt;/li&gt;
&lt;/ul&gt;

&lt;h4&gt;
  
  
  4. Operational Improvement Proposals
&lt;/h4&gt;

&lt;ul&gt;
&lt;li&gt;🏷️ Unified tag strategy (localize common tags)&lt;/li&gt;
&lt;li&gt;✔️ Add variable validation (IP format, priority range validation)&lt;/li&gt;
&lt;li&gt;🔄 Build CI/CD pipeline&lt;/li&gt;
&lt;li&gt;📊 Monitoring and alerting setup&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Implementation Roadmap
&lt;/h3&gt;

&lt;p&gt;The operational perspective presented a phased improvement plan:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;📅 Phase 1 (1 week)&lt;/strong&gt;: Remote state file implementation&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;📅 Phase 2 (2 weeks)&lt;/strong&gt;: Security enhancement&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;📅 Phase 3 (1 month)&lt;/strong&gt;: CI/CD construction&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;📅 Phase 4 (3 months)&lt;/strong&gt;: Modularization and policy management&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  🏗️ Architectural Perspective Review by kiro
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Review Overview
&lt;/h3&gt;

&lt;p&gt;The architectural perspective focused on code design quality and future scalability.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Overall Rating&lt;/strong&gt;: B+ → A (after improvements)&lt;/p&gt;

&lt;h3&gt;
  
  
  Evaluated Strengths
&lt;/h3&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;⚡ Dynamic Resource Generation&lt;/strong&gt;: Excellent automatic splitting logic for ALB limitations (4 IPs/rule)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;📋 Variable Separation&lt;/strong&gt;: Externalization of configuration through terraform.tfvars&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;🏷️ Tag Standardization&lt;/strong&gt;: Consistent tag strategy&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;🎯 Constraint Handling&lt;/strong&gt;: Creative solutions for AWS limitations&lt;/li&gt;
&lt;/ol&gt;

&lt;h3&gt;
  
  
  Major Improvement Proposals
&lt;/h3&gt;

&lt;h4&gt;
  
  
  1. Modularization Recommendation (Priority: Low)
&lt;/h4&gt;

&lt;ul&gt;
&lt;li&gt;Migration from current flat structure to module-based structure&lt;/li&gt;
&lt;li&gt;🗂️ Environment-specific directory separation (production/staging)&lt;/li&gt;
&lt;li&gt;♻️ Creation of reusable modules&lt;/li&gt;
&lt;/ul&gt;

&lt;h4&gt;
  
  
  2. Dynamic Retrieval via Data Sources (Priority: Medium)
&lt;/h4&gt;

&lt;ul&gt;
&lt;li&gt;Dynamically retrieve hardcoded ARNs using Data Sources&lt;/li&gt;
&lt;li&gt;More flexible and maintainable design
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight hcl"&gt;&lt;code&gt;&lt;span class="c1"&gt;# After improvement&lt;/span&gt;
&lt;span class="nx"&gt;data&lt;/span&gt; &lt;span class="s2"&gt;"aws_lb"&lt;/span&gt; &lt;span class="s2"&gt;"main"&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="nx"&gt;var&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;load_balancer_name&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="nx"&gt;data&lt;/span&gt; &lt;span class="s2"&gt;"aws_lb_listener"&lt;/span&gt; &lt;span class="s2"&gt;"https"&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;load_balancer_arn&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;aws_lb&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;main&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;arn&lt;/span&gt;
  &lt;span class="nx"&gt;port&lt;/span&gt;              &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;443&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h4&gt;
  
  
  3. Enhanced Input Validation (Priority: High)
&lt;/h4&gt;

&lt;ul&gt;
&lt;li&gt;🌐 CIDR format validation&lt;/li&gt;
&lt;li&gt;🔢 IP count validation&lt;/li&gt;
&lt;li&gt;💬 More detailed error messages
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight hcl"&gt;&lt;code&gt;&lt;span class="nx"&gt;variable&lt;/span&gt; &lt;span class="s2"&gt;"devinvm_ip_addresses"&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;description&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"Devin VM IP addresses"&lt;/span&gt;
  &lt;span class="nx"&gt;type&lt;/span&gt;        &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;list&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;string&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

  &lt;span class="nx"&gt;validation&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;condition&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;alltrue&lt;/span&gt;&lt;span class="p"&gt;([&lt;/span&gt;
      &lt;span class="nx"&gt;for&lt;/span&gt; &lt;span class="nx"&gt;ip&lt;/span&gt; &lt;span class="nx"&gt;in&lt;/span&gt; &lt;span class="nx"&gt;var&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;devinvm_ip_addresses&lt;/span&gt; &lt;span class="err"&gt;:&lt;/span&gt;
      &lt;span class="nx"&gt;can&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;cidrhost&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;ip&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="p"&gt;])&lt;/span&gt;
    &lt;span class="nx"&gt;error_message&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"All IP addresses must be valid CIDR notation."&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;h4&gt;
  
  
  4. Output Improvements
&lt;/h4&gt;

&lt;ul&gt;
&lt;li&gt;📝 Add detailed descriptions&lt;/li&gt;
&lt;li&gt;📊 Output rule summaries&lt;/li&gt;
&lt;li&gt;🗂️ More structured output format&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Comparative Analysis
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Common Ground: Issues Identified by Both Perspectives
&lt;/h3&gt;

&lt;p&gt;The following items were identified by both kiro's operational and architectural perspectives and are &lt;strong&gt;items that should definitely be improved&lt;/strong&gt;:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;✔️ Adding Input Validation&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;🔧 Operational perspective: Preventing operational mistakes&lt;/li&gt;
&lt;li&gt;🏗️ Architectural perspective: Improving code quality&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;📌 Stricter Version Management&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;🔧 Operational perspective: Preventing unexpected behavior&lt;/li&gt;
&lt;li&gt;🏗️ Architectural perspective: Ensuring reproducibility&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;State Management Improvement&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;🔧 Operational perspective: Team development safety&lt;/li&gt;
&lt;li&gt;🏗️ Architectural perspective: Infrastructure reliability&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Security Enhancement&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;🔧 Operational perspective: Protecting sensitive information&lt;/li&gt;
&lt;li&gt;🏗️ Architectural perspective: Design soundness&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ol&gt;

&lt;h3&gt;
  
  
  Differences: Priority Variations by Perspective
&lt;/h3&gt;

&lt;h4&gt;
  
  
  Readability Aspect
&lt;/h4&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Perspective&lt;/th&gt;
&lt;th&gt;Priority&lt;/th&gt;
&lt;th&gt;Approach&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;🔧 Operational&lt;/td&gt;
&lt;td&gt;🟡 Medium&lt;/td&gt;
&lt;td&gt;🏷️ Tag unification, clear naming conventions&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;🏗️ Architectural&lt;/td&gt;
&lt;td&gt;🔴 High&lt;/td&gt;
&lt;td&gt;📦 Modularization, structured output&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;&lt;strong&gt;Analysis&lt;/strong&gt;: Architectural perspective emphasizes structural code readability (modularization). Operational perspective emphasizes clarity in daily work (tags, naming).&lt;/p&gt;

&lt;h4&gt;
  
  
  Maintainability Aspect
&lt;/h4&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Perspective&lt;/th&gt;
&lt;th&gt;Priority&lt;/th&gt;
&lt;th&gt;Approach&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;🔧 Operational&lt;/td&gt;
&lt;td&gt;🔴 High&lt;/td&gt;
&lt;td&gt;🔄 CI/CD, 📊 monitoring, 🔍 drift detection&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;🏗️ Architectural&lt;/td&gt;
&lt;td&gt;🔴 High&lt;/td&gt;
&lt;td&gt;📦 Modularization, 🔍 Data Source utilization&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;&lt;strong&gt;Analysis&lt;/strong&gt;: Both prioritize maintainability, but operational perspective focuses on "operational processes" while architectural perspective focuses on "code design".&lt;/p&gt;

&lt;h4&gt;
  
  
  Performance Aspect
&lt;/h4&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Perspective&lt;/th&gt;
&lt;th&gt;Priority&lt;/th&gt;
&lt;th&gt;Approach&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;🔧 Operational&lt;/td&gt;
&lt;td&gt;🟢 Low&lt;/td&gt;
&lt;td&gt;No specific mention&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;🏗️ Architectural&lt;/td&gt;
&lt;td&gt;🟢 Low&lt;/td&gt;
&lt;td&gt;No specific mention&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;&lt;strong&gt;Analysis&lt;/strong&gt;: For this Terraform code, performance was not a major concern for either perspective.&lt;/p&gt;

&lt;h3&gt;
  
  
  Priority Differences
&lt;/h3&gt;

&lt;h4&gt;
  
  
  Operational Perspective Priorities
&lt;/h4&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;🔴 Highest Priority (Immediate Response)
  ├─ State Management
  └─ Security

🟡 Medium Priority (Medium-term Improvement)
  ├─ CI/CD
  └─ Monitoring

🟢 Low Priority (Long-term Improvement)
  └─ Modularization
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h4&gt;
  
  
  Architectural Perspective Priorities
&lt;/h4&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;🔴 Highest Priority
  └─ Input Validation

🟡 Medium Priority
  ├─ Data Source Implementation
  └─ State Management

🟢 Low Priority
  └─ Modularization
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Important Discovery&lt;/strong&gt;: &lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;🔧 Operational perspective: "State Management" as highest priority → Focus on &lt;strong&gt;problems that could occur immediately&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;🏗️ Architectural perspective: "Input Validation" as highest priority → Focus on &lt;strong&gt;code quality fundamentals&lt;/strong&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Both perspectives rated "Modularization" as low priority because the current code is simple and functions adequately.&lt;/p&gt;

&lt;h3&gt;
  
  
  Different Focus Points by Perspective
&lt;/h3&gt;

&lt;p&gt;Kiro's operational and architectural perspectives focus on significantly different points even when looking at the same code.&lt;/p&gt;

&lt;h4&gt;
  
  
  Items Emphasized by Operational Perspective (Not mentioned by architectural perspective)
&lt;/h4&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;🔄 CI/CD Pipeline Construction&lt;/strong&gt;: Reducing human errors through automation&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;📊 Monitoring &amp;amp; Alert Setup&lt;/strong&gt;: Early problem detection and response&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;🔍 Drift Detection&lt;/strong&gt;: Detecting discrepancies between actual infrastructure and code&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Phased Implementation Roadmap&lt;/strong&gt;: Realistic improvement plan&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Team Development Conflict Risks&lt;/strong&gt;: Safety during multi-person work&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;These focus on "actual daily operational challenges".&lt;/p&gt;

&lt;h4&gt;
  
  
  Items Emphasized by Architectural Perspective (Not mentioned by operational perspective)
&lt;/h4&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;📦 Detailed Module Structure Design&lt;/strong&gt;: Ensuring reusability and scalability&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;🗂️ Environment-specific Directory Separation&lt;/strong&gt;: Logical structuring&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;📤 Output Structuring and Detailing&lt;/strong&gt;: Systematic information organization&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;🔍 Specific Implementation of Dynamic Retrieval via Data Sources&lt;/strong&gt;: Highly flexible design&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;These focus on "design considering future changes and extensions".&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Important Insight&lt;/strong&gt;: Both perspectives capture problems on different time scales. Operational perspective looks at "today and tomorrow", while architectural perspective looks at "months and years ahead".&lt;/p&gt;

&lt;h2&gt;
  
  
  Analysis
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Value Brought by Perspective Diversity
&lt;/h3&gt;

&lt;p&gt;From kiro's two-perspective comparison, we gained the following important insights:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Complementary Perspectives&lt;/strong&gt;: Operational and architectural perspectives don't oppose each other but complement each other. Operational perspective captures "current" problems, while architectural perspective captures "future" problems.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Importance of Common Issues&lt;/strong&gt;: Items commonly identified by both perspectives (input validation, version management, state management, security) are important matters that should definitely be addressed.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Value of Priority Differences&lt;/strong&gt;: The difference between operational perspective's "State Management priority" and architectural perspective's "Input Validation priority" should be judged based on project circumstances (team size, development phase, risk tolerance) rather than which is correct.&lt;/p&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;h3&gt;
  
  
  Balancing Operational and Architectural Perspectives
&lt;/h3&gt;

&lt;h4&gt;
  
  
  Recommended Approach
&lt;/h4&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Initial Stage&lt;/strong&gt;: Prioritize operational perspective&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;State management, security, etc., ensure basic safety&lt;/li&gt;
&lt;li&gt;Establish team development foundation&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Growth Stage&lt;/strong&gt;: Incorporate architectural perspective&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Code structuring, modularization&lt;/li&gt;
&lt;li&gt;Scalability improvement&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Maturity Stage&lt;/strong&gt;: Integration of both perspectives&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Balance operational processes and code design&lt;/li&gt;
&lt;li&gt;Continuous improvement cycle&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ol&gt;

&lt;h3&gt;
  
  
  Application to Development Teams
&lt;/h3&gt;

&lt;p&gt;Perspective-driven reviews can provide the following benefits:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Junior Developer Growth&lt;/strong&gt;: Learn practical skills from operational perspective reviews and design philosophy from architectural perspective reviews&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Reviewer Burden Reduction&lt;/strong&gt;: Clarifying perspectives makes review intentions clear and discussions constructive&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Team Diversity Utilization&lt;/strong&gt;: Members with different backgrounds can contribute from their respective perspectives&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Importance of Code Simplicity
&lt;/h3&gt;

&lt;p&gt;Through this analysis, we confirmed that "code simplicity" is valued by both perspectives:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Architectural perspective also values practical design over excessive abstraction&lt;/li&gt;
&lt;li&gt;Operational perspective emphasizes clear, maintainable code over complex structures&lt;/li&gt;
&lt;li&gt;Both perspectives rated the current implementation (ALB limitation handling) as "excellent" and "creative"&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This is because code maintained by diverse members requires understandable and easily modifiable code.&lt;/p&gt;

&lt;h2&gt;
  
  
  Conclusion
&lt;/h2&gt;

&lt;h3&gt;
  
  
  New Insights from Changing Review Perspectives
&lt;/h3&gt;

&lt;p&gt;From this report's analysis, the following values became clear:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Comprehensive Quality Improvement&lt;/strong&gt;: Problems overlooked by a single perspective can be complemented by multiple perspectives&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Priority Clarification&lt;/strong&gt;: Common issues should definitely be addressed, while perspective-specific issues can be judged based on circumstances&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Learning Opportunity Creation&lt;/strong&gt;: Reviews from different perspectives provide valuable opportunities to broaden developers' horizons&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Promoting Constructive Discussion&lt;/strong&gt;: Clarifying perspectives makes "why that feedback is given" clear and deepens discussions&lt;/p&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;h3&gt;
  
  
  Recommendations for Future Review Culture
&lt;/h3&gt;

&lt;h4&gt;
  
  
  1. Perspective Clarification
&lt;/h4&gt;

&lt;p&gt;Clearly marking perspectives (operational, architectural, security, etc.) in review comments makes intentions easier to understand.&lt;/p&gt;

&lt;h4&gt;
  
  
  2. Encouraging Diverse Perspectives
&lt;/h4&gt;

&lt;p&gt;Foster a culture that actively seeks feedback from reviewers with different backgrounds within the team.&lt;/p&gt;

&lt;h4&gt;
  
  
  3. Accepting Gradual Improvement
&lt;/h4&gt;

&lt;p&gt;Rather than seeking perfection, distinguish between operational perspective's "immediately necessary improvements" and architectural perspective's "future improvements", accepting gradual improvement.&lt;/p&gt;

&lt;h4&gt;
  
  
  4. Utilizing AI Reviews
&lt;/h4&gt;

&lt;p&gt;As demonstrated in this report, using AI to efficiently obtain reviews from multiple perspectives can reduce human reviewer burden while improving quality.&lt;/p&gt;

&lt;h3&gt;
  
  
  Concrete Actions You Can Start Tomorrow
&lt;/h3&gt;

&lt;p&gt;Perspective-driven reviews can be started today without special preparation.&lt;/p&gt;

&lt;h4&gt;
  
  
  🤖 Perspective-Specific Reviews Using Generative AI
&lt;/h4&gt;

&lt;p&gt;The simplest method is to &lt;strong&gt;🎯 specify perspectives&lt;/strong&gt; when requesting reviews from generative AI:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Example 1: 🏗️ Architectural Perspective Review Request&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;"Please review this Terraform code from an 🏗️ architectural perspective.
Focus on design elegance, scalability, and future maintainability for feedback."
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Example 2: 🔧 Operational Perspective Review Request&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;"Please review this Terraform code from an 🔧 operational perspective.
Focus on daily operational safety, team development practicality, and troubleshooting ease for feedback."
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;By simply &lt;strong&gt;📝 splitting requests into two&lt;/strong&gt;, you can gain insights from different perspectives. Please try it!&lt;/p&gt;

&lt;h4&gt;
  
  
  👥 Team Implementation
&lt;/h4&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;🏷️ Add Perspective Tags to Review Comments&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Tags like &lt;code&gt;[🔧 Operational Perspective]&lt;/code&gt; &lt;code&gt;[🏗️ Architectural Perspective]&lt;/code&gt; clarify intentions&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;📝 Specify Expected Perspectives in Review Requests&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;"Please review focusing on 🔧 operational issues"&lt;/li&gt;
&lt;li&gt;"Could you provide feedback from a 🏗️ design perspective?"&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;👥 Perspective Role Assignment&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Operations staff review from 🔧 operational perspective, architects from 🏗️ design perspective&lt;/li&gt;
&lt;li&gt;Obtaining both perspectives enables comprehensive quality improvement&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ol&gt;

&lt;h3&gt;
  
  
  🎉 Final Words
&lt;/h3&gt;

&lt;p&gt;Perspective-driven reviews are not mere "✅ checking tasks" but &lt;strong&gt;👥 venues for sharing team-wide knowledge and experience&lt;/strong&gt;. By combining 🔧 operational and 🏗️ architectural perspectives with various other viewpoints, we can build more robust and maintainable codebases.&lt;/p&gt;

&lt;p&gt;This initiative promotes 🌱 junior developer growth, shares 👨‍💼 senior developer insights, and ultimately contributes to 📈 overall project quality improvement. Accepting perspective diversity and building better software together through constructive discussion is the essential value of development teams.&lt;/p&gt;




&lt;h2&gt;
  
  
  📚 References
&lt;/h2&gt;

&lt;p&gt;Please feel free to check out my first post:&lt;br&gt;&lt;br&gt;
&lt;a href="https://dev.to/kirodotdev/understanding-the-differences-kiro-ide-kiro-cli-and-amazon-q-developer-2hib"&gt;Understanding the Differences: Kiro IDE, Kiro CLI, and Amazon Q Developer - DEV Community&lt;/a&gt;&lt;/p&gt;

</description>
      <category>kiro</category>
      <category>ai</category>
      <category>codereview</category>
    </item>
    <item>
      <title>How Kiro Transformed My Development Workflow — Building Dashtact in Record Time</title>
      <dc:creator>Fouad Abatouy</dc:creator>
      <pubDate>Fri, 05 Dec 2025 16:55:49 +0000</pubDate>
      <link>https://forem.com/kirodotdev/how-kiro-transformed-my-development-workflow-building-dashtact-in-record-time-32me</link>
      <guid>https://forem.com/kirodotdev/how-kiro-transformed-my-development-workflow-building-dashtact-in-record-time-32me</guid>
      <description></description>
      <category>kiro</category>
      <category>kirodotdev</category>
    </item>
    <item>
      <title>👻 GHOSTNET — A Cyber Maze Survival Game Built on Kiro AI</title>
      <dc:creator>Pragati GP</dc:creator>
      <pubDate>Fri, 05 Dec 2025 15:26:21 +0000</pubDate>
      <link>https://forem.com/kirodotdev/ghostnet-a-cyber-maze-survival-game-built-on-kiro-ai-1bf6</link>
      <guid>https://forem.com/kirodotdev/ghostnet-a-cyber-maze-survival-game-built-on-kiro-ai-1bf6</guid>
      <description>&lt;h2&gt;
  
  
  👻 GHOSTNET — Enter the Maze. Outsmart the System.
&lt;/h2&gt;

&lt;p&gt;Welcome to &lt;strong&gt;GHOSTNET&lt;/strong&gt;, a cyber-maze adventure where YOU become the hacker navigating glowing alleys, collecting digital cubes, escaping enemies, and beating the network from the inside.&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.amazonaws.com%2Fuploads%2Farticles%2Fztxt3pqb921m4j2c9deh.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.amazonaws.com%2Fuploads%2Farticles%2Fztxt3pqb921m4j2c9deh.png" alt="Intro Page" width="800" height="800"&gt;&lt;/a&gt;&lt;br&gt;
This blog dives into:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;the concept behind the game
&lt;/li&gt;
&lt;li&gt;how the mechanics work
&lt;/li&gt;
&lt;li&gt;why Kiro makes development faster
&lt;/li&gt;
&lt;li&gt;and the fun twist that makes GhostNet stand out
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Let’s jack-in.&lt;/p&gt;




&lt;h2&gt;
  
  
  🕹️ What is GHOSTNET?
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;GHOSTNET&lt;/strong&gt; is a short, intense cyber-maze survival game.&lt;/p&gt;

&lt;p&gt;You’re not just a player.&lt;br&gt;&lt;br&gt;
You’re a &lt;strong&gt;system infiltrator&lt;/strong&gt;, an undercover hacker inside a glowing grid filled with traps, secrets, and patrolling ghosts.&lt;/p&gt;

&lt;p&gt;Your mission:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;collect every &lt;strong&gt;Digital Cube&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;avoid the &lt;strong&gt;Ghosts&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;find the &lt;strong&gt;Exit Portal&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;escape before the network wipes you out&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;If a ghost catches you → &lt;em&gt;game over&lt;/em&gt;.&lt;br&gt;&lt;br&gt;
If you miss a cube → &lt;em&gt;the exit rejects you&lt;/em&gt;.&lt;br&gt;&lt;br&gt;
Simple. Addictive. Brutal. Perfect.&lt;/p&gt;

&lt;h2&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.amazonaws.com%2Fuploads%2Farticles%2F6hcsybfyq678aovuoy1b.png" alt="The Dark East Alley Where it All Started" width="800" height="457"&gt;
&lt;/h2&gt;

&lt;h2&gt;
  
  
  🧩 The Game Concept
&lt;/h2&gt;

&lt;p&gt;The world of GhostNet is built using:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;glowing neon alleys&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;unexpected intersections&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;deadly blind turns&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;strategic cube placements&lt;/strong&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Each digital cube represents:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;a broken piece of encrypted data
&lt;/li&gt;
&lt;li&gt;a fragment of the system’s truth
&lt;/li&gt;
&lt;li&gt;your key to activating the final exit
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The ghosts represent corrupted watchdog AIs.&lt;br&gt;&lt;br&gt;
They don’t chase… they lurk.&lt;br&gt;&lt;br&gt;
They wait.&lt;br&gt;&lt;br&gt;
They predict.&lt;br&gt;&lt;br&gt;
They watch every move you make inside the maze.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;em&gt;You are in their world. You are the glitch. They are the system.&lt;/em&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&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.amazonaws.com%2Fuploads%2Farticles%2F6kpc04btcv3g5sr63j8v.png" alt="The MAP" width="800" height="533"&gt;
&lt;/h2&gt;

&lt;h2&gt;
  
  
  ✨ The Tech Behind the Game: The Frank-Christ Layout
&lt;/h2&gt;

&lt;p&gt;GhostNet uses a &lt;strong&gt;Frank-Christ inspired grid&lt;/strong&gt;, meaning:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;dynamic intersections
&lt;/li&gt;
&lt;li&gt;maze-like structure
&lt;/li&gt;
&lt;li&gt;predictable paths mixed with unpredictable danger
&lt;/li&gt;
&lt;li&gt;perfect for maze-based logic and chase mechanics
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;It makes the player:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;think quickly
&lt;/li&gt;
&lt;li&gt;calculate turns
&lt;/li&gt;
&lt;li&gt;stay alert at every corner
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This structure is &lt;em&gt;simple to understand&lt;/em&gt; but &lt;em&gt;hard to master&lt;/em&gt;, which is exactly why GhostNet becomes addictive.&lt;/p&gt;




&lt;h2&gt;
  
  
  🤖 Why We Built It With Kiro
&lt;/h2&gt;

&lt;p&gt;Kiro makes game prototyping insanely fast.&lt;/p&gt;

&lt;h3&gt;
  
  
  &lt;strong&gt;Plus Points of Using Kiro:&lt;/strong&gt;
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;lightning-fast code testing
&lt;/li&gt;
&lt;li&gt;instant debugging
&lt;/li&gt;
&lt;li&gt;real-time iteration
&lt;/li&gt;
&lt;li&gt;easy asset management
&lt;/li&gt;
&lt;li&gt;smooth event-trigger logic
&lt;/li&gt;
&lt;li&gt;perfect for beginners AND power users
&lt;/li&gt;
&lt;/ul&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.amazonaws.com%2Fuploads%2Farticles%2Fipb890pe0mn2gugnznoj.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.amazonaws.com%2Fuploads%2Farticles%2Fipb890pe0mn2gugnznoj.png" alt="Design Implementation with Kiro" width="800" height="403"&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.amazonaws.com%2Fuploads%2Farticles%2Fe26883cvimdawg4tdyih.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.amazonaws.com%2Fuploads%2Farticles%2Fe26883cvimdawg4tdyih.png" alt="Task Completion with Kiro" width="800" height="412"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Kiro helps turn:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;rough sketches → working systems
&lt;/li&gt;
&lt;li&gt;ideas → prototypes
&lt;/li&gt;
&lt;li&gt;imagination → playable levels
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;GhostNet exists because Kiro lets you build without limits.&lt;/p&gt;

&lt;h2&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.amazonaws.com%2Fuploads%2Farticles%2Fjgvo8asgqlu8162cchej.png" alt="Welcome screen" width="800" height="450"&gt;
&lt;/h2&gt;

&lt;h2&gt;
  
  
  🌐 Gameplay Loop
&lt;/h2&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Spawn in the maze&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Scan for the first cube&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Walk alley by alley&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;If there’s a ghost → &lt;strong&gt;back up or die&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;If there’s a cube → &lt;strong&gt;collect it&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;Grab all cubes
&lt;/li&gt;
&lt;li&gt;Find the glowing exit
&lt;/li&gt;
&lt;li&gt;Escape before the network crashes you&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Fast. Simple. Addictive.&lt;/p&gt;




&lt;h2&gt;
  
  
  🎮 Why Players Love GhostNet
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;short but intense
&lt;/li&gt;
&lt;li&gt;neon aesthetic that feels alive
&lt;/li&gt;
&lt;li&gt;danger hidden around every corner
&lt;/li&gt;
&lt;li&gt;clean and satisfying movement
&lt;/li&gt;
&lt;li&gt;exploration + survival + puzzle combined
&lt;/li&gt;
&lt;li&gt;every run feels different
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This is the kind of game people finish… and restart immediately.&lt;/p&gt;




&lt;h2&gt;
  
  
  🧠 Design Philosophy
&lt;/h2&gt;

&lt;p&gt;GhostNet focuses on:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;clarity
&lt;/li&gt;
&lt;li&gt;simplicity
&lt;/li&gt;
&lt;li&gt;tension
&lt;/li&gt;
&lt;li&gt;player curiosity
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The maze isn’t just “streets.”&lt;br&gt;&lt;br&gt;
It’s a &lt;strong&gt;system&lt;/strong&gt;.&lt;br&gt;
The cubes aren’t “collectibles.”&lt;br&gt;&lt;br&gt;
They’re &lt;strong&gt;data fragments you must reconstruct&lt;/strong&gt;.&lt;br&gt;
The ghosts aren’t “monsters.”&lt;br&gt;&lt;br&gt;
They’re &lt;strong&gt;AI protectors doing their job&lt;/strong&gt;.&lt;br&gt;
You’re the intruder.&lt;br&gt;&lt;br&gt;
The network wants you gone.&lt;br&gt;&lt;br&gt;
That conflict is the heart of the game.&lt;/p&gt;




&lt;h2&gt;
  
  
  🌀 What’s Next?
&lt;/h2&gt;

&lt;p&gt;Future updates may include:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;moving ghosts
&lt;/li&gt;
&lt;li&gt;timed portals
&lt;/li&gt;
&lt;li&gt;multiple levels
&lt;/li&gt;
&lt;li&gt;special cube types
&lt;/li&gt;
&lt;li&gt;stealth zones
&lt;/li&gt;
&lt;li&gt;player powers
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;GhostNet is only the beginning.&lt;/p&gt;




&lt;h2&gt;
  
  
  🏁 Final Thoughts
&lt;/h2&gt;

&lt;p&gt;GhostNet is more than a game.&lt;br&gt;&lt;br&gt;
It’s a neon puzzle. A digital chase.&lt;br&gt;&lt;br&gt;
A test of instinct, timing, and courage.&lt;br&gt;
Built on Kiro.&lt;br&gt;&lt;br&gt;
Powered by creativity.&lt;br&gt;&lt;br&gt;
Driven by players like you.&lt;br&gt;
Welcome to the network.&lt;br&gt;&lt;br&gt;
Survive if you can.&lt;/p&gt;

</description>
      <category>webdev</category>
      <category>beginners</category>
      <category>gamedev</category>
      <category>kiro</category>
    </item>
    <item>
      <title>Requiem Records: How Kiro Haunted My Codebase into a Screaming Success</title>
      <dc:creator>Krithika</dc:creator>
      <pubDate>Fri, 05 Dec 2025 12:00:32 +0000</pubDate>
      <link>https://forem.com/kirodotdev/requiem-memory-how-kiro-haunted-my-codebase-into-a-screaming-success-43pm</link>
      <guid>https://forem.com/kirodotdev/requiem-memory-how-kiro-haunted-my-codebase-into-a-screaming-success-43pm</guid>
      <description>&lt;p&gt;Three days ago, We had a wild idea: build a haunted vinyl diary web app where users record their voice, and the app transforms their words into gothic horror stories. The concept was clear in my mind dark aesthetics, creepy animations, voice-to-story generation but the path to building it? That was the real horror story.&lt;/p&gt;

&lt;p&gt;What you're about to read isn't just another "We built an app" story. This is about how an AI-powered IDE didn't just help me code it fundamentally transformed how We think about development. In 72 hours, We went from a rough concept to a fully functional, production-ready web application with features We didn't even know that we needed.&lt;/p&gt;

&lt;h2&gt;
  
  
  Quick Project Snapshot
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Category:&lt;/strong&gt; It is for the Resurrection category for kiroween competition&lt;br&gt;
&lt;strong&gt;Platform:&lt;/strong&gt; Accessible in Web&lt;br&gt;
&lt;strong&gt;Each Room/Screen:&lt;/strong&gt;&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Intro Screen&lt;/li&gt;
&lt;li&gt;Main Hub&lt;/li&gt;
&lt;li&gt;Recording Room&lt;/li&gt;
&lt;li&gt;Log Room&lt;/li&gt;
&lt;li&gt;Story Room&lt;/li&gt;
&lt;li&gt;Vinyl Room&lt;/li&gt;
&lt;li&gt;Senance Room
&lt;strong&gt;Core idea:&lt;/strong&gt; The idea is  recording out voice and turning it in a scary story &lt;/li&gt;
&lt;/ol&gt;
&lt;h2&gt;
  
  
  The Project: Building Haunted Vinyl Diary for Kiroween
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Requiem Record&lt;/strong&gt; is not just a web app, it is a ritual of memory and fear.&lt;br&gt;
Users step into a gothic, gamified world where their own voice becomes the trigger for horror.&lt;/p&gt;

&lt;p&gt;Every whispered confession, fear, or memory is recorded, transcribed in real time, and reborn as a personalized gothic nightmare — written by an AI that turns emotions into chilling folklore.&lt;br&gt;
The app features:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt; Voice recording with real-time transcription&lt;/li&gt;
&lt;li&gt; AI-generated scary stories based on your words&lt;/li&gt;
&lt;li&gt; Horror music playlist with 10 atmospheric tracks&lt;/li&gt;
&lt;li&gt; Gothic UI with blood-red text and haunting animations&lt;/li&gt;
&lt;li&gt; Fully responsive design&lt;/li&gt;
&lt;li&gt; Cinematic video transitions&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Tech Stack&lt;/strong&gt;: Vanilla JavaScript, Web Audio API, Speech Recognition API, LocalStorage, CSS3 Animations&lt;/p&gt;
&lt;h2&gt;
  
  
  Before Kiro: The Ghosts That Haunted My Development Journey
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Planning Paralysis&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Before Kiro, We lived in a loop of overthinking. We had notebooks overflowing with ideas. UI sketches, feature lists, story logic, audio concepts, yet We were paralyzed when it came time to code. I didn’t know where to begin. Should we start with the UI? The recording engine? Local Storage? Story generation? Every path felt equally important and equally risky. The fear of choosing the “wrong” first step kept me frozen, planning infinitely and building nothing.&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.amazonaws.com%2Fuploads%2Farticles%2Fahrqw3n4h50nuixj29dw.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.amazonaws.com%2Fuploads%2Farticles%2Fahrqw3n4h50nuixj29dw.png" alt="My confustion" width="797" height="426"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Debug Nightmares&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Whenever something finally stopped working...and it always did, everything spiraled. A tiny bug meant hours of console.log() breadcrumbs across files, chasing ghosts in spaghetti code. The deeper we dug, the more we forgot our original intention. Debugging didn’t feel like solving problems; it felt like losing ourself inside them.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Feature Creep Curse&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;The scariest part was how easily excitement became self-destruction. “Maybe we should add this one more feature…” turned into days of derailment. A new animation, a new mood filter, a new diary mechanic, each small idea multiplied into new threads of work. The finish line kept pulling away, and we became trapped in the illusion of progress while the project refused to end.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Where All This Led&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;We weren't stuck because of lack of passion, we were drowning in it. we were building constantly, but never finishing. we had creativity, but no direction. We weren't fighting the code… We were fighting ourself.&lt;/p&gt;

&lt;p&gt;And that’s when Kiro appeared.&lt;/p&gt;
&lt;h2&gt;
  
  
  Vibe Coding Revolution: How Natural Language Built My App
&lt;/h2&gt;

&lt;p&gt;Here's where things got wild. With Kiro, We could literally describe what we wanted in plain English:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Me&lt;/strong&gt;: "We need a vinyl room with a playlist of 10 horror songs. Each song should play completely without skipping. Show which track is playing with a pulsing animation."&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Kiro&lt;/strong&gt;: &lt;em&gt;Generates complete implementation with:&lt;/em&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Playlist UI with 10 tracks&lt;/li&gt;
&lt;li&gt;Event delegation for click handling&lt;/li&gt;
&lt;li&gt;Audio playback with proper cleanup&lt;/li&gt;
&lt;li&gt;CSS animations for active track&lt;/li&gt;
&lt;li&gt;Error handling for failed loads&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;But it wasn't just code generation. Kiro &lt;em&gt;understood context&lt;/em&gt;. When we said "the text is blurry," it knew we were talking about the story display and that the blur was coming from a CSS filter on the parent element. It didn't just remove the blur, it moved it to a pseudo-element so the background stayed blurred while the text remained sharp.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The Magic Moment&lt;/strong&gt;: We said "make the text blood red and clearer" and Kiro:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Changed color to &lt;code&gt;#8B0000&lt;/code&gt; (dark blood red)&lt;/li&gt;
&lt;li&gt;Increased font size for readability&lt;/li&gt;
&lt;li&gt;Added proper z-index layering&lt;/li&gt;
&lt;li&gt;Removed the blur filter from text&lt;/li&gt;
&lt;li&gt;Applied blur only to the background&lt;/li&gt;
&lt;li&gt;Adjusted font weight for clarity&lt;/li&gt;
&lt;/ol&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.amazonaws.com%2Fuploads%2Farticles%2F0zr7evmsyrnqxfs600fb.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.amazonaws.com%2Fuploads%2Farticles%2F0zr7evmsyrnqxfs600fb.png" alt="All We said is make it dark red" width="800" height="384"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;This wasn't autocomplete. This was &lt;em&gt;understanding&lt;/em&gt;.&lt;/p&gt;
&lt;h2&gt;
  
  
  Spec-Driven Development: From Requirements to Reality
&lt;/h2&gt;

&lt;p&gt;The real revolution for me was Kiro’s spec-driven workflow. Instead of jumping into code and improvising my way through features, Kiro pushed us to think like an engineer first and a coder second. Everything began with a requirements phase rather than an implementation phase, and that one shift alone transformed chaos into clarity.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;1. Requirements Phase&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Kiro helped us transform vague, emotional ideas into structured and testable requirements using the EARS (Easy Approach to Requirements Syntax) format. What previously felt like a shapeless feature “We want people to record memories”  became an actionable specification. For example:&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.amazonaws.com%2Fuploads%2Farticles%2Fl6dci5uqyouci6q4lffg.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.amazonaws.com%2Fuploads%2Farticles%2Fl6dci5uqyouci6q4lffg.png" alt="Requirement phase" width="800" height="547"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Nothing was ambiguous anymore, every single requirement was clear, measurable, and directly tied to a visible user outcome.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;2. Design Phase&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Once the requirements were locked in, Kiro generated a complete design document that became the guiding blueprint for the entire project. It included Mermaid-based architecture diagrams that illustrated how each component interacted, well-defined component interfaces with single responsibilities, and data models describing how audio, entries, and transcriptions would persist across the application. The design also specified correctness properties for property-based testing and outlined error-handling strategies before a single feature was coded. For the first time, I never had to stop and ask, “What were we building again?” the design doc always held the answer.&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.amazonaws.com%2Fuploads%2Farticles%2F6mcgfkaesf7uvhqeznn9.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.amazonaws.com%2Fuploads%2Farticles%2F6mcgfkaesf7uvhqeznn9.png" alt="Design phase" width="800" height="521"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;3. Implementation Phase&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Kiro then broke the design into 47 actionable tasks, each one small enough to complete confidently but meaningful enough to push the project forward. Tasks were sequenced intelligently and tied to the exact requirement they satisfied. A typical section looked like:&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.amazonaws.com%2Fuploads%2Farticles%2Fv46axcaoaqwdrz3tyhif.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.amazonaws.com%2Fuploads%2Farticles%2Fv46axcaoaqwdrz3tyhif.png" alt="Implementation phase" width="800" height="564"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Every task was specific, testable, and positioned in the order that made the most technical sense. Instead of guessing what to do next, We followed a crystal-clear roadmap, and development became smooth, predictable, and deeply satisfying.&lt;/p&gt;
&lt;h2&gt;
  
  
  Agent Hooks Magic: Automating My Workflow
&lt;/h2&gt;

&lt;p&gt;The feature that completely changed the rhythm of our development was Kiro’s Agent Hooks. They allowed our project to react automatically to our actions, almost like the codebase was alive and working alongside us. We configured hooks so that the moment we saved a file, Kiro would immediately run the tests associated with the component we had just edited. It looked 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;{
  "trigger": "onFileSave",
  "pattern": "js/*.js",
  "action": "npm test -- {{filename}}"
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;With a single hook, the endless cycle of “wait, did we run the tests?” disappeared. Every save meant instant feedback, no excuses, no forgetfulness, and zero stale bugs left behind.&lt;/p&gt;

&lt;p&gt;We then pushed automation even further. Whenever a specification document changed, Kiro would automatically regenerate the task list to reflect the new requirements. In other words, feature updates and planning stayed perfectly synchronized without manual effort:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;{
  "trigger": "onFileChange",
  "pattern": ".kiro/specs/*/design.md",
  "action": "regenerate-tasks"
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Suddenly, the entire workflow became self-maintaining. We never ended up with outdated tasks, forgotten tests, or misaligned specs. Development finally felt like flow instead of management, We could focus on creativity and problem-solving while the system quietly handled discipline and structure in the background.&lt;/p&gt;

&lt;h2&gt;
  
  
  Steering Documents: Giving Kiro Clear Direction
&lt;/h2&gt;

&lt;p&gt;Steering docs acted like a style guide for our AI development workflow. By defining my standards up-front, Kiro consistently generated clean, predictable, and scalable code—removing the usual tech-debt chaos from the start.&lt;/p&gt;

&lt;h2&gt;
  
  
  MCP Integration: Unlocking Kiro’s Full Project Awareness
&lt;/h2&gt;

&lt;p&gt;Integrating the @modelcontextprotocol/server-filesystem gave Kiro deep visibility into my entire codebase — not just the file we were working on. This transformed the AI from a code assistant into a full project-aware collaborator.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;{
  "mcpServers": {
    "filesystem": {
      "command": "npx",
      "args": ["-y", "@modelcontextprotocol/server-filesystem", "./"]
    }
  }
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;A simple filesystem MCP server connected Kiro to the full project directory.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;What Kiro Gained&lt;/strong&gt;&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Awareness of full folder and file structure&lt;/li&gt;
&lt;li&gt;Ability to search and open related files automatically
3.Cross-file refactoring suggestions
4.Detection of naming and logic inconsistencies across modules&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;&lt;strong&gt;Real-World Example&lt;/strong&gt;&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;When we asked “make the vinyl room buttons work”, 
2.Kiro found the UI manager file
3.Located event handlers in app.js
4.Detected a CSS stacking/z-index conflict
5.Updated all three files in one coordinated fix&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;&lt;strong&gt;The Outcome&lt;/strong&gt;&lt;br&gt;
No more context explaining, copy-pasting code, or hunting through folders  Kiro understood the whole project and applied changes precisely where they needed to happen.&lt;/p&gt;

&lt;h2&gt;
  
  
  Surprising Discoveries
&lt;/h2&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Kiro Learns Your Style&lt;/strong&gt;: After a few iterations, Kiro started matching our coding style without prompting&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Context is King&lt;/strong&gt;: The more context we provided, the better the results&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Specs Are Living Documents&lt;/strong&gt;: Updating the spec mid-project helped Kiro stay aligned&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Property-Based Testing is Powerful&lt;/strong&gt;: Kiro made it accessible; caught bugs we never have found&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  Recording Screen - Capturing Souls
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;What it does:&lt;/strong&gt; Records voice audio with a pulsing red indicator, timer, and back button.&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.amazonaws.com%2Fuploads%2Farticles%2Fwdbv404vdlfp7b8nykz4.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.amazonaws.com%2Fuploads%2Farticles%2Fwdbv404vdlfp7b8nykz4.png" alt="Recording room" width="800" height="386"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;How Kiro helped:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;Microphone Access: Built robust Recording Service with proper error handling for permission denied, no microphone, device in use, etc.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;MediaRecorder API: Implemented browser audio recording with proper blob creation and duration tracking&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;UI Feedback: Created pulsing recording indicator, live timer display, and button state changes&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Error Handling: Added extensive logging and user-friendly error messages for debugging recording issues&lt;/p&gt;&lt;/li&gt;
&lt;/ul&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.amazonaws.com%2Fuploads%2Farticles%2Fvsat6o7svhcd2fuqznjq.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.amazonaws.com%2Fuploads%2Farticles%2Fvsat6o7svhcd2fuqznjq.png" alt="Recording code" width="800" height="555"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Transcription Integration: Connected to Transcription Service for live speech-to-text&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Story Screen - The Horror Transformation
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;What it does:&lt;/strong&gt; Plays a video, shows a diary, then displays your voice recording transformed into a gothic horror story.&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.amazonaws.com%2Fuploads%2Farticles%2Fkuvl27xzk5e8d93xtqsc.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.amazonaws.com%2Fuploads%2Farticles%2Fkuvl27xzk5e8d93xtqsc.png" alt="Story" width="800" height="379"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;How Kiro helped:&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;1.&lt;strong&gt;Story Generation Service:&lt;/strong&gt; Created StoryGenerationService with 1,200+ unique story combinations using:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;15 different spooky locations&lt;/li&gt;
&lt;li&gt;8 themes with 4 adjectives each&lt;/li&gt;
&lt;li&gt;10 story styles (diary entry, confession, warning, etc.)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Transformation Algorithm:&lt;/strong&gt; Built a system that takes YOUR actual words and transforms them into horror narrative 
Implemented proper event listeners and timeouts&lt;/li&gt;
&lt;li&gt;**Animation: **Added handwriting-style text animation with paragraph delays&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Vinyl Room - The Music Sanctuary
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;What it does:&lt;/strong&gt; Plays 10 local horror-themed music tracks with a vinyl record aesthetic and playlist.&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.amazonaws.com%2Fuploads%2Farticles%2Fbraix6x420b8ef2d3pkp.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.amazonaws.com%2Fuploads%2Farticles%2Fbraix6x420b8ef2d3pkp.png" alt="vinyl" width="800" height="385"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;How Kiro helped:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Playlist System:&lt;/strong&gt; Built dynamic playlist rendering with all 10 local music files from assets folder&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Music Playback:&lt;/strong&gt; Implemented audio player with track selection, next song button, and now playing display&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Event Delegation:&lt;/strong&gt; Fixed button click issues by using single event listener instead of multiple handlers&lt;/li&gt;
&lt;/ul&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.amazonaws.com%2Fuploads%2Farticles%2F8uorrra9psfv5b1ko9z8.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.amazonaws.com%2Fuploads%2Farticles%2F8uorrra9psfv5b1ko9z8.png" alt="code" width="800" height="557"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Error Handling:&lt;/strong&gt; Added graceful error messages instead of auto-skipping on audio errors&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Volume Control:&lt;/strong&gt; Set vinyl room volume to 70% for better listening experience&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Séance Room - Summoning the Dead
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;What it does:&lt;/strong&gt; An interactive ghost summoning experience with 11 unique spirits, probability-based encounters, danger levels, timeout mechanics, and atmospheric effects.&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.amazonaws.com%2Fuploads%2Farticles%2Fpkylqwa7geit4w9uyo3m.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.amazonaws.com%2Fuploads%2Farticles%2Fpkylqwa7geit4w9uyo3m.png" alt="Senance room" width="800" height="369"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;How Kiro helped:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Ghost Data Structure&lt;/strong&gt;: Created 11 detailed ghost entities with unique backstories, danger levels (1-10), rarity tiers (Common, Uncommon, Rare, Very Rare), and personalized dialogue&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Probability System&lt;/strong&gt;: Implemented weighted random selection algorithm that tracks recently summoned ghosts to prevent repetition and ensure variety&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Danger Mechanics&lt;/strong&gt;: Built dynamic danger level visualization with color-coded bars, heartbeat audio that adjusts speed/volume based on threat level, and progressive vignette effects&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Timeout System&lt;/strong&gt;: Added 20-second idle timer that triggers scary glitch effects, screen shake, distorted text overlay, and warning messages if user doesn't summon&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;State Management&lt;/strong&gt;: Orchestrated complex state transitions between SEANCE → SUMMONING → GHOST_ENCOUNTER with proper cleanup and media handling&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Key Features:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;11 Unique Ghosts&lt;/strong&gt;: Each with custom artwork, tragic backstory, and personality&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Weighted Probability&lt;/strong&gt;: Common (40%), Uncommon (25%), Rare (20%), Very Rare (15%)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Anti-Repetition&lt;/strong&gt;: Tracks last 3 summoned ghosts to ensure variety&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Danger Levels&lt;/strong&gt;: 

&lt;ul&gt;
&lt;li&gt;1-2: Harmless (green) - No effects&lt;/li&gt;
&lt;li&gt;3-4: Unsettling (yellow) - Light vignette&lt;/li&gt;
&lt;li&gt;5-6: Dangerous (orange) - Heartbeat + medium vignette&lt;/li&gt;
&lt;li&gt;7-8: Extremely Dangerous (red) - Fast heartbeat + heavy vignette&lt;/li&gt;
&lt;li&gt;9-10: LETHAL (dark red) - Intense heartbeat + extreme vignette&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;li&gt;

&lt;strong&gt;Atmospheric Audio&lt;/strong&gt;: Dynamic heartbeat that changes speed/volume based on danger&lt;/li&gt;

&lt;li&gt;

&lt;strong&gt;Visual Effects&lt;/strong&gt;: Black vignette, screen shake, glitch animations, transition overlays&lt;/li&gt;

&lt;li&gt;

&lt;strong&gt;History Reveal&lt;/strong&gt;: Players can choose to hear each ghost's tragic backstory or decline (triggers red vignette effect)&lt;/li&gt;

&lt;li&gt;

&lt;strong&gt;Timeout Punishment&lt;/strong&gt;: Wait too long without summoning and face scary consequences&lt;/li&gt;

&lt;/ul&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.amazonaws.com%2Fuploads%2Farticles%2Ffy0a3879ap1ye8s77f8r.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.amazonaws.com%2Fuploads%2Farticles%2Ffy0a3879ap1ye8s77f8r.png" alt="The ghost summoned" width="800" height="375"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The Real Victory&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Requiem Records isn’t just functional, it’s polished, tested, and production-ready. But the true victory wasn’t the finished product. It was the experience of building it. We enjoyed every step. There were no draining 3 AM debugging sessions, no moments of burnout, and no frustration spirals. Instead, We had meaningful discussions with Kiro about architecture, refined designs through iteration, and focused on creativity rather than wrestling with syntax. It reminded us that this is what development is supposed to feel like.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Final Thoughts&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;If you’re reading this and haven’t tried Kiro, the best thing you can do is stop reading and start building. I understand the skepticism, I was skeptical too. But here’s the truth: the future of development isn’t about writing more code, it’s about thinking more clearly. Kiro helps you think clearly, and when clarity becomes effortless, the code follows naturally.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Dare to witness the darkness:&lt;/strong&gt; &lt;a href="https://krithika-r2007.github.io/Requiemrecords/" rel="noopener noreferrer"&gt;Deployed&lt;/a&gt;&lt;br&gt;
&lt;strong&gt;If you’re brave enough… enter my GitHub crypt:&lt;/strong&gt; &lt;a href="https://github.com/Krithika-R2007/Requiemrecords" rel="noopener noreferrer"&gt;Github&lt;/a&gt;&lt;br&gt;
&lt;strong&gt;Summon the cursed footage here:&lt;/strong&gt; &lt;a href="https://www.youtube.com/watch?v=jmsIqz6gLn4" rel="noopener noreferrer"&gt;https://www.youtube.com/watch?v=jmsIqz6gLn4&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;🎃 Happy Kiroween! 🎃&lt;/p&gt;

</description>
      <category>kiro</category>
      <category>kiroween</category>
      <category>kirodotdev</category>
      <category>hookedonkiro</category>
    </item>
    <item>
      <title>A CURSED TAROT</title>
      <dc:creator>Marie Otoo</dc:creator>
      <pubDate>Fri, 05 Dec 2025 04:15:55 +0000</pubDate>
      <link>https://forem.com/kirodotdev/a-cursed-tarot-3i0k</link>
      <guid>https://forem.com/kirodotdev/a-cursed-tarot-3i0k</guid>
      <description>&lt;p&gt;Imagine attending a Halloween party to enjoy the pumpkin pies and gooky atmosphere, devoid of online scare. It gives a party vibe with a touch of horror. Now, a characteristic spookiness could be added to the scary event and revamped with the Cursed Tarot application.&lt;/p&gt;

&lt;p&gt;The Cursed Tarot app was built with Kiro IDE. It is a Gothic app that features a horrifying welcome page to scare users, a horrific tale, with a silent candle flickering eerily, a dark card collection from which to draw a game of electrifying fate, and a smashing zombie game. This wicked app has a Harry Potter theme in the middle, detailing the houses, named in the movie, owl-wise quizzes about the book, and pure randomness. The technological language stack iciness is loosely based on the revived COBOL to serve the backend logic of this cursed application. In addition, the Frankenstein element combines the age-old COBOL with modern HTML in an oddly eerie fashion. It is just chaotic. In an invisible note, the design consists of small ogres, vampires, and werewolves, with haunted mansions in the background.&lt;/p&gt;

&lt;p&gt;To conclude, this subtly ghastly application is run locally on a computer with a web browser pre-installed. This is a card game that possesses obsolete technology, intertwined with current stacks for a complete goosebump-inducing experience.&lt;/p&gt;

</description>
      <category>kiro</category>
    </item>
  </channel>
</rss>
