<?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>🌑 Blue October</title>
      <dc:creator>Anna Villarreal</dc:creator>
      <pubDate>Fri, 16 Oct 2026 15:44:45 +0000</pubDate>
      <link>https://dev.to/annavi11arrea1/blue-october-1jia</link>
      <guid>https://dev.to/annavi11arrea1/blue-october-1jia</guid>
      <description>&lt;p&gt;TLDR: A brief summary of how I got here.&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%2Fyzkmume67kjrw6exghgo.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%2Fyzkmume67kjrw6exghgo.png" alt="divider" width="793" height="35"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  The Mindset
&lt;/h2&gt;

&lt;p&gt;For the last 3 years, I have been learning tech. It started with feeling tired of working at dead end jobs. The trigger was asking myself "How bad could it be if I just tried to learn programming?" &lt;/p&gt;

&lt;p&gt;This mindset was around 2023 when I was working at a printing company doing graphic mock-ups for clients and reviewing installer plans, among other things. I was good at this but it was very boring and the smell from acrylic being cut by lasers periodically gave me frequent headaches and made me feel nauseous. I had additional stresses going on in my life that made me feel, to put it lightly, not so great. &lt;/p&gt;

&lt;p&gt;I ended up quitting that job because my performance started to tank, and I knew it. I would be sitting at my desk and trying very hard to hold back my emotions. I quit, and a month later I had an emergency surgery. While laying on the couch post-surgery and unable to basically walk around for a month, I decided my laptop was my new best friend. 💻&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%2F3r7cesefc0dzbilwv8fn.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%2F3r7cesefc0dzbilwv8fn.png" alt="divider" width="793" height="35"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  The Student
&lt;/h2&gt;

&lt;p&gt;I approached programming like an academic - researching schools, bootcamps, salaries, potential wages, life-work balance, and job outlook. Making the conscious decision to learn programming in the post-pandemic landscape was not a venture for money, but of personal curiosity. If I must work, I want to do something I actually like and find interesting. A long, long time ago, in a galaxy far away, I had taken an interest in the programming classes offered at my high school. Of course, back in 2005 with myspace near its peak, I thought I was some king s*** styling my page with HTML and CSS. I wanted to have what all the other cool people had, so I figured out how to do it myself. IT WAS SO COOL 😂&lt;/p&gt;





&lt;h3&gt;
  
  
  Unique Experiences
&lt;/h3&gt;

&lt;p&gt;Anyways, I got away from all that. I pursued art and nutrition at the university. I was inspired to become a video game creator/3D model artist after taking some not-for-credit classes at the Academy of Art in San Francisco as a teenager. I absolutely loved that experience. I remember the game design teacher, a large man with a ponytail. He commented on my stature since I was in wrestling in high school for a while and had continued to maintain physical fitness in the years the followed. For a large chunk of my teenage and younger adult life, I was very physically fit, and unafraid of humans in general. &lt;/p&gt;

&lt;p&gt;That same summer, living with my sister, I got to experience san-fran through teenager eyes. I visited amazing restaurants, like "The Stinking Rose" - a garlic based restaurant where I had probably the best roasted garlic I will ever eat in my life. My sister was also into wellness, and she took me to some edgy meditation classes. I'll never forget seeing the people sitting around me, covered in tattoos and piercings, the most metal-head looking people you ever saw, sitting peacefully on cushions on the ground leading a meditation. This group is still around, they are called Dharma Punx. I highly recommend checking them out &lt;a href="https://www.againstthestream.com/dharma-punx-merch/dharma-punx-book" rel="noopener noreferrer"&gt;Dharma Punx&lt;/a&gt;. I am far removed but it's on my agenda at some point to visit them again. &lt;/p&gt;





&lt;h3&gt;
  
  
  High School Frustrations
&lt;/h3&gt;

&lt;p&gt;I think it was probably that same year returning to the high school where I had another interesting experience. There was a girl at the high school that would always, always, ALWAYS make fun of me and she would get her friends on board making fun of me. I was never sure why, I never did anything to her. She didn't like me when I made my move from the nerd table to the goth table after obtaining a boyfriend. Retrospectively she may have been jealous. But I didn't see that. I saw hate, accusations, lies, and repeated embarrassment. She was pretty bad. Everyday, I couldn't sit at that table without getting hated on. Of course I did it for love. A cute boy liked me, I must persevere. What stupidity. After months of this, one day at the lunch table she said something that totally triggered me. I think it had to do with my boyfriend pretending to like me or repeated name calling. &lt;/p&gt;

&lt;p&gt;Whatever the case, a storm was brewing inside my hormone-fueled teenage body. Something inside of me snapped. I quite literally hunted her down in the hallway. She had no idea I was coming for her. I found her, slammed her to the ground and drew back a fist. I looked her straight in the eye pinning her down with the other hand. I said absolutely nothing. I thought with every cell in my body I was going to knock her out. &lt;/p&gt;

&lt;p&gt;A tiny voice inside me held my fist back: "She is weak, and she is not actually trying to kill you." Frustrated, I let her go. I think the knowledge of knowing what I could do was enough for me. But as soon as let her go, I picked her up and literally threw her in a garbage can. (She remained physically unharmed.) Everyone was watching at this point. I was generally quiet, and probably shocked a few people. I was suspended for a week. It was awesome. I felt like I had proven a point after years of bullying. I was high on pride and my mom bought me DDR and some of those flexible dance pads, and I played DDR the whole week staying home "suspended". My dad told me he was proud of me after I explained myself. That meant a lot. Those were not words I heard a lot. He is an engineer, and is very intelligent. I never felt the need to do such a thing ever again.&lt;/p&gt;

&lt;h3&gt;
  
  
  Trying Things
&lt;/h3&gt;

&lt;p&gt;Let me circle back. I tried a few things at the university, with interests in illustration, green engineering, meteorology, graphic design, and nutrition. I changed my major so many times I was lucky I graduated at all. My problem is that I like everything, because everything is interesting. It has been a real problem. Top that with a people pleasing personality for many years and I was basically lost and confused. &lt;/p&gt;

&lt;p&gt;After I graduated I discovered electronic dance music and got heavy into the festival scene. So much so that I started a vending business called Ever Fluorescent where I would sell my art and clothing at festivals around the nation. This was a main stream of income for a while. It's still alive, but on the back burner at the moment. The unpredictable nature of income generated from festivals makes it extremely difficult for a solo artist to budget life expenses. So, needing structure, I started working regular full time jobs. Starting with that printing factory I mentioned earlier. &lt;/p&gt;

&lt;p&gt;That's a whole lot of life summarization compacted very small. Now that you know I didn't just randomly arrive here, I can continue.&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%2Fw4vzbooz3brygin631qd.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%2Fw4vzbooz3brygin631qd.png" alt="divider" width="793" height="35"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Recent Timeline
&lt;/h2&gt;

&lt;p&gt;I think a timeline would work here. 📈&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Late 2023 - Started relearning HTML and CSS&lt;/li&gt;
&lt;li&gt;Very early 2024 - Explored JavaScript&lt;/li&gt;
&lt;li&gt;Spring 2024 - Found and Applied to Full Stack Web Dev Apprenticeship&lt;/li&gt;
&lt;li&gt;Summer 2024 - Studied Ruby/Rails&lt;/li&gt;
&lt;li&gt;Fall 2024 - Started Apprenticeship&lt;/li&gt;
&lt;li&gt;Winter 2024 - Completed Apprenticeship&lt;/li&gt;
&lt;li&gt;Early 2025- Completed Google IT Support Cert&lt;/li&gt;
&lt;li&gt;Spring 2025 - Worked temporarily as TA at University of Chicago, then got first part-time IT job&lt;/li&gt;
&lt;li&gt;Winter 2025 - Received an offer for fulltime work doing IT elsewhere&lt;/li&gt;
&lt;li&gt;Early 2026 - Hired full time as building tech for a middle school&lt;/li&gt;
&lt;li&gt;Currently - Unemployed&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%2Fxijp38zulhqzhw31nwxl.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%2Fxijp38zulhqzhw31nwxl.png" alt="divider" width="793" height="35"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Current Status
&lt;/h2&gt;

&lt;p&gt;Through all of these changes I have also been pursuing classes for a second bachelors in computer science. I have even taken a few masters level classes. This would be my second bachelors. I have also been studying CCNA topics on the side. After being let go from my previous job after nearly a year, I have been left feeling defeated despite trying my hardest. I started this tech journey wanting to simply be a web developer. With the current state of things - post pandemic layoffs, AI, fierce competition - I sometimes feel like I waste my time.&lt;/p&gt;

&lt;p&gt;Frustrated, I try to reframe my thinking. That is part of the reason why I thought studying CCNA topics would be a good idea. This way, I can have a deeper general understanding of things and why they are the way they are. I went so far as to buy multiple books and networking equipment to aid the journey, as I am a fan of tangible items. The news of losing my job that I was trying so hard at has left me in a turbulent emotional state. I did the only thing I know I could do - find a distraction. This time, that distraction is HTB. Why this choice, you may ask. Well, as any developer would likely do, I ran to Google for some counseling. I simply asked it: "Would knowledge of penetration testing help me find a job as a developer?" I'll let you hypothesize what the answer was. As someone who appreciates a good rabbit hole, this is both a blessing and a curse. &lt;/p&gt;

&lt;p&gt;I cannot say for sure where I am going, but I can tell you that I will get there in an unrelenting manner, persevering in the darkness with no compass, and no flashlight. Yesterday I learned what a .ko file was after asking "What is ELF&amp;gt;?" &lt;/p&gt;

&lt;p&gt;There is a humongous unexplored world hiding right in front of me. 😮 &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%2Fk48qvaz3gjp4cgx8e3qu.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%2Fk48qvaz3gjp4cgx8e3qu.png" alt="divider" width="793" height="35"&gt;&lt;/a&gt;&lt;/p&gt;

</description>
      <category>webdev</category>
      <category>programming</category>
      <category>mentalhealth</category>
    </item>
    <item>
      <title>💅 Keyboard Makeover</title>
      <dc:creator>Anna Villarreal</dc:creator>
      <pubDate>Sun, 11 Oct 2026 02:25:47 +0000</pubDate>
      <link>https://dev.to/annavi11arrea1/keyboard-makeover-i6a</link>
      <guid>https://dev.to/annavi11arrea1/keyboard-makeover-i6a</guid>
      <description>&lt;p&gt;I have had this interesting piece of psychedelic looking vinyl for some time. Today I finally found a use case for it. I turned a regular keyboard into a way more fun and exciting one. &lt;/p&gt;

&lt;p&gt;I shared my general process and supplies in the video if anyone wants to try! It's a super affordable way to make your space feel more like yours. &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%2F76lnrjsolg9ta0v9eadt.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%2F76lnrjsolg9ta0v9eadt.jpg" alt="Fun Keyboard" width="800" height="600"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;I'd also love to see anyone else creations!&lt;/p&gt;





&lt;h2&gt;
  
  
  Update: 10-16-2026
&lt;/h2&gt;

&lt;p&gt;Some women like flowers, some women like jewelry, but you know he loves you when he buys you CNC cut metal keys for your keyboard! I am obsessed with the the see-thorough edges that let the led light shine through. My keyboard is starting to feel like a cyberpunk festival. Where psychedelic meets industrial is sort of my vibe. Artists like&lt;br&gt;
&lt;a href="https://samuelfarrand.com/?srsltid=AU7gw4VuRVGXolJ7HIyvcaDbN5inB3A3EHhlQ0W6EK_n414IgW4VPdzs" rel="noopener noreferrer"&gt;Samuel Farrand&lt;/a&gt; and &lt;a href="https://brizbazaar.com/" rel="noopener noreferrer"&gt;BrizBazaar&lt;/a&gt; have been inspirations to me for some time.&lt;/p&gt;

&lt;p&gt;Anyways, update photo time! &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%2F04xehp9gnsaidr5rl765.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%2F04xehp9gnsaidr5rl765.jpg" alt="metal keycaps" width="800" height="600"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;I love it so much! It's some sort of aluminum/steel alloy and they came in a cool little carbon fiber box. A cool surprise I did not expect, was a new tool. I'm not sure what it's called so I guess i'll refer to it as a 'key-grabber'. 😂&lt;/p&gt;

