<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: Anna Villarreal</title>
    <description>The latest articles on DEV Community by Anna Villarreal (@annavi11arrea1).</description>
    <link>https://dev.to/annavi11arrea1</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F1467610%2F3e4ce925-fffd-4643-9aae-8ee77cf01828.png</url>
      <title>DEV Community: Anna Villarreal</title>
      <link>https://dev.to/annavi11arrea1</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/annavi11arrea1"/>
    <language>en</language>
    <item>
      <title>🦄 Sharing DEV Followers Count on Github Profile 🦄</title>
      <dc:creator>Anna Villarreal</dc:creator>
      <pubDate>Wed, 23 Sep 2026 22:42:13 +0000</pubDate>
      <link>https://dev.to/annavi11arrea1/sharing-dev-followers-count-on-github-profile-bj3</link>
      <guid>https://dev.to/annavi11arrea1/sharing-dev-followers-count-on-github-profile-bj3</guid>
      <description>&lt;h4&gt;
  
  
  TLDR - I finished a small project that I am sharing for my friends on DEV, that allows you to put your followers count on your Github!
&lt;/h4&gt;





&lt;p&gt;I had started this nonsense last year, and like many silly side projects, let it go to the wayside. But there was one consistent tiny reminder in my email everyday: "Run failed for Github Actions..." 😂&lt;/p&gt;

&lt;p&gt;I said well, I could just delete it all. But that bothered me. I should be able to get it to work, right? Feeling determined today, I said enough is enough with the annoying emails and I am going to fix this. A little tweak to the api calling and a little deletion of unused code that I had commented out and things began to get much more clear. &lt;/p&gt;

&lt;p&gt;I'm going to share the full code of my working github actions file that is working for me, so someone else on here that wants it doesn't have to silently succumb to a year of annoying emails like I did. Evidence of that can be seen with successful run #343 - the first successful run.&lt;/p&gt;

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





&lt;h4&gt;
  
  
  Here's the good part you have been looking for.
&lt;/h4&gt;

&lt;p&gt;YAML File:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Update DEV.to Followers Count&lt;/span&gt;

&lt;span class="na"&gt;on&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;schedule&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="c1"&gt;# Runs at midnight UTC daily&lt;/span&gt;
    &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;cron&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;0&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;0&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;*&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;*&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;*'&lt;/span&gt;
  &lt;span class="na"&gt;workflow_dispatch&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;

&lt;span class="na"&gt;permissions&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;contents&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;write&lt;/span&gt;

&lt;span class="na"&gt;jobs&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;update-count&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;runs-on&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;ubuntu-latest&lt;/span&gt;
    &lt;span class="na"&gt;steps&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Checkout repository&lt;/span&gt;
        &lt;span class="na"&gt;uses&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;actions/checkout@v4&lt;/span&gt;

      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Set up Node.js&lt;/span&gt;
        &lt;span class="na"&gt;uses&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;actions/setup-node@v4&lt;/span&gt;
        &lt;span class="na"&gt;with&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
          &lt;span class="na"&gt;node-version&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;22'&lt;/span&gt;

      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Run the update script&lt;/span&gt;
        &lt;span class="na"&gt;env&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
          &lt;span class="na"&gt;DEVTO_API_KEY&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;${{ secrets.DEVTO_API_KEY }}&lt;/span&gt;
          &lt;span class="na"&gt;DEVTO_USERNAME&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;annavi11arrea1&lt;/span&gt;
        &lt;span class="na"&gt;run&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;node update_script.js&lt;/span&gt;

      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Commit updated README&lt;/span&gt;
        &lt;span class="na"&gt;run&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="pi"&gt;|&lt;/span&gt;
          &lt;span class="s"&gt;if git diff --quiet -- README.md; then&lt;/span&gt;
            &lt;span class="s"&gt;echo "Follower count has not changed."&lt;/span&gt;
            &lt;span class="s"&gt;exit 0&lt;/span&gt;
          &lt;span class="s"&gt;fi&lt;/span&gt;
          &lt;span class="s"&gt;git config user.name "github-actions[bot]"&lt;/span&gt;
          &lt;span class="s"&gt;git config user.email "41898282+github-actions[bot]@users.noreply.github.com"&lt;/span&gt;
          &lt;span class="s"&gt;git add README.md&lt;/span&gt;
          &lt;span class="s"&gt;git commit -m "Update DEV.to follower count"&lt;/span&gt;
          &lt;span class="s"&gt;git push&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Notes:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;Please remember to use environment variables in your code for API keys, don't hard code them into this! We want to keep our privilege to use DEV API for free. 🦋&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;You will need to specify where in your README.md to place the output. You will need appropriate tags. I did it like this:&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Scroll so you see the whole thing, there is a &lt;em&gt;start&lt;/em&gt; and &lt;em&gt;end&lt;/em&gt; tag.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight html"&gt;&lt;code&gt;&lt;span class="c"&gt;&amp;lt;!-- DEVTO-FOLLOWERS-COUNT:START --&amp;gt;&lt;/span&gt;**34996** DEV.to followers&lt;span class="c"&gt;&amp;lt;!-- DEVTO-FOLLOWERS-COUNT:END --&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;







&lt;p&gt;I'm so psyched I got this working. I'm not here to brag about my followers, but we might all want to brag silently on our own profiles elsewhere. Haha.&lt;/p&gt;

&lt;p&gt;Important changes made: make sure that I gave it write permissions, and also that the write is auto-pushed when the job runs. Key factors for a real update. Hmm, I wonder how else I might use Github actions for auto-updates?&lt;/p&gt;





