<?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: Eugene Kozlovsky</title>
    <description>The latest articles on DEV Community by Eugene Kozlovsky (@koztechie).</description>
    <link>https://dev.to/koztechie</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%2F3340118%2Fb7a95fb0-4366-48e6-a83d-3a589891c52e.jpg</url>
      <title>DEV Community: Eugene Kozlovsky</title>
      <link>https://dev.to/koztechie</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/koztechie"/>
    <language>en</language>
    <item>
      <title>Building NICHLYST: How to Code a Survival Engine When You Are Failing to Survive</title>
      <dc:creator>Eugene Kozlovsky</dc:creator>
      <pubDate>Sat, 29 Aug 2026 15:58:32 +0000</pubDate>
      <link>https://dev.to/koztechie/building-nichlyst-how-to-code-a-survival-engine-when-you-are-failing-to-survive-54nl</link>
      <guid>https://dev.to/koztechie/building-nichlyst-how-to-code-a-survival-engine-when-you-are-failing-to-survive-54nl</guid>
      <description>&lt;h1&gt;
  
  
  Building NICHLYST: How to Code a Survival Engine When You Are Failing to Survive
&lt;/h1&gt;

&lt;p&gt;I track everything. It is an occupational habit of a systems architect. You cannot fix what you do not measure — not a failing build, not a leaking color, and not developer burnout. And if you are reading this while grinding through the RevenueCat Shipaton 2026 yourself, you already know that the hardest metric to log honestly is your own state.&lt;/p&gt;

&lt;p&gt;So, let me share some metrics.&lt;/p&gt;

&lt;h3&gt;
  
  
  Clinical Baseline: A PHQ-9 Depression Score of 21
&lt;/h3&gt;

&lt;p&gt;On May 11, 2026, my clinical assessment scores were:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;PHQ-9 (Depression): 21.&lt;/strong&gt; Severe. Immediate professional intervention required.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;GAD-7 (Anxiety): 11.&lt;/strong&gt; Moderate.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;On August 22, 2026, in the middle of the RevenueCat Shipaton, I took the assessment again.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;PHQ-9: 21.&lt;/strong&gt; No improvement.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;GAD-7: 16.&lt;/strong&gt; High anxiety. Daily functioning severely impaired.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;If you have never read a GAD-7 anxiety assessment, 16 sits deep in the high-anxiety band — the zone where "daily functioning severely impaired" stops being a clinical phrase and becomes your actual schedule.&lt;/p&gt;

&lt;p&gt;While I was typing the very first lines of this post, a massive explosion went off, loud enough to make my ears pop. About an hour later, the local news feeds brought the context: an attack drone had been shot down over a park roughly two and a half kilometers from my house. According to the updates, the falling debris killed a two-year-old child and injured two adults.&lt;/p&gt;

&lt;p&gt;I am developing a narrative game about survival, the fragility of life, and human behavior under immense pressure. But here, in Kyiv, these are not abstract game mechanics or dramatic tropes to be monetized. They are the immediate, absurd, and brutal reality outside my window.&lt;/p&gt;

&lt;p&gt;I am exhausted. The clinical scores haven't moved in months. I sleep in the middle of the day because my nervous system simply shuts down. I am looking at this hackathon as a final, desperate push to build something sustainable.&lt;/p&gt;

&lt;p&gt;But here is the thing about the antifragile development plan I wrote about in my first devlog: &lt;strong&gt;it does not require me to be okay.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;The code does not care about my PHQ-9 score. The system is designed to absorb the collapse of its creator and keep moving forward.&lt;/p&gt;

&lt;p&gt;Here is exactly how NICHLYST advanced technically and narratively over the last grueling week, while its developer was barely holding on.&lt;/p&gt;




&lt;h2&gt;
  
  
  The Git Gatekeeper: A Git Pre-commit Hook Enforcing a 5-color Dictatorship
&lt;/h2&gt;

&lt;p&gt;NICHLYST has a strict, uncompromising visual identity. It is built exclusively on a &lt;strong&gt;limited color palette&lt;/strong&gt; of five: &lt;code&gt;#232626&lt;/code&gt; (Pitch Dark), &lt;code&gt;#3E403F&lt;/code&gt; (Slate), &lt;code&gt;#736758&lt;/code&gt; (Sepia), &lt;code&gt;#A69B8D&lt;/code&gt; (Ash), and &lt;code&gt;#BFB3A4&lt;/code&gt; (Bone).&lt;/p&gt;

&lt;p&gt;I spent hours auditing the entire codebase via terminal (&lt;code&gt;grep -rnE "#([0-9a-fA-F]{3,8})\b"&lt;/code&gt;) to eradicate every stray &lt;code&gt;#FFFFFF&lt;/code&gt; or &lt;code&gt;#000000&lt;/code&gt; from CSS, inline SVGs, and Android &lt;code&gt;capacitor.config.ts&lt;/code&gt;. I even wrote a Python script to batch-process all graphic assets through a mathematical gradient map, ensuring absolute compliance.&lt;/p&gt;

&lt;p&gt;But human beings make mistakes. Exhausted human beings make a lot of mistakes.&lt;/p&gt;

&lt;p&gt;So, I took the human out of the loop. I wrote a &lt;code&gt;pre-commit&lt;/code&gt; Git hook (&lt;code&gt;check_colors.sh&lt;/code&gt;) that acts as an Anti-Color-Leak Gatekeeper, physically verifying every staged file against our strict 5-color whitelist before allowing a commit:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;#!/bin/bash&lt;/span&gt;
&lt;span class="c"&gt;# ==============================================================================&lt;/span&gt;
&lt;span class="c"&gt;# NICHLYST — Anti-Color-Leak Gatekeeper (Pre-commit Hook)&lt;/span&gt;
&lt;span class="c"&gt;# Strictly allowed 5-color palette (case-insensitive, 6 or 8-digit hex):&lt;/span&gt;
&lt;span class="c"&gt;# #232626, #3E403F, #736758, #A69B8D, #BFB3A4&lt;/span&gt;
&lt;span class="c"&gt;# ==============================================================================&lt;/span&gt;

&lt;span class="nv"&gt;ALLOWED_PATTERN&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"^#((232626|3e403f|736758|a69b8d|bfb3a4)([0-9a-fA-F]{2})?)$"&lt;/span&gt;

&lt;span class="c"&gt;# Determine files to check: staged in Git, or all www/ files on manual execution&lt;/span&gt;
&lt;span class="nv"&gt;FILES_TO_CHECK&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="si"&gt;$(&lt;/span&gt;git diff &lt;span class="nt"&gt;--cached&lt;/span&gt; &lt;span class="nt"&gt;--name-only&lt;/span&gt; &lt;span class="nt"&gt;--diff-filter&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;ACM 2&amp;gt;/dev/null&lt;span class="si"&gt;)&lt;/span&gt;

&lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="o"&gt;[&lt;/span&gt; &lt;span class="nt"&gt;-z&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$FILES_TO_CHECK&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="o"&gt;]&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="k"&gt;then
  &lt;/span&gt;&lt;span class="nv"&gt;FILES_TO_CHECK&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="si"&gt;$(&lt;/span&gt;find www &lt;span class="nt"&gt;-type&lt;/span&gt; f &lt;span class="se"&gt;\(&lt;/span&gt; &lt;span class="nt"&gt;-name&lt;/span&gt; &lt;span class="s2"&gt;"*.css"&lt;/span&gt; &lt;span class="nt"&gt;-o&lt;/span&gt; &lt;span class="nt"&gt;-name&lt;/span&gt; &lt;span class="s2"&gt;"*.js"&lt;/span&gt; &lt;span class="nt"&gt;-o&lt;/span&gt; &lt;span class="nt"&gt;-name&lt;/span&gt; &lt;span class="s2"&gt;"*.html"&lt;/span&gt; &lt;span class="se"&gt;\)&lt;/span&gt;&lt;span class="si"&gt;)&lt;/span&gt;
&lt;span class="k"&gt;fi

&lt;/span&gt;&lt;span class="nv"&gt;EXIT_CODE&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;0
&lt;span class="nv"&gt;FOUND_ERRORS&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;0