&lt;p&gt;Here is the new kit details in photos:&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%2F5timcdelqbrvp8nj58qb.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%2F5timcdelqbrvp8nj58qb.jpg" alt="metal keycap kit with key grabber" width="800" height="600"&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%2Ftoblwkcuiiehdublh9m1.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%2Ftoblwkcuiiehdublh9m1.jpg" alt="key grabber demo" width="800" height="600"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Additionally, I wanted to let everyone know that the vinyl is holding up well despite my best efforts to power through a HTB module. (Post incoming LOL) Sides staying down, no peeling, vinyl staying put!&lt;/p&gt;

</description>
      <category>mentalhealth</category>
      <category>craft</category>
    </item>
    <item>
      <title>⚡ Proprietary RJ50 Cable Reveals the Truth ⚡</title>
      <dc:creator>Anna Villarreal</dc:creator>
      <pubDate>Wed, 07 Oct 2026 01:19:17 +0000</pubDate>
      <link>https://dev.to/annavi11arrea1/proprietary-rj50-cable-reveals-the-truth-5dlc</link>
      <guid>https://dev.to/annavi11arrea1/proprietary-rj50-cable-reveals-the-truth-5dlc</guid>
      <description>&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%2Fmosr7t04n0xr5e2gg6f3.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%2Fmosr7t04n0xr5e2gg6f3.png" alt="divider"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Follow-up Post
&lt;/h2&gt;

&lt;p&gt;Previously I wrote about my experience where I built an app for my dad to analyze our home power use. You can read about that here:&lt;/p&gt;


&lt;div class="ltag__link--embedded"&gt;
  &lt;div class="crayons-story "&gt;
  &lt;a href="https://dev.to/annavi11arrea1/dad-complains-about-electric-bill-so-i-built-him-an-energy-dashboard-and-found-the-panels-lied-3jhg" class="crayons-story__hidden-navigation-link"&gt;⚡ Dad Complains About Electric Bill, So I Built Him an Energy Dashboard and Found the Panels Lied ⚡&lt;/a&gt;


  &lt;div class="crayons-story__body crayons-story__body-full_post"&gt;
      &lt;a href="https://dev.to/annavi11arrea1/dad-complains-about-electric-bill-so-i-built-him-an-energy-dashboard-and-found-the-panels-lied-3jhg" class="crayons-article__context-note crayons-article__context-note__feed"&gt;&lt;p&gt;Hacktoberfest Weekend Challenge: Build for a Friend 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-4798737" 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/dad-complains-about-electric-bill-so-i-built-him-an-energy-dashboard-and-found-the-panels-lied-3jhg" class="crayons-story__tertiary fs-xs"&gt;&lt;time&gt;Oct 5&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/dad-complains-about-electric-bill-so-i-built-him-an-energy-dashboard-and-found-the-panels-lied-3jhg" id="article-link-4798737"&gt;
          ⚡ Dad Complains About Electric Bill, So I Built Him an Energy Dashboard and Found the Panels Lied ⚡
        &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/hf26challenge"&gt;&lt;span class="crayons-tag__prefix"&gt;#&lt;/span&gt;hf26challenge&lt;/a&gt;
            &lt;a class="crayons-tag  crayons-tag--monochrome " href="/t/opensource"&gt;&lt;span class="crayons-tag__prefix"&gt;#&lt;/span&gt;opensource&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/dad-complains-about-electric-bill-so-i-built-him-an-energy-dashboard-and-found-the-panels-lied-3jhg" 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;21&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/dad-complains-about-electric-bill-so-i-built-him-an-energy-dashboard-and-found-the-panels-lied-3jhg#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;
            7 min read
          &lt;/small&gt;
        &lt;/div&gt;
      &lt;/div&gt;
    &lt;/div&gt;
  &lt;/div&gt;
&lt;/div&gt;

&lt;/div&gt;


&lt;p&gt;But for this post... Ahhh, &lt;em&gt;this&lt;/em&gt; post...&lt;/p&gt;

&lt;p&gt;My proprietary wire has arrived and and my UPS has been communicating to my computer since yesterday. &lt;/p&gt;

&lt;p&gt;First I want to explain Mr. RJ50, and how I got here. &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%2Fmosr7t04n0xr5e2gg6f3.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%2Fmosr7t04n0xr5e2gg6f3.png" alt="divider"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;I wanna give dad the run down on my personal electrical consumption, so I can dismiss myself from being soley responsible for the high electric bill. Was there ever a greater force? 😂&lt;/p&gt;

&lt;p&gt;I don't have a nice streamlined method in place for collecting electrical usage around the house. That changes now. With the most important reading first: my personal desktop. 😂 As any inquisitive person would do, I take a peek at the back of my UPS, knowing I saw some suspicious ports back there at one point. Upon researching, I discover that the only usable port on my UPS for my use case is the specific data port. Now, this port, has secrets. It looks like you can just pop an ethernet cable in and your good to go. I tried that, and no. The answer is no. 😂 Don't do what I did. &lt;/p&gt;

&lt;p&gt;It is better to accept that you need a proprietary cable that is not sold in your nearby big-box box retailer or same-day on amazon. You must &lt;em&gt;wait&lt;/em&gt; for your wire to arrive. SUCH impatience. Come to terms with that now. I nearly threw a fit like a child when I found out I couldn't have my wire in .5 seconds (Not the first time, or even the second, probably not the last). Somewhere between proprietary cable 2 and 3, you accept that you are a bonified techie. Look, I was so close to trying to solder my own wire so I could be proud of it, but then I discovered, that will likely not work. I was forced to buy one. All of this drama, to excuse myself of the mental burden of the electric bill. Woe is me and my $15 proprietary wire. &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%2Fsyrj9kl84v2jpwesn39x.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%2Fsyrj9kl84v2jpwesn39x.gif" alt="Drama"&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%2Fmosr7t04n0xr5e2gg6f3.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%2Fmosr7t04n0xr5e2gg6f3.png" alt="divider"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Let me explain Mr. proprietary RJ50 in comparison with our standard RJ45:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Feature&lt;/th&gt;
&lt;th&gt;RJ45&lt;/th&gt;
&lt;th&gt;RJ50&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Common configuration&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;8P8C&lt;/td&gt;
&lt;td&gt;10P10C&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Contact positions&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;8&lt;/td&gt;
&lt;td&gt;10&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Typical application&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Ethernet networking&lt;/td&gt;
&lt;td&gt;Specialized equipment&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Typical wiring&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;T568A or T568B&lt;/td&gt;
&lt;td&gt;Usually device-specific&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Common equipment&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Routers, switches, computers and cameras&lt;/td&gt;
&lt;td&gt;Scanners, POS terminals and controllers&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Standardization&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Highly standardized for Ethernet&lt;/td&gt;
&lt;td&gt;Often proprietary&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Directly interchangeable&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;No&lt;/td&gt;
&lt;td&gt;No&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;RJ50 is wider, has a different pinout, and is often proprietary. Not only did I need RJ50, but I needed the other end to terminate as some sort of USB to connect to my desktop.&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%2Fmosr7t04n0xr5e2gg6f3.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%2Fmosr7t04n0xr5e2gg6f3.png" alt="divider"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Here is the special wire:&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%2Fmiddh2gyeancnmnj1fij.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%2Fmiddh2gyeancnmnj1fij.jpg" alt="RJ50"&gt;&lt;/a&gt;&lt;/p&gt;
Finally, I hold the proper wire in my possession







&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%2Fpd8s1ob08246fx3tm2to.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%2Fpd8s1ob08246fx3tm2to.jpg" alt="RJ50 plugged into UPS"&gt;&lt;/a&gt;&lt;/p&gt;
RJ50 snuggly clicked in its proprietary hole



&lt;p&gt;Bam. Data! Ok no, its not that simple. 😂&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%2Fmosr7t04n0xr5e2gg6f3.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%2Fmosr7t04n0xr5e2gg6f3.png" alt="divider"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;I found some open source software to give me a neat presentation on my desktop initially. That looks like this:&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%2F21byxo015evq9mja2589.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%2F21byxo015evq9mja2589.png" alt="open source software"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;If you have an APC UPS and would like to try this yourself, I will share that I used the open source software found at &lt;a href="https://sourceforge.net/projects/apcupsd/" rel="noopener noreferrer"&gt;Sourceforge&lt;/a&gt;. &lt;/p&gt;

&lt;p&gt;After I got that working, I was able to use the data from that and pull it back into my custom solar panel app where I put an entry at the bottom. That looks like this: &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%2Fzgbd0synzo7mmb0ij27i.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%2Fzgbd0synzo7mmb0ij27i.png" alt="Not Guilty"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;As you can see, my average responsibility for my workstation is $22 a month. And that's without taking into consideration energy generated from solar panels. Considering the current home use and solar panel production for this time of year  (solar panels covering 41% of energy needs in their current state) This makes my contribution to the electric bill (from my workstation, the area in question, of course "all that stuff in the basement" 😂) a whopping $13 at best. &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%2Feqljn1zu2p5in26uf0v7.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%2Feqljn1zu2p5in26uf0v7.png" alt="dabbing unicorn"&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%2Fksvad28j6vdrdf486qbg.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%2Fksvad28j6vdrdf486qbg.png" alt="divider"&gt;&lt;/a&gt;&lt;/p&gt;

</description>
      <category>hardware</category>
      <category>rj50</category>
      <category>powergrid</category>
      <category>iot</category>
    </item>
    <item>
      <title>⚡ Dad Complains About Electric Bill, So I Built Him an Energy Dashboard and Found the Panels Lied ⚡</title>
      <dc:creator>Anna Villarreal</dc:creator>
      <pubDate>Mon, 05 Oct 2026 03:20:58 +0000</pubDate>
      <link>https://dev.to/annavi11arrea1/dad-complains-about-electric-bill-so-i-built-him-an-energy-dashboard-and-found-the-panels-lied-3jhg</link>
      <guid>https://dev.to/annavi11arrea1/dad-complains-about-electric-bill-so-i-built-him-an-energy-dashboard-and-found-the-panels-lied-3jhg</guid>
      <description>&lt;p&gt;&lt;em&gt;This is a submission for the &lt;a href="https://dev.to/challenges/hacktoberfest-weekend-2026-10-01"&gt;Hacktoberfest Weekend Challenge: Build for a Friend&lt;/a&gt;&lt;/em&gt;&lt;/p&gt;

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

&lt;p&gt;My dad recently complained about how high the electric bill was. Most of us know the feeling: the impending doom of being the culprit, because you're the "computer person." (We'll see about that, Dad!)&lt;/p&gt;

&lt;p&gt;So I set out on a mission to prove I was not the sole contributor to the high electric bill, and I built my dad a resource to actually understand it. It turned into much more than a re-skinned utility app. It pulls together:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Live data from our solar panels&lt;/strong&gt;, straight from the Enphase gateway on our home network, every minute, down to each individual panel&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;A Problems list&lt;/strong&gt; that flags dead microinverters, panels producing less than their neighbors, expiring tokens, and big jumps in home usage&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Government data on what our roof &lt;em&gt;should&lt;/em&gt; produce&lt;/strong&gt;: PVWatts, a model from the U.S. national renewable energy lab, drawn as markers on the monthly chart so we can see if we're falling short&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Our actual electric bills&lt;/strong&gt; from ComEd, combined with the solar data to work out how much power the house really uses, compared to last year and to the average Illinois home&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Where the power goes&lt;/strong&gt;: per-device and per-room usage from smart plugs in Home Assistant, ranked by what each one costs per month (Smart plugs ordered, not here yet!)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Dad's briefing&lt;/strong&gt;: a short, plain-English note at the top of the dashboard, written by &lt;strong&gt;Gemma running locally on our home PC&lt;/strong&gt;, that explains all of the above without a single chart&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;I'll be honest, it turned into chaos.&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%2Fmz8ze9ndysz9k0uixz8s.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%2Fmz8ze9ndysz9k0uixz8s.png" alt="dashboard charts" width="800" height="771"&gt;&lt;/a&gt;&lt;/p&gt;





&lt;h3&gt;
  
  
  The roof mystery
&lt;/h3&gt;