&lt;p&gt;Shout out to &lt;a class="mentioned-user" href="https://dev.to/fm"&gt;@fm&lt;/a&gt; for calling me out. Here is my update_script.js 😂&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;fs&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;require&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;fs&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;https&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;require&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;https&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;DEVTO_API_KEY&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;process&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;env&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;DEVTO_API_KEY&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;DEVTO_USERNAME&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;process&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;env&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;DEVTO_USERNAME&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;annavi11arrea1&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;README_FILE&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;README.md&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;START_MARKER&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;&amp;lt;!-- DEVTO-FOLLOWERS-COUNT:START --&amp;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;END_MARKER&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;&amp;lt;!-- DEVTO-FOLLOWERS-COUNT:END --&amp;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;USER_AGENT&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;AnnaVi11arrea1-GitHub-Actions&lt;/span&gt;&lt;span class="dl"&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;DEVTO_API_KEY&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;throw&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;Error&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Missing required DEVTO_API_KEY environment variable.&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;parseResponsePreview&lt;/span&gt; &lt;span class="o"&gt;=&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="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;trimmed&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;data&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="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;trimmed&lt;/span&gt; &lt;span class="p"&gt;?&lt;/span&gt; &lt;span class="nx"&gt;trimmed&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;slice&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;500&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;&amp;lt;empty&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="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;fetchJson&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;path&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;options&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="na"&gt;hostname&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;dev.to&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&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;span class="nx"&gt;path&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;method&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;GET&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;headers&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;api-key&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;DEVTO_API_KEY&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="na"&gt;Accept&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;application/vnd.forem.api-v1+json&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="s2"&gt;User-Agent&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;USER_AGENT&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="p"&gt;},&lt;/span&gt;
    &lt;span class="na"&gt;timeout&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;15000&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="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;Promise&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="nx"&gt;resolve&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;reject&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;req&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;https&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;request&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;options&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;res&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;data&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;res&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;on&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;data&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;chunk&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;data&lt;/span&gt; &lt;span class="o"&gt;+=&lt;/span&gt; &lt;span class="nx"&gt;chunk&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
      &lt;span class="p"&gt;});&lt;/span&gt;

      &lt;span class="nx"&gt;res&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;on&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;end&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="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;res&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;statusCode&lt;/span&gt; &lt;span class="o"&gt;!==&lt;/span&gt; &lt;span class="mi"&gt;200&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;preview&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;parseResponsePreview&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="nf"&gt;reject&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
            &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;Error&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
              &lt;span class="s2"&gt;`DEV.to API request failed (&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;res&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;statusCode&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt; &lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;res&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;statusMessage&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Unknown&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;). Response preview: &lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;preview&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;`&lt;/span&gt;
            &lt;span class="p"&gt;)&lt;/span&gt;
          &lt;span class="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;try&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
          &lt;span class="nf"&gt;resolve&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;data&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;catch &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;error&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
          &lt;span class="nf"&gt;reject&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;Error&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;`Failed to parse API response. Response data: &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="s2"&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="nx"&gt;req&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;on&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;timeout&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="nx"&gt;req&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;destroy&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;Error&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;DEV.to API request timed out.&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)));&lt;/span&gt;
    &lt;span class="nx"&gt;req&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;on&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;error&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;reject&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="nx"&gt;req&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;end&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="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;getFollowersCount&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;async &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;perPage&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;1000&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;page&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="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;totalCount&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="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="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;followers&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;fetchJson&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
      &lt;span class="s2"&gt;`/api/followers/users?page=&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;page&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;&amp;amp;per_page=&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;perPage&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;`&lt;/span&gt;
    &lt;span class="p"&gt;);&lt;/span&gt;

    &lt;span class="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="nb"&gt;Array&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;isArray&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;followers&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="k"&gt;throw&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;Error&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;DEV.to followers endpoint returned an invalid response.&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;totalCount&lt;/span&gt; &lt;span class="o"&gt;+=&lt;/span&gt; &lt;span class="nx"&gt;followers&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="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;followers&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;lt;&lt;/span&gt; &lt;span class="nx"&gt;perPage&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;totalCount&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="nx"&gt;page&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="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;updateReadme&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;async &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;count&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;getFollowersCount&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;readmeContent&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;fs&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;readFileSync&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;README_FILE&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;utf8&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;newContent&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;`&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;START_MARKER&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;**&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;count&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;** DEV.to followers&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;END_MARKER&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;`&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;regex&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;RegExp&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;`&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;START_MARKER&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;[&lt;/span&gt;&lt;span class="se"&gt;\\&lt;/span&gt;&lt;span class="s2"&gt;s&lt;/span&gt;&lt;span class="se"&gt;\\&lt;/span&gt;&lt;span class="s2"&gt;S]*?&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;END_MARKER&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;`&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;g&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="nx"&gt;readmeContent&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;readmeContent&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="nx"&gt;regex&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;newContent&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

  &lt;span class="nx"&gt;fs&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;writeFileSync&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;README_FILE&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;readmeContent&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;README updated with new follower count:&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;count&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;};&lt;/span&gt;

&lt;span class="nf"&gt;updateReadme&lt;/span&gt;&lt;span class="p"&gt;().&lt;/span&gt;&lt;span class="k"&gt;catch&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="nx"&gt;error&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;error&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;error&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="nx"&gt;process&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;exit&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="p"&gt;});&lt;/span&gt;

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



</description>
      <category>github</category>
      <category>webdev</category>
      <category>tooling</category>
      <category>githubactions</category>
    </item>
    <item>
      <title>Examining the "Boolean-Switch" in Java \_(ツ)_/</title>
      <dc:creator>Anna Villarreal</dc:creator>
      <pubDate>Tue, 15 Sep 2026 23:22:51 +0000</pubDate>
      <link>https://dev.to/annavi11arrea1/examining-the-boolean-switch-in-java-tu--39j4</link>
      <guid>https://dev.to/annavi11arrea1/examining-the-boolean-switch-in-java-tu--39j4</guid>
      <description>&lt;p&gt;While reading through some ai-generated code today, I was caught off guard when I saw a switch statement inside of a boolean. What on earth has ai done now, I wondered. Here is the suspect:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight java"&gt;&lt;code&gt;    &lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="kt"&gt;boolean&lt;/span&gt; &lt;span class="nf"&gt;canChangeTo&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;OrderStatus&lt;/span&gt; &lt;span class="n"&gt;newStatus&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;switch&lt;/span&gt; &lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
            &lt;span class="k"&gt;case&lt;/span&gt; &lt;span class="nl"&gt;PLACED:&lt;/span&gt;
                &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;newStatus&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="no"&gt;PROCESSING&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="n"&gt;newStatus&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="no"&gt;CANCELLED&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;
            &lt;span class="k"&gt;case&lt;/span&gt; &lt;span class="nl"&gt;PROCESSING:&lt;/span&gt;
                &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;newStatus&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="no"&gt;SHIPPED&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="n"&gt;newStatus&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="no"&gt;CANCELLED&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;
            &lt;span class="k"&gt;case&lt;/span&gt; &lt;span class="nl"&gt;SHIPPED:&lt;/span&gt;
                &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;newStatus&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="no"&gt;DELIVERED&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;
            &lt;span class="k"&gt;default&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;
                &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;
        &lt;span class="o"&gt;}&lt;/span&gt;
    &lt;span class="o"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Shortly after marveling at "ooo, this is a thing?" I started to question it. Why does this feel &lt;em&gt;different&lt;/em&gt;?&lt;/p&gt;

&lt;p&gt;Basically in every other case besides enum, this is bad. Enum, to my knowledge, is the only case where this is acceptable. Why? Because in a flow where there can be one and only one direction, like order processing, this checks out. Each one will hit in the order it is intended to hit. Each case will be found true at the right time in the process, and will update the status accordingly. &lt;/p&gt;

&lt;p&gt;We can define our fixed, or set values (enums, short for enumeration) to use later in our switch.&lt;/p&gt;

&lt;p&gt;That might look like:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight java"&gt;&lt;code&gt;&lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="kd"&gt;enum&lt;/span&gt; &lt;span class="nc"&gt;OrderStatus&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
    &lt;span class="no"&gt;PLACED&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Placed"&lt;/span&gt;&lt;span class="o"&gt;),&lt;/span&gt;
    &lt;span class="no"&gt;PROCESSING&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Processing"&lt;/span&gt;&lt;span class="o"&gt;),&lt;/span&gt;
    &lt;span class="no"&gt;SHIPPED&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Shipped"&lt;/span&gt;&lt;span class="o"&gt;),&lt;/span&gt;
    &lt;span class="no"&gt;DELIVERED&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Delivered"&lt;/span&gt;&lt;span class="o"&gt;),&lt;/span&gt;
    &lt;span class="no"&gt;CANCELLED&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Cancelled"&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
&lt;span class="o"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;We use enums when we have a distinct list of choices that do not change. This could also work for days of the week or the current states of your most recent job applications. Because we are using enum, we avoid typos. Also, using your choices as enum values makes them classes in Java - a whole 'nother level of excitement.&lt;/p&gt;

&lt;p&gt;But back to my boolean-switch...&lt;/p&gt;

&lt;p&gt;Upon doing further digger I discovered that what claude had spat out (no hate on claude, I love claude!) was actually not optimized. There is a better way to write the boolean-switch. Since Java 14, we can use arrows to slim down our writing.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight java"&gt;&lt;code&gt;&lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="kt"&gt;boolean&lt;/span&gt; &lt;span class="nf"&gt;canChangeTo&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;OrderStatus&lt;/span&gt; &lt;span class="n"&gt;newStatus&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nf"&gt;switch&lt;/span&gt; &lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;case&lt;/span&gt; &lt;span class="no"&gt;PLACED&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;newStatus&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="no"&gt;PROCESSING&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="n"&gt;newStatus&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="no"&gt;CANCELLED&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;
        &lt;span class="k"&gt;case&lt;/span&gt; &lt;span class="no"&gt;PROCESSING&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;newStatus&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="no"&gt;SHIPPED&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="n"&gt;newStatus&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="no"&gt;CANCELLED&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;
        &lt;span class="k"&gt;case&lt;/span&gt; &lt;span class="no"&gt;SHIPPED&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;newStatus&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="no"&gt;DELIVERED&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;
        &lt;span class="k"&gt;default&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;
    &lt;span class="o"&gt;};&lt;/span&gt;
&lt;span class="o"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;...slimmer and concise! My brain likes this better.&lt;/p&gt;

&lt;p&gt;A peculiar boolean-switch has made me realize that using enums is incredibly more powerful and efficient than writing a string in an if-else statement. By using the &lt;em&gt;this&lt;/em&gt; keyword on the switch statement itself, it evaluates the enum value and not the boolean value. If you want to "switch" on the boolean, then just use an if else statement, because there is only two values anyways...&lt;/p&gt;

&lt;p&gt;I used to lean into if-else statements, but the switch is starting to grow on me.&lt;/p&gt;

</description>
      <category>java</category>
      <category>software</category>
      <category>programming</category>
      <category>67</category>
    </item>
    <item>
      <title>Web Traffic Inspector Dash</title>
      <dc:creator>Anna Villarreal</dc:creator>
      <pubDate>Mon, 14 Sep 2026 02:41:36 +0000</pubDate>
      <link>https://dev.to/annavi11arrea1/web-traffic-inspector-dash-3af8</link>
      <guid>https://dev.to/annavi11arrea1/web-traffic-inspector-dash-3af8</guid>
      <description>&lt;h2&gt;
  
  
  Mischievous Behavior Detected 🔎
&lt;/h2&gt;

&lt;p&gt;It all started when I discovered an anonymous user was uploading a file with a strange extension and then trying to execute it. &lt;/p&gt;

&lt;p&gt;&lt;em&gt;Disclaimer: It's called experimental microserver for a reason. I am learning. My firewall was clearly lacking. Honestly I'm glad this happened. Let me explain why.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;On my new mission to tighten up security, I wanted a dashboard to learn about my server traffic. I got that setup and working nicely, building a child spawned from Go Access data. During this time, I discovered this 'malicious' photo uploading activity. I was not expecting this. I went down a semi-lengthy dashboard building process. I learned so much about what "suspicious activity" or "red flags" might be. It was very eye-opening and I learned a lot about general web traffic. &lt;/p&gt;





&lt;h3&gt;
  
  
  High Level Understanding - What IPs Should be Blocked?
&lt;/h3&gt;

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

&lt;p&gt;I found ways to sort the activity, giving me starting criteria for considering blocking. Loosely put, its sort of some combination of:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;IP addresses that try visiting routes that don't exist. 404 flag.&lt;/li&gt;
&lt;li&gt;IP addresses snooping for places to upload things or search for .env files. Snooper flag.&lt;/li&gt;
&lt;li&gt;IP addresses that make any attempt to access or change anything on your server that they should not. These are offenders. They get the "Tamper" flag.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The flags are assigned automatically in the dashboard to help me understand who is doing what at a very far distance. &lt;/p&gt;

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





&lt;h3&gt;
  
  
  Don't Accidentally Ban Yourself
&lt;/h3&gt;

&lt;p&gt;Important: You may need to tweak your filtering so that you don't block IP addresses that look 'bad' on a high level, but are actually very important. 😂 Don't ban yourself from yourself. I almost did that!&lt;/p&gt;

&lt;p&gt;At the top of my dashboard I've added a block button, and for rage-mode, multi-block. I have now made blocking IP addresses a fun game I suppose.&lt;/p&gt;

&lt;p&gt;I also created an incident report area to track the worst just for fun. &lt;/p&gt;

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

&lt;p&gt;I learned about the types of rules one might want to create for their firewall based on how the "bad" IP addresses are trying to interact with your system. I've created certain auto-ban rules after examining the traffic. That's cool. It's nice to know why you are doing something, why it's important. &lt;/p&gt;

&lt;p&gt;Experience is the best teacher. There is web development, and then there is security. When you have a server, you must do both. You don't built a castle and leave all the doors unlocked. Nobody would. &lt;/p&gt;





&lt;h3&gt;
  
  
  Recommendations from Learning
&lt;/h3&gt;

&lt;p&gt;I recommend checking out &lt;a href="https://www.abuseipdb.com/" rel="noopener noreferrer"&gt;https://www.abuseipdb.com/&lt;/a&gt; for a free API key! You can easily set that up on your own. I found them to be an excellent starting resource for answers.&lt;/p&gt;

</description>
      <category>webdev</category>
      <category>security</category>
      <category>cybersecurity</category>
      <category>analytics</category>
    </item>
    <item>
      <title>🟣 Ever Fluorescent: Live Again!</title>
      <dc:creator>Anna Villarreal</dc:creator>
      <pubDate>Tue, 08 Sep 2026 00:43:39 +0000</pubDate>
      <link>https://dev.to/annavi11arrea1/ever-fluorescent-live-again-132b</link>
      <guid>https://dev.to/annavi11arrea1/ever-fluorescent-live-again-132b</guid>
      <description>&lt;h2&gt;
  
  
  ⭐Excitement!
&lt;/h2&gt;

&lt;p&gt;I've had had stores on Shopify, and a successful Etsy store. But after years of ups and downs and general nonsense, I'm done living by someone else's standards. I wanted to build my own fully functional shop. It had been thrown on the backburner for a long time. Today -- I present a working Ecommerce site built by yours truley!&lt;/p&gt;

&lt;h2&gt;
  
  
  Integrations:
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Stripe&lt;/li&gt;
&lt;li&gt;Cloudflare&lt;/li&gt;
&lt;li&gt;Gorgeously simple admin dashboard that is clear and makes sense&lt;/li&gt;
&lt;li&gt;A small art gallery to represent myself as an artist (only a few pictures for now)&lt;/li&gt;
&lt;li&gt;Product uploads from varying places (like excel 2003, smh)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;I've ran it through basic SEO tests to make sure I'm not totally failing.&lt;/p&gt;

&lt;p&gt;It's live. It will accept payments! &lt;/p&gt;

&lt;p&gt;-- proud developer moment --&lt;/p&gt;

&lt;p&gt;I'm going to share some picks but here is the link:&lt;/p&gt;

&lt;h2&gt;
  
  
  &lt;a href="https://everfluorescent.com" rel="noopener noreferrer"&gt;Everfluorescent.com&lt;/a&gt;
&lt;/h2&gt;

&lt;p&gt;Eeeeeeeeeeee!!!!!&lt;/p&gt;

&lt;p&gt;Main Page:&lt;br&gt;
&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fm214b8kwcf22lazo4m6g.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fm214b8kwcf22lazo4m6g.png" alt="Ever Fluorescent Main Page" width="799" height="673"&gt;&lt;/a&gt;&lt;/p&gt;

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





&lt;p&gt;Let me know if you find a bug! &amp;lt;3&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F56zxlr6lv086ezw0v09j.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F56zxlr6lv086ezw0v09j.gif" alt="Excited kitty slaps table repeatedly" width="220" height="219"&gt;&lt;/a&gt;&lt;/p&gt;



&lt;br&gt;
Edit 9/18/2026

&lt;p&gt;I'm adding a video for those of you that would like to meet the artist side of me! &lt;/p&gt;

&lt;p&gt;  &lt;iframe src="https://www.youtube.com/embed/8hPXt9hriQY" width="710" height="399"&gt;
  &lt;/iframe&gt;
&lt;/p&gt;

</description>
      <category>webdev</category>
      <category>rails</category>
      <category>programming</category>
      <category>community</category>
    </item>
    <item>
      <title>Remembering Why We Code: Dropping AI for Fundamental Learning</title>
      <dc:creator>Anna Villarreal</dc:creator>
      <pubDate>Sun, 06 Sep 2026 19:18:44 +0000</pubDate>
      <link>https://dev.to/annavi11arrea1/remembering-why-we-code-dropping-ai-for-fundamental-learning-4868</link>
      <guid>https://dev.to/annavi11arrea1/remembering-why-we-code-dropping-ai-for-fundamental-learning-4868</guid>
      <description>&lt;h3&gt;
  
  
  Excitement
&lt;/h3&gt;

&lt;p&gt;I wanted to share my excitement of forcing myself to build without using AI.&lt;/p&gt;

&lt;p&gt;I would say I've been learning to code semi-consistently for roughly 3 years. Most recently, I started a class called Java 2. It builds on foundational concepts learned in the previous class. I feel I might be spread a bit thin, dabbling over here in Ruby, dabbling over there in Python, and then poking around at Java on Sunday afternoon. Good thing they share the overarching concepts or it would be utter confusion. ¯(°_o)/¯&lt;/p&gt;

&lt;p&gt;As many of us have been leaning heavily into AI to build amazing things at lightspeed, I made the conscious decision to not open a single AI tool as I power through the first assignment of the class. I said to myself "No, I'm going to struggle because I want to understand." And I can tell you, after using AI constantly, this was a wakeup call to the real rewards that exist when you stop relying on AI.&lt;/p&gt;

&lt;p&gt;I did google a few things, but that is allowed if you ask me. This is the internet, after all.&lt;/p&gt;

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

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fimj3fgrwgsan7pcgr68m.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fimj3fgrwgsan7pcgr68m.png" alt="peter griffin bird is the word" width="800" height="596"&gt;&lt;/a&gt;&lt;/p&gt;





&lt;h3&gt;
  
  
  Getting Things to Click Again
&lt;/h3&gt;

&lt;p&gt;At some point in my assignment things started clicking again. That was an amazing moment. A class can have methods, we can call those in the main function, I can nest switch statements for nested menus, and I can debug in real life with minimal resources! Twice I got stuck, and twice I came out the other sided with that excited "**** yes, it's working!" moment - a feeling I have been missing for some time. The feeling that you struggled and powered through it and figured out what was seemingly impossible by yourself.&lt;/p&gt;

&lt;p&gt;Here is the sub-menu I built today with a nested switch statement.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight java"&gt;&lt;code&gt;&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="nn"&gt;java.util.ArrayList&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;
&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="nn"&gt;java.util.List&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;
&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="nn"&gt;java.util.Scanner&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;

&lt;span class="c1"&gt;// Runs a console program that lets the user add and analyze texts,&lt;/span&gt;
&lt;span class="c1"&gt;// then stores and prints the texts entered during the session.&lt;/span&gt;
&lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;Main&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
    &lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="kd"&gt;static&lt;/span&gt; &lt;span class="kt"&gt;void&lt;/span&gt; &lt;span class="nf"&gt;main&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;String&lt;/span&gt;&lt;span class="o"&gt;[]&lt;/span&gt; &lt;span class="n"&gt;args&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
        &lt;span class="nc"&gt;Scanner&lt;/span&gt; &lt;span class="n"&gt;scanner&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;Scanner&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;System&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;in&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
        &lt;span class="nc"&gt;List&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;String&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;texts&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;ArrayList&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&amp;gt;();&lt;/span&gt;
        &lt;span class="kt"&gt;boolean&lt;/span&gt; &lt;span class="n"&gt;running&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;

        &lt;span class="nc"&gt;System&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;out&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;println&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;" --------------------------------"&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
        &lt;span class="nc"&gt;System&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;out&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;println&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"|      Paragraph Parser         |"&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
        &lt;span class="nc"&gt;System&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;out&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;println&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;" --------------------------------"&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
        &lt;span class="nc"&gt;System&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;out&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;println&lt;/span&gt;&lt;span class="o"&gt;();&lt;/span&gt;


        &lt;span class="k"&gt;while&lt;/span&gt; &lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;running&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
            &lt;span class="nc"&gt;System&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;out&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;println&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"1. General Text Analysis"&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
            &lt;span class="nc"&gt;System&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;out&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;println&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"2. Do a specific text analysis"&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
            &lt;span class="nc"&gt;System&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;out&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;println&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"3. Display Texts"&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
            &lt;span class="nc"&gt;System&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;out&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;println&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"4. Exit"&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;

            &lt;span class="kt"&gt;int&lt;/span&gt; &lt;span class="n"&gt;choice&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;readInt&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;scanner&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"Choose an option: "&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;

            &lt;span class="k"&gt;switch&lt;/span&gt; &lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;choice&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
                &lt;span class="k"&gt;case&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;
                    &lt;span class="nc"&gt;Text&lt;/span&gt; &lt;span class="n"&gt;analysisResult&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;displayText&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;scanner&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"Add text to analyze:\n"&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
                    &lt;span class="nc"&gt;System&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;out&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;println&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;analysisResult&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
                    &lt;span class="k"&gt;break&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;
                &lt;span class="k"&gt;case&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;
                    &lt;span class="c1"&gt;// nested switch for sub-menu text analysis tools&lt;/span&gt;
                    &lt;span class="nc"&gt;Scanner&lt;/span&gt; &lt;span class="n"&gt;scannerNestedScanner&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;Scanner&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;System&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;in&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
                    &lt;span class="kt"&gt;boolean&lt;/span&gt; &lt;span class="n"&gt;extraTestRunning&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;

                    &lt;span class="k"&gt;while&lt;/span&gt; &lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;extraTestRunning&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
                        &lt;span class="nc"&gt;System&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;out&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;println&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"1. Word frequency finder"&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
                        &lt;span class="nc"&gt;System&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;out&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;println&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"2. Find frequency of specific character"&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
                        &lt;span class="nc"&gt;System&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;out&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;println&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"3. Find the number of unique words"&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
                        &lt;span class="nc"&gt;System&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;out&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;println&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"4. Return to main menu"&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;

                        &lt;span class="kt"&gt;int&lt;/span&gt; &lt;span class="n"&gt;extraChoice&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;readInt&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;scannerNestedScanner&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"Choose text analysis tool (Enter number): "&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
                    &lt;span class="k"&gt;switch&lt;/span&gt; &lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;extraChoice&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
                        &lt;span class="k"&gt;case&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;
                            &lt;span class="nc"&gt;WordFrequency&lt;/span&gt; &lt;span class="n"&gt;wordFrequency&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;WordFrequency&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;displayText&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;scannerNestedScanner&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"Enter the text to search through:\n"&lt;/span&gt;&lt;span class="o"&gt;).&lt;/span&gt;&lt;span class="na"&gt;content&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;toLowerCase&lt;/span&gt;&lt;span class="o"&gt;());&lt;/span&gt;
                            &lt;span class="nc"&gt;String&lt;/span&gt; &lt;span class="n"&gt;wordToFind&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;displayText&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;scannerNestedScanner&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"Enter the word to find its frequency:\n"&lt;/span&gt;&lt;span class="o"&gt;).&lt;/span&gt;&lt;span class="na"&gt;content&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;
                            &lt;span class="n"&gt;wordToFind&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;wordToFind&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;replaceAll&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"\\p{Punct}+"&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="s"&gt;""&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
                            &lt;span class="c1"&gt;//strip punctuation so it doesn't affect the word frequency count&lt;/span&gt;
                            &lt;span class="nc"&gt;System&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;out&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;println&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Word frequency analysis for: "&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt;&lt;span class="n"&gt;wordToFind&lt;/span&gt;&lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="s"&gt;" -&amp;gt; "&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="n"&gt;wordFrequency&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;findWordFrequency&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;wordToFind&lt;/span&gt;&lt;span class="o"&gt;));&lt;/span&gt;
                            &lt;span class="k"&gt;break&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;
                        &lt;span class="k"&gt;case&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;
                            &lt;span class="nc"&gt;CharacterFrequency&lt;/span&gt; &lt;span class="n"&gt;charFrequency&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;CharacterFrequency&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;displayText&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;scannerNestedScanner&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"Enter the text to analyze:\n"&lt;/span&gt;&lt;span class="o"&gt;).&lt;/span&gt;&lt;span class="na"&gt;content&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
                            &lt;span class="nc"&gt;System&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;out&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;println&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Character frequency analysis for: "&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="n"&gt;charFrequency&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;getCharacterFrequencyCount&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;displayText&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;scannerNestedScanner&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"Which character are we looking for? Enter a character:\n"&lt;/span&gt;&lt;span class="o"&gt;).&lt;/span&gt;&lt;span class="na"&gt;content&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;charAt&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="o"&gt;)));&lt;/span&gt;
                            &lt;span class="c1"&gt;// Add your extra test logic here&lt;/span&gt;
                            &lt;span class="k"&gt;break&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;
                        &lt;span class="k"&gt;case&lt;/span&gt; &lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;
                            &lt;span class="nc"&gt;UniqueWords&lt;/span&gt; &lt;span class="n"&gt;uniqueWords&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;UniqueWords&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;displayText&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;scannerNestedScanner&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"Add text to count unique words:\n"&lt;/span&gt;&lt;span class="o"&gt;).&lt;/span&gt;&lt;span class="na"&gt;content&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
                            &lt;span class="nc"&gt;System&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;out&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;println&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Number of unique words: "&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="n"&gt;uniqueWords&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;getUniqueWordCount&lt;/span&gt;&lt;span class="o"&gt;());&lt;/span&gt;
                            &lt;span class="k"&gt;break&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;
                        &lt;span class="k"&gt;case&lt;/span&gt; &lt;span class="mi"&gt;4&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;
                            &lt;span class="n"&gt;extraTestRunning&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;
                            &lt;span class="k"&gt;break&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;
                        &lt;span class="k"&gt;default&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;
                            &lt;span class="nc"&gt;System&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;out&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;println&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Invalid option for extra test."&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
                        &lt;span class="o"&gt;}&lt;/span&gt;
                    &lt;span class="o"&gt;}&lt;/span&gt;
                    &lt;span class="k"&gt;break&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt; &lt;span class="c1"&gt;// Add this break to prevent fall-through from case 2 to case 3 in the main switch.&lt;/span&gt;

                &lt;span class="k"&gt;case&lt;/span&gt; &lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;
                    &lt;span class="nc"&gt;System&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;out&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;println&lt;/span&gt;&lt;span class="o"&gt;();&lt;/span&gt;
                    &lt;span class="n"&gt;displayTexts&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;texts&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
                    &lt;span class="k"&gt;break&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;
                &lt;span class="k"&gt;case&lt;/span&gt; &lt;span class="mi"&gt;4&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;
                    &lt;span class="n"&gt;running&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;
                    &lt;span class="k"&gt;break&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;
                    &lt;span class="c1"&gt;// Fall through to default case if needed.&lt;/span&gt;
                &lt;span class="k"&gt;default&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;
                    &lt;span class="nc"&gt;System&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;out&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;println&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Invalid option. Please try again."&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
            &lt;span class="o"&gt;}&lt;/span&gt;
        &lt;span class="o"&gt;}&lt;/span&gt;

        &lt;span class="nc"&gt;System&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;out&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;println&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"\nFinal text list:"&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
        &lt;span class="n"&gt;displayTexts&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;texts&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
        &lt;span class="n"&gt;scanner&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;close&lt;/span&gt;&lt;span class="o"&gt;();&lt;/span&gt;
    &lt;span class="o"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;I discovered that having nested switch statements is both doable and useful. I heard somewhere that this is often used in games and it's nice to have an understanding of how one might incorporate that.&lt;/p&gt;

&lt;p&gt;Guys I seriously encourage you, put down the AI just for a few hours and fight with something. Your old self, those feelings you have been missing, they will return! Instant youth, almost guaranteed. XD&lt;/p&gt;

&lt;p&gt;Tell me, what have you recently challenged yourself to build without using AI?&lt;/p&gt;

</description>
      <category>learning</category>
      <category>beginners</category>
      <category>programming</category>
      <category>java</category>
    </item>
    <item>
      <title>Engineering: Dreams, or Self-Actualization?</title>
      <dc:creator>Anna Villarreal</dc:creator>
      <pubDate>Tue, 18 Aug 2026 23:55:01 +0000</pubDate>
      <link>https://dev.to/annavi11arrea1/engineering-dreams-or-self-actualization-41ok</link>
      <guid>https://dev.to/annavi11arrea1/engineering-dreams-or-self-actualization-41ok</guid>
      <description>&lt;p&gt;At what point did you start to think you were an engineer?&lt;/p&gt;

&lt;p&gt;An engineer. &lt;em&gt;What a lofty, prestigious-sounding title!&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;I am not lofty or prestigious.&lt;/p&gt;

&lt;p&gt;But I do question things. &lt;/p&gt;

&lt;p&gt;Like why do people follow archaic procedures and thinking patterns, if there is a clearly better path? When you work in a school everything is sort of need-to-know only. So I don’t know why things are done the way they are done, until I plow through 20 brick walls. &lt;/p&gt;

&lt;p&gt;Persistence answers yesterday's reasoning.&lt;/p&gt;

&lt;p&gt;Today builds tomorrow.&lt;/p&gt;

&lt;p&gt;I cannot stand it when people do not care. How could you not care about improving? You wanna sit there, following the same pointless protocol you have been doing for 15 years, just because that is what is being done? Please - someone needs to give a sh*t. I am surrounded by nincompoops. At previous employeers, I have always had questions, and the only people that could potentially answer are out of reach, typically. I have found meaning in helping the people that I interact with on a daily basis. The tiny sliver of help I can give them brings meaning to my life.&lt;/p&gt;

&lt;p&gt;The person in the room next to me runs the laminator for everyone. This person was not present for months. I took it upon myself to learn how to use an industrial size laminator with no instructions, and no idea. It is not in my job description, and I hurt my hands doing it. The end result was many thankful people, curbing the sudden popularity of the single laminator at the beginning of the year.&lt;/p&gt;





&lt;p&gt;I’ve had a weird itch that is unshakable, this weird thought: “Am I an engineer? What makes one an engineer? What are the defining characteristics of an engineer, and do I have this mindset?”&lt;/p&gt;

&lt;p&gt;Today, while googling the truths about my own existence I came to realize the act of asking questions, and digging for answers backed by sound reasoning, is actually a very &lt;em&gt;engineer&lt;/em&gt; thing to do. I laughed at myself when I realized this. But then I wondered, no, I’m not an engineer, I am a philosopher. Well, technically, anyone can have a philosophy. I’m going to end it there.&lt;/p&gt;

&lt;p&gt;This thought pattern of thinking “Am I an engineer?” was sparked while working at a previous employer. I went poking around one day and discovered that my name was listed under “Engineering Team”. I did not point it out to anyone that I saw it, and no one told me about it, ever. I think my heart stopped beating for a moment. That simple moment of seeing my name on anything related to &lt;em&gt;engineer&lt;/em&gt; felt staggering. Someone thought so much of me as to put me into such a category? What an honor. In case it was a mistake, I did not want to find out, and was not going to say anything because I liked it, and wanted it to stay. &lt;/p&gt;

&lt;p&gt;My actual title was something like “Network Installation and Computer Technician Apprentice” or some such nonsense. Terminating cables outside in the freezing cold of winter with my bare hands, I definitely earned whatever my title was at the time. That is one of my last memories at that job by the way. Making less than $20,000 that year, I knew I took that job as my foot in the door to tech, IT, and one day, if I am lucky, maybe software engineering, if AI doesn't ruin my tiny plans.&lt;/p&gt;

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

&lt;p&gt;I would certainly never claim to be an engineer, coming from an art and nutrition background, I am out of place. In Canada, they have strict requirements. In the USA, you can slap “software engineer” on a skeleton and people make memes about it. Does the title have meaning, actually? Further contemplation.&lt;/p&gt;

&lt;p&gt;I have always been one to analyze myself, in the worst of times and the best of times. I learn the hard way, but the lessons I have learned in life in general, have given me the ability to be an unshakable tank, or dive 110% into something. This is a skill I have always had, but for much of my life, I was distracted, I was always chasing things that were weightless. &lt;/p&gt;

&lt;p&gt;I started going to school again for many reasons, but mostly because I was bored with life. Frustrated at the lack of interesting things happening around me. This cannot be it, I am still certain there is more. I am forever a student. I have easily 8 years of college under my belt. Not to mention assorted other learnings.&lt;/p&gt;

&lt;p&gt;I feel like I still know absolutely nothing. &lt;/p&gt;

&lt;p&gt;I am only just beginning. &lt;/p&gt;

&lt;p&gt;Im not sure what I am, besides &lt;em&gt;present.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;Jay Shetty states in his book &lt;em&gt;How to Think Like a Monk&lt;/em&gt; that the only thing that is truly with you your entire life is your breath.&lt;/p&gt;

&lt;p&gt;…If thats the case, then everything is a variable, and anything is possible. Perhaps one day, a woman with questions can be a lofty engineer.&lt;/p&gt;

</description>
      <category>webdev</category>
      <category>mentalhealth</category>
      <category>programming</category>
      <category>software</category>
    </item>
    <item>
      <title>Exploring Sentry: Reducing Load Time 🐢</title>
      <dc:creator>Anna Villarreal</dc:creator>
      <pubDate>Sun, 09 Aug 2026 14:44:41 +0000</pubDate>
      <link>https://dev.to/annavi11arrea1/exploring-sentry-reducing-load-time-52gf</link>
      <guid>https://dev.to/annavi11arrea1/exploring-sentry-reducing-load-time-52gf</guid>
      <description>&lt;p&gt;&lt;em&gt;This is a submission for &lt;a href="https://dev.to/bugsmash"&gt;DEV's Summer Bug Smash: Smash Stories&lt;/a&gt; powered by &lt;a href="https://sentry.io/" rel="noopener noreferrer"&gt;Sentry&lt;/a&gt;.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;🟣 It took a little bit getting used to understanding Sentry, learning how to navigate and what can be done. In my spare time over the last few weeks, I was able to do some poking around. I connected my page at annavillarreal.com since it has a fair amount of content coming in, to make it more interesting to analyze.&lt;/p&gt;





&lt;h2&gt;
  
  
  Investigation
&lt;/h2&gt;

&lt;p&gt;I explored the dashboard, trying to figure out how to get readings from my traffic, which didn't take to long to figure out. I found traces particularly interesting. Why? Because this is where I discovered an actual performance issue.  &lt;/p&gt;

&lt;p&gt;A trace shows a 4,229ms pageload. Here's what's driving the slowness:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The critical path is sequential and bottlenecked at the server:&lt;/strong&gt;&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
  &lt;tbody&gt;
&lt;tr&gt;
    &lt;th&gt;Span&lt;/th&gt;
    &lt;th&gt;Duration&lt;/th&gt;
  &lt;/tr&gt;
  &lt;tr&gt;
    &lt;td&gt;browser.DNS&lt;/td&gt;
    &lt;td&gt;107ms&lt;/td&gt;
  &lt;/tr&gt;
  &lt;tr&gt;
    &lt;td&gt;browser.connect&lt;/td&gt;
    &lt;td&gt;553ms&lt;/td&gt;
  &lt;/tr&gt;
  &lt;tr&gt;
    &lt;td&gt;browser.TLS/SSL&lt;/td&gt;
    &lt;td&gt;281ms&lt;/td&gt;
  &lt;/tr&gt;
  &lt;tr&gt;
    &lt;td&gt;browser.request (TTFB)&lt;/td&gt;
    &lt;td&gt;2,068ms&lt;/td&gt;
  &lt;/tr&gt;
  &lt;tr&gt;
    &lt;td&gt;browser.response&lt;/td&gt;
    &lt;td&gt;1,052ms&lt;/td&gt;
  &lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

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

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;browser.request&lt;/code&gt; at 2,068ms&lt;/strong&gt;. This means that server took over 2 seconds just to start sending a response. That alone is ~49% of the total trace time. &lt;/li&gt;
&lt;li&gt;Notably, &lt;code&gt;browser.response&lt;/code&gt; at 1,052ms (slow download) consumes an additional ~3.1 seconds just in the request/response cycle.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The average for this pageload is ~&lt;strong&gt;194ms&lt;/strong&gt;, so the server response time being 2 seconds is the primary reason for the spike. I really don't want a new user waiting 4 or more seconds when visiting my page, that's a long time, relatively speaking.&lt;/p&gt;





&lt;h3&gt;
  
  
  Understanding the Data
&lt;/h3&gt;

&lt;p&gt;Using Seer, Sentry's built-in AI, I was able to quickly understand the information presented before me. It explained to me that:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;The GET &lt;a href="https://dev.to/api/articles"&gt;https://dev.to/api/articles&lt;/a&gt; call averages 361ms and hits 1.37 seconds at p95 — so it's consistently adding latency to your pageload, and on bad days it's the biggest bottleneck. &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.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Ftiks2yikbxxppu1whlfw.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Ftiks2yikbxxppu1whlfw.png" alt="slow request found" width="800" height="466"&gt;&lt;/a&gt;&lt;/p&gt;





&lt;ul&gt;
&lt;li&gt;Googlefonts was also eating up some pageload time:&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.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F9dmmyra1jgcrdhlt5uxw.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F9dmmyra1jgcrdhlt5uxw.png" alt="slow loading google fonts" width="800" height="644"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Ways to Improve
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt; Instead of the browser fetching dev.to articles on every pageload, the backend can fetch and cache them. By serving the cached result to the browser, load time is reduced. Articles don't change every second, so a background cache refresh leaves users largely unaffected.&lt;/li&gt;
&lt;li&gt;Keep the client-side fetch, but make sure it doesn't block the initial render. Load the dev.to content lazily after the page is interactive and use a loading skeleton so users see content quickly while articles load in the background. &lt;em&gt;Note: This does not make the call faster but removes it from the critical path. Makes a pleasant experience for visitors viewing non-critical data.&lt;/em&gt;
&lt;/li&gt;
&lt;/ul&gt;





&lt;h2&gt;
  
  
  Particularly Interesting Performance Feature
&lt;/h2&gt;

&lt;p&gt;The p95 problem specifically is handled by proxy_cache_background_update + proxy_cache_use_stale: when the 15-minute TTL expires, the next visitor gets the existing copy right away while nginx refreshes behind them. Nobody waits on dev.to after the first fill, and if dev.to is down or rate-limiting, the blog section keeps rendering off the last good copy for up to a day rather than showing "could not load posts."&lt;/p&gt;

&lt;p&gt;Changing the cache has a real impact, the data over the last week proves it. Looking at the javascript-microserver project's performance over the past week I see:&lt;/p&gt;

&lt;h3&gt;
  
  
  Overall pageload (p50) trend for annavillarreal.com/
&lt;/h3&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
  &lt;tbody&gt;
&lt;tr&gt;
    &lt;th&gt;Date&lt;/th&gt;
    &lt;th&gt;p50&lt;/th&gt;
    &lt;th&gt;p95&lt;/th&gt;
  &lt;/tr&gt;
  &lt;tr&gt;
    &lt;td&gt;Jul 31&lt;/td&gt;
    &lt;td&gt;708ms&lt;/td&gt;
    &lt;td&gt;708ms&lt;/td&gt;
  &lt;/tr&gt;
  &lt;tr&gt;
    &lt;td&gt;Aug 1&lt;/td&gt;
    &lt;td&gt;655ms&lt;/td&gt;
    &lt;td&gt;1.6s&lt;/td&gt;
  &lt;/tr&gt;
  &lt;tr&gt;
    &lt;td&gt;Aug 2&lt;/td&gt;
    &lt;td&gt;2.1s&lt;/td&gt;
    &lt;td&gt;2.6s&lt;/td&gt;
  &lt;/tr&gt;
  &lt;tr&gt;
    &lt;td&gt;Aug 3&lt;/td&gt;
    &lt;td&gt;2.6s&lt;/td&gt;
    &lt;td&gt;3.5s&lt;/td&gt;
  &lt;/tr&gt;
  &lt;tr&gt;
    &lt;td&gt;Aug 4&lt;/td&gt;
    &lt;td&gt;3.3s&lt;/td&gt;
    &lt;td&gt;4.2s&lt;/td&gt;
  &lt;/tr&gt;
  &lt;tr&gt;
    &lt;td&gt;Aug 5&lt;/td&gt;
    &lt;td&gt;1.3s&lt;/td&gt;
    &lt;td&gt;2.5s&lt;/td&gt;
  &lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&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.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F2uh4bbmyamvc4ksjp6kq.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F2uh4bbmyamvc4ksjp6kq.png" alt="page load drastically cut" width="800" height="332"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;That's a ~60% p50 improvement on Aug 5 vs Aug 4. Fantastic.&lt;/p&gt;





&lt;h2&gt;
  
  
  Why the Cache Matters
&lt;/h2&gt;

&lt;p&gt;The page fetches articles via GET /api/devto, which internally calls &lt;a href="https://dev.to/api/articles"&gt;https://dev.to/api/articles&lt;/a&gt;. Without caching, that server-side call to dev.to was taking anywhere from 74ms to 832ms depending on the day. In this current trace, GET /api/devto completed in just 90ms, which is well below even the best prior direct-to-dev.to call. It is now being served from cache rather than hitting the external API.&lt;br&gt;
In this case, the cache particularly valuable because it insulates the page from upstream latency entirely, streamlining performance for visitors to the page.&lt;/p&gt;





&lt;h2&gt;
  
  
  Takeaway
&lt;/h2&gt;

&lt;p&gt;I really enjoyed the challenge of exploring this tool and trying to make sense of it. I can definitely see how it could lead to massive performance optimizations for end users, as I experienced it myself. I am happy I found a performance issue before many users might have the bad experience of a slow page load. Gross!&lt;/p&gt;

</description>
      <category>devchallenge</category>
      <category>bugsmash</category>
      <category>webdev</category>
      <category>programming</category>
    </item>
    <item>
      <title>🛻CSS Art: Smoothie Food Truck</title>
      <dc:creator>Anna Villarreal</dc:creator>
      <pubDate>Sun, 09 Aug 2026 13:23:55 +0000</pubDate>
      <link>https://dev.to/annavi11arrea1/css-art-smoothie-food-truck-36gg</link>
      <guid>https://dev.to/annavi11arrea1/css-art-smoothie-food-truck-36gg</guid>
      <description>&lt;p&gt;&lt;em&gt;This is a submission for &lt;a href="https://dev.to/challenges/frontend-2026-07-29"&gt;Frontend Challenge - Comfort Food Edition, CSS Art&lt;/a&gt;.&lt;/em&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Inspiration
&lt;/h2&gt;

&lt;p&gt;💠I was not sure I was going to enter this challenge at first, because I was not feeling inspired. I do not like to force things. Then I started to think a little outside of the normal. I remembered Space Fruit, a food vendor that is a favorite of mine to find at music festivals. Nothing beats an ice cold smoothie topped with frozen fruit bits on a sweltering, hot day. When you see Space fruit, you stop, and make a direct cut to their truck. You do not hesitate. So, after some nostalgia, I was feeling motivated remembering the days when I used to vend, selling my art and being an active member of the festival community.&lt;/p&gt;





&lt;h2&gt;
  
  
  Demo
&lt;/h2&gt;

&lt;p&gt;&lt;small&gt;Change preview zoom to .5 &lt;/small&gt;&lt;br&gt;
&lt;iframe height="600" src="https://codepen.io/editor/AnnaVi11arrea1/embed/019fe698-300e-79e2-a685-4752dfb367c5?height=600&amp;amp;default-tab=result&amp;amp;embed-version=2"&gt;
&lt;/iframe&gt;
&lt;/p&gt;

&lt;p&gt;Here is a screenshot of it working on codepen, it might not be great on some devices:&lt;/p&gt;

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

&lt;ul&gt;
&lt;li&gt;I start each idea generally by blocking out the approximate size and shape of the object I want to add. &lt;/li&gt;
&lt;li&gt;I use gradients, box shadows, and layering to add depth and dimension.&lt;/li&gt;
&lt;li&gt;A smattering of javascript was used to randomly place stars and have them twinkle.&lt;/li&gt;
&lt;li&gt;Found a way to imitate an LED strip with some fancy background gradient patterns.&lt;/li&gt;
&lt;li&gt;Pushed myself to create a wrinkling effect on the tablecloth to imitate fabric.&lt;/li&gt;
&lt;/ul&gt;





&lt;h2&gt;
  
  
  Journey
&lt;/h2&gt;

&lt;p&gt;⭐ I made a timelapse for you guys, so I'll leave that as my journey! It's sometimes hard to imagine how much work someone puts into something without evidence. A video will communicate this pretty quickly, plus it is also satisfying to see progress happen so quickly. The biggest challenge was getting the window cut out in the back of the food truck, so you could see the stars on the other side, just like in real life. &lt;/p&gt;

&lt;p&gt;  &lt;iframe src="https://www.youtube.com/embed/WsTfEQJA908" width="710" height="399"&gt;
  &lt;/iframe&gt;
&lt;/p&gt;





&lt;h2&gt;
  
  
  Code
&lt;/h2&gt;

&lt;p&gt;Github Repo: &lt;a href="https://github.com/AnnaVi11arrea1/cssart" rel="noopener noreferrer"&gt;cssart&lt;/a&gt;&lt;/p&gt;

</description>
      <category>frontendchallenge</category>
      <category>devchallenge</category>
      <category>css</category>
      <category>programming</category>
    </item>
    <item>
      <title>Catbot Development Series</title>
      <dc:creator>Anna Villarreal</dc:creator>
      <pubDate>Sat, 01 Aug 2026 16:13:32 +0000</pubDate>
      <link>https://dev.to/annavi11arrea1/catbot-development-series-nlf</link>
      <guid>https://dev.to/annavi11arrea1/catbot-development-series-nlf</guid>
      <description>&lt;h2&gt;
  
  
  Sequence of Articles Related to Catbot's Developement
&lt;/h2&gt;

&lt;h3&gt;
  
  
  &lt;em&gt;Updated on an ongoing basis&lt;/em&gt;
&lt;/h3&gt;


&lt;div class="ltag__link--embedded"&gt;
  &lt;div class="crayons-story "&gt;
  &lt;a href="https://dev.to/annavi11arrea1/catbot-the-custom-ai-harness-that-lives-on-desktop-layottu-35kb" class="crayons-story__hidden-navigation-link"&gt;🐈Catbot: The Custom AI Harness That Lives on Desktop ᓚᘏᗢ&lt;/a&gt;


  &lt;div class="crayons-story__body crayons-story__body-full_post"&gt;
      &lt;a href="https://dev.to/annavi11arrea1/catbot-the-custom-ai-harness-that-lives-on-desktop-layottu-35kb" class="crayons-article__context-note crayons-article__context-note__feed"&gt;&lt;p&gt;DEV Weekend Challenge: Passion Edition Submission&lt;/p&gt;

&lt;/a&gt;
    &lt;div class="crayons-story__top"&gt;
      &lt;div class="crayons-story__meta"&gt;
        &lt;div class="crayons-story__author-pic"&gt;

          &lt;a href="/annavi11arrea1" class="crayons-avatar  crayons-avatar--l  "&gt;
            &lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F1467610%2F3e4ce925-fffd-4643-9aae-8ee77cf01828.png" alt="annavi11arrea1 profile" class="crayons-avatar__image"&gt;
          &lt;/a&gt;
        &lt;/div&gt;
        &lt;div&gt;
          &lt;div&gt;
            &lt;a href="/annavi11arrea1" class="crayons-story__secondary fw-medium m:hidden"&gt;
              Anna Villarreal
            &lt;/a&gt;
            &lt;div class="profile-preview-card relative mb-4 s:mb-0 fw-medium hidden m:inline-block"&gt;
              
                Anna Villarreal
                &lt;a href="/++"&gt;&lt;img alt="Subscriber" class="subscription-icon" src="https://assets.dev.to/assets/subscription-icon-805dfa7ac7dd660f07ed8d654877270825b07a92a03841aa99a1093bd00431b2.png"&gt;&lt;/a&gt;
                &lt;img alt="Community Curator" class="community-leader-icon" src="https://assets.dev.to/assets/community-leader-icon-b72c9e74eff54916e5c46c962f47ba40c9f611a71f8b157511f9613f69c0001b.svg"&gt;
              
              &lt;div id="story-author-preview-content-4129482" class="profile-preview-card__content crayons-dropdown branded-7 p-4 pt-0"&gt;
                &lt;div class="gap-4 grid"&gt;
                  &lt;div class="-mt-4"&gt;
                    &lt;a href="/annavi11arrea1" class="flex"&gt;
                      &lt;span class="crayons-avatar crayons-avatar--xl mr-2 shrink-0"&gt;
                        &lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F1467610%2F3e4ce925-fffd-4643-9aae-8ee77cf01828.png" class="crayons-avatar__image" alt=""&gt;
                      &lt;/span&gt;
                      &lt;span class="crayons-link crayons-subtitle-2 mt-5"&gt;Anna Villarreal&lt;/span&gt;
                    &lt;/a&gt;
                  &lt;/div&gt;
                  &lt;div class="print-hidden"&gt;
                    
                      Follow
                    
                  &lt;/div&gt;
                  &lt;div class="author-preview-metadata-container"&gt;&lt;/div&gt;
                &lt;/div&gt;
              &lt;/div&gt;
            &lt;/div&gt;

          &lt;/div&gt;
          &lt;a href="https://dev.to/annavi11arrea1/catbot-the-custom-ai-harness-that-lives-on-desktop-layottu-35kb" class="crayons-story__tertiary fs-xs"&gt;&lt;time&gt;Jul 13&lt;/time&gt;&lt;span class="time-ago-indicator-initial-placeholder"&gt;&lt;/span&gt;&lt;/a&gt;
        &lt;/div&gt;
      &lt;/div&gt;

    &lt;/div&gt;

    &lt;div class="crayons-story__indention"&gt;
      &lt;h2 class="crayons-story__title crayons-story__title-full_post"&gt;
        &lt;a href="https://dev.to/annavi11arrea1/catbot-the-custom-ai-harness-that-lives-on-desktop-layottu-35kb" id="article-link-4129482"&gt;
          🐈Catbot: The Custom AI Harness That Lives on Desktop ᓚᘏᗢ
        &lt;/a&gt;
      &lt;/h2&gt;
        &lt;div class="crayons-story__tags"&gt;
            &lt;a class="crayons-tag  crayons-tag--monochrome " href="/t/devchallenge"&gt;&lt;span class="crayons-tag__prefix"&gt;#&lt;/span&gt;devchallenge&lt;/a&gt;
            &lt;a class="crayons-tag  crayons-tag--monochrome " href="/t/weekendchallenge"&gt;&lt;span class="crayons-tag__prefix"&gt;#&lt;/span&gt;weekendchallenge&lt;/a&gt;
            &lt;a class="crayons-tag  crayons-tag--monochrome " href="/t/ai"&gt;&lt;span class="crayons-tag__prefix"&gt;#&lt;/span&gt;ai&lt;/a&gt;
            &lt;a class="crayons-tag  crayons-tag--monochrome " href="/t/webdev"&gt;&lt;span class="crayons-tag__prefix"&gt;#&lt;/span&gt;webdev&lt;/a&gt;
        &lt;/div&gt;
      &lt;div class="crayons-story__bottom"&gt;
        &lt;div class="crayons-story__details"&gt;
          &lt;a href="https://dev.to/annavi11arrea1/catbot-the-custom-ai-harness-that-lives-on-desktop-layottu-35kb" class="crayons-btn crayons-btn--s crayons-btn--ghost crayons-btn--icon-left"&gt;
            &lt;div class="multiple_reactions_aggregate"&gt;
              &lt;span class="multiple_reactions_icons_container"&gt;
                  &lt;span class="crayons_icon_container"&gt;
                    &lt;img src="https://assets.dev.to/assets/exploding-head-daceb38d627e6ae9b730f36a1e390fca556a4289d5a41abb2c35068ad3e2c4b5.svg" width="18" height="18"&gt;
                  &lt;/span&gt;
                  &lt;span class="crayons_icon_container"&gt;
                    &lt;img src="https://assets.dev.to/assets/multi-unicorn-b44d6f8c23cdd00964192bedc38af3e82463978aa611b4365bd33a0f1f4f3e97.svg" width="18" height="18"&gt;
                  &lt;/span&gt;
                  &lt;span class="crayons_icon_container"&gt;
                    &lt;img src="https://assets.dev.to/assets/sparkle-heart-5f9bee3767e18deb1bb725290cb151c25234768a0e9a2bd39370c382d02920cf.svg" width="18" height="18"&gt;
                  &lt;/span&gt;
              &lt;/span&gt;
              &lt;span class="aggregate_reactions_counter"&gt;16&lt;span class="hidden s:inline"&gt;&amp;nbsp;reactions&lt;/span&gt;&lt;/span&gt;
            &lt;/div&gt;
          &lt;/a&gt;
            &lt;a href="https://dev.to/annavi11arrea1/catbot-the-custom-ai-harness-that-lives-on-desktop-layottu-35kb#comments" class="crayons-btn crayons-btn--s crayons-btn--ghost crayons-btn--icon-left flex items-center"&gt;
              

              2&lt;span class="hidden s:inline"&gt;&amp;nbsp;comments&lt;/span&gt;
            &lt;/a&gt;
        &lt;/div&gt;
        &lt;div class="crayons-story__save"&gt;
          &lt;small class="crayons-story__tertiary fs-xs mr-2"&gt;
            3 min read
          &lt;/small&gt;
        &lt;/div&gt;
      &lt;/div&gt;
    &lt;/div&gt;
  &lt;/div&gt;
&lt;/div&gt;

&lt;/div&gt;



&lt;div class="ltag__link--embedded"&gt;
  &lt;div class="crayons-story "&gt;
  &lt;a href="https://dev.to/annavi11arrea1/check-out-catbots-new-leg-3cfg" class="crayons-story__hidden-navigation-link"&gt;🐈‍⬛Check Out Catbot's New Leg!&lt;/a&gt;


  &lt;div class="crayons-story__body crayons-story__body-full_post"&gt;
      &lt;a href="https://dev.to/annavi11arrea1/check-out-catbots-new-leg-3cfg" class="crayons-article__context-note crayons-article__context-note__feed"&gt;&lt;p&gt;The evolution from sketch to robo-leg&lt;/p&gt;

&lt;/a&gt;
    &lt;div class="crayons-story__top"&gt;
      &lt;div class="crayons-story__meta"&gt;
        &lt;div class="crayons-story__author-pic"&gt;

          &lt;a href="/annavi11arrea1" class="crayons-avatar  crayons-avatar--l  "&gt;
            &lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F1467610%2F3e4ce925-fffd-4643-9aae-8ee77cf01828.png" alt="annavi11arrea1 profile" class="crayons-avatar__image"&gt;
          &lt;/a&gt;
        &lt;/div&gt;
        &lt;div&gt;
          &lt;div&gt;
            &lt;a href="/annavi11arrea1" class="crayons-story__secondary fw-medium m:hidden"&gt;
              Anna Villarreal
            &lt;/a&gt;
            &lt;div class="profile-preview-card relative mb-4 s:mb-0 fw-medium hidden m:inline-block"&gt;
              
                Anna Villarreal
                &lt;a href="/++"&gt;&lt;img alt="Subscriber" class="subscription-icon" src="https://assets.dev.to/assets/subscription-icon-805dfa7ac7dd660f07ed8d654877270825b07a92a03841aa99a1093bd00431b2.png"&gt;&lt;/a&gt;
                &lt;img alt="Community Curator" class="community-leader-icon" src="https://assets.dev.to/assets/community-leader-icon-b72c9e74eff54916e5c46c962f47ba40c9f611a71f8b157511f9613f69c0001b.svg"&gt;
              
              &lt;div id="story-author-preview-content-4227517" class="profile-preview-card__content crayons-dropdown branded-7 p-4 pt-0"&gt;
                &lt;div class="gap-4 grid"&gt;
                  &lt;div class="-mt-4"&gt;
                    &lt;a href="/annavi11arrea1" class="flex"&gt;
                      &lt;span class="crayons-avatar crayons-avatar--xl mr-2 shrink-0"&gt;
                        &lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F1467610%2F3e4ce925-fffd-4643-9aae-8ee77cf01828.png" class="crayons-avatar__image" alt=""&gt;
                      &lt;/span&gt;
                      &lt;span class="crayons-link crayons-subtitle-2 mt-5"&gt;Anna Villarreal&lt;/span&gt;
                    &lt;/a&gt;
                  &lt;/div&gt;
                  &lt;div class="print-hidden"&gt;
                    
                      Follow
                    
                  &lt;/div&gt;
                  &lt;div class="author-preview-metadata-container"&gt;&lt;/div&gt;
                &lt;/div&gt;
              &lt;/div&gt;
            &lt;/div&gt;

          &lt;/div&gt;
          &lt;a href="https://dev.to/annavi11arrea1/check-out-catbots-new-leg-3cfg" class="crayons-story__tertiary fs-xs"&gt;&lt;time&gt;Jul 25&lt;/time&gt;&lt;span class="time-ago-indicator-initial-placeholder"&gt;&lt;/span&gt;&lt;/a&gt;
        &lt;/div&gt;
      &lt;/div&gt;

    &lt;/div&gt;

    &lt;div class="crayons-story__indention"&gt;
      &lt;h2 class="crayons-story__title crayons-story__title-full_post"&gt;
        &lt;a href="https://dev.to/annavi11arrea1/check-out-catbots-new-leg-3cfg" id="article-link-4227517"&gt;
          🐈‍⬛Check Out Catbot's New Leg!
        &lt;/a&gt;
      &lt;/h2&gt;
        &lt;div class="crayons-story__tags"&gt;
            &lt;a class="crayons-tag  crayons-tag--monochrome " href="/t/ai"&gt;&lt;span class="crayons-tag__prefix"&gt;#&lt;/span&gt;ai&lt;/a&gt;
            &lt;a class="crayons-tag  crayons-tag--monochrome " href="/t/programming"&gt;&lt;span class="crayons-tag__prefix"&gt;#&lt;/span&gt;programming&lt;/a&gt;
            &lt;a class="crayons-tag  crayons-tag--monochrome " href="/t/learning"&gt;&lt;span class="crayons-tag__prefix"&gt;#&lt;/span&gt;learning&lt;/a&gt;
            &lt;a class="crayons-tag  crayons-tag--monochrome " href="/t/pet"&gt;&lt;span class="crayons-tag__prefix"&gt;#&lt;/span&gt;pet&lt;/a&gt;
        &lt;/div&gt;
      &lt;div class="crayons-story__bottom"&gt;
        &lt;div class="crayons-story__details"&gt;
          &lt;a href="https://dev.to/annavi11arrea1/check-out-catbots-new-leg-3cfg" class="crayons-btn crayons-btn--s crayons-btn--ghost crayons-btn--icon-left"&gt;
            &lt;div class="multiple_reactions_aggregate"&gt;
              &lt;span class="multiple_reactions_icons_container"&gt;
                  &lt;span class="crayons_icon_container"&gt;
                    &lt;img src="https://assets.dev.to/assets/fire-f60e7a582391810302117f987b22a8ef04a2fe0df7e3258a5f49332df1cec71e.svg" width="18" height="18"&gt;
                  &lt;/span&gt;
                  &lt;span class="crayons_icon_container"&gt;
                    &lt;img src="https://assets.dev.to/assets/raised-hands-74b2099fd66a39f2d7eed9305ee0f4553df0eb7b4f11b01b6b1b499973048fe5.svg" width="18" height="18"&gt;
                  &lt;/span&gt;
                  &lt;span class="crayons_icon_container"&gt;
                    &lt;img src="https://assets.dev.to/assets/sparkle-heart-5f9bee3767e18deb1bb725290cb151c25234768a0e9a2bd39370c382d02920cf.svg" width="18" height="18"&gt;
                  &lt;/span&gt;
              &lt;/span&gt;
              &lt;span class="aggregate_reactions_counter"&gt;22&lt;span class="hidden s:inline"&gt;&amp;nbsp;reactions&lt;/span&gt;&lt;/span&gt;
            &lt;/div&gt;
          &lt;/a&gt;
            &lt;a href="https://dev.to/annavi11arrea1/check-out-catbots-new-leg-3cfg#comments" class="crayons-btn crayons-btn--s crayons-btn--ghost crayons-btn--icon-left flex items-center"&gt;
              

              7&lt;span class="hidden s:inline"&gt;&amp;nbsp;comments&lt;/span&gt;
            &lt;/a&gt;
        &lt;/div&gt;
        &lt;div class="crayons-story__save"&gt;
          &lt;small class="crayons-story__tertiary fs-xs mr-2"&gt;
            3 min read
          &lt;/small&gt;
        &lt;/div&gt;
      &lt;/div&gt;
    &lt;/div&gt;
  &lt;/div&gt;
&lt;/div&gt;

&lt;/div&gt;



&lt;div class="ltag__link--embedded"&gt;
  &lt;div class="crayons-story "&gt;
  &lt;a href="https://dev.to/annavi11arrea1/catbot-custom-grammar-problem-fixed-oc5" class="crayons-story__hidden-navigation-link"&gt;Catbot: Custom Grammar Problem Fixed&lt;/a&gt;


  &lt;div class="crayons-story__body crayons-story__body-full_post"&gt;
      &lt;a href="https://dev.to/annavi11arrea1/catbot-custom-grammar-problem-fixed-oc5" class="crayons-article__context-note crayons-article__context-note__feed"&gt;&lt;p&gt;Summer Bug Smash: Smash Stories 🐛🛹&lt;/p&gt;

&lt;/a&gt;
    &lt;div class="crayons-story__top"&gt;
      &lt;div class="crayons-story__meta"&gt;
        &lt;div class="crayons-story__author-pic"&gt;

          &lt;a href="/annavi11arrea1" class="crayons-avatar  crayons-avatar--l  "&gt;
            &lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F1467610%2F3e4ce925-fffd-4643-9aae-8ee77cf01828.png" alt="annavi11arrea1 profile" class="crayons-avatar__image"&gt;
          &lt;/a&gt;
        &lt;/div&gt;
        &lt;div&gt;
          &lt;div&gt;
            &lt;a href="/annavi11arrea1" class="crayons-story__secondary fw-medium m:hidden"&gt;
              Anna Villarreal
            &lt;/a&gt;
            &lt;div class="profile-preview-card relative mb-4 s:mb-0 fw-medium hidden m:inline-block"&gt;
              
                Anna Villarreal
                &lt;a href="/++"&gt;&lt;img alt="Subscriber" class="subscription-icon" src="https://assets.dev.to/assets/subscription-icon-805dfa7ac7dd660f07ed8d654877270825b07a92a03841aa99a1093bd00431b2.png"&gt;&lt;/a&gt;
                &lt;img alt="Community Curator" class="community-leader-icon" src="https://assets.dev.to/assets/community-leader-icon-b72c9e74eff54916e5c46c962f47ba40c9f611a71f8b157511f9613f69c0001b.svg"&gt;
              
              &lt;div id="story-author-preview-content-4288467" class="profile-preview-card__content crayons-dropdown branded-7 p-4 pt-0"&gt;
                &lt;div class="gap-4 grid"&gt;
                  &lt;div class="-mt-4"&gt;
                    &lt;a href="/annavi11arrea1" class="flex"&gt;
                      &lt;span class="crayons-avatar crayons-avatar--xl mr-2 shrink-0"&gt;
                        &lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F1467610%2F3e4ce925-fffd-4643-9aae-8ee77cf01828.png" class="crayons-avatar__image" alt=""&gt;
                      &lt;/span&gt;
                      &lt;span class="crayons-link crayons-subtitle-2 mt-5"&gt;Anna Villarreal&lt;/span&gt;
                    &lt;/a&gt;
                  &lt;/div&gt;
                  &lt;div class="print-hidden"&gt;
                    
                      Follow
                    
                  &lt;/div&gt;
                  &lt;div class="author-preview-metadata-container"&gt;&lt;/div&gt;
                &lt;/div&gt;
              &lt;/div&gt;
            &lt;/div&gt;

          &lt;/div&gt;
          &lt;a href="https://dev.to/annavi11arrea1/catbot-custom-grammar-problem-fixed-oc5" class="crayons-story__tertiary fs-xs"&gt;&lt;time&gt;Aug 1&lt;/time&gt;&lt;span class="time-ago-indicator-initial-placeholder"&gt;&lt;/span&gt;&lt;/a&gt;
        &lt;/div&gt;
      &lt;/div&gt;

    &lt;/div&gt;

    &lt;div class="crayons-story__indention"&gt;
      &lt;h2 class="crayons-story__title crayons-story__title-full_post"&gt;
        &lt;a href="https://dev.to/annavi11arrea1/catbot-custom-grammar-problem-fixed-oc5" id="article-link-4288467"&gt;
          Catbot: Custom Grammar Problem Fixed
        &lt;/a&gt;
      &lt;/h2&gt;
        &lt;div class="crayons-story__tags"&gt;
            &lt;a class="crayons-tag  crayons-tag--monochrome " href="/t/devchallenge"&gt;&lt;span class="crayons-tag__prefix"&gt;#&lt;/span&gt;devchallenge&lt;/a&gt;
            &lt;a class="crayons-tag  crayons-tag--monochrome " href="/t/bugsmash"&gt;&lt;span class="crayons-tag__prefix"&gt;#&lt;/span&gt;bugsmash&lt;/a&gt;
            &lt;a class="crayons-tag  crayons-tag--monochrome " href="/t/catbot"&gt;&lt;span class="crayons-tag__prefix"&gt;#&lt;/span&gt;catbot&lt;/a&gt;
            &lt;a class="crayons-tag  crayons-tag--monochrome " href="/t/ai"&gt;&lt;span class="crayons-tag__prefix"&gt;#&lt;/span&gt;ai&lt;/a&gt;
        &lt;/div&gt;
      &lt;div class="crayons-story__bottom"&gt;
        &lt;div class="crayons-story__details"&gt;
          &lt;a href="https://dev.to/annavi11arrea1/catbot-custom-grammar-problem-fixed-oc5" class="crayons-btn crayons-btn--s crayons-btn--ghost crayons-btn--icon-left"&gt;
            &lt;div class="multiple_reactions_aggregate"&gt;
              &lt;span class="multiple_reactions_icons_container"&gt;
                  &lt;span class="crayons_icon_container"&gt;
                    &lt;img src="https://assets.dev.to/assets/fire-f60e7a582391810302117f987b22a8ef04a2fe0df7e3258a5f49332df1cec71e.svg" width="18" height="18"&gt;
                  &lt;/span&gt;
                  &lt;span class="crayons_icon_container"&gt;
                    &lt;img src="https://assets.dev.to/assets/raised-hands-74b2099fd66a39f2d7eed9305ee0f4553df0eb7b4f11b01b6b1b499973048fe5.svg" width="18" height="18"&gt;
                  &lt;/span&gt;
                  &lt;span class="crayons_icon_container"&gt;
                    &lt;img src="https://assets.dev.to/assets/sparkle-heart-5f9bee3767e18deb1bb725290cb151c25234768a0e9a2bd39370c382d02920cf.svg" width="18" height="18"&gt;
                  &lt;/span&gt;
              &lt;/span&gt;
              &lt;span class="aggregate_reactions_counter"&gt;53&lt;span class="hidden s:inline"&gt;&amp;nbsp;reactions&lt;/span&gt;&lt;/span&gt;
            &lt;/div&gt;
          &lt;/a&gt;
            &lt;a href="https://dev.to/annavi11arrea1/catbot-custom-grammar-problem-fixed-oc5#comments" class="crayons-btn crayons-btn--s crayons-btn--ghost crayons-btn--icon-left flex items-center"&gt;
              

              25&lt;span class="hidden s:inline"&gt;&amp;nbsp;comments&lt;/span&gt;
            &lt;/a&gt;
        &lt;/div&gt;
        &lt;div class="crayons-story__save"&gt;
          &lt;small class="crayons-story__tertiary fs-xs mr-2"&gt;
            10 min read
          &lt;/small&gt;
        &lt;/div&gt;
      &lt;/div&gt;
    &lt;/div&gt;
  &lt;/div&gt;
&lt;/div&gt;

&lt;/div&gt;


</description>
      <category>webdev</category>
      <category>programming</category>
      <category>ai</category>
      <category>beginners</category>
    </item>
    <item>
      <title>Catbot: Custom Grammar Problem Fixed</title>
      <dc:creator>Anna Villarreal</dc:creator>
      <pubDate>Sat, 01 Aug 2026 16:03:02 +0000</pubDate>
      <link>https://dev.to/annavi11arrea1/catbot-custom-grammar-problem-fixed-oc5</link>
      <guid>https://dev.to/annavi11arrea1/catbot-custom-grammar-problem-fixed-oc5</guid>
      <description>&lt;p&gt;&lt;em&gt;This is a submission for &lt;a href="https://dev.to/bugsmash"&gt;DEV's Summer Bug Smash: Smash Stories&lt;/a&gt; powered by &lt;a href="https://sentry.io/" rel="noopener noreferrer"&gt;Sentry&lt;/a&gt;.&lt;/em&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Overview
&lt;/h2&gt;

&lt;p&gt;Sometimes we discover our limitations through a series of failures. We don’t understand why something is not working, despite having tried multiple improvements and optimizations. My desktop pet, Catbot, is no exception. I had issues with Catbot cutting me off or not letting me finish before it started talking. It would constantly tell me that I was calling it cat bought and not Catbot. This was an actual pain in the rear. Catbot would spend a lot of time explaining this to me. &lt;/p&gt;

&lt;p&gt;I was able to tweak the listening window, so that Catbot would stop listening after a 1.5 second pause. This gives me enough time to finish my sentence, given my natural pauses and end of sentences. Controlling the pause gap helped tremendously. Going from .5 seconds to 1.5 seconds made all the difference in the world.&lt;/p&gt;

&lt;p&gt;After adding a better layer of control to Catbot’s listening skills, I still had difficulties using Catbot’s name. It thought I was saying “Catfight”, “Cat bought”, and even “Lawrence” one time. This will seem brief to the reader but it was only after multiple sessions of frustration that I came to the realization that perhaps my webcam mic was not a great listening tool for Catbot - as it picks up all sounds in the room. After some quick googling, I was very convinced that a better mic would help. I popped on my fancy cat ear headset and not to my surprise, Catbot could understand what I was saying much better. We need to make sure we are using the best hardware for the job. My headset mic is a close range omni directional mic that does a good job leaving out all the background noise.&lt;/p&gt;

&lt;p&gt;However, Catbot refused to respond to its name and would even argue with me! It would tell me that it wants to be called by it's actual name, Catbot. (Cat bot) So everytime I said its name, there was a debate and some long side tangent dialogue to follow. This was an awful experience.&lt;/p&gt;

&lt;p&gt;THE BUG: Catbot not listening correctly.&lt;/p&gt;





&lt;h2&gt;
  
  
  DEMO
&lt;/h2&gt;


&lt;div&gt;
    &lt;iframe src="https://www.youtube.com/embed/Nhddz9BwU8g" width="315" height="560"&gt;
    &lt;/iframe&gt;
  &lt;/div&gt;


&lt;p&gt;This is an interesting type of bug, not the typical software battle, but rather a conceptual bug. The type of situation where massive learning happens. &lt;/p&gt;





&lt;h2&gt;
  
  
  Much Ado About Graphs
&lt;/h2&gt;

&lt;p&gt;I discovered that runtime graphs are not supported by vosk-model-en-us-0.22, which was the preferred, larger  model. The training graphs were being ignored. Which means training Catbot on how I say “catbot” is ignored. &lt;em&gt;This is an outrage.&lt;/em&gt; The decoding graph is pre-composed into one frozen file, and there is nowhere to swap a grammar into. &lt;/p&gt;

&lt;p&gt;Transversely, a model that does support custom grammar is vosk-model-en-us-0.22-lgraph. However, It is important that we are not really adding custom commands, but controlling the output choices available. These feel like custom commands but they are better stated as custom directions, or disallow lists.&lt;/p&gt;

&lt;p&gt;Catbot uses kaldi, which decodes against a single finite state transducer, HCLG, built by composing 4 layers: H, C, L, G. I will discuss the G layer.&lt;/p&gt;

&lt;p&gt;G is the grammar / language model, which maps words to words. It assigns a cost to every possible word sequence. The decoder then picks a winner. A score is a negative log probability, and every arc in the graph carries weight, which add along the path. Negative log probabilities mean that a lower number is better, and adding costs is multiplying probabilities. Here is the english version:&lt;/p&gt;

&lt;p&gt;Total cost = &lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;th&gt;Acoustic cost&lt;/th&gt;
&lt;th&gt;Language cost&lt;/th&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;“How well do these phones match the audio frames?”&lt;/td&gt;
&lt;td&gt;“How likely is anyone to say this sentence in the first place?”&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;
&lt;h2&gt;
  
  
  Facts about the situation
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Neither of these alone is useful! &lt;/li&gt;
&lt;li&gt;The Acoustics can not differentiate “ice cream” from “I scream”. They are the same sounds. &lt;/li&gt;
&lt;li&gt;The language model does not hear. The decoder searches for the sequence minimizing the sum, which is why an LM’s job is best described as scoring sequences. &lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;But wait... why does this situation feel like Helen Keller? Because it's a similar situation.&lt;/p&gt;

&lt;p&gt;In the real life story of Helen Keller and Anne Sullivan, there was a communication problem. An intermediary interface must bridge two completely different sensory and data modalities. In computer science, this is called multimodal alignment. This method is just like the tactile “finger-spelling” method that connected Hellen Keller to the world.&lt;/p&gt;

&lt;p&gt;It was not until realizing I needed two different models to handle specific use cases that I realized this fundamental problem of having AI process real analogue data, which has now placed itself at the forefront of my curiosity and this project.&lt;/p&gt;

&lt;p&gt;A simple table will help you wrap your head around this idea without needing to explain too much:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
    &lt;thead&gt;
        &lt;tr&gt;
            &lt;th&gt;Element&lt;/th&gt;
            &lt;th&gt;Helen Keller&lt;/th&gt;
            &lt;th&gt;Anne Sullivan (The Teacher)&lt;/th&gt;
            &lt;th&gt;The Outside World&lt;/th&gt;
        &lt;/tr&gt;
    &lt;/thead&gt;
    &lt;tbody&gt;
        &lt;tr&gt;
            &lt;td&gt;AI Role&lt;/td&gt;
            &lt;td&gt;Acoustics Model (Vosk)&lt;/td&gt;
            &lt;td&gt;The Alignment Interface (Your Code Pipeline)&lt;/td&gt;
            &lt;td&gt;Language Model (LLM)&lt;/td&gt;
        &lt;/tr&gt;
        &lt;tr&gt;
            &lt;td&gt;Data Type&lt;/td&gt;
            &lt;td&gt;Raw sensory inputs (audio waves/frequencies).&lt;/td&gt;
            &lt;td&gt;The translator bridging two completely different modalities.&lt;/td&gt;
            &lt;td&gt;Symbolic language (written words/tokens).&lt;/td&gt;
        &lt;/tr&gt;
        &lt;tr&gt;
            &lt;td&gt;Function&lt;/td&gt;
            &lt;td&gt;Perceives physical sounds but completely lacks semantic context or logic.&lt;/td&gt;
            &lt;td&gt;Maps raw physical acoustic patterns directly into structured text blocks.&lt;/td&gt;
            &lt;td&gt;Processes structured text tokens logically but cannot directly "hear" physical waves.&lt;/td&gt;
        &lt;/tr&gt;
    &lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;Let’s take a look at how words are “chosen”:&lt;/p&gt;

&lt;p&gt;A state in G represents history, the last n-1 words, and each outgoing arc is a next possible word, weighted by -log P(word | history):&lt;/p&gt;

&lt;p&gt;“The cat” –sat (cost 4.1)--&amp;gt;&lt;br&gt;
               –ate (cost 5.3)--&amp;gt;&lt;br&gt;
              –sad (cost 9.8)--&amp;gt;&lt;/p&gt;

&lt;p&gt;…So basically words are chosen via likeliness. Catbot is not a real word, so catbot was never going to understand it as such, thus interpreting “Catbot engage” as “kappa engage” and other oddity phrases that would make sense to the model as it is trained.&lt;/p&gt;

&lt;p&gt;I had Claude synthesize some phrases for me, and it proves the issue I am having plain as day.&lt;/p&gt;

&lt;p&gt;SPOKEN: Catbot, engage.&lt;br&gt;
    1. conf  114.1   kappa engage&lt;br&gt;
    2. conf  112.1   cappa engage&lt;/p&gt;

&lt;p&gt;Kappa engage (a.k.a. “Catbot, engage.”) is a vocabulary trap. There is no path through G that emits Catbot, so the decoder does the only thing it can, it finds the closest match to the sound of the word. This is why saying “Catbot” failed EVERY. SINGLE. TIME. This speech recognition bug lies in the grammar available. The word “Catbot” is not really an actual word. I made that up. This is an infinite impossibility. &lt;/p&gt;

&lt;p&gt;Shortly before this deep dive, I told Catbot to respond to “CB”. This is English alphabet, real letters that have to exist. Catbot is now configured to respond to CB. When I say CB, it knows I am talking about it. Unmistakable. Functional. As a quick fix!&lt;/p&gt;

&lt;p&gt;As stated previously, we can disallow the decoder to consider certain words, such as “kappa” as noted above. &lt;/p&gt;

&lt;p&gt;If I say “Catbot, engage” without restrictions, the model hears “Kappa engage”. If we restrict the model, disallowing “kappa”, Catbot will hear “cat bot engage”. Since both “cat” and “bot” are commonly used words, they are available to the model.&lt;/p&gt;

&lt;p&gt;As you can see, training an interactive voice model is a long journey full of mysteries. From bad hardware to understanding model boundaries, sometimes we can actually gain more, with less! (Less meaning removing words from vocabulary, in this case.) &lt;/p&gt;

&lt;p&gt;And this can only be done by using the vosk-model-en-us-0.22-lgraph alongside vosk-model-en-us-0.22. This gives Catbot the tools it needs to respond in ways that make sense to custom words.&lt;/p&gt;




&lt;h2&gt;
  
  
  Code Samples
&lt;/h2&gt;

&lt;p&gt;So then, I Implemented a dual recognizer into Catbot:&lt;/p&gt;

&lt;p&gt;Catbot_voice.py&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;
 &lt;span class="mi"&gt;44&lt;/span&gt;  &lt;span class="n"&gt;KOKORO_VOICES&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;ROOT&lt;/span&gt; &lt;span class="o"&gt;/&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;models&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt; &lt;span class="o"&gt;/&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;voices-v1.0.bin&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;
 &lt;span class="mi"&gt;45&lt;/span&gt;  &lt;span class="n"&gt;VOSK_BIG&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;ROOT&lt;/span&gt; &lt;span class="o"&gt;/&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;models&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt; &lt;span class="o"&gt;/&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;vosk-model-en-us-0.22-lgraph&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;
 &lt;span class="mi"&gt;46&lt;/span&gt;  &lt;span class="n"&gt;VOSK_SMALL&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;ROOT&lt;/span&gt; &lt;span class="o"&gt;/&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;models&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt; &lt;span class="o"&gt;/&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;vosk-model-small-en-us-0.15&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;
 &lt;span class="mi"&gt;47&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt;&lt;span class="c1"&gt;# Dynamic-graph model — the only kind that accepts a runtime grammar. A model
&lt;/span&gt; &lt;span class="mi"&gt;48&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt;&lt;span class="c1"&gt;# that ships a pre-composed graph/HCLG.fst has no seam to insert one into;
&lt;/span&gt; &lt;span class="mi"&gt;49&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt;&lt;span class="c1"&gt;# Vosk logs "Runtime graphs are not supported by this model" and decodes
&lt;/span&gt; &lt;span class="mi"&gt;50&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt;&lt;span class="c1"&gt;# freely, which is worse than having no grammar recognizer at all. Detected by
&lt;/span&gt; &lt;span class="mi"&gt;51&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt;&lt;span class="c1"&gt;# the presence of graph/Gr.fst (the language model kept as a separate file).
&lt;/span&gt; &lt;span class="mi"&gt;52&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt;&lt;span class="n"&gt;VOSK_DYNAMIC&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;ROOT&lt;/span&gt; &lt;span class="o"&gt;/&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;models&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt; &lt;span class="o"&gt;/&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;vosk-model-en-us-0.22-lgraph&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;
 &lt;span class="mi"&gt;53&lt;/span&gt;
 &lt;span class="mi"&gt;54&lt;/span&gt;  &lt;span class="n"&gt;SAMPLE_RATE&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;16000&lt;/span&gt;
 &lt;span class="mi"&gt;55&lt;/span&gt;  &lt;span class="n"&gt;BASE&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;https://localhost:&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;os&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getenv&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;CATBOT_PORT&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;8800&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



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

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt; &lt;span class="mi"&gt;102&lt;/span&gt;          &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="bp"&gt;None&lt;/span&gt;
 &lt;span class="mi"&gt;103&lt;/span&gt;  &lt;span class="n"&gt;RE_LEARNED_WAKE&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;_load_learned_wake&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
 &lt;span class="mi"&gt;104&lt;/span&gt;
 &lt;span class="mi"&gt;105&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt;&lt;span class="c1"&gt;# ---- grammar-constrained wake/command recognizer ------------------------
&lt;/span&gt; &lt;span class="mi"&gt;106&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt;&lt;span class="c1"&gt;# A SECOND recognizer decodes the same audio against nothing but the phrases
&lt;/span&gt; &lt;span class="mi"&gt;107&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt;&lt;span class="c1"&gt;# below. It can't hear anything else, so "catbot engage" can't come out as
&lt;/span&gt; &lt;span class="mi"&gt;108&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt;&lt;span class="c1"&gt;# "kappa engage" — that word doesn't exist in its graph. Anything Anna says
&lt;/span&gt; &lt;span class="mi"&gt;109&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt;&lt;span class="c1"&gt;# that isn't a listed phrase decodes as "[unk]", which is exactly why this
&lt;/span&gt; &lt;span class="mi"&gt;110&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt;&lt;span class="c1"&gt;# only ever SUPPLEMENTS the free recognizer (which still handles all
&lt;/span&gt; &lt;span class="mi"&gt;111&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt;&lt;span class="c1"&gt;# conversation) instead of replacing it.
&lt;/span&gt; &lt;span class="mi"&gt;112&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt;&lt;span class="c1"&gt;#
&lt;/span&gt; &lt;span class="mi"&gt;113&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt;&lt;span class="c1"&gt;# Every word must be in the model's vocabulary or Vosk silently drops it:
&lt;/span&gt; &lt;span class="mi"&gt;114&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt;&lt;span class="c1"&gt;# "catbot" is not an English word, but "cat bot" is two of them. That's the
&lt;/span&gt; &lt;span class="mi"&gt;115&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt;&lt;span class="c1"&gt;# whole reason his name is spelled out here — _build_grammar checks each word
&lt;/span&gt; &lt;span class="mi"&gt;116&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt;&lt;span class="c1"&gt;# against the model and logs anything it had to drop.
&lt;/span&gt; &lt;span class="mi"&gt;117&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt;&lt;span class="n"&gt;WAKE_NAME_FORMS&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;cat bot&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;cat pot&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;cat bought&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;cat bottom&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;cat but&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
 &lt;span class="mi"&gt;118&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt;                   &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;catfight&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;combat&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;cabot&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;kat bot&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;cap pot&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
 &lt;span class="mi"&gt;119&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt;&lt;span class="c1"&gt;# what can follow his name (empty = just the name, which wakes him too)
&lt;/span&gt; &lt;span class="mi"&gt;120&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt;&lt;span class="n"&gt;GRAMMAR_TAILS&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;""&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;engage&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;engaged&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;wake up&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;wake&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
 &lt;span class="mi"&gt;121&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt;                 &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;are you listening&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;are you there&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;you there&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
 &lt;span class="mi"&gt;122&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt;                 &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;go to sleep&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;sleep&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;stop&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;stop listening&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
 &lt;span class="mi"&gt;123&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt;                 &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;be quiet&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;quiet&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;shut up&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;hush&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
 &lt;span class="mi"&gt;124&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt;                 &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;open terminal&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;close terminal&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;approve&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;skip&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
 &lt;span class="mi"&gt;125&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt;&lt;span class="c1"&gt;# phrases that stand alone, with or without his name
&lt;/span&gt; &lt;span class="mi"&gt;126&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt;&lt;span class="n"&gt;GRAMMAR_BARE&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;open terminal&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;close terminal&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
 &lt;span class="mi"&gt;127&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt;
 &lt;span class="mi"&gt;128&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt;
 &lt;span class="mi"&gt;129&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt;&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;_build_grammar&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;model&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="nb"&gt;str&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="bp"&gt;None&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
 &lt;span class="mi"&gt;130&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt;    &lt;span class="sh"&gt;"""&lt;/span&gt;&lt;span class="s"&gt;JSON phrase list for the constrained recognizer, filtered to words the
 131 +    model actually knows. Returns None if nothing usable survived.&lt;/span&gt;&lt;span class="sh"&gt;"""&lt;/span&gt;
 &lt;span class="mi"&gt;132&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt;    &lt;span class="n"&gt;names&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;list&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;WAKE_NAME_FORMS&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
 &lt;span class="mi"&gt;133&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt;    &lt;span class="c1"&gt;# whatever wake_training.py learned from Anna's own mic counts too
&lt;/span&gt; &lt;span class="mi"&gt;134&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt;    &lt;span class="k"&gt;try&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
 &lt;span class="mi"&gt;135&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt;        &lt;span class="n"&gt;learned&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;json&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;loads&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="n"&gt;ROOT&lt;/span&gt; &lt;span class="o"&gt;/&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;catbot_wake_variants.json&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;read_text&lt;/span&gt;&lt;span class="p"&gt;())&lt;/span&gt;
 &lt;span class="mi"&gt;136&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt;        &lt;span class="n"&gt;names&lt;/span&gt; &lt;span class="o"&gt;+=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;w&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;strip&lt;/span&gt;&lt;span class="p"&gt;().&lt;/span&gt;&lt;span class="nf"&gt;lower&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;w&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;learned&lt;/span&gt;
 &lt;span class="mi"&gt;137&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt;                  &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;w&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;strip&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="ow"&gt;and&lt;/span&gt; &lt;span class="n"&gt;w&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;strip&lt;/span&gt;&lt;span class="p"&gt;().&lt;/span&gt;&lt;span class="nf"&gt;lower&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="ow"&gt;not&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;names&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
 &lt;span class="mi"&gt;138&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt;    &lt;span class="k"&gt;except&lt;/span&gt; &lt;span class="nb"&gt;Exception&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
 &lt;span class="mi"&gt;139&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt;        &lt;span class="k"&gt;pass&lt;/span&gt;
 &lt;span class="mi"&gt;140&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt;
 &lt;span class="mi"&gt;141&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt;    &lt;span class="n"&gt;phrases&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;lead&lt;/span&gt;&lt;span class="si"&gt;}{&lt;/span&gt;&lt;span class="n"&gt;name&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s"&gt; &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;tail&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;strip&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
 &lt;span class="mi"&gt;142&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt;               &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;lead&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;""&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;hey &lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
 &lt;span class="mi"&gt;143&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt;               &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;name&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;names&lt;/span&gt;
 &lt;span class="mi"&gt;144&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt;               &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;tail&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;GRAMMAR_TAILS&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
 &lt;span class="mi"&gt;145&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt;    &lt;span class="n"&gt;phrases&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;update&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;GRAMMAR_BARE&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
 &lt;span class="mi"&gt;146&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt;
 &lt;span class="mi"&gt;147&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt;    &lt;span class="n"&gt;known&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;dict&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nb"&gt;str&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nb"&gt;bool&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="mi"&gt;148&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt;    &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;in_vocab&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;word&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;str&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="nb"&gt;bool&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
 &lt;span class="mi"&gt;149&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt;        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;word&lt;/span&gt; &lt;span class="ow"&gt;not&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;known&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
 &lt;span class="mi"&gt;150&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt;            &lt;span class="k"&gt;try&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
 &lt;span class="mi"&gt;151&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt;                &lt;span class="n"&gt;known&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;word&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;model&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;vosk_model_find_word&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;word&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;=&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;
 &lt;span class="mi"&gt;152&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt;            &lt;span class="k"&gt;except&lt;/span&gt; &lt;span class="nb"&gt;Exception&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
 &lt;span class="mi"&gt;153&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt;                &lt;span class="n"&gt;known&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;word&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="bp"&gt;True&lt;/span&gt;      &lt;span class="c1"&gt;# can't check — assume fine
&lt;/span&gt; &lt;span class="mi"&gt;154&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt;        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;known&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;word&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
 &lt;span class="mi"&gt;155&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt;
 &lt;span class="mi"&gt;156&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt;    &lt;span class="n"&gt;kept&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;dropped&lt;/span&gt; &lt;span class="o"&gt;=&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="mi"&gt;157&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt;    &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;phrase&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="nf"&gt;sorted&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;phrases&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
 &lt;span class="mi"&gt;158&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt;        &lt;span class="n"&gt;missing&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;w&lt;/span&gt; &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;w&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;phrase&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;split&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="ow"&gt;not&lt;/span&gt; &lt;span class="nf"&gt;in_vocab&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;w&lt;/span&gt;&lt;span class="p"&gt;)]&lt;/span&gt;
 &lt;span class="mi"&gt;159&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt;        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;missing&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
 &lt;span class="mi"&gt;160&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt;            &lt;span class="n"&gt;dropped&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;update&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;missing&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
 &lt;span class="mi"&gt;161&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt;            &lt;span class="k"&gt;continue&lt;/span&gt;
 &lt;span class="mi"&gt;162&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt;        &lt;span class="n"&gt;kept&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;append&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;phrase&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
 &lt;span class="mi"&gt;163&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt;    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;dropped&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
 &lt;span class="mi"&gt;164&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt;        &lt;span class="n"&gt;logger&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;info&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;grammar: dropped phrases using words the model doesn&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;t &lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;
 &lt;span class="mi"&gt;165&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt;                    &lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;know: &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nf"&gt;sorted&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;dropped&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
 &lt;span class="mi"&gt;166&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt;    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="ow"&gt;not&lt;/span&gt; &lt;span class="n"&gt;kept&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
 &lt;span class="mi"&gt;167&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt;        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="bp"&gt;None&lt;/span&gt;
 &lt;span class="mi"&gt;168&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt;    &lt;span class="c1"&gt;# "[unk]" is the escape hatch: it lets the decoder say "that wasn't any of
&lt;/span&gt; &lt;span class="mi"&gt;169&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt;    &lt;span class="c1"&gt;# these" instead of forcing Anna's conversation into the nearest command
&lt;/span&gt; &lt;span class="mi"&gt;170&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt;    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;json&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;dumps&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;kept&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;[unk]&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt;
 &lt;span class="mi"&gt;171&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt;
 &lt;span class="mi"&gt;172&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt;
 &lt;span class="mi"&gt;173&lt;/span&gt;  &lt;span class="c1"&gt;# in-conversation commands
&lt;/span&gt; &lt;span class="mi"&gt;174&lt;/span&gt;  &lt;span class="n"&gt;PROVIDER_WORDS&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
 &lt;span class="mi"&gt;175&lt;/span&gt;      &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;claude&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;anthropic&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;anthropic&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;anthropic&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;As well as…&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;
 &lt;span class="mi"&gt;470&lt;/span&gt;          &lt;span class="c1"&gt;# Lower CATBOT_END_SILENCE = faster to answer but more likely to cut you
&lt;/span&gt; &lt;span class="mi"&gt;471&lt;/span&gt;          &lt;span class="c1"&gt;# off mid-sentence; higher = more patient. 0.65s is a good balance.
&lt;/span&gt; &lt;span class="mi"&gt;472&lt;/span&gt;          &lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;end_silence&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;float&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;os&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getenv&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;CATBOT_END_SILENCE&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;0.65&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
 &lt;span class="mi"&gt;473&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt;
 &lt;span class="mi"&gt;474&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt;        &lt;span class="c1"&gt;# Second recognizer, constrained to the wake/command grammar above.
&lt;/span&gt; &lt;span class="mi"&gt;475&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt;        &lt;span class="c1"&gt;# Runs on its own dynamic-graph model so the big model keeps handling
&lt;/span&gt; &lt;span class="mi"&gt;476&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt;        &lt;span class="c1"&gt;# conversation with its full rescoring stack. Cost, measured: ~200 MB
&lt;/span&gt; &lt;span class="mi"&gt;477&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt;        &lt;span class="c1"&gt;# of RAM and roughly DOUBLE the decode CPU (4.3 -&amp;gt; 8.0 ms per 62 ms
&lt;/span&gt; &lt;span class="mi"&gt;478&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt;        &lt;span class="c1"&gt;# block) — the grammar shrinks the search, but a second acoustic model
&lt;/span&gt; &lt;span class="mi"&gt;479&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt;        &lt;span class="c1"&gt;# still runs on every block. Still only 0.13x realtime, so it adds no
&lt;/span&gt; &lt;span class="mi"&gt;480&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt;        &lt;span class="c1"&gt;# latency; set CATBOT_GRAMMAR=0 if that ever stops being true.
&lt;/span&gt; &lt;span class="mi"&gt;481&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt;        &lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;_grec&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;_gmodel&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="bp"&gt;None&lt;/span&gt;
 &lt;span class="mi"&gt;482&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt;        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;os&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getenv&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;CATBOT_GRAMMAR&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;1&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;split&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;#&lt;/span&gt;&lt;span class="sh"&gt;"&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="nf"&gt;strip&lt;/span&gt;&lt;span class="p"&gt;().&lt;/span&gt;&lt;span class="nf"&gt;lower&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; \
 &lt;span class="mi"&gt;483&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt;                &lt;span class="ow"&gt;not&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;0&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;off&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;no&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;false&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
 &lt;span class="mi"&gt;484&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt;            &lt;span class="k"&gt;try&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
 &lt;span class="mi"&gt;485&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt;                &lt;span class="n"&gt;name&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;os&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getenv&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;CATBOT_GRAMMAR_MODEL&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;""&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;split&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;#&lt;/span&gt;&lt;span class="sh"&gt;"&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="nf"&gt;strip&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
 &lt;span class="mi"&gt;486&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt;                &lt;span class="n"&gt;gdir&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;ROOT&lt;/span&gt; &lt;span class="o"&gt;/&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;models&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt; &lt;span class="o"&gt;/&lt;/span&gt; &lt;span class="n"&gt;name&lt;/span&gt; &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;name&lt;/span&gt; &lt;span class="k"&gt;else&lt;/span&gt; &lt;span class="n"&gt;VOSK_DYNAMIC&lt;/span&gt;
 &lt;span class="mi"&gt;487&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt;                &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="ow"&gt;not&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;gdir&lt;/span&gt; &lt;span class="o"&gt;/&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;graph&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt; &lt;span class="o"&gt;/&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Gr.fst&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;exists&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt;
 &lt;span class="mi"&gt;488&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt;                    &lt;span class="n"&gt;logger&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;warning&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
 &lt;span class="mi"&gt;489&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt;                        &lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;grammar recognizer OFF: &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;gdir&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;name&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s"&gt; has no dynamic &lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;
 &lt;span class="mi"&gt;489&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt;                        &lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;graph (graph/Gr.fst) — runtime grammars need one&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
 &lt;span class="mi"&gt;490&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt;                &lt;span class="k"&gt;else&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
 &lt;span class="mi"&gt;491&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt;                    &lt;span class="c1"&gt;# keep the Model referenced for as long as the recognizer
&lt;/span&gt; &lt;span class="mi"&gt;492&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt;                    &lt;span class="c1"&gt;# lives (the binding only borrows its handle)
&lt;/span&gt; &lt;span class="mi"&gt;493&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt;                    &lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;_gmodel&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;vosk&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;Model&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;str&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;gdir&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
 &lt;span class="mi"&gt;494&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt;                    &lt;span class="n"&gt;grammar&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;_build_grammar&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;_gmodel&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
 &lt;span class="mi"&gt;495&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt;                    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="ow"&gt;not&lt;/span&gt; &lt;span class="n"&gt;grammar&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
 &lt;span class="mi"&gt;496&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt;                        &lt;span class="k"&gt;raise&lt;/span&gt; &lt;span class="nc"&gt;RuntimeError&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;no usable grammar phrases&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
 &lt;span class="mi"&gt;497&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt;                    &lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;_grec&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;vosk&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;KaldiRecognizer&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
 &lt;span class="mi"&gt;498&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt;                        &lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;_gmodel&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;SAMPLE_RATE&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;grammar&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
 &lt;span class="mi"&gt;499&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt;                    &lt;span class="n"&gt;logger&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;info&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Grammar recognizer ready (&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;gdir&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;name&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s"&gt;, &lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;
 &lt;span class="mi"&gt;500&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt;                                &lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nf"&gt;len&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;json&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;loads&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;grammar&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s"&gt; phrases)&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
 &lt;span class="mi"&gt;501&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt;            &lt;span class="k"&gt;except&lt;/span&gt; &lt;span class="nb"&gt;Exception&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
 &lt;span class="mi"&gt;502&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt;                &lt;span class="n"&gt;logger&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;exception&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;grammar recognizer failed to load — &lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;
 &lt;span class="mi"&gt;503&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt;                                 &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;continuing with the free recognizer only&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
 &lt;span class="mi"&gt;504&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt;                &lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;_grec&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;_gmodel&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="bp"&gt;None&lt;/span&gt;
 &lt;span class="mi"&gt;505&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt;
 &lt;span class="mi"&gt;506&lt;/span&gt;          &lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;_kokoro&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;Kokoro&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;str&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;KOKORO_ONNX&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="nf"&gt;str&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;KOKORO_VOICES&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
 &lt;span class="mi"&gt;507&lt;/span&gt;          &lt;span class="c1"&gt;# warm up the TTS so the FIRST spoken reply isn't slowed by cold-start
&lt;/span&gt; &lt;span class="mi"&gt;508&lt;/span&gt;          &lt;span class="k"&gt;try&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;So basically, both models run in parallel, as two arguments, against the same regexes. The grammar model helps with commands and the bigger model is used for more conversational interactions. This effectively makes Catbot a more efficient desktop companion. It is now much more pleasant to hang out with Catbot.&lt;/p&gt;





&lt;h2&gt;
  
  
  Learning
&lt;/h2&gt;

&lt;p&gt;This entire project, it's all learning, all new territory. Tuning Catbot's ears is a real challenge. I had an awakening about hardware limitations, and an enlightening experience about why different models are used and for what purpose. Since I cannot recompose the larger model with new grammar, I can use the lgraph model on the side to handle custom needs more gracefully. When I made the connection to Helen Keller, I understood the whole situation from a very global perspective. It's not really something I could fully understand without diving into chaos.&lt;/p&gt;

&lt;p&gt;Thank you for checking out Catbot's progress!&lt;/p&gt;

</description>
      <category>devchallenge</category>
      <category>bugsmash</category>
      <category>catbot</category>
      <category>ai</category>
    </item>
    <item>
      <title>🐈‍⬛Check Out Catbot's New Leg!</title>
      <dc:creator>Anna Villarreal</dc:creator>
      <pubDate>Sat, 25 Jul 2026 01:40:56 +0000</pubDate>
      <link>https://dev.to/annavi11arrea1/check-out-catbots-new-leg-3cfg</link>
      <guid>https://dev.to/annavi11arrea1/check-out-catbots-new-leg-3cfg</guid>
      <description>&lt;p&gt;This is fun. I absolutely must share.&lt;/p&gt;

&lt;p&gt;So I recently created a desktop pet, called Catbot. Catbot is voice activated and has memory in an obsidian vault that it shares with models of choice as needed. Catbot is a harness that feels like a pet. I quickly built Catbot a body using AI and was excited to see the result was a furry creature with a robotic leg, which I had asked for. This worked for messing around and building Catbot to start. Here are some images I generated with ai in a previous week, just to catch you up to speed:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fsxfgg3tro3w0tpe4s6c4.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fsxfgg3tro3w0tpe4s6c4.png" alt="awkward rendering, but pretty on point" width="799" height="436"&gt;&lt;/a&gt;&lt;/p&gt;
Awkward initial rendering needing refinement



&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F5uazzg5v337off5q0fi8.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F5uazzg5v337off5q0fi8.png" alt="high level technical drawing" width="799" height="436"&gt;&lt;/a&gt;&lt;/p&gt;
A high level mock technical drawing generated by Gemini I used to generate Catbot's original body



&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fp525lgzgaudl2ol5135y.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fp525lgzgaudl2ol5135y.png" alt="Catbot's first 3D body" width="452" height="488"&gt;&lt;/a&gt;&lt;/p&gt;
Original Catbot appearance.







&lt;p&gt;But that badly represented robotic pet leg was getting on my nerves. I knew it had to be better. In the back of my head I was like you know what, it needs a way better leg. I let it fester in my head for a while. Then finally, I couldn’t take it anymore. The next moment of free time I sat down and drew up what I thought a robotic pet leg might look like. I drew this quickly, in about 15 minutes or so. I was happy with the overall idea, and believed it made sense. Here is my drawing:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fxkysw0u5inpvdjqsmmyt.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.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fxkysw0u5inpvdjqsmmyt.jpg" alt="Catbot leg idea" width="800" height="790"&gt;&lt;/a&gt;&lt;/p&gt;
My original drawing







&lt;p&gt;After creating the drawing, I knew that I had to have multiple views to have enough data for ai to create a working prototype. I gave my drawing to Gemini using the following prompt:&lt;/p&gt;

&lt;p&gt;&lt;em&gt;“I created this robotic cat leg and I need some good technical drawings of top, side view, front view and back. Can you make me a good technical drawing I can use to share my idea? “&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;And having luck previously with a different type of schematic (the original Catbot) I knew that this would at least produce something marginally workable. I was absolutely blown away by the mock schematic it produced for me. It is freaking beautiful, exactly what I wanted!&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fc09c7kayl99aj6x9uqc8.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fc09c7kayl99aj6x9uqc8.png" alt="Really awesome robotic leg schematic for Catbot" width="800" height="790"&gt;&lt;/a&gt;&lt;/p&gt;
Nice looking technical drawing by Gemini.







&lt;p&gt;I then gave the schematic with a detailed description to opus for creation using existing Catbot architecture, (using three.js) to create the fantastic leg for me. It did a pretty good job!&lt;/p&gt;

&lt;p&gt;I’ve added a video so you can see an interaction with Catbot. It’s entertaining until it frustrates you. Work in progress for sure. LOL. It was difficult to get Catbot's opintion of it's new leg:&lt;/p&gt;

&lt;p&gt;  &lt;iframe src="https://www.youtube.com/embed/nc5-3uJkZAY" width="710" height="399"&gt;
  &lt;/iframe&gt;
&lt;/p&gt;

&lt;p&gt;Catbot is a brat. Do not give your AI pet sass. (You know, unless you can deal with it.) 😂&lt;/p&gt;

&lt;p&gt;And let's not forget where we started:&lt;/p&gt;

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



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



&lt;p&gt;So I guess this latest Catbot, is Catbot the 4th. &lt;/p&gt;

&lt;p&gt;Has anyone else created an AI pet?&lt;/p&gt;

</description>
      <category>ai</category>
      <category>programming</category>
      <category>learning</category>
      <category>pet</category>
    </item>
    <item>
      <title>FLUX on a 4070 Graphics Card 🖼️</title>
      <dc:creator>Anna Villarreal</dc:creator>
      <pubDate>Sat, 18 Jul 2026 14:32:08 +0000</pubDate>
      <link>https://dev.to/annavi11arrea1/flux-on-a-4070-graphics-card-297</link>
      <guid>https://dev.to/annavi11arrea1/flux-on-a-4070-graphics-card-297</guid>
      <description>&lt;h2&gt;
  
  
  Preliminary
&lt;/h2&gt;

&lt;p&gt;In the past, I have tried a few things to get local video and image generation to work to my satisfaction. I was irritated for a while that my older macbook didn’t have an M chip because I know you can do some cool stuff with Ollama. But I am not buying a computer every year. That’s unrealistic and wasteful. One of my recent stunts involved trying to get fable to setup a workflow on comfyUI. While that’s novel and impressive-looking, the output was poor at best and I was not amused. Node confusion, half of them in languages I cannot read. I already have too many projects, I’m not about to decipher foreign languages on the fly or watch 30 node handling videos on Youtube over the course of 3 months. Time is a real factor here.&lt;/p&gt;

&lt;p&gt;With the 3rd extension of free fable use I’m squeezing every last drop out of it. I’m not wasting it either - I’ve got it dropping all kinds of notes to my vault so I can reproduce things on my own later. And honestly, that’s serious real world value for my workflow.&lt;/p&gt;

&lt;p&gt;Before I ramble, I must give credit to Youtuber Richard Aragon for sharing his magnificent google colab notebook. I stumbled into this whole process on accident looking for videos about quantization in an effort to know more about what the eff is quantization really. Little did I know this attempted deep dive would lead me down a path to resolve a personal problem of local generation. By the way, he mentions being able to do this on a 4060, so guys, all is not lost if you have a little bit of patience and good timing. I have come to find out that as developers we have unparalleled patience. So then, this post is for you. If I can save a single person from massive frustration, then I have done my job. It is better to share than to hoard. Because, like Richard Aragon, I would much prefer to share and help. &amp;lt;3&lt;/p&gt;





&lt;h2&gt;
  
  
  Start Your Own Workflow
&lt;/h2&gt;

&lt;p&gt;Here is Richard’s Youtube link and the google colab is in the notes: &lt;a href="https://www.youtube.com/watch?v=gjkVW3qOUMA%22" rel="noopener noreferrer"&gt;AI Quantization Easily Explained&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;What is really amazing is that I discovered I can use a quantized model locally and train it on my own content. As an artist, I was extremely curious to see how AI might create images for me in my art style. Mission accepted!&lt;/p&gt;

&lt;h3&gt;
  
  
  Here is EXACTLY what I did with no shame:
&lt;/h3&gt;

&lt;p&gt;❤️ I gave Fable the link to the google colab notebook, explaining what I was trying to accomplish. Fable fetched the data from the notebook and created it locally - a private copy of the workflow for me to modify as I wish.&lt;br&gt;
🧡 Using the 34GB  base model from Hugging Face, I am now able to train it on my own images to create my own style. This is accomplished by using a LoRA file which acts as a “lense” that attaches to the base model. This means that you can train it to have many lens (styles!) on your local workflow.  In his video, Richard uses pieces from the late Alphonse Mucha (Famous artist if you haven’t heard) to train the model. The results were on point, and even after quantization they still held enough data to hold onto most defining characteristics.  Using this knowledge, I ran the same tests as him for a control.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;One test without training.&lt;/li&gt;
&lt;li&gt;One test with training.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;💛 After testing, I knew the structure was in place for experimentation. The first thing I needed to do was to create a folder with my artwork, 14 images, and a metadata.jsonl file. (made my first jsonl file from scratch yesterday, cool haha). This metadata pairs the description of the image to the actual image so FLUX can interpret it for training.&lt;/p&gt;




&lt;h2&gt;
  
  
  Demystification
&lt;/h2&gt;

&lt;p&gt;Here is essentially how we get a 12GB card to handle a 24 GB model. I’ve simplified the terminology for learning and demystification purposes:&lt;/p&gt;

&lt;p&gt;💚 &lt;strong&gt;Shrink/Quantization&lt;/strong&gt; Specifically 4-bit NF4 quantization (NormalFloat4), done by the bitsandbytes library. "8-bit" and "4-bit" refer to how many bits store each model weight.            &lt;/p&gt;

&lt;p&gt;🩵 &lt;strong&gt;Freeze/Parameter-efficient fine-tuning&lt;/strong&gt; (PEFT), specifically LoRA (Low-Rank Adaptation). The base model's parameters are frozen, so you train small low-rank adapter matrices instead.  &lt;/p&gt;

&lt;p&gt;💜 &lt;strong&gt;Precompute/Embedding&lt;/strong&gt; caching and and latent caching.                                                                                                &lt;/p&gt;

&lt;p&gt;🩷 &lt;strong&gt;Right-size/No single fancy word&lt;/strong&gt; Memory footprint management: lower training resolution, batch size 1 with gradient accumulation, gradient checkpointing (recompute instead of store), and an 8-bit optimizer. Each trades a little speed or precision for a lot of memory.  &lt;/p&gt;

&lt;p&gt;No fancy UI at the moment (...already have plan for that 😂) but using the terminal and running the python script afterwards with optional parameters offers additional flexibility of image generation. &lt;/p&gt;




&lt;h2&gt;
  
  
  Here are the generations from my first local tests
&lt;/h2&gt;
&lt;h3&gt;
  
  
  Control 1 - untrained
&lt;/h3&gt;

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




&lt;h3&gt;
  
  
  Control 2 - Alphonse Mucha
&lt;/h3&gt;

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




&lt;h3&gt;
  
  
  Training 1 - 100 step artwork (fast, minutes)
&lt;/h3&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F1pzdvu3mhntdgidwug6z.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F1pzdvu3mhntdgidwug6z.png" alt="14 pieces of art" width="493" height="534"&gt;&lt;/a&gt;&lt;/p&gt;
&lt;h3&gt;
  
  
  Test 1 - 100 step artwork (EVERFLUO style)
&lt;/h3&gt;

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




&lt;h3&gt;
  
  
  Training 2 - 700 step artwork (over an hour)
&lt;/h3&gt;
&lt;h3&gt;
  
  
  Test 2 - 700 step artwork (EVERFLUO style)
&lt;/h3&gt;

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






&lt;h3&gt;
  
  
  Test 3 - Ran EVERFLUO style with very custom prompt to see limitations, needs work/additional training to be extra fancy.
&lt;/h3&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fdwe8knlap1ewwuifjv1i.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fdwe8knlap1ewwuifjv1i.png" alt="Pushing model outside of training" width="799" height="520"&gt;&lt;/a&gt;&lt;br&gt;
Pushing model outside of training to see what happens.&lt;/p&gt;




&lt;h2&gt;
  
  
  What about video?
&lt;/h2&gt;

&lt;p&gt;I was able to piggy-back off of the pre-existing image generation setup to build a video generation workflow. I am currently taking a stab at a 5 second video on the 4070 on one of my paintings. It's going to be at least an hour and I will update this post.&lt;/p&gt;

&lt;p&gt;Here is the first video!&lt;/p&gt;


&lt;div&gt;
    &lt;iframe src="https://www.youtube.com/embed/plcdkiTGEZY" width="315" height="560"&gt;
    &lt;/iframe&gt;
  &lt;/div&gt;






&lt;p&gt;On that note, I'll survive another year without a 5090, maybe XD.&lt;/p&gt;

</description>
      <category>ai</category>
      <category>image</category>
      <category>video</category>
      <category>local</category>
    </item>
  </channel>
</rss>