&lt;span class="k"&gt;for &lt;/span&gt;file &lt;span class="k"&gt;in&lt;/span&gt; &lt;span class="nv"&gt;$FILES_TO_CHECK&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
  &lt;span class="c"&gt;# Skip binary assets, fonts, audio files, and the check script itself&lt;/span&gt;
  &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="o"&gt;[[&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$file&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt;~ &lt;span class="se"&gt;\.&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;png|webp|jpg|jpeg|svg|ico|ttf|woff|woff2|mp3|ogg|wav&lt;span class="o"&gt;)&lt;/span&gt;&lt;span class="nv"&gt;$ &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="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$file&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="s2"&gt;"scripts/check_colors.sh"&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="o"&gt;!&lt;/span&gt; &lt;span class="nt"&gt;-f&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$file&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="o"&gt;]]&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="k"&gt;then
    continue
  fi&lt;/span&gt;

  &lt;span class="c"&gt;# Find all 3, 6, and 8-character HEX colors&lt;/span&gt;
  &lt;span class="nv"&gt;HEX_MATCHES&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="si"&gt;$(&lt;/span&gt;&lt;span class="nb"&gt;grep&lt;/span&gt; &lt;span class="nt"&gt;-onE&lt;/span&gt; &lt;span class="s2"&gt;"#[0-9a-fA-F]{3,8}&lt;/span&gt;&lt;span class="se"&gt;\b&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$file&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; 2&amp;gt;/dev/null&lt;span class="si"&gt;)&lt;/span&gt;

  &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="o"&gt;[&lt;/span&gt; &lt;span class="nt"&gt;-n&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$HEX_MATCHES&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="o"&gt;]&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="k"&gt;then
    while &lt;/span&gt;&lt;span class="nv"&gt;IFS&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;read&lt;/span&gt; &lt;span class="nt"&gt;-r&lt;/span&gt; match&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="k"&gt;do
      &lt;/span&gt;&lt;span class="nv"&gt;LINE&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="si"&gt;$(&lt;/span&gt;&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$match&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; | &lt;span class="nb"&gt;cut&lt;/span&gt; &lt;span class="nt"&gt;-d&lt;/span&gt;: &lt;span class="nt"&gt;-f1&lt;/span&gt;&lt;span class="si"&gt;)&lt;/span&gt;
      &lt;span class="nv"&gt;HEX&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="si"&gt;$(&lt;/span&gt;&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$match&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; | &lt;span class="nb"&gt;cut&lt;/span&gt; &lt;span class="nt"&gt;-d&lt;/span&gt;: &lt;span class="nt"&gt;-f2&lt;/span&gt;&lt;span class="si"&gt;)&lt;/span&gt;
      &lt;span class="nv"&gt;HEX_LOWER&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="si"&gt;$(&lt;/span&gt;&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$HEX&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; | &lt;span class="nb"&gt;tr&lt;/span&gt; &lt;span class="s1"&gt;'[:upper:]'&lt;/span&gt; &lt;span class="s1"&gt;'[:lower:]'&lt;/span&gt;&lt;span class="si"&gt;)&lt;/span&gt;

      &lt;span class="c"&gt;# Validate against the allowed palette whitelist&lt;/span&gt;
      &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="o"&gt;!&lt;/span&gt; &lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$HEX_LOWER&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; | &lt;span class="nb"&gt;grep&lt;/span&gt; &lt;span class="nt"&gt;-qE&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$ALLOWED_PATTERN&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="k"&gt;then
        &lt;/span&gt;&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"🚨 [STOP-LOSS] Unauthorized color '&lt;/span&gt;&lt;span class="nv"&gt;$HEX&lt;/span&gt;&lt;span class="s2"&gt;' in &lt;/span&gt;&lt;span class="nv"&gt;$file&lt;/span&gt;&lt;span class="s2"&gt;:&lt;/span&gt;&lt;span class="nv"&gt;$LINE&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;
        &lt;span class="nv"&gt;EXIT_CODE&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;1
        &lt;span class="o"&gt;((&lt;/span&gt;FOUND_ERRORS++&lt;span class="o"&gt;))&lt;/span&gt;
      &lt;span class="k"&gt;fi
    done&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&amp;lt;&amp;lt;&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$HEX_MATCHES&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;
  &lt;span class="k"&gt;fi
done

if&lt;/span&gt; &lt;span class="o"&gt;[&lt;/span&gt; &lt;span class="nv"&gt;$EXIT_CODE&lt;/span&gt; &lt;span class="nt"&gt;-ne&lt;/span&gt; 0 &lt;span class="o"&gt;]&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="k"&gt;then
  &lt;/span&gt;&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;""&lt;/span&gt;
  &lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"❌ COMMIT REJECTED! Found &lt;/span&gt;&lt;span class="nv"&gt;$FOUND_ERRORS&lt;/span&gt;&lt;span class="s2"&gt; unauthorized color values."&lt;/span&gt;
  &lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"   NICHLYST strictly allows only 5 colors: #232626, #3E403F, #736758, #A69B8D, #BFB3A4."&lt;/span&gt;
  &lt;span class="nb"&gt;exit &lt;/span&gt;1
&lt;span class="k"&gt;fi

&lt;/span&gt;&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"✅ [COLOR-GUARD] All colors comply with the official NICHLYST palette."&lt;/span&gt;
&lt;span class="nb"&gt;exit &lt;/span&gt;0
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If I am tired and accidentally style a button with &lt;code&gt;#FFF&lt;/code&gt;, the system physically rejects the commit. The system enforces the aesthetic when my brain cannot.&lt;/p&gt;




&lt;h2&gt;
  
  
  The Narrative Rewrite: Narrative Game Design — Why I Threw Away the Script a Month Before Release
&lt;/h2&gt;

&lt;p&gt;A few days ago, I booted up the local server, clicked through the first three days, and felt nothing.&lt;/p&gt;

&lt;p&gt;The engine worked perfectly. The resources depleted. The RevenueCat SDK integration was flawless (the paywall triggers exactly after Day 3). But the text was dry.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;“Khoma arrives. He has evidence. Do you take it?”&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;If you are going to charge $4.99 for a narrative game — my entire indie game monetization strategy is that one honest purchase, no dark patterns — the text must react to the player's conscience. It cannot be a telegram. The player must feel like they are reading an elite, heavy book where they are the protagonist.&lt;/p&gt;

&lt;p&gt;So, I rewrote the JSON schema and refactored the UI (&lt;code&gt;DayScreen.js&lt;/code&gt;).&lt;/p&gt;

&lt;p&gt;Instead of a single string, the engine now accepts a &lt;code&gt;chapter_narrative&lt;/code&gt; array, breaking every day into four distinct phases:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Awakening:&lt;/strong&gt; The somatic feeling of the basement. Cold concrete, heavy lungs.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Approach:&lt;/strong&gt; The footsteps outside the blast door. The anxiety before the knock.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Encounter:&lt;/strong&gt; The dialogue and the evidence.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Dilemma:&lt;/strong&gt; The moral trap where there are no good options.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;I updated the CSS to handle this like true typography. I added an isolated scrolling container, a cascading fade-in animation, and a drop-cap (&lt;code&gt;::first-letter&lt;/code&gt;) to the opening paragraph so the text feels tactile and deliberate:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight css"&gt;&lt;code&gt;&lt;span class="c"&gt;/* ─── Narrative Scroll Area (multi-paragraph chapter_narrative) ── */&lt;/span&gt;
&lt;span class="nc"&gt;.nl-narrative-scroll-area&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nl"&gt;flex&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;1&lt;/span&gt; &lt;span class="m"&gt;1&lt;/span&gt; &lt;span class="nb"&gt;auto&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nl"&gt;overflow-y&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;auto&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nl"&gt;max-height&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;calc&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="m"&gt;100vh&lt;/span&gt; &lt;span class="n"&gt;-&lt;/span&gt; &lt;span class="m"&gt;290px&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="nl"&gt;padding-right&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;8px&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nl"&gt;margin-bottom&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;1.25rem&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="py"&gt;scrollbar-width&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;thin&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="py"&gt;scrollbar-color&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;var&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;--c-3&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="n"&gt;var&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;--c-1&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="nl"&gt;-webkit-overflow-scrolling&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;touch&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="nc"&gt;.nl-narrative-scroll-area&lt;/span&gt;&lt;span class="nd"&gt;::-webkit-scrollbar&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nl"&gt;width&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;4px&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="nc"&gt;.nl-narrative-scroll-area&lt;/span&gt;&lt;span class="nd"&gt;::-webkit-scrollbar-track&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nl"&gt;background&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;var&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;--c-1&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="nc"&gt;.nl-narrative-scroll-area&lt;/span&gt;&lt;span class="nd"&gt;::-webkit-scrollbar-thumb&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nl"&gt;background&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;var&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;--c-3&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="nl"&gt;border-radius&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;2px&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="c"&gt;/* ─── Narrative Paragraph (book typography) ──────────────────── */&lt;/span&gt;
&lt;span class="nc"&gt;.nl-narrative-paragraph&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nl"&gt;font-family&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;var&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;--font-serif&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;"Georgia"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nb"&gt;serif&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="nl"&gt;font-size&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;1.05rem&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nl"&gt;line-height&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;1.7&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nl"&gt;color&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;var&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;--c-5&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="nl"&gt;margin-bottom&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;1.25rem&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nl"&gt;text-align&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;justify&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nl"&gt;opacity&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nl"&gt;animation&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;nlParagraphFadeIn&lt;/span&gt; &lt;span class="m"&gt;0.35s&lt;/span&gt; &lt;span class="n"&gt;ease&lt;/span&gt; &lt;span class="n"&gt;forwards&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="nc"&gt;.nl-narrative-paragraph&lt;/span&gt;&lt;span class="nd"&gt;:nth-child&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="err"&gt;1&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nl"&gt;animation-delay&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;0.05s&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="nc"&gt;.nl-narrative-paragraph&lt;/span&gt;&lt;span class="nd"&gt;:nth-child&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="err"&gt;2&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nl"&gt;animation-delay&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;0.15s&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="nc"&gt;.nl-narrative-paragraph&lt;/span&gt;&lt;span class="nd"&gt;:nth-child&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="err"&gt;3&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nl"&gt;animation-delay&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;0.25s&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="nc"&gt;.nl-narrative-paragraph&lt;/span&gt;&lt;span class="nd"&gt;:nth-child&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="err"&gt;4&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nl"&gt;animation-delay&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;0.35s&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="nc"&gt;.nl-narrative-paragraph&lt;/span&gt;&lt;span class="nd"&gt;:nth-child&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nt"&gt;n&lt;/span&gt;&lt;span class="o"&gt;+&lt;/span&gt;&lt;span class="err"&gt;5&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nl"&gt;animation-delay&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;0.45s&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="c"&gt;/* Drop cap on the opening paragraph */&lt;/span&gt;
&lt;span class="nc"&gt;.nl-narrative-paragraph&lt;/span&gt;&lt;span class="nd"&gt;:first-of-type::first-letter&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nl"&gt;font-size&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;2.6rem&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nl"&gt;line-height&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;0.85&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nl"&gt;float&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;left&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nl"&gt;margin-right&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;0.45rem&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nl"&gt;margin-top&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;0.15rem&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nl"&gt;font-family&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;var&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;--font-serif&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;"Georgia"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nb"&gt;serif&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="nl"&gt;color&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;var&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;--c-5&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="nl"&gt;font-weight&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;bold&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="k"&gt;@keyframes&lt;/span&gt; &lt;span class="n"&gt;nlParagraphFadeIn&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nt"&gt;from&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nl"&gt;opacity&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="nl"&gt;transform&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;translateY&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="m"&gt;4px&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="nt"&gt;to&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nl"&gt;opacity&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;1&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="nl"&gt;transform&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;translateY&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="m"&gt;0&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The engine now supports deep, multi-paragraph storytelling while gracefully falling back to old single-string descriptions for days I haven't rewritten yet.&lt;/p&gt;




&lt;h3&gt;
  
  
  Expanding the Dictionary of Despair
&lt;/h3&gt;

&lt;p&gt;To ensure the new writing hits the exact psychological notes required, I expanded the game's internal &lt;code&gt;game_data.json&lt;/code&gt; dictionaries. I added new tone keywords for the AI-assisted generation prompts:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;em&gt;Wave 4:&lt;/em&gt; &lt;code&gt;chemical apathy&lt;/code&gt;, &lt;code&gt;cognitive dissonance&lt;/code&gt;, &lt;code&gt;claustrophobic giant&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;
&lt;em&gt;Wave 6:&lt;/em&gt; &lt;code&gt;telegraphic despair&lt;/code&gt;, &lt;code&gt;spatial erasure&lt;/code&gt;, &lt;code&gt;terminal apathy&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;
&lt;em&gt;Wave 8:&lt;/em&gt; &lt;code&gt;somatic overflow&lt;/code&gt;, &lt;code&gt;phantom flora&lt;/code&gt;, &lt;code&gt;sensory dissonance&lt;/code&gt;.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;I also added a new piece of deep lore to the Codex: &lt;strong&gt;Directive 44&lt;/strong&gt;. In the world of NICHLYST, the totalitarian Directorate has officially banned "vocal warmth" and empathy in speech as signs of psychological instability, punishable by ration forfeiture. This justifies the dry, brutal tone of the game's UI.&lt;/p&gt;




&lt;h3&gt;
  
  
  Securing the Perimeter (Via Negativa)
&lt;/h3&gt;

&lt;p&gt;Marketing is not just about posting; it is about squatting. I spent one evening locking down the digital perimeter.&lt;/p&gt;

&lt;p&gt;I registered the GitHub Organization, secured the domain &lt;code&gt;koztechie.pp.ua&lt;/code&gt; (and deployed a blazing-fast Eleventy static hub onto Netlify), claimed &lt;code&gt;@nichlyst&lt;/code&gt; on X, Instagram, TikTok, Bluesky, Threads, and Itch.io.&lt;/p&gt;

&lt;p&gt;I also had "Create a subreddit" on my to-do list. But I looked at Reddit's current corporate ecosystem, the CAPTCHAs, the shadowbans, and the decay of the open web.&lt;/p&gt;

&lt;p&gt;Aaron Swartz didn't fight for a closed corporate silo. I crossed the task off. NICHLYST is anti-Reddit. We rely on the open web, RSS, Dev.to, HackerNoon, and direct code. This is Taleb’s &lt;em&gt;Via Negativa&lt;/em&gt; in action: removing the fragile elements to make the whole system stronger.&lt;/p&gt;




&lt;h3&gt;
  
  
  The 20-Tester Wall Remains
&lt;/h3&gt;

&lt;p&gt;The game is structurally complete. The monetization is wired. The narrative is being rewritten into something profoundly heavy.&lt;/p&gt;

&lt;p&gt;But Google Play’s bureaucratic wall still stands. I need 20 active testers to keep the app installed for 14 days, or I cannot launch on time for the Shipaton.&lt;/p&gt;

&lt;p&gt;I currently have 15. I need &lt;strong&gt;5 more&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;If you are an Android user and want to help a developer who is pouring the last drops of his nervous system into this engine, please fill out this form. No heavy QA needed. Just accept the invite, install, and hold it on your phone.&lt;/p&gt;

&lt;p&gt;👉 &lt;strong&gt;&lt;a href="https://forms.gle/4hJkWsmuyKp3Tgtr8" rel="noopener noreferrer"&gt;https://forms.gle/4hJkWsmuyKp3Tgtr8&lt;/a&gt;&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;I am starting my day with a selective serotonin reuptake inhibitor (SSRI) and eight other pills or capsules differing in shape, color, size, and purpose, just to keep my body and nervous system functioning. But the engine breathes. The infrastructure holds.&lt;/p&gt;

&lt;p&gt;The antifragile plan works. I'll see you at the finish line.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Follow this devlog for the #BuildInPublic journey toward the RevenueCat Shipaton 2026.&lt;/em&gt;&lt;/p&gt;

</description>
      <category>shipaton</category>
      <category>buildinpublic</category>
      <category>gamedev</category>
      <category>indiedev</category>
    </item>
    <item>
      <title>Building NICHLYST: Two Mugs, a Black Screen, and the Weekend the Engine Woke Up</title>
      <dc:creator>Eugene Kozlovsky</dc:creator>
      <pubDate>Sun, 16 Aug 2026 13:46:07 +0000</pubDate>
      <link>https://dev.to/koztechie/building-nichlyst-two-mugs-a-black-screen-and-the-weekend-the-engine-woke-up-d05</link>
      <guid>https://dev.to/koztechie/building-nichlyst-two-mugs-a-black-screen-and-the-weekend-the-engine-woke-up-d05</guid>
      <description>&lt;p&gt;A friend came by last Thursday and handed me a mug she had brought from Bulgaria. On the mug was a Kama Sutra position so acrobatic that gravity itself seemed ready to file a complaint. "So that there would be love," she said. It felt strangely appropriate: the thing on my desk that week was a half-asleep HTML5 game engine, and it was going to need exactly that kind of stubborn love to wake up.&lt;/p&gt;

&lt;p&gt;I already own a hackathon mug. It says: "I participated in the world's largest hackathon and all I got was this mug." And what is written there is the honest truth.&lt;/p&gt;

&lt;p&gt;That mug is my main one now. Every day I brew Assam in it, plus a soluble chicory-and-barley drink, and I sit with it through work and through my own projects. The project I received it for last summer no longer exists. I chose not to develop it, for reasons that deserve their own separate silence. But the mug stayed — as a reminder of what I was able to build once, and as a quiet, stubborn motivation to do it again.&lt;/p&gt;

&lt;p&gt;The new mug won for pure practicality. Six hundred milliliters. When you spend your days holding other people's infrastructure together and your evenings turning a vision into prompts, you are always drinking something, and 600 ml is the right amount of continuity.&lt;/p&gt;

&lt;p&gt;Projects die. Mugs survive. Objects outlive the work that made them. I keep thinking about that while I build a game about keeping records after everything else has burned.&lt;/p&gt;

&lt;p&gt;By the morning of August 16th, the plan had quietly eaten steps 49 through 86. Let me explain.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Audio Phase: ElevenLabs Sound Effects
&lt;/h2&gt;

&lt;p&gt;The second devlog ended on a promise: the audio phase, then the engine. The audio came together first. I wrote the key voiceover lines and generated the voice — and then, without planning to, made a discovery: ElevenLabs also generates sound effects astonishingly well. The ambient layers and the small diegetic motifs of the game were born there too. I did not expect a voice tool to hand me wind, hum, and a door that creaks without moving. I normalized everything on my Xiaomi, committed, and backed the folder up before I allowed myself to feel satisfaction.&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%2F7cerk225q5abkz79pjqk.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%2F7cerk225q5abkz79pjqk.jpg" alt="VS Code workspace of the NICHLYST HTML5 game: AI-generated audio folders (ambient layers, sound motifs, English voiceover), audio_manifest.json, and codemagic.yaml with the Capacitor Android debug APK build steps — a free CI/CD pipeline for solo developers." width="800" height="420"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Vibe Coding Tools 2026: OpenCode vs Antigravity CLI
&lt;/h3&gt;

&lt;p&gt;The engine needed a different kind of help. Until recently I vibe coded with Antigravity CLI, and it was extremely convenient — exactly as long as my paid Google subscription lasted. The moment the subscription ended, the limits became laughable. Less than thirty minutes of vibe coding, and the tank was empty. So I switched to OpenCode. Even unpaid, its limits are enough to actually work, and OpenCode vibe coding quickly became my default rhythm: open the terminal, describe the intent, review the diff, commit. On Saturday I opened it, and something clicked.&lt;/p&gt;

&lt;p&gt;If you are compiling your own shortlist of the best vibe coding tools 2026, here is my field data from one weekend: Antigravity CLI is brilliant while the quota lasts, and OpenCode is the tool that keeps a solo dev walking when the quota dies. The winner of any comparison is not the smartest model — it is the one whose limits let you finish an evening session.&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%2Fjf70rpjecc01ycjijzso.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%2Fjf70rpjecc01ycjijzso.jpg" alt="OpenCode terminal splash screen — the free vibe coding CLI running the MiMo V2.5 model, used to build the NICHLYST game engine after Antigravity CLI subscription limits ran out." width="800" height="420"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  The Engine Wakes Up
&lt;/h3&gt;

&lt;p&gt;What followed is hard to describe as a sequence, because it stopped being one. The plan's steps blurred into a single long evening and then another. A state manager that reads the frozen JSON and loads its initial truths from it. A persistence layer — autosave after every meaningful event, because a mobile game that loses your guilt when the OS kills the process is not a game, it's a scam. A renderer for the day screen. A pipeline that resolves choices and their delayed consequences. A parser that reads condition strings and picks an ending from the data. Transitions between narrative layers. A UI that slowly decays after the finale, the way the design document always promised it should.&lt;/p&gt;

&lt;p&gt;Then the wiring: backgrounds, portraits, audio — everything the art pipeline had produced, connected to the machine. Every subsystem arrived with a checklist and at least one test scenario, because on a weak PC with a weak margin for error, "it seems to work" is not a status. Commits landed before 23:00. The 23:00 rule from the first devlog is still alive; some rules survive precisely because they are boring.&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%2F222keqw88l6st7gnbiey.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%2F222keqw88l6st7gnbiey.jpg" alt="Git commit history of the NICHLYST weekend sprint: steps 52–58 — six sound motifs, consolidated normalized audio, MARKETING_PROOF.md, and the StateManager class design that woke the engine up." width="800" height="420"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Thirty-eight game development milestones closed in one weekend. Three of them had to be closed twice. That ratio is the honest metric of solo development.&lt;/p&gt;

&lt;h3&gt;
  
  
  The Black Screen
&lt;/h3&gt;

&lt;p&gt;Because here is what happened the first time I wired the art: the game launched and showed me a header, two buttons, and a black void where the narrative was supposed to be.&lt;/p&gt;

&lt;p&gt;Diagnosis first, guess-fix never. The render pipeline was interrupting itself the moment an asset manifest request failed. And the manifest was a lie I had written: it expected file names I had invented, while the disk held file names the generator had invented. Two fictions that had never met.&lt;/p&gt;

&lt;p&gt;The fix became a contract. The narrative renders even if every optional subsystem dies. Every fetch gets a catch. And the manifest is no longer written by hand — it is regenerated from the disk, because the disk is the only source of truth; anything the script can't recognize goes to a quarantine list, never into the game by guesswork. When I reloaded and heard the knock — cautious, irregular, exactly as designed — I sat still for a while. The JSON was no longer a document. It was a place.&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%2Favcxykb0qdzhameajrjp.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%2Favcxykb0qdzhameajrjp.jpg" alt="JavaScript asset manifest code for the NICHLYST HTML5 engine: a function mapping filenames to phase keys and returning relative paths — the disk as the single source of truth after the black screen bug fix." width="800" height="420"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Monday Is Coming
&lt;/h3&gt;

&lt;p&gt;On Monday, rehabilitation begins. Three to four hours a day, including the road. I don't yet know the exact slot, which means the schedule has to absorb an unknown shape — again. My sessions with the psychologist are also coming to an end; one is left, and I caught myself grieving in advance for a structure that taught me to stand without structures.&lt;/p&gt;

&lt;p&gt;This is what the war years do to a plan. Not always with sirens. Sometimes just with the slow accumulation of appointments, procedures, endings, and beginnings, while the city stays hot and the air stays loud enough to be a baseline. You don't get heroic days. You get days. The antifragile plan from the first devlog was written for exactly this: the schedule bends, it does not break. Rehabilitation goes into the calendar. The 23:00 rule stays. The mug travels to every desk I sit at.&lt;/p&gt;

&lt;h3&gt;
  
  
  The Tester Wall: Google Play 20 Testers, Almost Down
&lt;/h3&gt;

&lt;p&gt;Since the second devlog, the tester form has grown to 14 emails. I need 6 more. That's it. That is the whole distance between me and the Google Play production track.&lt;/p&gt;

&lt;p&gt;If you are reading this, have an Android device, and two minutes to spare:&lt;/p&gt;

&lt;p&gt;👉 &lt;a href="https://forms.gle/4hJkWsmuyKp3Tgtr8" rel="noopener noreferrer"&gt;https://forms.gle/4hJkWsmuyKp3Tgtr8&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Accept the invite later, install the app, keep it for 14 days. No deep QA. Just your presence to satisfy the algorithm.&lt;/p&gt;

&lt;h3&gt;
  
  
  What Comes Next
&lt;/h3&gt;

&lt;p&gt;The engine breathes. Next is money and time: the RevenueCat SDK and the paywall, the push-notification architecture — the diegetic knocks that arrive hours after a choice — the demo video under two minutes, and a Codemagic build my testers can hold.&lt;/p&gt;

&lt;p&gt;NICHLYST is coming. And now it remembers.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Follow this devlog for the #BuildInPublic journey.&lt;/em&gt;&lt;/p&gt;

</description>
      <category>buildinpublic</category>
      <category>shipaton</category>
      <category>indiedev</category>
      <category>gamedev</category>
    </item>
    <item>
      <title>Building NICHLYST: The Weight of 40 Days, 29 Endings, and a Style Anchor</title>
      <dc:creator>Eugene Kozlovsky</dc:creator>
      <pubDate>Thu, 13 Aug 2026 21:01:28 +0000</pubDate>
      <link>https://dev.to/koztechie/building-nichlyst-the-weight-of-40-days-29-endings-and-a-style-anchor-2o0f</link>
      <guid>https://dev.to/koztechie/building-nichlyst-the-weight-of-40-days-29-endings-and-a-style-anchor-2o0f</guid>
      <description>&lt;p&gt;Three days ago, I published the first devlog for the RevenueCat Shipaton 2026. Diving deep into the heavy, emotional trenches of narrative game design, I had a 128-step plan, a blank APK, and 3 Google Play testers. Today, I have 40 days of structured narrative content, 29 endings, a full art pipeline, and a very familiar tightness in my chest.&lt;/p&gt;

&lt;p&gt;Let me explain.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Content Freeze
&lt;/h2&gt;

&lt;p&gt;Between August 10th and August 13th, I completed a massive chunk of the antifragile plan. The most critical milestone was the content freeze.&lt;/p&gt;

&lt;p&gt;What does "frozen" mean? It means NICHLYST now has a definitive shape. Unless a miracle happens and I find myself far ahead of schedule with time to consider expanding the lore, the scope is now locked. The JSON file that drives the entire game has been committed, tagged, and backed up to Google Drive. &lt;/p&gt;

&lt;p&gt;Here is what that structure looks like:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;40 days&lt;/strong&gt; of narrative content.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;29 endings&lt;/strong&gt; (main and hidden).&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Multiple resource counters&lt;/strong&gt; (some visible to the player to manage survival, others running silently under the hood, tracking psychological and moral degradation).&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Over 100 global flags&lt;/strong&gt; tracking every choice the player makes.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Instead of relying on a rigid game design document template, I wrote the initial script as a markdown document, then structured it directly into JSON. The English translations were finalized alongside the Ukrainian originals, as the hackathon requires all submission materials in English.&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%2Ffxiy1jzs18ebf847qlwf.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%2Ffxiy1jzs18ebf847qlwf.jpg" alt="game_data.json in VS Code: the JSON game data structure driving NICHLYST — version 4.0 " width="800" height="420"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  The Moral Audit
&lt;/h2&gt;

&lt;p&gt;Before freezing the content, I ran a moral complexity audit on the entire game. My design formula is simple: &lt;strong&gt;every choice must pit the value of testimony against the cost of survival&lt;/strong&gt;. No binary good/evil. No easy outs.&lt;/p&gt;

&lt;p&gt;The audit checked every single day for six criteria: no binary good/evil, testimony vs. survival tension, consequence weight, emotional cost, no easy outs, and narrative coherence.&lt;/p&gt;

&lt;p&gt;The audit found 39 out of 40 days passing all six checks. One day failed.&lt;/p&gt;

&lt;p&gt;It was a breathing space between two heavy acts. The problem? Both choices had zero mechanical consequences. No resource deltas. No flags set. The player could experience the day as cost-free.&lt;/p&gt;

&lt;p&gt;That is a violation of the core design philosophy. In NICHLYST, nothing is free. So I fixed it: accepting help now costs a psychological resource (the moral weight of compromise), and refusing help costs a social resource (the cost of pride). The fix was committed, and the content was frozen.&lt;/p&gt;

&lt;p&gt;The audit also identified some of the game's strongest moral scenes — moments where the player must decide whose memory gets preserved, whose identity gets erased, and whether to warm the paper archive or the living people in the freezing basement. These are not fun choices. They are not supposed to be.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Art Pipeline: Style Anchor and Privacy
&lt;/h2&gt;

&lt;p&gt;With content frozen, I moved to art generation. The design document specifies a "muted gouache and ink-wash illustration" style — desaturated, painterly, Eastern-European post-industrial. This is the style anchor.&lt;/p&gt;

&lt;p&gt;I am using a single AI model for this: &lt;strong&gt;MAI-Image-2.5-Pro&lt;/strong&gt;. &lt;/p&gt;

&lt;p&gt;I rejected other tools because their creations are public by default. I am not publishing art before the game ships. Privacy is a stop-loss. &lt;/p&gt;

&lt;p&gt;I generated a series of test images to validate the style anchor. Then I generated:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Character sheets and portraits for the Archivist and all visitors.&lt;/li&gt;
&lt;li&gt;Base background locations for the underground node and the besieged city.&lt;/li&gt;
&lt;li&gt;Postscript locations reflecting the slow decay after the finale.&lt;/li&gt;
&lt;li&gt;Flat papercut UI icons for the visible resources.&lt;/li&gt;
&lt;li&gt;The 1024×1024 app icon.&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%2Fd7v9obty0hgbmbh13mmb.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%2Fd7v9obty0hgbmbh13mmb.jpg" alt="Muted gouache and ink-wash character sheet for the Archivist in NICHLYST — the AI-generated style anchor: worn winter coat and rust scarf, journal desk in a besieged-city archive, plus turnaround views and collar detail studies." width="800" height="420"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Every single image was manually audited against a negative checklist: no real flags, no military insignia, no celebrity likenesses, no readable text, no brand logos. One image initially had a pattern too close to real military gear. I regenerated it with explicit instructions for nondescript, worn winter gear without any insignia, and passed the audit.&lt;/p&gt;

&lt;h2&gt;
  
  
  The 23:00 Rule Met Reality
&lt;/h2&gt;

&lt;p&gt;In my first devlog, I wrote about the 23:00 hard stop. I said an antifragile plan must absorb bad days.&lt;/p&gt;

&lt;p&gt;I had bad days.&lt;/p&gt;

&lt;p&gt;War breaks a person. Even a civilian one. It shatters the psyche. But paradoxically, it also builds a strange kind of callus. You start perceiving things not as acutely as you might have years ago. Explosions become background noise. But the internal pressure remains.&lt;/p&gt;

&lt;p&gt;On August 10th, I felt the pre-panic state creeping in. A tightness in the chest, shallow breathing. I knew exactly what it was — I've been treating GAD for over a year. It didn't escalate into a full attack, but the physical toll was there. Sitting at my desk, trying to write JSON while my nervous system was bracing for an impact that wasn't coming.&lt;/p&gt;

&lt;p&gt;On August 11th, Russian ballistic missiles hit Kyiv again. The explosions were loud. But I didn't panic. I just kept working. There is a strange detachment that develops — you hear the booms, you acknowledge the danger, and you return to the code. The anxiety that day wasn't about the missiles; it was the quiet, creeping dread of an exhausted mind.&lt;/p&gt;

&lt;p&gt;None of these days were perfectly productive hackathon days. The plan bent.&lt;/p&gt;

&lt;p&gt;But the plan did not break.&lt;/p&gt;

&lt;p&gt;On the morning of August 13th, I sat down, opened VS Code, and committed the final content freeze. The JSON was valid. The art was generated. The moral audit was complete. The structure was intact.&lt;/p&gt;

&lt;p&gt;The antifragile plan absorbed days of anxiety and war, and the project still shipped its milestones on schedule.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Tester Update: Surviving the Google Play 20 Testers Requirement
&lt;/h2&gt;

&lt;p&gt;In my first post, I asked for help with this exact bureaucracy. &lt;/p&gt;

&lt;p&gt;I now have 6 emails. &lt;/p&gt;

&lt;p&gt;It is progress, but the math is still unforgiving. I need 20 testers to bypass the 14-day closed testing wall before I can publish to production.&lt;/p&gt;

&lt;p&gt;So, I am putting this directly into this devlog again. If you are reading this, have an Android device, and 2 minutes to spare, please help a fellow builder bypass this bureaucratic wall.&lt;br&gt;
👉 &lt;a href="https://forms.gle/4hJkWsmuyKp3Tgtr8" rel="noopener noreferrer"&gt;https://forms.gle/4hJkWsmuyKp3Tgtr8&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;You will just need to accept an invite from Google Play later, download the app, and keep it installed for 14 days. No deep QA required. Just your presence to satisfy the algorithm.&lt;/p&gt;

&lt;h2&gt;
  
  
  What Comes Next
&lt;/h2&gt;

&lt;p&gt;The content is frozen. The art is generated (resizing and compression are deferred to pre-APK build — another stop-loss decision to avoid perfectionism). &lt;/p&gt;

&lt;p&gt;Next up: Phase 4 (Audio) and Phase 5 (Engineering — the game engine in HTML5/JS, reading from the JSON). The hardest part is still ahead. Writing a JavaScript state manager that tracks all these counters, flags, and ending conditions — all running inside a Capacitor Android wrapper on a phone.&lt;/p&gt;

&lt;p&gt;But the foundation is no longer fragile. It is concrete that was poured during a heatwave, under missile alerts, through the fog of GAD.&lt;/p&gt;

&lt;p&gt;NICHLYST is coming. And the content is frozen.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Follow this devlog for the #BuildInPublic journey.&lt;/em&gt;&lt;/p&gt;

</description>
      <category>buildinpublic</category>
      <category>shipaton</category>
      <category>indiedev</category>
      <category>gamedev</category>
    </item>
    <item>
      <title>Building NICHLYST: An Antifragile Approach to Surviving a Hackathon (and Reality)</title>
      <dc:creator>Eugene Kozlovsky</dc:creator>
      <pubDate>Sun, 09 Aug 2026 21:30:57 +0000</pubDate>
      <link>https://dev.to/koztechie/building-nichlyst-an-antifragile-approach-to-surviving-a-hackathon-and-reality-2ehn</link>
      <guid>https://dev.to/koztechie/building-nichlyst-an-antifragile-approach-to-surviving-a-hackathon-and-reality-2ehn</guid>
      <description>&lt;p&gt;I’m 36. From Kyiv.&lt;/p&gt;

&lt;p&gt;Right now, my city is suffocating under a heatwave. At night, the air raid sirens wail, and the psychological weight of a war with no end in sight presses down on every decision. I am typing this on an AMD A4-4020 with 8GB of RAM — a machine so weak it chokes on modern web browsers. If you are reading this while also grinding through the RevenueCat Shipaton 2026, you already know the unique pressure of shipping under extreme constraints.&lt;/p&gt;

&lt;p&gt;I am not a traditional developer. I am a practitioner of vibe coding with AI. To me, this isn't just a buzzword; it's a philosophy of orchestrating AI. It’s the ability to translate a feeling, an idea, or a complex vision into a precise prompt for a machine. It’s about bringing a concept to life, rather than getting bogged down in the syntax of writing code from scratch. By day, I work as a webmaster and system architect, navigating a full-time job and a complex medical regimen.&lt;/p&gt;

&lt;p&gt;Yet, I am entering the hackathon. Alone. Against teams with M2 Macs and budgets.&lt;/p&gt;

&lt;p&gt;Why? Because I don't just want to build an app. I want to prove that constraints don't kill projects. Fragility does.&lt;/p&gt;

&lt;p&gt;This is the story of NICHLYST — a slow narrative survival-archive sim for Android — and the 128-step antifragile plan I wrote to ensure it actually ships by September 28th.&lt;/p&gt;

&lt;h3&gt;
  
  
  The Concept: Survival as a Gameplay Loop
&lt;/h3&gt;

&lt;p&gt;In NICHLYST, you are an archivist in a besieged city. You don't fight. You record. Every day, someone knocks on your door with a story. You must choose: record the full truth and risk your safety, or edit it to survive. The consequences don't arrive immediately — they arrive days later, via a real-time push notification, forcing you to live with the anxiety of your choices.&lt;/p&gt;

&lt;p&gt;It’s a game about documentation, truth, and the civilian experience of conflict.&lt;/p&gt;

&lt;h3&gt;
  
  
  Phase 0: The Pre-Mortem
&lt;/h3&gt;

&lt;p&gt;Most hackathon projects die because developers start coding on day one. I started by writing a 128-step plan, but before writing a single line of code, I forced myself to do a pre-mortem.&lt;/p&gt;

&lt;p&gt;I listed the 10 most likely ways NICHLYST would fail. Here they are, raw and unfiltered:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;My weak local PC freezing during an Android build.&lt;/li&gt;
&lt;li&gt;Solo development bottlenecks.&lt;/li&gt;
&lt;li&gt;Missed evenings due to fatigue or schedule conflicts.&lt;/li&gt;
&lt;li&gt;Google Play App Review delays.&lt;/li&gt;
&lt;li&gt;AI art style mismatch or inconsistency.&lt;/li&gt;
&lt;li&gt;RevenueCat sandbox failure or SDK integration issues.&lt;/li&gt;
&lt;li&gt;Missing the hard deadline entirely due to scope creep.&lt;/li&gt;
&lt;li&gt;Psychological burnout.&lt;/li&gt;
&lt;li&gt;Catastrophic loss of content or code.&lt;/li&gt;
&lt;li&gt;Conflict with an existing 32-day evening schedule I must maintain.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Then, I applied Nassim Taleb's Antifragility framework. I didn't just want to mitigate these risks; I wanted the project to &lt;em&gt;benefit&lt;/em&gt; from the stress.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The Barbell Strategy:&lt;/strong&gt; I allocated 90% of my time to the core, guaranteed MVP. The other 10% is strictly time-boxed for bonus categories. If you are constantly researching how to avoid scope creep, the secret is that the "middle ground" — medium-risk, medium-reward tasks — must be entirely eliminated.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Stop-Loss Dates:&lt;/strong&gt; I hardcoded triggers into my plan. If the AI art pipeline isn't done by August 26th, I don't argue with myself. I automatically cut the art scope to a stark, 1-bit CSS visual filter and move on. Emotion has no place in the schedule.&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%2F3h32uhxr4he69mekvelr.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%2F3h32uhxr4he69mekvelr.jpg" alt="STOP_LOSS_DATES.md in VS Code: the antifragile stop-loss contract for the NICHLYST hackathon project — automatic scope-cut triggers, the August 26, 2026 art deadline, a 1-bit CSS visual fallback, and the ordered MVP cut list." width="800" height="420"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Developer Burnout Prevention
&lt;/h3&gt;

&lt;p&gt;To survive a solo hackathon, I rely on a strict checklist to protect my mental bandwidth:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Hard stop at 23:00.&lt;/li&gt;
&lt;li&gt;Sleep treated as a strategic investment.&lt;/li&gt;
&lt;li&gt;Zero guilt on low-energy days.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  The 23:00 Rule and the Reality of Mental Health
&lt;/h3&gt;

&lt;p&gt;My plan includes a strict rule: work ceases at 23:00. A tired solo dev creates more bugs than they fix. Sleep is a strategic investment.&lt;/p&gt;

&lt;p&gt;But that’s the theory. The reality is messier.&lt;/p&gt;

&lt;p&gt;Sometimes external factors — late shifts at my day job, or the air defense activity in Kyiv — make the 23:00 deadline impossible. And then there are the internal factors. I live with GAD and depression. There are days when there are no missiles, no extra work, but my brain simply refuses to cooperate. The energy tank is empty. On those days, the only achievable victory is making it to the shower and getting into bed.&lt;/p&gt;

&lt;p&gt;An antifragile plan can’t be a rigid, brittle clockwork. It must absorb these bad days. If I miss an evening due to a depressive episode, the Stop-Loss dates protect the project. The schedule bends, but it doesn't break. It accommodates the reality of being human.&lt;/p&gt;

&lt;h3&gt;
  
  
  Phase 1: Infrastructure and the Art of Being Broke
&lt;/h3&gt;

&lt;p&gt;I operate on a strict, near-zero budget. My monthly income is about $700 USD, and rent consumes half of it. The only financial investment I made was paying the $25 Google Play Console registration fee — a serious amount of money for me right now.&lt;/p&gt;

&lt;p&gt;The Shipaton sponsors heavily promoted Replit for cloud builds. Replit even offers a 40% discount for hackathon participants. But even with the discount, a Replit Pro subscription is financially impossible for me.&lt;/p&gt;

&lt;p&gt;So, I engineered a workaround. I set up a local development environment on my Xubuntu 26.04 LTS machine using VS Code. For the heavy lifting — compiling the Capacitor Android APK — I connected my GitHub repo to &lt;strong&gt;Codemagic's free tier&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;The setup wasn't painless. Capacitor v7 requires Java 21, but Codemagic defaulted to an older version. My first cloud build failed with &lt;code&gt;error: invalid source release: 21&lt;/code&gt;. A quick edit to the &lt;code&gt;codemagic.yaml&lt;/code&gt; file fixed the environment variables, and the build went green.&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%2F153xyd7z8hdpp7xprmi9.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%2F153xyd7z8hdpp7xprmi9.jpg" alt="Finished Codemagic CI/CD workflow " width="800" height="420"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;When I sideloaded that blank, debug APK onto my Xiaomi Redmi Note 9 Pro and saw it launch... it wasn't just a blank screen. It was proof that a pipeline running from a $0 local machine to a $0 cloud builder to a physical device actually worked. The cycle was closed before I wrote a single line of game logic.&lt;/p&gt;

&lt;h3&gt;
  
  
  The Google Play 20-Tester Bureaucracy
&lt;/h3&gt;

&lt;p&gt;Then came the infrastructure hurdle. Google Play now mandates a 14-day closed testing track with 20 active testers before a new personal account can publish to production.&lt;/p&gt;

&lt;p&gt;This is a bureaucratic nightmare for a solo dev with a ticking clock. If I wait until the game is done to start this timer, I will miss the deadline. So, I built a Google Form and posted it in the Shipaton Discord, my social channels, and sent it to colleagues.&lt;/p&gt;

&lt;p&gt;The result? Humbling.&lt;br&gt;
I got 3 emails from colleagues — my most loyal audience. From the Discord community? Zero. After 24 hours, the form remains agonizingly empty.&lt;/p&gt;

&lt;p&gt;I try not to succumb to panic. I try to filter out the noise, both from the outside world and my own anxiety. But the math is unforgiving: I need 20 testers, and I have 3.&lt;/p&gt;

&lt;p&gt;So, I am putting this directly into this devlog. If you are a fellow builder figuring out how to get 20 testers for Google Play, I see your struggle. But right now, I need your help to bypass this bureaucratic wall. If you have an Android device and 2 minutes to spare, please take action:&lt;br&gt;
👉 &lt;a href="https://forms.gle/4hJkWsmuyKp3Tgtr8" rel="noopener noreferrer"&gt;https://forms.gle/4hJkWsmuyKp3Tgtr8&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;You will just need to accept an invite from Google Play later, download the app, and keep it installed for 14 days. No deep QA required. Just your presence to satisfy the algorithm.&lt;/p&gt;

&lt;h3&gt;
  
  
  The Unseen Labor
&lt;/h3&gt;

&lt;p&gt;Right now, NICHLYST is just a 128-step plan, a handful of &lt;code&gt;.md&lt;/code&gt; files tracking risks and deadlines, a GitHub repository, and a &lt;code&gt;codemagic.yaml&lt;/code&gt; configuration. No art, no story code, no RevenueCat paywalls yet.&lt;/p&gt;

&lt;p&gt;But the foundation is poured. It is not fragile.&lt;/p&gt;

&lt;p&gt;The day before yesterday, after a 10-hour shift at my day job and helping my wife with her transcription work, I had zero energy left. I didn't code. I didn't force it. I followed the rule: I updated my &lt;code&gt;PLAN_STATE.md&lt;/code&gt; log with one sentence, did a &lt;code&gt;git commit&lt;/code&gt;, and went to sleep.&lt;/p&gt;

&lt;p&gt;In a hackathon, survival isn't about sprinting. It's about building a system that allows you to keep walking, even when the ground is shaking.&lt;/p&gt;

&lt;p&gt;NICHLYST is coming. And it will ship on time.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Follow this devlog for the #BuildInPublic journey.&lt;/em&gt;&lt;/p&gt;

</description>
      <category>buildinpublic</category>
      <category>shipaton</category>
      <category>indiedev</category>
      <category>gamedev</category>
    </item>
    <item>
      <title>One person, one project, one month: Building Svitlogics from Kyiv</title>
      <dc:creator>Eugene Kozlovsky</dc:creator>
      <pubDate>Sat, 12 Jul 2025 00:13:56 +0000</pubDate>
      <link>https://dev.to/koztechie/one-person-one-project-one-month-building-svitlogics-from-kyiv-318k</link>
      <guid>https://dev.to/koztechie/one-person-one-project-one-month-building-svitlogics-from-kyiv-318k</guid>
      <description>&lt;p&gt;&lt;em&gt;This is a submission for the &lt;a href="https://dev.to/challenges/wlh"&gt;World's Largest Hackathon Writing Challenge&lt;/a&gt;: Building with Bolt.&lt;/em&gt;&lt;/p&gt;




&lt;p&gt;I'm 34. From Kyiv. A city that has learned to live by the sound of sirens.&lt;br&gt;
Because of my health, office life is impossible for me. So my home became my studio.&lt;br&gt;
I’m not a professional programmer. I know some Python, that's it. My real strength is something else. A process known as "vibe coding"—the ability to translate a feeling, an idea, a vision into a clear prompt for a machine.&lt;/p&gt;

&lt;p&gt;When I saw the announcement for the World’s Largest Hackathon presented by Bolt, my first thought was crushing.&lt;br&gt;
&lt;em&gt;Who am I to compete?&lt;/em&gt;&lt;br&gt;
A 34-year-old guy from Ukraine. Not a developer. Alone. Against 70,000 participants. Against entire teams. It felt absurd.&lt;/p&gt;

&lt;p&gt;I almost gave up on the idea.&lt;br&gt;
But a day before registration closed, I asked myself: what did I really have to lose? Time? I waste it doomscrolling anyway. A prize? The odds were tiny.&lt;br&gt;
But the potential gain was incredible. Experience. And most importantly, a chance to build something that matters.&lt;/p&gt;

&lt;p&gt;That idea was already living in my head. &lt;strong&gt;&lt;a href="https://svitlogics.com/" rel="noopener noreferrer"&gt;Svitlogics&lt;/a&gt;&lt;/strong&gt;.&lt;br&gt;
The name combines the Ukrainian word "світло" (transliterated as svitlo, meaning "light") and "logics" for reason.&lt;br&gt;
This isn’t just a tool. It's my personal mission. Because I believe we must bring light into the information chaos. And while this is a global issue, here in Ukraine, the ability to see clearly has become an acute survival skill.&lt;/p&gt;

&lt;p&gt;On May 28th, I registered. On the 31st, I began.&lt;br&gt;
My first prompt was simple. Very simple. "Create the full structure for a web application. The design should have a brutalist vibe. The app's purpose is to analyze text." Then I hit "Enhance prompt" and let the AI work.&lt;/p&gt;

&lt;p&gt;The result was… magic.&lt;br&gt;
It wasn't what I envisioned, but it wasn't entirely wrong. Bolt.new had created a functional text analyzer, but one that focused on word counts and character metrics, not the nuanced analysis of manipulation I had in mind. But I didn't feel disappointment. I felt euphoria.&lt;br&gt;
That feeling when you see a first, wobbly, but incredibly important step.&lt;br&gt;
The foundation was laid. It was imperfect. It was raw. But it existed.&lt;br&gt;
The first light of Svitlogics had been switched on.&lt;/p&gt;

&lt;p&gt;Onwards.&lt;/p&gt;

&lt;h2&gt;
  
  
  Let me start with the questions…
&lt;/h2&gt;

&lt;p&gt;… The ones people ask.&lt;/p&gt;

&lt;p&gt;"Why does it look so... raw?"&lt;br&gt;
"Where are the shadows? The rounded corners?"&lt;br&gt;
"Isn't it a bit... primitive?"&lt;/p&gt;

&lt;p&gt;My answer is simple. And it’s always the same.&lt;br&gt;
This is not a mistake. It is a choice. It is a philosophy.&lt;/p&gt;

&lt;p&gt;I call it "Pure Minimalist-Brutalist."&lt;br&gt;
Because I believe form must serve substance. Not hide it.&lt;br&gt;
Svitlogics exists to cut through the noise. Its design must do the same. No exceptions.&lt;/p&gt;

&lt;p&gt;Modern UI uses soft shadows to create depth. It uses gentle gradients to feel "friendly."&lt;br&gt;
This is persuasion.&lt;br&gt;
It’s the same toolkit manipulators use, just with visuals instead of words.&lt;br&gt;
I refused to fall into that trap. For a tool that dissects lies, a "friendly" interface would be the biggest lie of all.&lt;/p&gt;

&lt;p&gt;So. No shadows. No gradients. No visual tricks.&lt;br&gt;
Because they are noise. Because they distract. Because they are not honest.&lt;/p&gt;

&lt;p&gt;The font is IBM Plex Mono. For one reason: absolute clarity. It sets a serious, analytical tone. It signals that this is a tool for thinking.&lt;br&gt;
The palette is even stricter. Black and white. For pure contrast.&lt;br&gt;
And one single color. Blue.&lt;br&gt;
It’s not for branding. It’s for function. It has only one job: to say, “You can act on this” or “This is a key result.”&lt;br&gt;
One color. One purpose.&lt;/p&gt;

&lt;p&gt;This design is not about being "beautiful."&lt;br&gt;
It is about being honest.&lt;br&gt;
And for a tool designed to fight for clarity, honesty is the only thing that matters.&lt;/p&gt;

&lt;p&gt;Unapologetically.&lt;/p&gt;

&lt;h2&gt;
  
  
  If the design is its soul…
&lt;/h2&gt;

&lt;p&gt;…the system prompt is its brain.&lt;br&gt;
It’s what turns a general AI into a specialized tool. And building it was the hardest part of this journey.&lt;/p&gt;

&lt;p&gt;My first approach was naive.&lt;br&gt;
I thought I could write one "universal" prompt for both English and Ukrainian.&lt;br&gt;
I fed it an English text. The result was… promising.&lt;br&gt;
Then I fed it a Ukrainian text.&lt;br&gt;
A total mess.&lt;br&gt;
The AI missed the cultural context. The scores were erratic. The justifications felt soull-ess.&lt;/p&gt;

&lt;p&gt;This wasn't a turning point; it was a confirmation of what I already knew, especially after Russia's full-scale invasion: language is a weapon. The way manipulative narratives are constructed in English-speaking media can be vastly different from the techniques used in texts aimed at a Ukrainian audience. A universal tool would be a blunt instrument where a scalpel was needed.&lt;br&gt;
So I threw out the universal idea. And started over.&lt;br&gt;
Two separate prompts. One for each language. Double the work. But it was the only way.&lt;/p&gt;

&lt;p&gt;The process became a relentless loop.&lt;br&gt;
Prompt. Test. Analyze. Refine. Repeat.&lt;/p&gt;

&lt;p&gt;First, the data. I realized my initial test texts were too simple.&lt;br&gt;
So I spent hours building a richer dataset. Corporate press releases. Political speeches. Opinion pieces. Even 20th-century propaganda.&lt;br&gt;
Then I did something more. I used another AI to create synthetic data. To generate perfect examples of disinformation, with every manipulation I wanted to teach Svitlogics to see. It was like a targeted training course for my AI.&lt;/p&gt;

&lt;p&gt;Then, the real grind began.&lt;br&gt;
I’d run a test. Svitlogics would miss a manipulation.&lt;br&gt;
Why?&lt;br&gt;
My prompt was too vague. "Look for logical fallacies" isn't enough.&lt;br&gt;
So I’d refine it.&lt;br&gt;
“Search for a 'Straw Man' argument. A 'Straw Man' is when you misrepresent someone's position to make it easier to attack. For example…”&lt;br&gt;
Then I’d run the test again. A small victory. It found the Straw Man. But it missed the Appeal to Emotion.&lt;br&gt;
Back to the prompt.&lt;br&gt;
Another iteration. Another test.&lt;br&gt;
For hours. Days. Living in this cycle.&lt;/p&gt;

&lt;p&gt;Every success was a jolt of pure ecstasy. Every failure, a command to think clearer. To instruct better.&lt;br&gt;
This is the unseen labor behind Svitlogics.&lt;br&gt;
It's not just an API call. It's a carefully calibrated instrument.&lt;br&gt;
And the prompts are its secret sauce. The result of dozens of hours of obsession.&lt;/p&gt;

&lt;h2&gt;
  
  
  The cascade eureka
&lt;/h2&gt;

&lt;p&gt;At first, Svitlogics was fragile. It relied on a single AI model.&lt;br&gt;
A choice born from pragmatism. I had a zero-dollar budget. It was free and generous.&lt;br&gt;
For a while, it worked.&lt;/p&gt;

&lt;p&gt;Then, during an intense testing session, it broke.&lt;br&gt;
429 Too Many Requests.&lt;br&gt;
I had hit the limit. My workflow, my focus — shattered.&lt;br&gt;
It was more than an annoyance. It was a fundamental flaw. A single point of failure.&lt;br&gt;
A tool for clarity cannot be this unreliable.&lt;/p&gt;

&lt;p&gt;The dilemma was classic. A smarter model with stricter limits? Or a dumber one that was always available?&lt;br&gt;
Neither was good enough. It needed to be smart &lt;em&gt;and&lt;/em&gt; resilient.&lt;/p&gt;

&lt;p&gt;The answer came to me while staring at the Google AI documentation.&lt;br&gt;
I didn't have to choose one model. I could use them all.&lt;br&gt;
The idea was a cascade. A sequential fallback system.&lt;br&gt;
It would try the best model first. If that failed, it wouldn't show an error. It would seamlessly try the next one. And the next.&lt;br&gt;
From a fragile pillar to a resilient, multi-layered structure.&lt;/p&gt;

&lt;p&gt;I built a priority list of seven models.&lt;br&gt;
The smartest and fastest at the top. The high-volume "workhorses" in the middle. The most limited one at the very end, defining the baseline for the whole app.&lt;br&gt;
This became the central nervous system.&lt;/p&gt;

&lt;p&gt;I chose to build this logic entirely on the client-side. In the browser.&lt;br&gt;
Simpler for a solo dev. Faster to deploy.&lt;br&gt;
I rewrote my API service into a recursive function.&lt;/p&gt;

&lt;p&gt;Here’s how it works.&lt;br&gt;
The system starts with the highest-priority model. It makes the API call.&lt;br&gt;
Success? Great. The job is done. The user gets their analysis.&lt;br&gt;
But if the API returns an error 429—meaning the model's quota is hit—there's no panic.&lt;br&gt;
The function simply calls itself again, this time targeting the next model down the cascade.&lt;br&gt;
This loop continues until it either gets a successful response or has tried all available models.&lt;br&gt;
To the user, it just works.&lt;/p&gt;

&lt;p&gt;Under the hood, it’s an elegant dance between seven different AI brains.&lt;br&gt;
Orchestrated by a few lines of JavaScript code running in the browser.&lt;br&gt;
Ensuring the light of Svitlogics never goes out. Even when some of its bulbs flicker.&lt;/p&gt;

&lt;h2&gt;
  
  
  My workflow: A symphony of two AIs
&lt;/h2&gt;

&lt;p&gt;I am not a programmer. Not a traditional one.&lt;br&gt;
This is the most important context.&lt;br&gt;
Svitlogics exists because of a new way to create. A process known as "vibe coding."&lt;br&gt;
It’s not about writing code from scratch. It's about having a clear vision and orchestrating AI to bring it to life.&lt;br&gt;
The project was built by a trio. Me, as the director. And two AI partners.&lt;/p&gt;

&lt;p&gt;Gemini was my strategic consultant. Bolt.new was my tireless engineer.&lt;br&gt;
A constant, fluid dance between them.&lt;/p&gt;

&lt;p&gt;I used Gemini as my architect and ultimate debugger. Before any big feature, I'd have a conversation with it. "I need to build a model cascade. What are the pitfalls?" It gave me blueprints. When Bolt's code broke, I didn't just ask Gemini to "fix it." I gave it the full context. The component. The prompt. The exact error. And it would find the root cause in seconds.&lt;/p&gt;

&lt;p&gt;Bolt.new was my implementation team. It built the project structure in minutes. My daily flow was simple: discuss logic with Gemini, refine details in Bolt's Discussion Mode, then execute with Build Mode. My job was to be the lead engineer. To review. To refine.&lt;br&gt;
This synergy is best illustrated by the story of when Svitlogics got "sick."&lt;/p&gt;

&lt;p&gt;It happened on June 8th. Until then, my confidence was soaring. The plan was simple: add a language switcher to the UI. I asked my strategist, Gemini. It recommended a solid, battle-tested library.&lt;br&gt;
But then, out of simple curiosity, I asked my engineer, Bolt, for its opinion. It suggested a different, lighter library. And I made a critical error. I deviated from my tested workflow and chose Bolt's suggestion.&lt;/p&gt;

&lt;p&gt;I gave it the prompt. "Install the library."&lt;br&gt;
And the machine I had come to trust turned against me.&lt;br&gt;
The application collapsed. A complete, catastrophic failure.&lt;/p&gt;

&lt;p&gt;My confidence evaporated. Frustration set in.&lt;br&gt;
I spent hours in a frantic loop. Feed error messages to Gemini. Translate its advice into prompts for Bolt. Each attempt was a gamble, burning my precious token allotment. The afternoon bled into evening. I was getting nowhere.&lt;/p&gt;

&lt;p&gt;Then, the air raid sirens began to wail in Kyiv.&lt;br&gt;
The dissonance was surreal. Wrestling with a broken app—a problem entirely of my own making—while the very real threat of missiles loomed outside. It was a stark reminder of priorities.&lt;/p&gt;

&lt;p&gt;In that moment, I had to make a decision. Not as a developer chasing perfection, but as a project manager with a hard deadline.&lt;br&gt;
I could keep fighting this losing battle. Keep burning time and tokens on a feature that wasn't core to the mission.&lt;br&gt;
Or I could retreat.&lt;/p&gt;

&lt;p&gt;I chose to retreat.&lt;br&gt;
The next day, my first command to Bolt was not to fix. It was to remove.&lt;br&gt;
"Delete the library. Remove every file. Every line of code. Revert everything."&lt;br&gt;
It felt like a small defeat. But it was the most mature decision I could make. I realized my desire for "one more feature" had endangered the entire project.&lt;/p&gt;

&lt;p&gt;A buggy app with a language switcher is infinitely worse than a stable, single-language app that does its job perfectly.&lt;br&gt;
This taught me the most valuable lesson of this hackathon.&lt;br&gt;
Ruthless prioritization.&lt;br&gt;
A great project is defined not just by what you add, but by what you wisely choose to remove.&lt;br&gt;
Sometimes, the most powerful move is not to write more code.&lt;br&gt;
It's to decisively delete it.&lt;/p&gt;

&lt;h2&gt;
  
  
  Building under fire: Finding focus amidst chaos
&lt;/h2&gt;

&lt;p&gt;This isn't just about code. It's about the human context.&lt;br&gt;
To build a hackathon project solo, with a full-time job, is a stretch.&lt;br&gt;
To do it from Kyiv, during a war, is to find focus in the heart of a storm.&lt;/p&gt;

&lt;p&gt;There were many nights like that. Nights that started not with code, but with the wail of air raid sirens. Nights that ended not with a successful commit, but with the grimly familiar routine: running to the hallway, waiting for the thud of our air defense systems at work, and hoping for the silence that follows.&lt;br&gt;
The next morning, you wake up a wreck. Utterly sleep-deprived.&lt;br&gt;
For me, sleep isn't a luxury; it's a pillar of stability. Its absence is a trigger for my depressive disorder. On those days, the energy tank is empty. Zero code gets written. Zero progress is made on Svitlogics.&lt;/p&gt;

&lt;p&gt;This is the part of the story often left out of triumphant tech narratives. The grind. The exhaustion. The many days where you produce nothing at all.&lt;br&gt;
And it was on one of those days, running on fumes, that I realized my perfectionism had become a trap.&lt;br&gt;
I was obsessed with making the system prompt "perfect." But the list of other essential tasks was a mountain.&lt;br&gt;
I was getting bogged down. Watering a single tree, while a whole ecosystem needed attention.&lt;/p&gt;

&lt;p&gt;So I made a strategic decision.&lt;br&gt;
I split my time into two tracks.&lt;br&gt;
&lt;strong&gt;Track 1: The Engineer.&lt;/strong&gt; Deep, focused work on the AI brain. Reserved for the days when I had strength.&lt;br&gt;
&lt;strong&gt;Track 2: The Product Manager.&lt;/strong&gt; Everything else. UI fixes, writing content, project planning. Less cognitively demanding tasks for the days of exhaustion.&lt;br&gt;
It was a way to keep moving forward. Always.&lt;/p&gt;

&lt;p&gt;This was a turning point. It felt empowering.&lt;br&gt;
It taught me that discipline isn't about forcing yourself to work when you have no energy left.&lt;br&gt;
It’s about being smart with the energy you have.&lt;br&gt;
On some days, progress is a complex new feature. On others, it’s fixing a single bug.&lt;br&gt;
Both are valid. Both are steps forward.&lt;/p&gt;

&lt;p&gt;This journey has been a lesson in resilience.&lt;br&gt;
The drive to create something meaningful is a powerful anchor in a sea of chaos.&lt;br&gt;
And the quiet act of writing a line of code, of building a tool for clarity while the world outside explodes, feels like a small act of defiance.&lt;br&gt;
An act of hope.&lt;br&gt;
And that is the most powerful motivator of all.&lt;/p&gt;




&lt;p&gt;34 days. What started as a last-minute decision has become one of the most rewarding experiences of my life.&lt;br&gt;
Svitlogics is now a real, working application. But for me, it is so much more.&lt;/p&gt;

&lt;p&gt;I came into this not as a programmer, but as a "vibe coder."&lt;br&gt;
I had a vision, a deep "why." But I believed my lack of traditional skills was a barrier.&lt;br&gt;
I was wrong.&lt;br&gt;
This journey proved to me that tools like Bolt.new are powerful equalizers. They shatter the walls between an idea and its execution. The future of building is here. And it’s incredibly empowering.&lt;/p&gt;

&lt;p&gt;Svitlogics itself is a project with a clear philosophy.&lt;br&gt;
It's not a judge that gives you answers. It's a scalpel to help you ask better questions.&lt;br&gt;
Its mission is to empower your own critical thinking. This belief is in every part of it—from its honest, brutalist design to its core analysis. Because the most effective way to fight disinformation is not by creating a new source of truth, but by fostering more critical minds.&lt;/p&gt;

&lt;p&gt;Building this from Kyiv gave it a meaning I never expected.&lt;br&gt;
The sounds of war are not a footnote to this story. They are the ink it was written with.&lt;br&gt;
It transformed a technical exercise into a personal act of resistance.&lt;/p&gt;

&lt;p&gt;I don't know if I will win. But I know I have already won the most important prize.&lt;br&gt;
The experience of building something I believe in, from zero.&lt;br&gt;
The lesson of managing a project, facing failure, and finding focus in chaos.&lt;br&gt;
The proof that my personal challenges do not define me.&lt;/p&gt;

&lt;p&gt;Whether it wins an award or not, Svitlogics exists. It works. It was built with purpose.&lt;br&gt;
For a solo developer from Kyiv, that is everything.&lt;/p&gt;

</description>
      <category>devchallenge</category>
      <category>wlhchallenge</category>
      <category>bolt</category>
      <category>ai</category>
    </item>
  </channel>
</rss>