&lt;p&gt;Everything was fun and games at first, connecting an API here, pulling in a spreadsheet there. Our solar company even has a free developer portal. Then I started digging deeper than I'd planned. I learned there was an optional consumption meter our system could have had, but it was never installed. I learned you can put devices on your circuit breaker to see which circuit (or room, like my brother's... &lt;em&gt;cough cough&lt;/em&gt;) is using what at any moment.&lt;/p&gt;

&lt;p&gt;We already knew two of the microinverters were bad. What alarmed me was when my app started warning that some &lt;em&gt;random&lt;/em&gt; panels were underperforming: four more, reporting between 11% and 30% of their normal output. They weren't clustered in one area.&lt;/p&gt;

&lt;p&gt;Confused, I stepped outside to see if a squirrel had built a house up there. Nope, just neatly positioned solar panels... wait. &lt;strong&gt;They run up the roof vertically.&lt;/strong&gt; That's not what they look like on the map. I went in and out of the house several times, standing on chairs, taking pictures, and showing my dad. &lt;em&gt;Dad! They are not horizontal!&lt;/em&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%2Fzc8pvt4lnrj0ah0kdwll.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%2Fzc8pvt4lnrj0ah0kdwll.png" alt="strange panels" width="800" height="261"&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%2Fkgfqjt2lhn8esqkl9usj.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%2Fkgfqjt2lhn8esqkl9usj.png" alt="panels in new app" width="799" height="246"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;It took some convincing from a difficult angle, but I proved it. And it wasn't just the orientation. The official app (and mine, since it uses the same layout) shows two long rows and then a short row. What's actually on the roof is &lt;strong&gt;long, short, long&lt;/strong&gt;. A bona fide discrepancy.&lt;/p&gt;

&lt;p&gt;Here's why. Two to three years ago, storms ripped through the neighborhood and damaged our roof. Insurance covered it, but to fix a roof you have to take the solar panels down. The whole process took 18 months. When the panels went back up, everything &lt;em&gt;seemed&lt;/em&gt; fine. Shortly after, the two microinverters stopped responding.&lt;/p&gt;

&lt;p&gt;I'd been blaming the underperformers on wildfire smoke, a dust storm, and bird poop. Then it clicked: whoever reinstalled the panels never updated the map. &lt;strong&gt;The system reports panels in places they are not.&lt;/strong&gt; To test it, I checked where a tree's shade actually lands. It does cover some panels. Just not the ones the map says are underperforming.&lt;/p&gt;

&lt;p&gt;Why does this matter? If I point a technician at "the second panel in the first row" and it's really the one next to it, he "fixes" the wrong panel, says "you'll see it working tomorrow," and I can't prove anything. That could get expensive. Before I start directing people, I need to know exactly which panel is which. That call to the solar company is coming, and it's going to be a serious one.&lt;/p&gt;





&lt;h3&gt;
  
  
  So, was it me?
&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%2Fk3gsc5qwfmthjew88dkm.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%2Fk3gsc5qwfmthjew88dkm.png" alt="Not guilty" width="800" height="429"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Partly in my defense: ComEd raised prices about 12% for all residents. Add two dead microinverters (plus whatever is going on with the mystery panels) and a full house, and the bill makes a lot more sense. To be fair, the dashboard also says the house really is using more power than last year, which is exactly why the smart plugs are next. Soon we'll know &lt;em&gt;who&lt;/em&gt;.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;DAD'S REACTION:&lt;/strong&gt;&lt;br&gt;
I have been instructed to contact Enphase and find out what is going on with the solar panels, at which point I will then be able to contact someone to come climb on the roof and do the actual fixing.&lt;/p&gt;




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

&lt;p&gt;The live dashboard is password-protected because it shows our home's real data. A temporary view has been created for judges with password that expires in 30 days: &lt;code&gt;Dev_judge$&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;Preview the drama in action: &lt;br&gt;
&lt;a href="https://solar-dashboard-coral.vercel.app/login" rel="noopener noreferrer"&gt;Solar Dashboard&lt;/a&gt;&lt;/p&gt;




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


&lt;div class="ltag-github-readme-tag"&gt;
  &lt;div class="readme-overview"&gt;
    &lt;h2&gt;
      &lt;img src="https://assets.dev.to/assets/github-logo-5a155e1f9a670af7944dd5e12375bc76ed542ea80224905ecaf878b9157cdefc.svg" alt="GitHub logo"&gt;
      &lt;a href="https://github.com/AnnaVi11arrea1" rel="noopener noreferrer"&gt;
        AnnaVi11arrea1
      &lt;/a&gt; / &lt;a href="https://github.com/AnnaVi11arrea1/solar-dashboard" rel="noopener noreferrer"&gt;
        solar-dashboard
      &lt;/a&gt;
    &lt;/h2&gt;
    &lt;h3&gt;
      
    &lt;/h3&gt;
  &lt;/div&gt;
  &lt;div class="ltag-github-body"&gt;
    
&lt;div id="readme" class="md"&gt;&lt;div class="markdown-heading"&gt;
&lt;h1 class="heading-element"&gt;Solar dashboard&lt;/h1&gt;
&lt;/div&gt;
&lt;p&gt;Private dashboard for the home Enphase system: &lt;a href="https://solar-dashboard-coral.vercel.app" rel="nofollow noopener noreferrer"&gt;https://solar-dashboard-coral.vercel.app&lt;/a&gt;&lt;/p&gt;
&lt;div class="snippet-clipboard-content notranslate position-relative overflow-auto"&gt;&lt;pre class="notranslate"&gt;&lt;code&gt;Home PC ── collector/collector.mjs (every 60 s)
   ├─ Ollama (gemma3:4b, local) ── Dad's briefing every 6 h
   ├─ IQ Gateway (192.168.1.166, local API, owner token)
   └─ APC UPS (not yet — needs APC USB cable 940-0127)
        │ POST /api/ingest  (Bearer INGEST_SECRET)
        ▼
Vercel (Next.js 16) ── Upstash Redis ── dashboard page (DASHBOARD_PASSWORD)
        ▲
        └─ /api/cron/daily (Vercel Cron, 11:00 UTC) → Enphase cloud API v4
           summary + daily history, ~3 calls/day of the free 1,000/month
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;div class="markdown-heading"&gt;
&lt;h2 class="heading-element"&gt;Collector&lt;/h2&gt;
&lt;/div&gt;
&lt;p&gt;Runs from the Windows scheduled task &lt;strong&gt;Solar Dashboard Collector&lt;/strong&gt; (starts at logon, hidden
logs to &lt;code&gt;collector/collector.log&lt;/code&gt;).&lt;/p&gt;
&lt;div class="highlight highlight-source-shell notranslate position-relative overflow-auto js-code-highlight"&gt;
&lt;pre&gt;node collector/collector.mjs --once --print   &lt;span class="pl-c"&gt;&lt;span class="pl-c"&gt;#&lt;/span&gt; one reading, printed and pushed&lt;/span&gt;
node collector/collector.mjs --probe          &lt;span class="pl-c"&gt;&lt;span class="pl-c"&gt;#&lt;/span&gt; dump raw gateway JSON to collector/probe/&lt;/span&gt;&lt;/pre&gt;

&lt;/div&gt;
&lt;p&gt;Config is &lt;code&gt;collector/.env&lt;/code&gt; (template: &lt;code&gt;collector/.env.example&lt;/code&gt;). The gateway token expires
yearly — the dashboard's Problems list warns 30 days ahead. Renew at
&lt;a href="https://entrez.enphaseenergy.com" rel="nofollow noopener noreferrer"&gt;https://entrez.enphaseenergy.com&lt;/a&gt; using the &lt;strong&gt;homeowner&lt;/strong&gt; login (not…&lt;/p&gt;&lt;/div&gt;
  &lt;/div&gt;
  &lt;div class="gh-btn-container"&gt;&lt;a class="gh-btn" href="https://github.com/AnnaVi11arrea1/solar-dashboard" rel="noopener noreferrer"&gt;View on GitHub&lt;/a&gt;&lt;/div&gt;
&lt;/div&gt;






&lt;h2&gt;
  
  
  How I Built It
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Home PC ── collector (Node, every 60 s)
   ├─ Enphase IQ Gateway (local API, per-panel data)
   ├─ Home Assistant (smart plug wattage + rooms)
   └─ Ollama + Gemma 3 4B ── Dad's briefing every 6 h
        │
        ▼
Vercel (Next.js 16) ── Upstash Redis ── dashboard
        ▲
        └─ daily cron → Enphase cloud API (history)
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Next.js 16 on Vercel&lt;/strong&gt; for the dashboard, with &lt;strong&gt;Upstash Redis&lt;/strong&gt; for storage&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;A small Node collector on our home PC&lt;/strong&gt; that reads the Enphase gateway on the local network and Home Assistant every minute&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Enphase's cloud API&lt;/strong&gt; (the free developer tier) for years of history, &lt;strong&gt;ComEd's Green Button export&lt;/strong&gt; for the bills, and &lt;strong&gt;PVWatts&lt;/strong&gt; for expected production&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;The open-source AI piece is Gemma 3 4B running in Ollama on that same home PC.&lt;/strong&gt; A dashboard full of charts is great for me, but my dad doesn't want charts. He wants to know three things: is the solar working, why is the bill what it is, and does he need to do anything?&lt;/p&gt;

&lt;p&gt;Every six hours the collector asks the dashboard for a small set of facts (today's production versus expected, the latest bill versus last year, the Problems list), hands them to Gemma with instructions to explain them like a helpful family member, and posts the result back to the top of the dashboard. It takes about 20 seconds. Here's a real one:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Overall, your solar panels produced a good amount of electricity this month.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;The microinverters 202015011110 and 202101033131 are not reporting. You need to contact Enphase support immediately with the serial numbers to get these fixed under warranty.&lt;/li&gt;
&lt;li&gt;Your electric bill is $270.48, which is much higher than last year's $46.12. This is because your home uses 76 kWh per day, compared to 44 kWh per day last year.&lt;/li&gt;
&lt;li&gt;Solar panels covered 41% of your electricity use.&lt;/li&gt;
&lt;/ul&gt;
&lt;/blockquote&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%2Fmwhml9oavflawveqrvbz.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%2Fmwhml9oavflawveqrvbz.png" alt="Dad's Briefing" width="799" height="315"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Building the briefing turned up a bug, too. After sunset, the dashboard told us &lt;strong&gt;0 of 23 panels were working&lt;/strong&gt;, with 21 critical alerts. Microinverters run on the panels' own power, so after dark every one of them goes quiet, and the gateway's flags can't tell a dead unit from nightfall. If Gemma had read that, it would have told my dad his whole roof had died. Now a panel only counts as dead after a full day with no report.&lt;/p&gt;





&lt;h2&gt;
  
  
  Why Does Open Innovation Matter? 🤔
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;The data stays home.&lt;/strong&gt; The briefing is built from our electric bills, our usage, and the details of our system. With a local model, none of that goes to an AI company. It never leaves the PC in our house.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;It costs nothing to run.&lt;/strong&gt; A note every six hours, forever, for $0. A paid API would turn a family tool into a monthly bill, which is a little ironic for an app about lowering the bill.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;I can swap the model with one line.&lt;/strong&gt; It's &lt;code&gt;OLLAMA_MODEL=gemma3:4b&lt;/code&gt; in a config file. If a better small model comes out, or Dad wants it to sound different, I change one setting. No vendor, no migration.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Open data made the whole thing possible.&lt;/strong&gt; The solar gateway has a local API on our network, PVWatts is free public data, and the utility exports bills in an open format. Being able to combine them is what exposed the panel mix-up in the first place.&lt;/li&gt;
&lt;/ul&gt;





&lt;h2&gt;
  
  
  My Agent Session
&lt;/h2&gt;

&lt;p&gt;I built this with Claude Code, and every session is tracked with &lt;strong&gt;Entire&lt;/strong&gt;, which links each AI session to the commits it produced.&lt;/p&gt;





&lt;h2&gt;
  
  
  Prize Categories
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Best Use of Entire&lt;/strong&gt;: every AI session in this build is saved and linked to its commits. I can run &lt;code&gt;entire why&lt;/code&gt; on any line to see the prompt, session, and commit that produced it, and &lt;code&gt;entire search&lt;/code&gt; across the whole build process. This is actually very organic feeling, and awesome. Example:&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%2Fbx0c99hgflpgsdlyqt4x.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%2Fbx0c99hgflpgsdlyqt4x.png" alt="Navigating with Entire" width="800" height="613"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Best Use of Gemma&lt;/strong&gt;: Gemma 3 4B, running locally, is what turns the dashboard into something my dad can actually read. (Dad's Briefing) 😂&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>devchallenge</category>
      <category>weekendchallenge</category>
      <category>hf26challenge</category>
      <category>opensource</category>
    </item>
    <item>
      <title>🎵 Burnout: I wrote a song - "Bleeding Knuckles"</title>
      <dc:creator>Anna Villarreal</dc:creator>
      <pubDate>Wed, 30 Sep 2026 21:44:27 +0000</pubDate>
      <link>https://dev.to/annavi11arrea1/burnout-i-wrote-a-song-bleeding-knuckles-43fp</link>
      <guid>https://dev.to/annavi11arrea1/burnout-i-wrote-a-song-bleeding-knuckles-43fp</guid>
      <description>&lt;p&gt;I was seeing a lot of people writing about burnout on here recently. I feel for you. I was in a mood and started writing lyrics and drawing a cartoon. Here is the aftermath. I think if you can't feel anymore, maybe you will feel this song. Drawing and writing got me out of bed today, that's dramatic and I'll spare details. But you all know that feeling. &amp;lt;3&lt;/p&gt;

&lt;p&gt;Sound and Images are AI generated to compliment the lyrics.&lt;/p&gt;

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

&lt;h2&gt;
  
  
  Original Lyrics from me
&lt;/h2&gt;

&lt;p&gt;Stress, nausea, feet beneath me.&lt;br&gt;
Waiting uncertainly no failsafe, depleted&lt;br&gt;
Untested beyond tested&lt;br&gt;
Weathered all the way through&lt;br&gt;
Apathy, lack of happiness&lt;br&gt;
Quivering like a shrew&lt;br&gt;
Forced to sign up&lt;br&gt;
Kicked on the way out&lt;br&gt;
No one hears the silent screams&lt;br&gt;
Trapped in a cycle&lt;br&gt;
Full of doubt&lt;/p&gt;

&lt;p&gt;The screams are so deafening&lt;br&gt;
But only in my thoughts&lt;br&gt;
Every resource taken&lt;br&gt;
Methodically forsaken&lt;br&gt;
Burnt to a crisp&lt;br&gt;
Wondering in the mist&lt;br&gt;
What is this life for&lt;br&gt;
To suffer and be ignored&lt;br&gt;
Based on my own principles&lt;br&gt;
And listening to others&lt;br&gt;
Ive built walls around myself&lt;br&gt;
No way to go forward&lt;/p&gt;

&lt;p&gt;If champions are forged &lt;br&gt;
from years of blood&lt;br&gt;
Then I should find my diamond statue&lt;br&gt;
saluting me back in the face&lt;br&gt;
in the future&lt;br&gt;
Where have all the heros gone&lt;br&gt;
To bring peace with a simple song&lt;br&gt;
Heavy burdens lose their weight&lt;br&gt;
The solo fighter stomps out hate&lt;/p&gt;

&lt;p&gt;Where are the moments&lt;br&gt;
Of joy and happiness&lt;br&gt;
For anyone else&lt;br&gt;
I would never wish this&lt;br&gt;
Forgiveness and care&lt;br&gt;
Have gotten me nowhere&lt;br&gt;
If I cannot be selfish&lt;br&gt;
Then why do I feel alone&lt;br&gt;
The highs and lows&lt;br&gt;
Have reached a breaking point&lt;br&gt;
I cannot function&lt;br&gt;
I can't feel&lt;br&gt;
Debating if anything here is real&lt;/p&gt;

&lt;p&gt;Just a few tiny threads remain&lt;br&gt;
Before I hit the edge again&lt;br&gt;
I feel im falling quickly&lt;br&gt;
Some say that I cant see&lt;br&gt;
making me believe&lt;br&gt;
That my bleeding knuckles&lt;br&gt;
Are just for vanity&lt;br&gt;
Sacrificing health to please the weak&lt;br&gt;
I see the ground is still beneath me&lt;br&gt;
Panicked heart waterfall tears&lt;br&gt;
Dread appears as friend&lt;/p&gt;

&lt;p&gt;If champions are forged &lt;br&gt;
from years of blood&lt;br&gt;
Then I should find my diamond statue&lt;br&gt;
saluting me back in the face&lt;br&gt;
in the future&lt;br&gt;
Where have all the heros gone&lt;br&gt;
To bring peace with a simple song&lt;br&gt;
Heavy burdens lose their weight&lt;br&gt;
The solo fighter stomps out hate&lt;/p&gt;

</description>
      <category>ai</category>
      <category>mentalhealth</category>
      <category>music</category>
    </item>
    <item>
      <title>🛜 The Journey of Data Through a Wi-Fi 7 NIC</title>
      <dc:creator>Anna Villarreal</dc:creator>
      <pubDate>Wed, 30 Sep 2026 15:50:50 +0000</pubDate>
      <link>https://dev.to/annavi11arrea1/the-journey-of-data-through-a-wi-fi-7-nic-1982</link>
      <guid>https://dev.to/annavi11arrea1/the-journey-of-data-through-a-wi-fi-7-nic-1982</guid>
      <description>&lt;h3&gt;
  
  
  A Deep Investigation
&lt;/h3&gt;

&lt;p&gt;I've been sitting on this project for a while, I haven't shared it until now. While learning about networking and on one of my massive side tangents, I thought it would be really cool to have a detailed breakdown of what the data is doing at what stage in the process. No, not another OSI Model post, but the Wi-Fi 7 NIC exclusively. I wanted to know, "What is the process for the cutting edge Wi-Fi technology?" - something I have limited knowledge about.&lt;/p&gt;

&lt;p&gt;That's a VERY BIG question. 🫣 And it deserved a project. And man, I'm impressed with what I was able to get AI to combobulate for me. However, as a non-expert, I would greatly appreciate feedback from a weathered wizard here. Is this presented properly? (I'm looking at you, network people! &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%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fqmmzxowxkxh12ux1mupj.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%2Fqmmzxowxkxh12ux1mupj.png" alt="wifi through a room" width="800" height="418"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;This static page allows you to click around and learn about the different stages and views at different scales. I feel it offers some real understanding. It is interactive. Play with it! I am obsessed with the board view. I think it's pretty freaking awesome.&lt;/p&gt;

&lt;p&gt;👾&lt;/p&gt;





&lt;h3&gt;
  
  
  Investigate Further and Contribute:
&lt;/h3&gt;


&lt;div class="ltag-github-readme-tag"&gt;
  &lt;div class="readme-overview"&gt;
    &lt;h2&gt;
      &lt;img src="https://assets.dev.to/assets/github-logo-5a155e1f9a670af7944dd5e12375bc76ed542ea80224905ecaf878b9157cdefc.svg" alt="GitHub logo"&gt;
      &lt;a href="https://github.com/AnnaVi11arrea1" rel="noopener noreferrer"&gt;
        AnnaVi11arrea1
      &lt;/a&gt; / &lt;a href="https://github.com/AnnaVi11arrea1/wifi7_NIC_explorer" rel="noopener noreferrer"&gt;
        wifi7_NIC_explorer
      &lt;/a&gt;
    &lt;/h2&gt;
    &lt;h3&gt;
      To gain a better understanding of data flow through a Wi-Fi 7 card.
    &lt;/h3&gt;
  &lt;/div&gt;
  &lt;div class="ltag-github-body"&gt;
    
&lt;div id="readme" class="md"&gt;&lt;div class="markdown-heading"&gt;
&lt;h2 class="heading-element"&gt;Wi-Fi 7 Network Card Explorer&lt;/h2&gt;

&lt;/div&gt;
&lt;p&gt;Disclaimer - Contains no proprietry data, only publicly available information.&lt;/p&gt;
&lt;div class="markdown-heading"&gt;
&lt;h3 class="heading-element"&gt;Purpose&lt;/h3&gt;

&lt;/div&gt;
&lt;p&gt;To gain a deeper understanding of the entire flow of network data through a wifi-7 card.&lt;/p&gt;

&lt;div class="markdown-heading"&gt;
&lt;h3 class="heading-element"&gt;Content&lt;/h3&gt;

&lt;/div&gt;
&lt;p&gt;It is a static page with 3 tabbed views. The original tool was created quickly with AI prompts to get the idea started. It has some minor imperfections but is generally working.&lt;/p&gt;

&lt;p&gt;Tab 1 - Packet Flow
&lt;a rel="noopener noreferrer" href="https://private-user-images.githubusercontent.com/154029176/662281096-5b5e2290-ed4e-4b37-911b-5afc21bafdfc.png?jwt=eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJpc3MiOiJnaXRodWIuY29tIiwiYXVkIjoicmF3LmdpdGh1YnVzZXJjb250ZW50LmNvbSIsImtleSI6ImtleTUiLCJleHAiOjE3OTA3ODM3ODQsIm5iZiI6MTc5MDc4MzQ4NCwicGF0aCI6Ii8xNTQwMjkxNzYvNjYyMjgxMDk2LTViNWUyMjkwLWVkNGUtNGIzNy05MTFiLTVhZmMyMWJhZmRmYy5wbmc_WC1BbXotQWxnb3JpdGhtPUFXUzQtSE1BQy1TSEEyNTYmWC1BbXotQ3JlZGVudGlhbD1BS0lBVkNPRFlMU0E1M1BRSzRaQSUyRjIwMjYwOTMwJTJGdXMtZWFzdC0xJTJGczMlMkZhd3M0X3JlcXVlc3QmWC1BbXotRGF0ZT0yMDI2MDkzMFQxNTUxMjRaJlgtQW16LUV4cGlyZXM9MzAwJlgtQW16LVNpZ25hdHVyZT0zMmJkMjhmNDc3NTdiOWQ5Y2QwMTE1YmUzNmJiZmJmMzAzZWIzNjg4NjQyMzg3NGMwZWY2ZmY2YTQ4NzgwYmVlJlgtQW16LVNpZ25lZEhlYWRlcnM9aG9zdCZyZXNwb25zZS1jb250ZW50LXR5cGU9aW1hZ2UlMkZwbmcifQ.JgHNA4Y-cLs2d6C12WHVGAvBoDQqKxqrSSPspxskEE8"&gt;&lt;img width="1600" height="844" alt="Screenshot 2026-09-30 100057" src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fprivate-user-images.githubusercontent.com%2F154029176%2F662281096-5b5e2290-ed4e-4b37-911b-5afc21bafdfc.png%3Fjwt%3DeyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJpc3MiOiJnaXRodWIuY29tIiwiYXVkIjoicmF3LmdpdGh1YnVzZXJjb250ZW50LmNvbSIsImtleSI6ImtleTUiLCJleHAiOjE3OTA3ODM3ODQsIm5iZiI6MTc5MDc4MzQ4NCwicGF0aCI6Ii8xNTQwMjkxNzYvNjYyMjgxMDk2LTViNWUyMjkwLWVkNGUtNGIzNy05MTFiLTVhZmMyMWJhZmRmYy5wbmc_WC1BbXotQWxnb3JpdGhtPUFXUzQtSE1BQy1TSEEyNTYmWC1BbXotQ3JlZGVudGlhbD1BS0lBVkNPRFlMU0E1M1BRSzRaQSUyRjIwMjYwOTMwJTJGdXMtZWFzdC0xJTJGczMlMkZhd3M0X3JlcXVlc3QmWC1BbXotRGF0ZT0yMDI2MDkzMFQxNTUxMjRaJlgtQW16LUV4cGlyZXM9MzAwJlgtQW16LVNpZ25hdHVyZT0zMmJkMjhmNDc3NTdiOWQ5Y2QwMTE1YmUzNmJiZmJmMzAzZWIzNjg4NjQyMzg3NGMwZWY2ZmY2YTQ4NzgwYmVlJlgtQW16LVNpZ25lZEhlYWRlcnM9aG9zdCZyZXNwb25zZS1jb250ZW50LXR5cGU9aW1hZ2UlMkZwbmcifQ.JgHNA4Y-cLs2d6C12WHVGAvBoDQqKxqrSSPspxskEE8" class="js-gh-image-fallback"&gt;&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;Tab 2 - Board View (The cool tab, honestly)
&lt;a rel="noopener noreferrer" href="https://private-user-images.githubusercontent.com/154029176/662280587-99c97cc5-52ba-471b-bed1-3184a195d32f.png?jwt=eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJpc3MiOiJnaXRodWIuY29tIiwiYXVkIjoicmF3LmdpdGh1YnVzZXJjb250ZW50LmNvbSIsImtleSI6ImtleTUiLCJleHAiOjE3OTA3ODM3ODQsIm5iZiI6MTc5MDc4MzQ4NCwicGF0aCI6Ii8xNTQwMjkxNzYvNjYyMjgwNTg3LTk5Yzk3Y2M1LTUyYmEtNDcxYi1iZWQxLTMxODRhMTk1ZDMyZi5wbmc_WC1BbXotQWxnb3JpdGhtPUFXUzQtSE1BQy1TSEEyNTYmWC1BbXotQ3JlZGVudGlhbD1BS0lBVkNPRFlMU0E1M1BRSzRaQSUyRjIwMjYwOTMwJTJGdXMtZWFzdC0xJTJGczMlMkZhd3M0X3JlcXVlc3QmWC1BbXotRGF0ZT0yMDI2MDkzMFQxNTUxMjRaJlgtQW16LUV4cGlyZXM9MzAwJlgtQW16LVNpZ25hdHVyZT1lMTA3OWNkMGEzOTVhYTQ2NjNiNGQ5NWFjM2E3MzMxMDY2NDUzN2I1ZjIxYWM0ZDEwNGE2ODJkMTdhYWY1MzdhJlgtQW16LVNpZ25lZEhlYWRlcnM9aG9zdCZyZXNwb25zZS1jb250ZW50LXR5cGU9aW1hZ2UlMkZwbmcifQ.xra8Aey8_wQlezLobN5QRejcd4z8RLzTLN6yAkvJhh4"&gt;&lt;img width="1615" height="842" alt="Screenshot 2026-09-30 100019" src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fprivate-user-images.githubusercontent.com%2F154029176%2F662280587-99c97cc5-52ba-471b-bed1-3184a195d32f.png%3Fjwt%3DeyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJpc3MiOiJnaXRodWIuY29tIiwiYXVkIjoicmF3LmdpdGh1YnVzZXJjb250ZW50LmNvbSIsImtleSI6ImtleTUiLCJleHAiOjE3OTA3ODM3ODQsIm5iZiI6MTc5MDc4MzQ4NCwicGF0aCI6Ii8xNTQwMjkxNzYvNjYyMjgwNTg3LTk5Yzk3Y2M1LTUyYmEtNDcxYi1iZWQxLTMxODRhMTk1ZDMyZi5wbmc_WC1BbXotQWxnb3JpdGhtPUFXUzQtSE1BQy1TSEEyNTYmWC1BbXotQ3JlZGVudGlhbD1BS0lBVkNPRFlMU0E1M1BRSzRaQSUyRjIwMjYwOTMwJTJGdXMtZWFzdC0xJTJGczMlMkZhd3M0X3JlcXVlc3QmWC1BbXotRGF0ZT0yMDI2MDkzMFQxNTUxMjRaJlgtQW16LUV4cGlyZXM9MzAwJlgtQW16LVNpZ25hdHVyZT1lMTA3OWNkMGEzOTVhYTQ2NjNiNGQ5NWFjM2E3MzMxMDY2NDUzN2I1ZjIxYWM0ZDEwNGE2ODJkMTdhYWY1MzdhJlgtQW16LVNpZ25lZEhlYWRlcnM9aG9zdCZyZXNwb25zZS1jb250ZW50LXR5cGU9aW1hZ2UlMkZwbmcifQ.xra8Aey8_wQlezLobN5QRejcd4z8RLzTLN6yAkvJhh4" class="js-gh-image-fallback"&gt;&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;Tab 3 - Scale Ladder, high level over view from what happens at a room level, and all the way down to a logic view. Cool Stuff.
&lt;a rel="noopener noreferrer" href="https://private-user-images.githubusercontent.com/154029176/662280179-399230b1-7ae9-40be-b19e-729d3674d262.png?jwt=eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJpc3MiOiJnaXRodWIuY29tIiwiYXVkIjoicmF3LmdpdGh1YnVzZXJjb250ZW50LmNvbSIsImtleSI6ImtleTUiLCJleHAiOjE3OTA3ODM3ODQsIm5iZiI6MTc5MDc4MzQ4NCwicGF0aCI6Ii8xNTQwMjkxNzYvNjYyMjgwMTc5LTM5OTIzMGIxLTdhZTktNDBiZS1iMTllLTcyOWQzNjc0ZDI2Mi5wbmc_WC1BbXotQWxnb3JpdGhtPUFXUzQtSE1BQy1TSEEyNTYmWC1BbXotQ3JlZGVudGlhbD1BS0lBVkNPRFlMU0E1M1BRSzRaQSUyRjIwMjYwOTMwJTJGdXMtZWFzdC0xJTJGczMlMkZhd3M0X3JlcXVlc3QmWC1BbXotRGF0ZT0yMDI2MDkzMFQxNTUxMjRaJlgtQW16LUV4cGlyZXM9MzAwJlgtQW16LVNpZ25hdHVyZT1hY2Y2NzQwNjY1NGQxZDc3ZTcyM2JhOGZjMTg3Y2U1YjRmNjQwNzNlNGZhMTYyMjQwNTJkOWIxZTk2MzQ4NWEwJlgtQW16LVNpZ25lZEhlYWRlcnM9aG9zdCZyZXNwb25zZS1jb250ZW50LXR5cGU9aW1hZ2UlMkZwbmcifQ.LaoEfIQcBtB4ZN3c3LTx5DdOxv5x4_lJh83cx_olwMw"&gt;&lt;img width="1615" height="844" alt="Screenshot 2026-09-30 095936" src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fprivate-user-images.githubusercontent.com%2F154029176%2F662280179-399230b1-7ae9-40be-b19e-729d3674d262.png%3Fjwt%3DeyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJpc3MiOiJnaXRodWIuY29tIiwiYXVkIjoicmF3LmdpdGh1YnVzZXJjb250ZW50LmNvbSIsImtleSI6ImtleTUiLCJleHAiOjE3OTA3ODM3ODQsIm5iZiI6MTc5MDc4MzQ4NCwicGF0aCI6Ii8xNTQwMjkxNzYvNjYyMjgwMTc5LTM5OTIzMGIxLTdhZTktNDBiZS1iMTllLTcyOWQzNjc0ZDI2Mi5wbmc_WC1BbXotQWxnb3JpdGhtPUFXUzQtSE1BQy1TSEEyNTYmWC1BbXotQ3JlZGVudGlhbD1BS0lBVkNPRFlMU0E1M1BRSzRaQSUyRjIwMjYwOTMwJTJGdXMtZWFzdC0xJTJGczMlMkZhd3M0X3JlcXVlc3QmWC1BbXotRGF0ZT0yMDI2MDkzMFQxNTUxMjRaJlgtQW16LUV4cGlyZXM9MzAwJlgtQW16LVNpZ25hdHVyZT1hY2Y2NzQwNjY1NGQxZDc3ZTcyM2JhOGZjMTg3Y2U1YjRmNjQwNzNlNGZhMTYyMjQwNTJkOWIxZTk2MzQ4NWEwJlgtQW16LVNpZ25lZEhlYWRlcnM9aG9zdCZyZXNwb25zZS1jb250ZW50LXR5cGU9aW1hZ2UlMkZwbmcifQ.LaoEfIQcBtB4ZN3c3LTx5DdOxv5x4_lJh83cx_olwMw" class="js-gh-image-fallback"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;div class="markdown-heading"&gt;
&lt;h3 class="heading-element"&gt;What needs to be done:&lt;/h3&gt;

&lt;/div&gt;
&lt;ul&gt;
&lt;li&gt;I'd prefer the styling in a separate file.&lt;/li&gt;
&lt;li&gt;Look for and simplify redundant code.&lt;/li&gt;
&lt;li&gt;Some of the graphics are slightly out of place, minor, but noticable.&lt;/li&gt;
&lt;li&gt;Open to ideas!&lt;/li&gt;
&lt;/ul&gt;
&lt;div class="markdown-heading"&gt;
&lt;h3 class="heading-element"&gt;Contributing&lt;/h3&gt;

&lt;/div&gt;
&lt;ul&gt;
&lt;li&gt;Fork and make a pull request&lt;/li&gt;
&lt;/ul&gt;
&lt;/div&gt;



&lt;/div&gt;
&lt;br&gt;
  &lt;div class="gh-btn-container"&gt;&lt;a class="gh-btn" href="https://github.com/AnnaVi11arrea1/wifi7_NIC_explorer" rel="noopener noreferrer"&gt;View on GitHub&lt;/a&gt;&lt;/div&gt;
&lt;br&gt;
&lt;/div&gt;
&lt;br&gt;


</description>
      <category>wifi7</category>
      <category>opensource</category>
      <category>learning</category>
      <category>html</category>
    </item>
    <item>
      <title>🎡 Social Ferris Wheel: Sanity is the Hub</title>
      <dc:creator>Anna Villarreal</dc:creator>
      <pubDate>Wed, 30 Sep 2026 01:25:29 +0000</pubDate>
      <link>https://dev.to/annavi11arrea1/social-ferris-wheel-sanity-is-the-hub-2m7j</link>
      <guid>https://dev.to/annavi11arrea1/social-ferris-wheel-sanity-is-the-hub-2m7j</guid>
      <description>&lt;p&gt;&lt;em&gt;This is a submission for the &lt;a href="https://dev.to/challenges/sanity-2026-09-16"&gt;Sanity Challenge, Path Two: Vibe-Code Something Strange&lt;/a&gt;&lt;/em&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%2F071vryv4acsy7bve8zk0.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%2F071vryv4acsy7bve8zk0.png" alt="divider" width="793" height="35"&gt;&lt;/a&gt;&lt;/p&gt;

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

&lt;p&gt;TLDR: Entrepreneur artist vibe codes her way to workflow optimization by consolidating all business social media posts through a single sanity content lake. &lt;/p&gt;

&lt;p&gt;Why: Social media management is a huge task. It needs to be simplified. If you are a party of one like myself, you need all the optimization you can get. &lt;/p&gt;

&lt;p&gt;I always thought it would be cool to have a single place to draft and send posts to all my social media platforms at once for my business. But I wanted to hold onto my final approval, to make sure a bot doesn't do something insane. We want to keep some sanity here. 😅&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%2Fhkngoltjqo30myxctxyi.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%2Fhkngoltjqo30myxctxyi.png" alt="divider" width="793" height="35"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  The Build:
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;A customized Sanity Studio&lt;/li&gt;
&lt;li&gt;A Claude agent that drafts seasonal social posts by reading the shop and a Knowledge Base over Sanity Context MCP&lt;/li&gt;
&lt;li&gt;A Node publisher that serves a human review queue&lt;/li&gt;
An App SDK app that reviews them live 
&lt;/ul&gt;

&lt;p&gt;All of it is built around my existing Rails storefront at everfluorescent.com. Nothing posts to a platform yet, and that is deliberate. The first half is the story of getting here, including the parts that cost me days; the second half is how the system works.&lt;/p&gt;





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

&lt;p&gt;  &lt;iframe src="https://www.youtube.com/embed/0VbrUZHQYm8" width="710" height="399"&gt;
  &lt;/iframe&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%2F86sevpny49el5s0szy7t.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%2F86sevpny49el5s0szy7t.png" alt="divider" width="793" height="35"&gt;&lt;/a&gt;&lt;/p&gt;

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

&lt;h3&gt;
  
  
  New Additions:
&lt;/h3&gt;


&lt;div class="ltag-github-readme-tag"&gt;
  &lt;div class="readme-overview"&gt;
    &lt;h2&gt;
      &lt;img src="https://assets.dev.to/assets/github-logo-5a155e1f9a670af7944dd5e12375bc76ed542ea80224905ecaf878b9157cdefc.svg" alt="GitHub logo"&gt;
      &lt;a href="https://github.com/AnnaVi11arrea1" rel="noopener noreferrer"&gt;
        AnnaVi11arrea1
      &lt;/a&gt; / &lt;a href="https://github.com/AnnaVi11arrea1/everfluorescent-desk" rel="noopener noreferrer"&gt;
        everfluorescent-desk
      &lt;/a&gt;
    &lt;/h2&gt;
    &lt;h3&gt;
      
    &lt;/h3&gt;
  &lt;/div&gt;
  &lt;div class="ltag-github-body"&gt;
    
&lt;div id="readme" class="md"&gt;&lt;div class="markdown-heading"&gt;
&lt;h1 class="heading-element"&gt;Review desk — a Sanity App SDK app&lt;/h1&gt;
&lt;/div&gt;
&lt;p&gt;A triage queue for social posts that an agent drafted and a person has to decide
on. Built with the &lt;a href="https://www.sanity.io/docs/app-sdk" rel="nofollow noopener noreferrer"&gt;Sanity App SDK&lt;/a&gt;, it runs
in the Sanity Dashboard beside the Studio for project &lt;code&gt;70komvgl&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;It approves. It does not post.&lt;/strong&gt; Approving sets &lt;code&gt;variant.status&lt;/code&gt; to &lt;code&gt;approved&lt;/code&gt;
which is the state the publish worker reads. Nothing here talks to Instagram.&lt;/p&gt;
&lt;div class="markdown-heading"&gt;
&lt;h2 class="heading-element"&gt;Why an app and not another Studio pane&lt;/h2&gt;
&lt;/div&gt;
&lt;p&gt;The Studio edits one document at a time, and a review queue is the opposite
shape. The thing that needs a decision is &lt;strong&gt;one platform variant&lt;/strong&gt;, and a post
holds several — a Halloween post has an Instagram variant and a Facebook variant
with different captions, different assets, and independent verdicts.&lt;/p&gt;
&lt;p&gt;So this lists variants across every post, each with the gate's verdict under it
and the buttons that move it…&lt;/p&gt;&lt;/div&gt;
  &lt;/div&gt;
  &lt;div class="gh-btn-container"&gt;&lt;a class="gh-btn" href="https://github.com/AnnaVi11arrea1/everfluorescent-desk" rel="noopener noreferrer"&gt;View on GitHub&lt;/a&gt;&lt;/div&gt;
&lt;/div&gt;



&lt;div class="ltag-github-readme-tag"&gt;
  &lt;div class="readme-overview"&gt;
    &lt;h2&gt;
      &lt;img src="https://assets.dev.to/assets/github-logo-5a155e1f9a670af7944dd5e12375bc76ed542ea80224905ecaf878b9157cdefc.svg" alt="GitHub logo"&gt;
      &lt;a href="https://github.com/AnnaVi11arrea1" rel="noopener noreferrer"&gt;
        AnnaVi11arrea1
      &lt;/a&gt; / &lt;a href="https://github.com/AnnaVi11arrea1/everfluorescent-cms" rel="noopener noreferrer"&gt;
        everfluorescent-cms
      &lt;/a&gt;
    &lt;/h2&gt;
    &lt;h3&gt;
      
    &lt;/h3&gt;
  &lt;/div&gt;
  &lt;div class="ltag-github-body"&gt;
    
&lt;div id="readme" class="md"&gt;&lt;div class="markdown-heading"&gt;
&lt;h1 class="heading-element"&gt;Ever Fluorescent — Content Desk&lt;/h1&gt;
&lt;/div&gt;
&lt;p&gt;A Sanity Studio where one post is written once and rendered per platform, with
a preview pane that draws from the same rules that guard the publish button —
and a nightly job that pulls the store catalog in on its own.&lt;/p&gt;
&lt;p&gt;Architecture write-up: &lt;a href="https://claude.ai/artifact/NXcVq3ZiwfBxrF4zRBBP1j" rel="nofollow noopener noreferrer"&gt;https://claude.ai/artifact/NXcVq3ZiwfBxrF4zRBBP1j&lt;/a&gt;&lt;/p&gt;
&lt;div class="markdown-heading"&gt;
&lt;h2 class="heading-element"&gt;What is here&lt;/h2&gt;
&lt;/div&gt;
&lt;div class="snippet-clipboard-content notranslate position-relative overflow-auto"&gt;&lt;pre class="notranslate"&gt;&lt;code&gt;schemaTypes/         the nine document types, plus the variant object
lib/platformSpec.ts  what each platform accepts — the single source of truth
lib/preflight.ts     the checks that stand between a variant and the queue
components/          the preview pane, the four platform mock-ups, and the
                     live sync console
actions/             the publish button (it approves; it does not post)
tools/obsidian-sync/ one-way sync from one vault folder into knowledge notes
functions/           code Sanity runs on its own — the catalog sync and its
                     nightly trigger
sanity.blueprint.ts  what to run and when
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;The rule that holds it together: &lt;strong&gt;&lt;code&gt;lib/platformSpec.ts&lt;/code&gt; is the only place a&lt;/strong&gt;…&lt;/p&gt;&lt;/div&gt;
  &lt;/div&gt;
  &lt;div class="gh-btn-container"&gt;&lt;a class="gh-btn" href="https://github.com/AnnaVi11arrea1/everfluorescent-cms" rel="noopener noreferrer"&gt;View on GitHub&lt;/a&gt;&lt;/div&gt;
&lt;/div&gt;


&lt;h3&gt;
  
  
  Pre-existing Store:
&lt;/h3&gt;


&lt;div class="ltag-github-readme-tag"&gt;
  &lt;div class="readme-overview"&gt;
    &lt;h2&gt;
      &lt;img src="https://assets.dev.to/assets/github-logo-5a155e1f9a670af7944dd5e12375bc76ed542ea80224905ecaf878b9157cdefc.svg" alt="GitHub logo"&gt;
      &lt;a href="https://github.com/AnnaVi11arrea1" rel="noopener noreferrer"&gt;
        AnnaVi11arrea1
      &lt;/a&gt; / &lt;a href="https://github.com/AnnaVi11arrea1/neoncart" rel="noopener noreferrer"&gt;
        neoncart
      &lt;/a&gt;
    &lt;/h2&gt;
    &lt;h3&gt;
      
    &lt;/h3&gt;
  &lt;/div&gt;
  &lt;div class="ltag-github-body"&gt;
    
&lt;div id="readme" class="md"&gt;&lt;div class="markdown-heading"&gt;
&lt;h1 class="heading-element"&gt;Neoncart — self-hosted store for Ever Fluorescent&lt;/h1&gt;
&lt;/div&gt;
&lt;p&gt;Rails 7.1 ecommerce app: Stripe Checkout (card / Google Pay / Apple Pay)
dropshipper integrations (Printify wired end-to-end; ThisNew, ArtsAdd
Yoycol via configurable adapters + manual queue), branded shipping
emails with free tracking links, support ticket desk, and a partner
REST API + signed webhooks for FestConnect / goVend.&lt;/p&gt;
&lt;p&gt;No paid add-on services: jobs run on GoodJob (Postgres — your Neon DB),
images on local disk via Active Storage, tracking via free carrier links,
email over plain SMTP.&lt;/p&gt;
&lt;div class="markdown-heading"&gt;
&lt;h2 class="heading-element"&gt;Stack&lt;/h2&gt;

&lt;/div&gt;
&lt;p&gt;Rails 7.1 · PostgreSQL (Neon) · Puma · Hotwire (importmap, no Node) ·
Devise · Stripe · GoodJob · Faraday&lt;/p&gt;
&lt;div class="markdown-heading"&gt;
&lt;h2 class="heading-element"&gt;First-time setup&lt;/h2&gt;

&lt;/div&gt;
&lt;div class="highlight highlight-source-shell notranslate position-relative overflow-auto js-code-highlight"&gt;
&lt;pre&gt;sudo apt install libvips            &lt;span class="pl-c"&gt;&lt;span class="pl-c"&gt;#&lt;/span&gt; image variants&lt;/span&gt;
bundle install
cp .env.example .env                &lt;span class="pl-c"&gt;&lt;span class="pl-c"&gt;#&lt;/span&gt; then fill it in&lt;/span&gt;
bin/setup                           &lt;span class="pl-c"&gt;&lt;span class="pl-c"&gt;#&lt;/span&gt; installs GoodJob tables, migrates, seeds&lt;/span&gt;
bin/rails s&lt;/pre&gt;

&lt;/div&gt;
&lt;p&gt;Seeds create the admin user from &lt;code&gt;ADMIN_EMAIL&lt;/code&gt; / &lt;code&gt;ADMIN_PASSWORD&lt;/code&gt;, four
suppliers (Printify auto, the other…&lt;/p&gt;&lt;/div&gt;
  &lt;/div&gt;
  &lt;div class="gh-btn-container"&gt;&lt;a class="gh-btn" href="https://github.com/AnnaVi11arrea1/neoncart" rel="noopener noreferrer"&gt;View on GitHub&lt;/a&gt;&lt;/div&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%2Fejfxw5i3978mii3g8aek.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%2Fejfxw5i3978mii3g8aek.png" alt="divider" width="793" height="35"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  My Build Process
&lt;/h2&gt;

&lt;p&gt;I built this project from a terminal using Claude CLI. When Claude started to irritate me, Github Copilot CLI came to the rescue once. Ok, maybe I opened Vim, like 10 times. I prefer to not pass my api keys around if possible. Rather do that myself.&lt;/p&gt;

&lt;p&gt;I kept notes as I worked, I could tell this was an undertaking. &lt;/p&gt;

&lt;h3&gt;
  
  
  Here is a brief timeline:
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Up to 9/23: the store side.&lt;/strong&gt; The Phase 0 Studio was in place: schema, per-platform previews, preflight checks, a publish gate, Obsidian sync and catalog sync. On the neoncart side, four PRs merged: a product id and gallery endpoint, the Retro post search archive, the admin approve queue, and follow-up fixes. I also built a publisher tool into my admin dashboard, so I can preview business posts and publish them from inside my own store; it pulls from Sanity and saves published posts for reuse or modification later. I ran the migration, restarted the Jetson services, and both new admin tabs loaded.&lt;/li&gt;
&lt;/ul&gt;





&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;9/24: reconnected the shop to Sanity through Vercel.&lt;/strong&gt;&lt;/li&gt;
&lt;/ul&gt;





&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;9/26: the agent.&lt;/strong&gt; With the shop connected and posts populating in the approval area, it was time to hand the heavy lifting to an agent. generate.mjs already drafted campaign posts at needs_review from a template (captionFor). The agent replaces that template and the rest of the pipeline stays as it was. Its drafts now reach the admin queue, and the review card shows where sources disagreed and what each caption was based on.&lt;/li&gt;
&lt;/ul&gt;





&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;9/27: refinement.&lt;/strong&gt; I collected upcoming events near my customers, plus holidays and seasons, into one file so posts could be scoped to the time of year. I wrote a longer prompt that also records timings, which is where the caching numbers below come from. That was also the day I found stale data coming out of Sanity and finally traced it to the sync (details in the next section). The re-run wrote five clean Halloween drafts.&lt;/li&gt;
&lt;/ul&gt;





&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Last: the review desk.&lt;/strong&gt; An App SDK app (everfluorescent-desk) lists variants across every post with the gate's verdict, and Approve / Back-to-draft write variant.status directly. It type-checks and builds, but I have not yet rendered it against my live data. I expect two things when I do: every card will say no knowledge notes were cited, and if no platformAccount document exists everything will show Blocked. Both would be real findings, not app bugs.&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%2Fbob9r032hjwtietxhmgj.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%2Fbob9r032hjwtietxhmgj.png" alt="no knowledge notes added" width="800" height="606"&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%2Fjjnlnto5w9vhpiftkgd0.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%2Fjjnlnto5w9vhpiftkgd0.png" alt="divider" width="793" height="35"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Time-Costing Traps 🥅
&lt;/h3&gt;

&lt;ol&gt;
&lt;li&gt;Turn the MCP feature on before creating the endpoint. You cannot see the dashboard page that creates it until the feature is enabled. If you give your project a token before that, you have to delete it and create a new one with Context Viewer access.&lt;/li&gt;
&lt;li&gt;Build the Knowledge Base entries after adding them. I built mine from the website and uploaded files once an Anthropic API key was in place. Entries that are added but not built are invisible to the agent, and this step is easy to miss.&lt;/li&gt;
&lt;li&gt;The sync has to run from my local computer. Changes to the knowledge base only reached my server after I ran the sync locally. What gave it away was a last-sync date five days old. Files also had to be added to the sync list or they were never pulled in, so the agent kept getting old or incomplete data. After fixing that I had to rebuild the knowledge base again and redeploy Sanity. Files that are missing can also be archived.&lt;/li&gt;
&lt;li&gt;The agent cannot see posts. The Context endpoint does not expose them, so my first &lt;code&gt;--write&lt;/code&gt; run proposed two products that already had template drafts from the Fall campaign. Both were correctly skipped, but that run's $0.49 was wasted. The agent is now told up front which products already have posts.&lt;/li&gt;
&lt;li&gt;Claude caught its own bug in the review desk. The projection selects only some fields of each variant, so writing that array back would have silently destroyed the assets. It found the fix (&lt;code&gt;useEditDocument&lt;/code&gt; with no path and a functional update) in the SDK docs rather than guessing.&lt;/li&gt;
&lt;/ol&gt;





&lt;p&gt;Claude Catches Itself:&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%2F52ydtyr09omin2o27ai1.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%2F52ydtyr09omin2o27ai1.png" alt="Claude is guilty" width="770" height="487"&gt;&lt;/a&gt;&lt;/p&gt;
Claude CLI confirms neatly stored data



&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%2Fs44pgtpapil4jypa9x0w.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%2Fs44pgtpapil4jypa9x0w.png" alt="divider" width="793" height="35"&gt;&lt;/a&gt;&lt;/p&gt;
&lt;h3&gt;
  
  
  Bad Data Exposed
&lt;/h3&gt;

&lt;p&gt;My sources were disagreeing with themselves. Sanity can be used for quality control.&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%2Fry1kqsn76a7y8u3fxb9p.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%2Fry1kqsn76a7y8u3fxb9p.png" alt="Discrepencies explained" width="800" height="581"&gt;&lt;/a&gt;&lt;/p&gt;




&lt;h3&gt;
  
  
  Early design questions
&lt;/h3&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
  &lt;thead&gt;
    &lt;tr&gt;
      &lt;th&gt;Challenge&lt;/th&gt;
      &lt;th&gt;How it was overcome&lt;/th&gt;
    &lt;/tr&gt;
  &lt;/thead&gt;
  &lt;tbody&gt;
    &lt;tr&gt;
      &lt;td&gt;Where does content live: Sanity, Obsidian or the store?&lt;/td&gt;
      &lt;td&gt;Obsidian stays my writing surface, with one-way sync into Sanity. Paintings mirror from the store instead of moving into Sanity.&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;Where does publishing actually run?&lt;/td&gt;
      &lt;td&gt;The publish worker runs on Vercel, not inside the neoncart store app.&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;Where does post approval happen?&lt;/td&gt;
      &lt;td&gt;Moved out of the Studio into my own store admin, with published posts archived in neoncart's database (the Retro post search tab).&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;Products with missing data (no price, no image)&lt;/td&gt;
      &lt;td&gt;Saved but flagged and blocked from posting, not silently skipped. Title, price and image are hard blocks; description and category only warn.&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;Lost catalog schema design doc&lt;/td&gt;
      &lt;td&gt;One salvaged copy survives (docs/catalog-schema-design.SALVAGE.md). Still open whether to rewrite it properly.&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;Per-platform post length and media limits (platformSpec.ts)&lt;/td&gt;
      &lt;td&gt;Found to be partly wrong against the real docs, so I am correcting them against the Instagram, Facebook, TikTok and LinkedIn developer documentation. Facebook's rate-limit documentation in particular is not good.&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;neoncart has no CI or tests, and deploy is manual on my Jetson&lt;/td&gt;
      &lt;td&gt;Every merged PR needs a git pull, rails db:migrate and a service restart by hand. Nothing auto-deploys.&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;New admin tabs not appearing after a merge&lt;/td&gt;
      &lt;td&gt;A stale running server: the migration alone does not restart Puma or the worker. Fixed with systemctl restart.&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%2Fs44pgtpapil4jypa9x0w.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%2Fs44pgtpapil4jypa9x0w.png" alt="divider" width="793" height="35"&gt;&lt;/a&gt;&lt;/p&gt;
&lt;h3&gt;
  
  
  Major Side Tangent
&lt;/h3&gt;
&lt;h4&gt;
  
  
  Prompt caching is a money saver. 💰
&lt;/h4&gt;

&lt;p&gt;I stumbled into prompt caching along the way, and it could arguably be a post of its own. &lt;/p&gt;

&lt;p&gt;Two cache points:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;one on the instructions and tool definitions, which do not change during a run.&lt;/li&gt;
&lt;li&gt;One that moves to the end of the conversation each turn so every turn reuses everything before it. 
The run summary line prints cache reads and writes with a dollar estimate, so I can see what each run costs.&lt;/li&gt;
&lt;/ul&gt;
&lt;h4&gt;
  
  
  Cost
&lt;/h4&gt;

&lt;p&gt;The same three-draft run went from about $1.50 to about $0.39. Across six runs I spent $4.34, against $5.89 without caching. Priced again with identical tokens and no caching, each cached run saved between 47% and 62%, and the saving grew with the number of turns.&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
  &lt;thead&gt;
    &lt;tr&gt;
      &lt;th&gt;Request&lt;/th&gt;
      &lt;th&gt;Time&lt;/th&gt;
      &lt;th&gt;Cache write&lt;/th&gt;
      &lt;th&gt;Cache read&lt;/th&gt;
      &lt;th&gt;Uncached input&lt;/th&gt;
      &lt;th&gt;Output&lt;/th&gt;
    &lt;/tr&gt;
  &lt;/thead&gt;
  &lt;tbody&gt;
    &lt;tr&gt;
      &lt;td&gt;1&lt;/td&gt;
      &lt;td&gt;3.1s&lt;/td&gt;
      &lt;td&gt;10,149&lt;/td&gt;
      &lt;td&gt;0&lt;/td&gt;
      &lt;td&gt;2&lt;/td&gt;
      &lt;td&gt;72&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;2&lt;/td&gt;
      &lt;td&gt;6.9s&lt;/td&gt;
      &lt;td&gt;4,223&lt;/td&gt;
      &lt;td&gt;10,149&lt;/td&gt;
      &lt;td&gt;2&lt;/td&gt;
      &lt;td&gt;608&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;3&lt;/td&gt;
      &lt;td&gt;6.8s&lt;/td&gt;
      &lt;td&gt;2,904&lt;/td&gt;
      &lt;td&gt;14,372&lt;/td&gt;
      &lt;td&gt;2&lt;/td&gt;
      &lt;td&gt;620&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;4&lt;/td&gt;
      &lt;td&gt;5.9s&lt;/td&gt;
      &lt;td&gt;8,034&lt;/td&gt;
      &lt;td&gt;17,276&lt;/td&gt;
      &lt;td&gt;2&lt;/td&gt;
      &lt;td&gt;473&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;5&lt;/td&gt;
      &lt;td&gt;20.0s&lt;/td&gt;
      &lt;td&gt;13,687&lt;/td&gt;
      &lt;td&gt;25,310&lt;/td&gt;
      &lt;td&gt;2&lt;/td&gt;
      &lt;td&gt;1,638&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;6&lt;/td&gt;
      &lt;td&gt;95.5s&lt;/td&gt;
      &lt;td&gt;11,125&lt;/td&gt;
      &lt;td&gt;38,997&lt;/td&gt;
      &lt;td&gt;2&lt;/td&gt;
      &lt;td&gt;8,863&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;Total&lt;/td&gt;
      &lt;td&gt;&lt;/td&gt;
      &lt;td&gt;50,122&lt;/td&gt;
      &lt;td&gt;106,104&lt;/td&gt;
      &lt;td&gt;12&lt;/td&gt;
      &lt;td&gt;12,274&lt;/td&gt;
    &lt;/tr&gt;
  &lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;Input and output are in tokens. After the first request, everything before it was read from the cache, and only 12 input tokens in the whole run were billed at the full rate. Without caching that run would have cost about $1.09, so caching saved about $0.41, or 38%.&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%2F9elqll7lsdfe0eztrx0n.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%2F9elqll7lsdfe0eztrx0n.png" alt="divider" width="793" height="35"&gt;&lt;/a&gt;&lt;/p&gt;
&lt;h3&gt;
  
  
  Drafts that admit where sources disagree 🎭
&lt;/h3&gt;

&lt;p&gt;Disclaimer: I can not correct all my data sources in a timely manner, so I've chosen to highlight this as a feature. Definitely not user error at all. 😅&lt;/p&gt;

&lt;p&gt;The 9/27 re-run looked eight weeks ahead, excluded the four products that already had a post, and wrote five Halloween drafts with none skipped: six turns, about $0.58. Where two sources contradicted each other, the agent left the disputed claim out of the caption and recorded the conflict instead:&lt;br&gt;
• UV-reactivity. The Knowledge Base lists the hoodies, joggers and sweatpants as UV-reactive. The product listings never mention it, and my own dataset note limits it to the cat tapestry, original paintings and handmade items. No caption makes a glow claim.&lt;br&gt;
• Care instructions. The joggers listing says do not bleach or tumble dry; the Knowledge Base says tumble dry is safe. The caption says nothing about care.&lt;br&gt;
• Sizes. The Psychedelia Sweatpants description and the Knowledge Base both say XS to 4XL, but the variants a customer can actually add to cart stop at XXL. The same mismatch shows on Drippy Kitty and Mechanical Fantasy. The caption states no size range.&lt;/p&gt;

&lt;p&gt;Those conflicts are now visible in the dashboard for me to see. Each review card has a cyan "Sources disagreed" box, and a "Drafted from N sources" list. The box does not block approval, because the caption already avoids the disputed claim. The sources come from the agent's run log.&lt;br&gt;
So I can use Sanity inside my own store to review generated posts that acknowledge conflicts and drop information that could mislead a customer. &lt;br&gt;
Added bonus with Sanity: editing an app inside Sanity turns out to be ordinary code. It was easy to set up the App SDK version of this.&lt;/p&gt;




&lt;h3&gt;
  
  
  High Level View of System
&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%2Fc3pdhcf2khwqn93ll1on.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%2Fc3pdhcf2khwqn93ll1on.png" alt="System Flow" width="800" height="606"&gt;&lt;/a&gt;&lt;/p&gt;
Claude created a diagram for me, and I had Gemini make it a bit nicer looking.



&lt;p&gt;The processes connect to 1 content lake in Sanity. The store never holds a CMS credential: a Sanity token cannot be narrowed below the project without an Enterprise plan, so one kept in the Rails app would also read the private knowledge notes. &lt;/p&gt;

&lt;p&gt;✨ The publisher holds it instead, runs the agent's tool loop locally, and serves the store a queue over loopback. Magnificent. ✨&lt;/p&gt;




&lt;h3&gt;
  
  
  Summary of Features
&lt;/h3&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
  &lt;thead&gt;
    &lt;tr&gt;
      &lt;th&gt;Feature&lt;/th&gt;
      &lt;th&gt;Where&lt;/th&gt;
      &lt;th&gt;What it does&lt;/th&gt;
    &lt;/tr&gt;
  &lt;/thead&gt;
  &lt;tbody&gt;
    &lt;tr&gt;
      &lt;td&gt;Custom structure&lt;/td&gt;
      &lt;td&gt;sanity.config.ts&lt;/td&gt;
      &lt;td&gt;Needs review / Queued / Published as GROQ-filtered lists, a brandVoice singleton, syncs ordered by date&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;Custom document action&lt;/td&gt;
      &lt;td&gt;actions/queueForPublish.ts&lt;/td&gt;
      &lt;td&gt;runs the gate on every draft variant and approves what passes&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;defaultDocumentNode&lt;/td&gt;
      &lt;td&gt;sanity.config.ts&lt;/td&gt;
      &lt;td&gt;a Preview pane beside the form, with four hand-built platform mock-ups&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;Custom tool&lt;/td&gt;
      &lt;td&gt;components/SyncConsole.tsx&lt;/td&gt;
      &lt;td&gt;a live catalog-sync terminal in the top nav, over client.listen()&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;Filtered templates&lt;/td&gt;
      &lt;td&gt;sanity.config.ts&lt;/td&gt;
      &lt;td&gt;brandVoice and syncRun cannot be created by hand&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;Sanity Functions&lt;/td&gt;
      &lt;td&gt;functions/sync-catalog&lt;/td&gt;
      &lt;td&gt;document-triggered, 900s, its own robot token scoped to Editor&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;Blueprints&lt;/td&gt;
      &lt;td&gt;sanity.blueprint.ts&lt;/td&gt;
      &lt;td&gt;a nightly sibling on 15 3 * * *, read in America/Chicago&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;App SDK&lt;/td&gt;
      &lt;td&gt;the review desk&lt;/td&gt;
      &lt;td&gt;useDocuments, useDocumentProjection, useEditDocument, useQuery&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;Context MCP&lt;/td&gt;
      &lt;td&gt;publisher/context.mjs&lt;/td&gt;
      &lt;td&gt;JSON-RPC over HTTP, both JSON and SSE framing, session ids&lt;/td&gt;
    &lt;/tr&gt;
  &lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;
&lt;h4&gt;
  
  
  Another Interesting Part
&lt;/h4&gt;

&lt;p&gt;The catalog sync kicks off an external API call, and its shape is the interesting part. The Studio button does not call the store. It creates a &lt;code&gt;syncRun&lt;/code&gt; document; the function wakes on that document appearing, calls the store's API, and streams its log back into the same document while it runs. The Studio never needs the store's API key.&lt;/p&gt;

&lt;p&gt;SyncConsole.tsx:241 — the entire action:&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;created&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;client&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;create&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
    &lt;span class="na"&gt;_type&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;syncRun&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;trigger&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;manual&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;outcome&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;running&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;startedAt&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;Date&lt;/span&gt;&lt;span class="p"&gt;().&lt;/span&gt;&lt;span class="nf"&gt;toISOString&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt; &lt;span class="nx"&gt;dryRun&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="p"&gt;})&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;No fetch to the store, no API key in the browser. The Studio's only&lt;br&gt;
  capability is creating a row.&lt;/p&gt;

&lt;p&gt;The function wakes on that row appearing.&lt;/p&gt;

&lt;p&gt;From sanity.blueprint.ts:&lt;br&gt;
&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="err"&gt;event:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="err"&gt;on:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="err"&gt;'create'&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="err"&gt;filter:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;'_type&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;==&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"syncRun"&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;&amp;amp;&amp;amp;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;outcome&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;==&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"running"&lt;/span&gt;&lt;span class="err"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="err"&gt;projection:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;'&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="err"&gt;_id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;trigger&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;outcome&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;dryRun&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="err"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;&lt;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%2Fzdvi0dw6mhmovr8lit16.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%2Fzdvi0dw6mhmovr8lit16.png" alt="divider" width="793" height="35"&gt;&lt;/a&gt;&lt;/p&gt;
&lt;h3&gt;
  
  
  The Drafting Agent: Claude
&lt;/h3&gt;

&lt;p&gt;&lt;code&gt;npm run draft&lt;/code&gt; asks Claude to find the season ahead and propose posts for the products that suit it, reading the shop and the Knowledge Base through two Sanity Context MCP endpoints. Each proposal carries its sources and any place two sources disagreed.&lt;/p&gt;

&lt;p&gt;Claude proposes, &lt;code&gt;planAgentDrafts&lt;/code&gt; decides. Every proposal is checked against the dataset before anything is written. &lt;br&gt;
The product must:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Exist &lt;/li&gt;
&lt;li&gt;Be active &lt;/li&gt;
&lt;li&gt;Have an image&lt;/li&gt;
&lt;li&gt;The hook must be five words or less&lt;/li&gt;
&lt;li&gt;Captions must pass preflight's caption rules. &lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Products that already have a post are left out of the prompt. &lt;/p&gt;
&lt;h4&gt;
  
  
  Importance of Structured Content Here 🚩
&lt;/h4&gt;

&lt;ul&gt;
&lt;li&gt;A dropship supplier writes "glow in the dark" into copy for a print that is actually UV-reactive. That claim is false, and it costs a refund when the customer finds out it needs a blacklight. My own glossary note says what is true. The agent surfaces both sources instead of silently picking one, whereas a keyword search for "glow" would return the supplier's claim with equal weight.&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%2Fxrh2b0hlfbsm84o39mtg.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%2Fxrh2b0hlfbsm84o39mtg.png" alt="divider" width="793" height="35"&gt;&lt;/a&gt;&lt;/p&gt;
&lt;h3&gt;
  
  
  The workflow is data 🎢
&lt;/h3&gt;

&lt;p&gt;A post is one idea with a variant per platform, so the status lives on the variant, not the post: an Instagram caption can be approved while the TikTok one goes back for a rewrite.&lt;br&gt;
The agent leaves its drafts at needs_review and a person moves them on. There is no separate agent status and human status to fall out of step, and no state the agent can reach that a person cannot see.&lt;br&gt;
The gate itself is written once. lib/platformSpec.ts in the CMS repo is the only place a platform limit is recorded — caption ceilings, hashtag caps, aspect ratios, asset counts, which of them are verified against the platform's own documentation and which are still carried from general knowledge. The Studio's preview pane reads it, the Studio's publish button reads it through lib/preflight.ts, the publisher reads a compiled copy, and the App SDK app reads the source copied verbatim. All four agree by construction.&lt;br&gt;
publishAttempt documents record what each send tried and what came back, request and response, so a failure is a document rather than a log line on one machine.&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%2Ffjqycn5v63cfd7rfpt8j.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%2Ffjqycn5v63cfd7rfpt8j.png" alt="screenshot" width="793" height="35"&gt;&lt;/a&gt;&lt;/p&gt;
&lt;h3&gt;
  
  
  The review desk, an App SDK app 🖥️
&lt;/h3&gt;

&lt;p&gt;The review desk lists variants for each post with the gate's verdict and the buttons that move it. It updates live, because the SDK subscribes rather than polls. A draft the agent writes appears without a reload.&lt;/p&gt;

&lt;p&gt;🎡 The layout is a wheel, (Ferris wheel!) with the post at the hub and its platforms orbiting on an ellipse. Angles divide evenly from twelve o'clock, and the vertical radius is solved as the smallest value at which no card overlaps another, so it adapts to any number of platforms automatically.&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%2Fxrh2b0hlfbsm84o39mtg.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%2Fxrh2b0hlfbsm84o39mtg.png" alt="divider" width="793" height="35"&gt;&lt;/a&gt;&lt;/p&gt;
&lt;h3&gt;
  
  
  The Storefront is Rails 🚂
&lt;/h3&gt;

&lt;p&gt;This storefront is Rails 7.1 with Hotwire, running on a Jetson at home behind a Cloudflare tunnel, and it predates the challenge by a long way. It sells things, takes payments through Stripe, and pushes orders to dropshippers. I wasn't going to rewrite it for a contest.&lt;br&gt;
The custom app built on the content is React. The App SDK app, in TSX, live on top of Sanity. Rails is the business the content pipeline serves, not the deliverable. And it is the reason several things here are real rather than demonstrations:&lt;br&gt;
• the catalog sync pulls a genuine product feed from a genuine store, over a tunnel, with a documented failure mode about Authorization headers being dropped across a hostname redirect&lt;br&gt;
• the store's admin consumes the publisher's API over loopback, which answers "a workflow that kicks off an external API call" twice over&lt;br&gt;
• &lt;strong&gt;a wrong claim about a product matters because it costs a refund, not a style point&lt;/strong&gt;&lt;/p&gt;




&lt;h3&gt;
  
  
  What is not done 🫣
&lt;/h3&gt;

&lt;ol&gt;
&lt;li&gt;Nothing posts to any platform. Approval is only a status, so the pipeline stops one step short of its purpose. That is deliberate.&lt;/li&gt;
&lt;li&gt;Platform limits need to be revisited.&lt;/li&gt;
&lt;li&gt;TikTok and LinkedIn variants are seeded, not generated. A script copied the Instagram caption into them so the review wheel would have four spokes. The TikTok length warning is real, but the content under it is scaffolding.&lt;/li&gt;
&lt;li&gt;Video duration is never checked. Sanity stores no duration, so the publisher and the App SDK app only warn. &lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fxrh2b0hlfbsm84o39mtg.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%2Fxrh2b0hlfbsm84o39mtg.png" alt="divider" width="793" height="35"&gt;&lt;/a&gt;&lt;/p&gt;
&lt;h2&gt;
  
  
  Sanity Project Details
&lt;/h2&gt;



&lt;p&gt;Project ID: 70komvgl&lt;/p&gt;

&lt;p&gt;Review Desk Link for those who can see it:&lt;br&gt;
&lt;/p&gt;
&lt;div class="crayons-card c-embed text-styles text-styles--secondary"&gt;
    &lt;div class="c-embed__content"&gt;
      &lt;div class="c-embed__body flex items-center justify-between"&gt;
        &lt;a href="https://www.sanity.io/api/dashboard/authenticate/session?redirectTo=%2F%40o8u0l0nzs%2Fstudio%2Fua92ddpqajzgyso4cf501x46%2Fdefault" rel="noopener noreferrer" class="c-link fw-bold flex items-center"&gt;
          &lt;span class="mr-2"&gt;sanity.io&lt;/span&gt;
          

        &lt;/a&gt;
      &lt;/div&gt;
    &lt;/div&gt;
&lt;/div&gt;



&lt;h2&gt;
  
  
  Agent Sessions
&lt;/h2&gt;

&lt;p&gt;Multiple days, multiple devices and terminals. A lot of sessions. &lt;/p&gt;

&lt;p&gt;Desktop:&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%2Fy0gsdmju20quqeidwe26.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%2Fy0gsdmju20quqeidwe26.png" alt="Session Screenshot from desktop" width="700" height="226"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Microserver:&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%2Fplxhw8xka8pmeugx7t81.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%2Fplxhw8xka8pmeugx7t81.png" alt="Session Screenshot from jetson" width="740" height="499"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;😅 And my terrible vibe coding in all its glory:&lt;/p&gt;

&lt;p&gt;Sep 26 - Dead Sanity Token&lt;br&gt;
&lt;/p&gt;
&lt;div class="crayons-card c-embed text-styles text-styles--secondary"&gt;
    &lt;div class="c-embed__content"&gt;
      &lt;div class="c-embed__body flex items-center justify-between"&gt;
        &lt;a href="https://claude.ai/artifact/KyCCKBU15q9jVXPqifEZRo" rel="noopener noreferrer" class="c-link fw-bold flex items-center"&gt;
          &lt;span class="mr-2"&gt;claude.ai&lt;/span&gt;
          

        &lt;/a&gt;
      &lt;/div&gt;
    &lt;/div&gt;
&lt;/div&gt;


&lt;p&gt;Sep 26 - Publisher Crash Looping&lt;br&gt;
&lt;/p&gt;
&lt;div class="crayons-card c-embed text-styles text-styles--secondary"&gt;
    &lt;div class="c-embed__content"&gt;
      &lt;div class="c-embed__body flex items-center justify-between"&gt;
        &lt;a href="https://claude.ai/artifact/Tm1zCKYjT9Gzkq6TjHyqV5" rel="noopener noreferrer" class="c-link fw-bold flex items-center"&gt;
          &lt;span class="mr-2"&gt;claude.ai&lt;/span&gt;
          

        &lt;/a&gt;
      &lt;/div&gt;
    &lt;/div&gt;
&lt;/div&gt;


&lt;p&gt;Sep 27 - New Data not Appearing&lt;br&gt;
&lt;/p&gt;
&lt;div class="crayons-card c-embed text-styles text-styles--secondary"&gt;
    &lt;div class="c-embed__content"&gt;
      &lt;div class="c-embed__body flex items-center justify-between"&gt;
        &lt;a href="https://claude.ai/artifact/SpHZa8WTVJ36oBe4xWM3BM" rel="noopener noreferrer" class="c-link fw-bold flex items-center"&gt;
          &lt;span class="mr-2"&gt;claude.ai&lt;/span&gt;
          

        &lt;/a&gt;
      &lt;/div&gt;
    &lt;/div&gt;
&lt;/div&gt;






&lt;p&gt;This is alot, thank you for reading! &amp;lt;3&lt;/p&gt;

</description>
      <category>devchallenge</category>
      <category>sanitychallenge</category>
      <category>sanity</category>
      <category>ai</category>
    </item>
    <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>
  </channel>
</rss>
