<?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: Jeremy Longshore</title>
    <description>The latest articles on DEV Community by Jeremy Longshore (@jeremy_longshore).</description>
    <link>https://dev.to/jeremy_longshore</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F3842419%2Ff5237770-f824-4823-a2bd-5a4ccb9b252f.png</url>
      <title>DEV Community: Jeremy Longshore</title>
      <link>https://dev.to/jeremy_longshore</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/jeremy_longshore"/>
    <language>en</language>
    <item>
      <title>Braves Booth — Idle Recap, Dashboard Density, and AI Pitcher Narratives</title>
      <dc:creator>Jeremy Longshore</dc:creator>
      <pubDate>Thu, 09 Apr 2026 21:18:19 +0000</pubDate>
      <link>https://dev.to/jeremy_longshore/braves-booth-idle-recap-dashboard-density-and-ai-pitcher-narratives-5boc</link>
      <guid>https://dev.to/jeremy_longshore/braves-booth-idle-recap-dashboard-density-and-ai-pitcher-narratives-5boc</guid>
      <description>&lt;p&gt;Between games, the Braves Booth dashboard used to show a tab bar with RECAP and PREVIEW buttons. Pick one. The problem: announcers don't want to choose. They want the full picture — what just happened and what's coming next — on a single screen with no clicks.&lt;/p&gt;

&lt;p&gt;Two commits, 29 files, 600 lines changed. The idle view got rebuilt, the entire UI got tighter, and the AI pitcher narrative system got replaced with something that actually works.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Idle View Problem
&lt;/h2&gt;

&lt;p&gt;The old between-games state showed either a recap or a preview, toggled by tab buttons in the GameStateBar. This forced a decision that shouldn't exist. When there's no live game, the announcer needs both: the line score and AI recap from the last game, plus a clickable card previewing the next one.&lt;/p&gt;

&lt;p&gt;The fix removes the tab buttons entirely. The idle page now renders the full recap — line score plus AI-generated game summary — with a next-game preview card below it. Click the preview card and it expands into the full pregame view.&lt;/p&gt;

&lt;p&gt;The backend side needed a new pregame route that fetches game context from the MLB schedule API for future games. The existing schedule fetcher only handled today's games. Now it looks ahead, pulls the matchup, and caches it with a &lt;code&gt;TTL.GAME_SCHEDULE&lt;/code&gt; of 300 seconds.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// DRY pregame fallback — one helper instead of three scattered checks&lt;/span&gt;
&lt;span class="k"&gt;static&lt;/span&gt; &lt;span class="nf"&gt;fromGameState&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;gameState&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;GameState&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt; &lt;span class="nx"&gt;PregameContext&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="na"&gt;homeTeam&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;gameState&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;homeTeam&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;awayTeam&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;gameState&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;awayTeam&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;probablePitchers&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;gameState&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;probablePitchers&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;venue&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;gameState&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;venue&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;gameTime&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;gameState&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;gameTime&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;PR review caught the duplication early. Three different components were building pregame fallback objects with slightly different shapes. The &lt;code&gt;fromGameState()&lt;/code&gt; helper killed all three.&lt;/p&gt;

&lt;h2&gt;
  
  
  Dashboard Density Overhaul
&lt;/h2&gt;

&lt;p&gt;The dashboard runs on a three-column grid. Before this refactor, the columns had loose padding and inconsistent widths across breakpoints. The new layout uses responsive rails: 280px at the smallest breakpoint, 320px at medium, 360px at large.&lt;/p&gt;

&lt;p&gt;Changes that sound small but affect every panel:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;GameStateBar&lt;/strong&gt; got a gradient background with larger score and inning text. The old bar had redundant labels — "Home:" and "Away:" before team names that were already obvious from the logo and position. Removed.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;PanelHeader&lt;/strong&gt; gained a &lt;code&gt;variant&lt;/code&gt; prop. The center column uses &lt;code&gt;primary&lt;/code&gt; (red accent border) to visually anchor it as the main content area. Side columns use the default.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Weather card&lt;/strong&gt; absorbed the Wind Impact panel. Two separate cards for weather and wind was a waste of vertical space. One card, two sections.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Ticker&lt;/strong&gt; got slowed from its original scroll speed to 75 seconds per cycle and reduced in height. The old ticker was distracting during live broadcasts.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  The Font Floor Sweep
&lt;/h2&gt;

&lt;p&gt;The second commit started with a specific fix — pitcher scouting reports — and turned into a codebase-wide audit. While tightening the center column, I found &lt;code&gt;text-[7px]&lt;/code&gt; and &lt;code&gt;text-[8px]&lt;/code&gt; classes scattered across 15 files. Sub-10px text on a dashboard meant for glancing during a live broadcast is useless.&lt;/p&gt;

&lt;p&gt;Every instance got bumped. OnDeckPanel, CohostPanel, BullpenPanel — all labels moved to a 10px minimum. The ghost number (a decorative large number behind panel content) got removed entirely. It looked clever in the mockup and was visual noise in practice.&lt;/p&gt;

&lt;p&gt;The center column went full TweetDeck-density: reduced padding, tighter gaps, more data per viewport pixel.&lt;/p&gt;

&lt;h2&gt;
  
  
  Replacing AI Narrative Duplication
&lt;/h2&gt;

&lt;p&gt;The PitcherCard had an AI narrative block that was supposed to give announcers a quick scouting summary. The problem: it duplicated the same narrative generation logic that existed elsewhere, and the output was unstructured prose that varied wildly between pitchers.&lt;/p&gt;

&lt;p&gt;The replacement is a dedicated &lt;code&gt;buildPitcherFacts()&lt;/code&gt; function in a new file:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// frontend/src/lib/build-facts.ts&lt;/span&gt;

&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;buildPitcherFacts&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;pitcher&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;PitcherStats&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt;&lt;span class="p"&gt;[]&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;facts&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt;&lt;span class="p"&gt;[]&lt;/span&gt; &lt;span class="o"&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;pitcher&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;era&lt;/span&gt; &lt;span class="o"&gt;!==&lt;/span&gt; &lt;span class="kc"&gt;undefined&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;facts&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;push&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;`ERA &lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;pitcher&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;era&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;toFixed&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;2&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;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;pitcher&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;strikeouts&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="nx"&gt;pitcher&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;inningsPitched&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;kPer9&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;pitcher&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;strikeouts&lt;/span&gt; &lt;span class="o"&gt;/&lt;/span&gt; &lt;span class="nx"&gt;pitcher&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;inningsPitched&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="mi"&gt;9&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="nx"&gt;facts&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;push&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;`&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;kPer9&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;toFixed&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="s2"&gt; K/9`&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;

  &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;pitcher&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;whip&lt;/span&gt; &lt;span class="o"&gt;!==&lt;/span&gt; &lt;span class="kc"&gt;undefined&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;facts&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;push&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;`WHIP &lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;pitcher&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;whip&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;toFixed&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;2&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="c1"&gt;// Velocity trend, pitch arsenal, awards follow the same pattern&lt;/span&gt;
  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;facts&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;78 lines. Structured output. Every fact is a string the announcer can read verbatim: "ERA 3.42", "11.2 K/9", "WHIP 1.08". No prose, no variation, no AI hallucination risk. The stats come from the MLB API, not from an LLM making up numbers.&lt;/p&gt;

&lt;p&gt;When stats aren't available — early season, minor league callup, whatever — the function returns an empty array and the card falls back to the basic stat line. No "Unable to generate narrative" error states.&lt;/p&gt;

&lt;h2&gt;
  
  
  H2H Matchup Polish
&lt;/h2&gt;

&lt;p&gt;The head-to-head matchup display got two changes that matter more than they sound:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Larger text.&lt;/strong&gt; The batter-vs-pitcher matchup numbers were the same size as surrounding panel text. They should be the focal point. Bumped up.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;OPS conditional coloring.&lt;/strong&gt; An OPS over .800 against the current pitcher is green. Under .600 is red. Between is neutral. The announcer glances at the card and instantly knows if the batter owns this pitcher or struggles against him. A summary callout line below reinforces it in plain English.&lt;/p&gt;

&lt;h2&gt;
  
  
  The CSS Hover Fix
&lt;/h2&gt;

&lt;p&gt;The PR review flagged React &lt;code&gt;onMouseEnter&lt;/code&gt;/&lt;code&gt;onMouseLeave&lt;/code&gt; handlers on the next-game preview card. These were controlling a hover state that CSS can handle natively:&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="nc"&gt;.next-game-card&lt;/span&gt;&lt;span class="nd"&gt;:hover&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;scale&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="m"&gt;1.02&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="nl"&gt;border-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;--braves-red&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 state management. No re-renders. CSS does this better in every dimension.&lt;/p&gt;

&lt;h2&gt;
  
  
  What Changed
&lt;/h2&gt;

&lt;p&gt;Two commits. 605 insertions, 335 deletions across 29 files. One new file (&lt;code&gt;build-facts.ts&lt;/code&gt;). The dashboard went from a tab-based idle view to a unified recap-plus-preview layout. Every font in the codebase is now 10px or larger. The AI pitcher narrative system went from duplicated LLM prose to a deterministic fact builder that announcers can trust.&lt;/p&gt;

&lt;p&gt;The pattern is the same one that keeps showing up on this project: broadcast tools need to be glanceable, trustworthy, and zero-interaction. Every change here moved in that direction.&lt;/p&gt;

&lt;h3&gt;
  
  
  Related Posts
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;a href="https://dev.to/posts/building-production-multi-agent-ai-brightstream-vertex-ai/"&gt;Building a Production Multi-Agent AI System&lt;/a&gt; — multi-agent architecture patterns relevant to the AI narrative pipeline&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://dev.to/posts/building-ai-friendly-codebase-documentation-real-time-claude-md-creation-journey/"&gt;Building an AI-Friendly Codebase&lt;/a&gt; — the CLAUDE.md-driven development approach used across this project&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://dev.to/posts/building-production-grade-testing-infrastructure-playwright-case-study/"&gt;Building Production-Grade Testing Infrastructure&lt;/a&gt; — testing patterns that apply to dashboard components&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>react</category>
      <category>typescript</category>
      <category>fullstack</category>
      <category>aiagents</category>
    </item>
    <item>
      <title>Software Supply Chain Security After Axios</title>
      <dc:creator>Jeremy Longshore</dc:creator>
      <pubDate>Mon, 06 Apr 2026 18:18:09 +0000</pubDate>
      <link>https://dev.to/jeremy_longshore/software-supply-chain-security-after-axios-3dh4</link>
      <guid>https://dev.to/jeremy_longshore/software-supply-chain-security-after-axios-3dh4</guid>
      <description>&lt;p&gt;On March 31, 2026, attackers published two malicious versions of axios — a package with roughly 100 million weekly npm downloads — during a window of a little over three hours. Google Threat Intelligence Group attributed the campaign to UNC1069, a North Korea-nexus threat actor. The malicious releases introduced a dependency that used a postinstall script to deploy a cross-platform remote access trojan.&lt;/p&gt;

&lt;p&gt;During that window, any CI/CD pipeline or developer workstation that freshly resolved the affected versions and allowed lifecycle scripts to run could have been compromised. Projects with previously committed lockfiles were far less likely to be affected.&lt;/p&gt;

&lt;p&gt;A 38-page academic paper had already explained exactly how and why this would happen.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Paper That Called It
&lt;/h2&gt;

&lt;p&gt;Williams et al. published "Research Directions in Software Supply Chain Security" in ACM TOSEM in May 2025. Fifteen researchers from the NSF-funded S3C2 consortium synthesized findings from eight summits with 131 practitioners across 42 industrial organizations and 15 US government agencies. They identified three attack vectors: code dependencies, build infrastructure, and humans.&lt;/p&gt;

&lt;p&gt;The axios compromise hit all three simultaneously.&lt;/p&gt;

&lt;p&gt;I read this paper the same week the axios incident broke. The overlap is not a coincidence — it's confirmation that we have a well-understood problem and a deeply inadequate response.&lt;/p&gt;

&lt;h2&gt;
  
  
  What Happened
&lt;/h2&gt;

&lt;p&gt;The group Google Threat Intelligence tracks as UNC1069 (Microsoft calls them Sapphire Sleet) compromised the npm account of axios maintainer jasonsaayman. They published two versions — 1.14.1 and 0.30.4 — targeting both the current and legacy release lines. Both added a new dependency: &lt;code&gt;plain-crypto-js&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;That dependency's &lt;code&gt;postinstall&lt;/code&gt; hook downloaded a platform-specific RAT from &lt;code&gt;sfrclak[.]com:8000&lt;/code&gt;. macOS, Windows, Linux — all covered. In npm, &lt;code&gt;postinstall&lt;/code&gt; scripts run automatically during &lt;code&gt;npm install&lt;/code&gt;. No user interaction required. No warning displayed.&lt;/p&gt;

&lt;p&gt;The exposure window was a little over three hours before community detection triggered a takedown. That's a long time for a package installed roughly 100 million times per week.&lt;/p&gt;

&lt;p&gt;The initial compromise path was familiar — credential theft followed by a malicious publish. But the downstream payload was serious: a staged dependency, a cross-platform RAT with macOS/Windows/Linux coverage, and cleanup behavior to hide the postinstall evidence. It worked against one of the most popular packages in the ecosystem.&lt;/p&gt;

&lt;h2&gt;
  
  
  Vector 1: The Dependency Problem
&lt;/h2&gt;

&lt;p&gt;Sonatype logged &lt;strong&gt;512,847 malicious packages&lt;/strong&gt; in 2024 — a &lt;strong&gt;156% increase&lt;/strong&gt; year-over-year. Meanwhile, 96% of commercial code bases contain open source components, and 77% of all code is open source (Synopsys). The attack surface grows faster than detection capability.&lt;/p&gt;

&lt;p&gt;The paper documents that detection tools for malicious packages have &lt;strong&gt;false positive rates exceeding 15%&lt;/strong&gt; (Vu et al.). At that rate, automated blocking is impractical — you can't reject one in seven legitimate packages. So most malicious packages get through, and the ones that get caught are caught by humans who happen to notice.&lt;/p&gt;

&lt;p&gt;The axios attacker exploited the most basic trust assumption in package management: that a published version of a popular package is safe, and that its dependencies are reviewed. &lt;code&gt;plain-crypto-js&lt;/code&gt; was a brand-new package. Nobody reviewed it because postinstall hooks execute automatically.&lt;/p&gt;

&lt;p&gt;The broader picture is worse. Three thousand popular npm packages (over 10K monthly downloads each) are abandoned. Vulnerabilities remain undiscovered for an average of &lt;strong&gt;three years&lt;/strong&gt;. When they are discovered, 63% of security advisories in the GitHub Advisory Database lack patch links. We're failing to respond to known vulnerabilities, let alone prevent new attacks.&lt;/p&gt;

&lt;h2&gt;
  
  
  Vector 2: Your CI/CD Pipeline Is the Weapon
&lt;/h2&gt;

&lt;p&gt;Any pipeline or workstation that freshly resolved axios during the exposure window and allowed lifecycle scripts to run was a potential victim. The RAT didn't need to trick a developer. It just needed the build pipeline to do what build pipelines do: resolve dependencies and run scripts. Projects with committed lockfiles that didn't update were not affected.&lt;/p&gt;

&lt;p&gt;The paper found that only &lt;strong&gt;20% of software builds&lt;/strong&gt; match bit-to-bit when reproduced. Eighty percent of builds can't prove they haven't been tampered with. The ARGUS study analyzed 2.8 million GitHub Actions workflows across one million repositories and found code injection vulnerabilities in 4,307 of them.&lt;/p&gt;

&lt;p&gt;SolarWinds demonstrated the category in 2020 — attackers compromised a vendor's build and update channel to sign malicious code with official keys. The axios incident is a different mechanic (registry publish via a compromised maintainer account) but the same underlying failure: build systems trust what they install, and attackers exploit that trust at whatever layer is weakest.&lt;/p&gt;

&lt;p&gt;SLSA (Supply-chain Levels for Software Artifacts) defines maturity levels for build provenance. Sigstore has accumulated over 2.2 million signatures across critical software projects. But most organizations have no provenance metadata, no build attestation, no verification. The tools exist. The adoption doesn't.&lt;/p&gt;

&lt;h2&gt;
  
  
  Vector 3: The Human Problem Has No Patch
&lt;/h2&gt;

&lt;p&gt;The axios attack started with a compromised human. Jasonsaayman's npm credentials were the entry point. Everything else followed from that single point of failure.&lt;/p&gt;

&lt;p&gt;The 2024 Tidelift survey found that &lt;strong&gt;60% of open source maintainers are unpaid&lt;/strong&gt;. They spend 11% of their time on security — up from 4% in 2021 — a nearly threefold increase in security burden with no increase in compensation. Only 14% have formal processes to prioritize security issues.&lt;/p&gt;

&lt;p&gt;The xz-utils attack in 2024 showed the extreme case: a multi-year social engineering campaign targeted a burned-out solo maintainer. The attacker gained trust, became a co-maintainer, and inserted a backdoor into a compression library used by virtually every Linux distribution. The paper's authors admit: "it is unclear if any existing security practice could have meaningfully prevented or even detected the xz-utils attack."&lt;/p&gt;

&lt;p&gt;You can mandate 2FA. You can enforce credential rotation. But the human attack surface is not a technical problem with a technical solution. It's a systemic problem rooted in how open source is funded, maintained, and governed.&lt;/p&gt;

&lt;h2&gt;
  
  
  What to Do Monday Morning
&lt;/h2&gt;

&lt;p&gt;The paper catalogs frameworks — SLSA, SBOMs, OpenSSF Scorecard, Sigstore — and documents their limitations. OpenSSF Scorecard has only 9 of 18 metrics that are reliably computable, and its ability to predict actual vulnerabilities is weak (R-squared of 9-12%, meaning it explains almost none of the variance). SBOMs are mandated by Executive Order 14028, but 71% of NVD entries lack the patch links that make them actionable.&lt;/p&gt;

&lt;p&gt;The frameworks matter, but none of them would have stopped the axios attack alone. Defense-in-depth is the only viable strategy. Six steps, ordered by effort:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;1. Pin exact dependency versions with integrity hashes.&lt;/strong&gt; Stop resolving to latest. If your lockfile specified axios 1.14.0 with a hash, the malicious 1.14.1 never installed.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;2. Disable postinstall scripts in CI.&lt;/strong&gt; Run &lt;code&gt;npm install --ignore-scripts&lt;/code&gt; and whitelist explicitly. The axios RAT relied entirely on a postinstall hook. No execution, no compromise.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;3. Enable 2FA on every package registry account.&lt;/strong&gt; npm, PyPI, RubyGems — all of them. The axios attacker got in through compromised credentials.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;4. Run OpenSSF Scorecard on your critical dependencies.&lt;/strong&gt; It's imperfect, but it surfaces obvious red flags: no branch protection, no code review, no signed releases.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;5. Adopt SLSA level 1 for your own builds.&lt;/strong&gt; Generate provenance metadata. It costs almost nothing and gives you a baseline for build integrity.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;6. Fund your dependencies.&lt;/strong&gt; If 60% of maintainers are unpaid and your business runs on their code, your security budget should include them. Tidelift, GitHub Sponsors, Open Collective — pick one.&lt;/p&gt;

&lt;p&gt;None of these are sufficient alone. The xz-utils attack would have bypassed most of them. That's the point — make the attacker's job harder at every layer.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Gap Between Knowledge and Action
&lt;/h2&gt;

&lt;p&gt;The Williams et al. paper is not a prediction. It's a diagnosis. The researchers documented failure modes that practitioners already know about, backed by data from 131 industry participants who confirmed: yes, these are the problems, and no, we haven't solved them.&lt;/p&gt;

&lt;p&gt;The axios compromise didn't reveal a new vulnerability. It demonstrated an old one, at scale, against a trusted target, by a nation-state-linked adversary. The three-hour window between compromise and remediation is a testament to the community's response speed — but lockfiles, &lt;code&gt;npm ci&lt;/code&gt;, and &lt;code&gt;--ignore-scripts&lt;/code&gt; would have blocked exposure without relying on detection at all. Response speed was the most visible defense that worked in real time. It shouldn't have been the primary one.&lt;/p&gt;

&lt;p&gt;Read the paper. It's 38 pages. It won't make your dependencies safe. But it will show you exactly where the risks are, what exists to counter them, and why none of it is enough yet.&lt;/p&gt;

</description>
      <category>security</category>
      <category>supplychain</category>
      <category>opensource</category>
      <category>cicd</category>
    </item>
    <item>
      <title>IntentCAD Viewer — Closing the DWG FastView Gap</title>
      <dc:creator>Jeremy Longshore</dc:creator>
      <pubDate>Sun, 29 Mar 2026 04:31:42 +0000</pubDate>
      <link>https://dev.to/jeremy_longshore/intentcad-viewer-closing-the-dwg-fastview-gap-2fdk</link>
      <guid>https://dev.to/jeremy_longshore/intentcad-viewer-closing-the-dwg-fastview-gap-2fdk</guid>
      <description>&lt;p&gt;DWG FastView is the benchmark. It's the viewer AEC professionals already use. It loads fast, zooms smooth, and never locks the UI on a 50MB drawing. If your browser-based CAD viewer doesn't match that bar, nobody will trust it with real work.&lt;/p&gt;

&lt;p&gt;IntentCAD's viewer wasn't there yet. It worked. But "works" and "feels right" are different categories. The drawing would freeze for a few seconds on load. Zoom was instant — which sounds good until you realize instant zoom with no interpolation is disorienting. And when you selected three entities, they all turned the same shade of blue. Good luck telling them apart.&lt;/p&gt;

&lt;p&gt;Two PRs this week fixed all of it.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Main Thread Problem
&lt;/h2&gt;

&lt;p&gt;The dxf-viewer library does everything on the main thread by default. Font loading, fetching, DXF parsing, geometry preparation — all of it blocks the UI. On a small test drawing, you don't notice. On a real architectural floor plan, you get 2-4 seconds of frozen interface.&lt;/p&gt;

&lt;p&gt;The fix is three lines in a web worker file:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// web/frontend/src/workers/dxf-viewer-worker.js&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;DxfViewer&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;dxf-viewer&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="nx"&gt;DxfViewer&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;SetupWorker&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Three lines. The library already supports worker-based parsing — it just needs a worker file and a factory function. The viewer component passes both:&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;workerFactory&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt;
  &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;Worker&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;URL&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;../workers/dxf-viewer-worker.js&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;import&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;meta&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;url&lt;/span&gt;&lt;span class="p"&gt;),&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;module&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="p"&gt;});&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now DXF parsing runs off the main thread. The UI stays responsive during load. No freezing.&lt;/p&gt;

&lt;h2&gt;
  
  
  Progress Feedback
&lt;/h2&gt;

&lt;p&gt;A responsive UI during load is necessary but not sufficient. The user still needs to know something is happening. DWG FastView shows a progress bar. We need one too.&lt;/p&gt;

&lt;p&gt;The dxf-viewer library exposes a &lt;code&gt;progressCbk&lt;/code&gt; callback with phase labels and byte counts:&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;progressCbk&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;phase&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;processedSize&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;totalSize&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;labels&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;font&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Loading fonts&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;fetch&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Downloading&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;parse&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Parsing&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;prepare&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Preparing&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="p"&gt;};&lt;/span&gt;
  &lt;span class="nf"&gt;setLoadPhase&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;labels&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;phase&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="nx"&gt;phase&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;phase&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;fetch&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="nx"&gt;totalSize&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nf"&gt;setLoadProgress&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;Math&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;round&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="nx"&gt;processedSize&lt;/span&gt; &lt;span class="o"&gt;/&lt;/span&gt; &lt;span class="nx"&gt;totalSize&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="mi"&gt;100&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;else&lt;/span&gt; &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;phase&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;prepare&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nf"&gt;setLoadProgress&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;90&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;Four phases, human-readable labels, a 4px progress bar with smooth CSS transitions. The user sees "Downloading... 34%" instead of a blank canvas.&lt;/p&gt;

&lt;h2&gt;
  
  
  Animated Zoom
&lt;/h2&gt;

&lt;p&gt;The old zoom behavior: click the + button, camera jumps to 1.3x. Instant. No interpolation.&lt;/p&gt;

&lt;p&gt;The problem is spatial orientation. When the view changes instantly, your brain has to re-locate where you are in the drawing. FastView animates its zoom. Every professional CAD viewer animates its zoom. There's a reason.&lt;/p&gt;

&lt;p&gt;The replacement uses requestAnimationFrame with an ease-out cubic curve:&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;animateZoom&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;useCallback&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="nx"&gt;targetZoom&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;startZoom&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;cam&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;zoom&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;startTime&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;performance&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;now&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;duration&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="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;step&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;now&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;t&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;Math&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;min&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="nx"&gt;now&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="nx"&gt;startTime&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;/&lt;/span&gt; &lt;span class="nx"&gt;duration&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="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;ease&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="nx"&gt;t&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;**&lt;/span&gt; &lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="c1"&gt;// ease-out cubic&lt;/span&gt;
    &lt;span class="nx"&gt;cam&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;zoom&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;startZoom&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;targetZoom&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="nx"&gt;startZoom&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="nx"&gt;ease&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="nx"&gt;cam&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;updateProjectionMatrix&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
    &lt;span class="nx"&gt;viewer&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;Render&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
    &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;t&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="nf"&gt;requestAnimationFrame&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;step&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="p"&gt;};&lt;/span&gt;
  &lt;span class="nf"&gt;requestAnimationFrame&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;step&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;200ms. Fast enough to not feel sluggish, slow enough for the eye to track the transition. The cubic ease-out decelerates at the end — the zoom settles rather than stopping dead.&lt;/p&gt;

&lt;h2&gt;
  
  
  Color-Cycling Selections
&lt;/h2&gt;

&lt;p&gt;When you select multiple entities in the viewer, you need to distinguish them visually. The old behavior gave every selection the same blue highlight. Select five entities and you get five blue outlines with no way to match them to their entries in the operations panel.&lt;/p&gt;

&lt;p&gt;The fix is a six-color palette applied by index: blue, emerald, amber, purple, pink, cyan. Each selected entity gets its own color in both the viewer overlay and the entity tag in the sidebar. Selection #1 is blue, #2 is emerald, #3 is amber, and so on.&lt;/p&gt;

&lt;p&gt;Six colors is intentional. You rarely select more than six entities at once, and the palette cycles if you do. The colors are chosen for contrast on both dark backgrounds (the viewer canvas) and light backgrounds (the operations panel).&lt;/p&gt;

&lt;h2&gt;
  
  
  Click-to-Focus Operations
&lt;/h2&gt;

&lt;p&gt;The operations panel lists planned changes. "Move HVAC-UNIT-01 to (240, 180)." Useful, but which entity is HVAC-UNIT-01? On a dense drawing, good luck finding it.&lt;/p&gt;

&lt;p&gt;Now each operation row is clickable. The backend adds &lt;code&gt;bounds&lt;/code&gt; to each operation via a new &lt;code&gt;_op_bounds()&lt;/code&gt; helper that looks up the target entity's bounding box. The frontend calls &lt;code&gt;focusOnBounds()&lt;/code&gt; to pan and zoom the viewer to the target entity with a highlight ring.&lt;/p&gt;

&lt;p&gt;Click an operation. The viewer flies to it. Simple interaction, large usability gain.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Small Stuff
&lt;/h2&gt;

&lt;p&gt;Two things that don't warrant their own section but matter:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Accessibility fix.&lt;/strong&gt; Operation checkboxes had been changed from &lt;code&gt;&amp;lt;label&amp;gt;&lt;/code&gt; to &lt;code&gt;&amp;lt;div&amp;gt;&lt;/code&gt; elements. Screen readers couldn't associate the checkbox with its text. Restored proper &lt;code&gt;&amp;lt;label&amp;gt;&lt;/code&gt; elements.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;CI hygiene.&lt;/strong&gt; Ruff flagged a SIM103 simplification lint. Fixed. Pygments has an unpatched CVE-2026-4539 — we don't use Pygments in production, so added an ignore rule rather than blocking CI on a dev-only dependency with no upstream fix.&lt;/p&gt;

&lt;h2&gt;
  
  
  Dependabot Housekeeping
&lt;/h2&gt;

&lt;p&gt;The google-adk bump from 1.18.0 to 1.28.0 arrived via Dependabot as PR #131. It branched before the lint and CVE fixes landed, so CI was red. Merged main into the dependabot branch, CI went green, squash-merged. Routine dependency maintenance.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Gap Is Closing
&lt;/h2&gt;

&lt;p&gt;FastView is still the reference. It has years of optimization behind it. But the gap is now about edge cases and scale, not fundamentals. IntentCAD's viewer loads off the main thread, shows progress, animates transitions, and gives you color-coded multi-selection. That's the baseline for a professional CAD viewer in the browser.&lt;/p&gt;

&lt;p&gt;Two PRs. About 15 files changed. 614 insertions. The viewer went from "demo-quality" to "production-ready."&lt;/p&gt;

&lt;h3&gt;
  
  
  Related Posts
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;a href="https://dev.to/posts/intentcad-entity-selection-diff-detection-ux"&gt;IntentCAD: Entity Selection + Diff Detection UX&lt;/a&gt; — the PR that built the selection system this work extends&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://dev.to/posts/intentcad-pascal-editor-cached-index-tool-narrowing"&gt;IntentCAD: Pascal Editor, Cached Index &amp;amp; Tool Narrowing&lt;/a&gt; — performance work on the backend that pairs with these frontend improvements&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://dev.to/posts/write-once-publish-everywhere-content-distribution-infra"&gt;Write Once, Publish Everywhere — Content Distribution Infra&lt;/a&gt; — the pipeline that publishes these posts&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>react</category>
      <category>webdev</category>
      <category>architecture</category>
      <category>testing</category>
    </item>
    <item>
      <title>Write Once, Publish Everywhere: Building Content Distribution Across Three Sites</title>
      <dc:creator>Jeremy Longshore</dc:creator>
      <pubDate>Fri, 27 Mar 2026 04:49:51 +0000</pubDate>
      <link>https://dev.to/jeremy_longshore/write-once-publish-everywhere-building-content-distribution-across-three-sites-3iij</link>
      <guid>https://dev.to/jeremy_longshore/write-once-publish-everywhere-building-content-distribution-across-three-sites-3iij</guid>
      <description>&lt;p&gt;Three websites. One hundred blog posts. Thirty-one field notes. Four plugin repos. All synced automatically. None of it existed yesterday.&lt;/p&gt;

&lt;p&gt;March 25th was about content distribution infrastructure. Not writing content — moving it. The problem: Intent Solutions runs three public-facing sites (startaitools.com, tonsofskills.com, intentsolutions.io) plus a plugin marketplace. Each site had its own content silo. Posts written here never appeared there. Plugin updates in source repos required manual marketplace syncs. The content existed. The distribution didn't.&lt;/p&gt;

&lt;p&gt;Eight commits across two primary repos fixed that.&lt;/p&gt;

&lt;h2&gt;
  
  
  100 Posts: Hugo to Astro
&lt;/h2&gt;

&lt;p&gt;tonsofskills.com is an Astro marketplace. startaitools.com is a Hugo blog. They use different content formats, different frontmatter schemas, different directory structures. Hugo uses TOML frontmatter with &lt;code&gt;+++&lt;/code&gt; delimiters. Astro expects YAML with &lt;code&gt;---&lt;/code&gt; delimiters. Hugo puts posts in &lt;code&gt;content/posts/&lt;/code&gt;. Astro wants them in &lt;code&gt;src/content/blog/&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;The backfill pulled 100 posts from the Hugo site into tonsofskills.com's new blog section. This isn't an RSS embed or an iframe. It's actual content transformation — frontmatter conversion, path rewriting, image reference updates.&lt;/p&gt;

&lt;p&gt;The section launched as "Blog" and got immediately renamed to "Work Diary." The name matters. A marketplace blog competes with the source blog for SEO. A work diary is a different content type — it's the builder's log, not the tutorial. Different intent, different audience, same underlying content.&lt;/p&gt;

&lt;p&gt;The &lt;code&gt;/pro&lt;/code&gt; page also got deactivated with a 301 redirect to homepage. One less page to maintain, one less nav link to confuse visitors. The Pro nav slot got replaced with the Work Diary link. Every page on a site should earn its place.&lt;/p&gt;

&lt;h2&gt;
  
  
  31 Field Notes: intentsolutions.io Gets Content
&lt;/h2&gt;

&lt;p&gt;The corporate site at intentsolutions.io had the opposite problem — no content section at all. A services company without published thinking is a brochure. Brochures don't rank and they don't build trust.&lt;/p&gt;

&lt;p&gt;The fix was a Field Notes section with 31 curated posts backfilled from startaitools.com. Not all 100. Thirty-one, filtered by professional tags. The corporate site gets the architecture posts, the production engineering posts, the CI/CD deep-dives. It doesn't get the daily work diary entries.&lt;/p&gt;

&lt;p&gt;The implementation touched six files:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;content.config.ts&lt;/code&gt; — Astro content collection with field-notes schema&lt;/li&gt;
&lt;li&gt;Listing page with card-slate design and JSON-LD structured data&lt;/li&gt;
&lt;li&gt;Detail pages with prose styles and canonical URL override&lt;/li&gt;
&lt;li&gt;RSS feed at &lt;code&gt;/field-notes/rss.xml&lt;/code&gt; via &lt;code&gt;@astrojs/rss&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Nav updates across desktop, mobile menu, and footer&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;Layout.astro&lt;/code&gt; modified to accept optional canonical prop&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The canonical URL override is the key detail. When the same post lives on both startaitools.com and intentsolutions.io, Google needs to know which is the original. The canonical prop lets each field note point back to the source URL, avoiding duplicate content penalties while still serving the content on both domains.&lt;/p&gt;

&lt;p&gt;A backfill script (&lt;code&gt;sync-to-intentsolutions.sh&lt;/code&gt;) handles the ongoing sync. Write a post on startaitools.com, run the script, it appears on intentsolutions.io if the tags match.&lt;/p&gt;

&lt;h2&gt;
  
  
  CodeRabbit Catches a No-Op Filter
&lt;/h2&gt;

&lt;p&gt;The second intentsolutions.io commit is the small one with the lesson. The field notes listing page had a filter:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="nx"&gt;posts&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;filter&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;post&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="nx"&gt;post&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;featured&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="nx"&gt;post&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;featured&lt;/span&gt; &lt;span class="o"&gt;!==&lt;/span&gt; &lt;span class="kc"&gt;undefined&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This filter does nothing. If &lt;code&gt;featured&lt;/code&gt; is falsy, the first condition is true. If &lt;code&gt;featured&lt;/code&gt; is truthy, it's definitely not undefined, so the second condition is true. Every post passes. It's a no-op masquerading as a filter.&lt;/p&gt;

&lt;p&gt;CodeRabbit caught it in PR review. Not a human. An AI reviewer. The fix was one line — remove the dead filter. But the pattern is instructive: conditional logic with OR operators where both branches are always true. It's the boolean equivalent of &lt;code&gt;if (true)&lt;/code&gt;. Easy to write, hard to spot in review, especially when the variable names suggest the filter should be doing something meaningful.&lt;/p&gt;

&lt;h2&gt;
  
  
  Cross-Repo Plugin Sync: repository_dispatch
&lt;/h2&gt;

&lt;p&gt;The plugin marketplace at tonsofskills.com lists plugins from four external repos: box-cloud-filesystem, x-bug-triage-plugin, claude-code-slack-channel, and pr-to-prompt. Until today, syncing required manual triggers. Push to a source repo, remember to trigger the marketplace sync, wait for CI, check it landed.&lt;/p&gt;

&lt;p&gt;The fix uses GitHub's &lt;code&gt;repository_dispatch&lt;/code&gt; event. Each source repo got a &lt;code&gt;notify-marketplace.yml&lt;/code&gt; workflow:&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;on&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;push&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;branches&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="pi"&gt;[&lt;/span&gt;&lt;span class="nv"&gt;main&lt;/span&gt;&lt;span class="pi"&gt;]&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;notify&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;uses&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;peter-evans/repository-dispatch@v3&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;token&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;${{ secrets.MARKETPLACE_SYNC_PAT }}&lt;/span&gt;
          &lt;span class="na"&gt;repository&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;owner/marketplace-repo&lt;/span&gt;
          &lt;span class="na"&gt;event-type&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;external-plugin-update&lt;/span&gt;
          &lt;span class="na"&gt;client-payload&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;{"repo":&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;github.repository&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;}}"}'&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The marketplace repo's &lt;code&gt;sync-external.yml&lt;/code&gt; listens for &lt;code&gt;repository_dispatch&lt;/code&gt; events with that event type. Push to any source repo, the marketplace syncs automatically. No human in the loop.&lt;/p&gt;

&lt;p&gt;The x-bug-triage-plugin was the first to use this pipeline. Five skills, four agents, and an MCP server synced to the marketplace on the same commit that deployed the dispatch trigger. box-cloud-filesystem also got re-synced with its latest upstream.&lt;/p&gt;

&lt;p&gt;This closes the loop on &lt;a href="https://dev.to/posts/external-plugin-sync-keeping-community-plugins-fresh/"&gt;the external sync system&lt;/a&gt; built in January. That system used a cron schedule — daily syncs at midnight. The &lt;code&gt;repository_dispatch&lt;/code&gt; approach is event-driven. Push to source, marketplace updates in minutes instead of waiting for the next cron window.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Distribution Graph
&lt;/h2&gt;

&lt;p&gt;After today, the content flow looks like this:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;startaitools.com&lt;/strong&gt; (Hugo, source of truth) publishes daily work diary posts. A subset gets synced to &lt;strong&gt;intentsolutions.io&lt;/strong&gt; (Astro, corporate) as field notes, filtered by professional tags, with canonical URLs pointing back to source. The full archive gets synced to &lt;strong&gt;tonsofskills.com&lt;/strong&gt; (Astro, marketplace) as the Work Diary section.&lt;/p&gt;

&lt;p&gt;Plugin repos push to main. &lt;code&gt;repository_dispatch&lt;/code&gt; fires. The marketplace syncs within minutes. No cron. No manual triggers.&lt;/p&gt;

&lt;p&gt;Three sites. One content pipeline. Zero manual steps after the initial publish.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Broader Pattern
&lt;/h2&gt;

&lt;p&gt;Content distribution is a solved problem for media companies. Syndication, RSS, cross-posting APIs — the tooling exists. For small dev shops running three Astro/Hugo sites and a plugin marketplace, the tooling doesn't exist. You build it yourself.&lt;/p&gt;

&lt;p&gt;The investment was eight commits across two repos. The return is that every post written on startaitools.com now reaches three audiences automatically, and every plugin update reaches the marketplace without human intervention. The marginal cost of the next post or the next plugin sync is zero.&lt;/p&gt;

&lt;p&gt;That's the whole point of infrastructure. You pay once, then it compounds.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Related Posts:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;a href="https://dev.to/posts/external-plugin-sync-keeping-community-plugins-fresh/"&gt;Building External Plugin Sync: Keeping Community Plugins Fresh&lt;/a&gt; — The January cron-based sync system that today's &lt;code&gt;repository_dispatch&lt;/code&gt; replaces&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://dev.to/posts/x-bug-triage-plugin-zero-to-v043-one-day/"&gt;X Bug Triage Plugin: Zero to v0.4.3 in One Day&lt;/a&gt; — The plugin that got its first marketplace sync today&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://dev.to/posts/content-quality-war-7-check-audit-across-340-plugins/"&gt;Content Quality War: 7-Check Audit Across 340 Plugins&lt;/a&gt; — The content quality standards that the sync pipeline now enforces automatically&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>automation</category>
      <category>architecture</category>
      <category>cicd</category>
      <category>webdev</category>
    </item>
    <item>
      <title>Usable, Not Just Functional: Entity Selection, Binary Eval, and 6 UX Fixes Across 4 Repos</title>
      <dc:creator>Jeremy Longshore</dc:creator>
      <pubDate>Fri, 27 Mar 2026 04:49:42 +0000</pubDate>
      <link>https://dev.to/jeremy_longshore/usable-not-just-functional-entity-selection-binary-eval-and-6-ux-fixes-across-4-repos-a03</link>
      <guid>https://dev.to/jeremy_longshore/usable-not-just-functional-entity-selection-binary-eval-and-6-ux-fixes-across-4-repos-a03</guid>
      <description>&lt;p&gt;Functional is table stakes. Usable is the moat.&lt;/p&gt;

&lt;p&gt;March 24th was a 40+ commit day spread across four repositories. The theme wasn't new capabilities. It was making existing capabilities pleasant to operate. Entity selection that highlights the right rectangle instead of a wrong one. Confirmation feedback after every review command. Color-coded tags that match their overlay boxes. The kind of work that separates a prototype from a tool someone uses twice.&lt;/p&gt;

&lt;h2&gt;
  
  
  CAD Entity Selection: The Bounding Box Problem
&lt;/h2&gt;

&lt;p&gt;The entity selection UX in &lt;a href="https://github.com/jeremylongshore/cad-dxf-agent" rel="noopener noreferrer"&gt;cad-dxf-agent&lt;/a&gt; had a fundamental problem. When you clicked an entity in the viewer, the highlight overlay was wrong.&lt;/p&gt;

&lt;p&gt;Not subtly wrong. Wrong by entire model units.&lt;/p&gt;

&lt;p&gt;The frontend was using hardcoded &lt;code&gt;±10&lt;/code&gt; model units from the entity centroid to draw selection rectangles. For a door schedule text block that spans 200 units, the highlight covered maybe 5% of the actual entity. For a tiny dimension tick, the highlight was 20x too large. Every selection looked broken because it was.&lt;/p&gt;

&lt;h3&gt;
  
  
  Real Bounding Boxes from the Backend
&lt;/h3&gt;

&lt;p&gt;The fix required the backend to compute actual entity bounding boxes and send them to the frontend. Not approximate. Not "close enough." Real bounds from the DXF geometry.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;compute_entity_bounds&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;entity&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="nb"&gt;dict&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="sh"&gt;"""&lt;/span&gt;&lt;span class="s"&gt;Compute actual bounding box from entity geometry, not centroids.&lt;/span&gt;&lt;span class="sh"&gt;"""&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;entity&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;dxftype&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;LINE&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="n"&gt;start&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;end&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;entity&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;dxf&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;start&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;entity&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;dxf&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;end&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;min_x&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nf"&gt;min&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;start&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;x&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;end&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;x&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
            &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;min_y&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nf"&gt;min&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;start&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;y&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;end&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;y&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
            &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;max_x&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nf"&gt;max&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;start&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;x&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;end&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;x&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
            &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;max_y&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nf"&gt;max&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;start&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;y&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;end&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;y&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="c1"&gt;# INSERT, TEXT, MTEXT, LWPOLYLINE — each has its own geometry extraction
&lt;/span&gt;    &lt;span class="bp"&gt;...&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Every DXF entity type stores its geometry differently. A LINE has start/end points. A LWPOLYLINE has a vertex list. An INSERT (block reference) requires resolving the block definition, applying scale and rotation, then computing the transformed bounds. TEXT and MTEXT need font metrics to get the width right.&lt;/p&gt;

&lt;p&gt;The hardcoded &lt;code&gt;±10&lt;/code&gt; was a shortcut from week one. It worked well enough to prove the concept. But "well enough" in a selection UI means "wrong every time in a way the user notices immediately."&lt;/p&gt;

&lt;h3&gt;
  
  
  Color-Cycling Selection Highlights
&lt;/h3&gt;

&lt;p&gt;With correct bounding boxes, the next step was visual differentiation. When you select three entities for a chat prompt, which highlight goes with which tag?&lt;/p&gt;

&lt;p&gt;Six-color palette, assigned by selection index:&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;SELECTION_COLORS&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
  &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;rgba(59, 130, 246, 0.3)&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;   &lt;span class="c1"&gt;// blue&lt;/span&gt;
  &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;rgba(16, 185, 129, 0.3)&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;   &lt;span class="c1"&gt;// emerald&lt;/span&gt;
  &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;rgba(245, 158, 11, 0.3)&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;   &lt;span class="c1"&gt;// amber&lt;/span&gt;
  &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;rgba(139, 92, 246, 0.3)&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;   &lt;span class="c1"&gt;// purple&lt;/span&gt;
  &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;rgba(236, 72, 153, 0.3)&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;   &lt;span class="c1"&gt;// pink&lt;/span&gt;
  &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;rgba(6, 182, 212, 0.3)&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;    &lt;span class="c1"&gt;// cyan&lt;/span&gt;
&lt;span class="p"&gt;];&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The same color index applies to both the viewer overlay rectangle and the entity tag in the chat panel. Select entity #1, get a blue box and a blue tag. Entity #2 gets emerald. No guessing which tag maps to which highlight.&lt;/p&gt;

&lt;h3&gt;
  
  
  Click-to-Focus Operations
&lt;/h3&gt;

&lt;p&gt;The diff comparison already generates a planned operations checklist — "MOVE door D-101 from grid A to grid B," "DELETE redundant hatch pattern." Those operations sat in a text list. You had to find the referenced entity yourself.&lt;/p&gt;

&lt;p&gt;Now they're clickable. Click an operation, the viewer pans and zooms to the target entity. The backend computes bounds per operation, the frontend does the camera math. The drawing is the navigation UI.&lt;/p&gt;

&lt;h3&gt;
  
  
  The Diff Detection Fix
&lt;/h3&gt;

&lt;p&gt;Separate from the UX work, the diff comparison engine had a bug: MODIFIED and MOVED categories never appeared. Everything was either ADDED or DELETED.&lt;/p&gt;

&lt;p&gt;Root cause: &lt;code&gt;match_search_radius&lt;/code&gt; and geometric &lt;code&gt;tolerance&lt;/code&gt; were the same value. The search radius determines how far the algorithm looks for potential matches. The tolerance determines how close two entities must be to count as the same entity. When they're identical, the algorithm finds a candidate and immediately rejects it — the match is &lt;em&gt;exactly&lt;/em&gt; at the boundary, and floating-point rounding pushes it out.&lt;/p&gt;

&lt;p&gt;The fix split them:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="n"&gt;MATCH_SEARCH_RADIUS&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mf"&gt;50.0&lt;/span&gt;   &lt;span class="c1"&gt;# how far to look for candidates
&lt;/span&gt;&lt;span class="n"&gt;GEOMETRIC_TOLERANCE&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mf"&gt;1.0&lt;/span&gt;     &lt;span class="c1"&gt;# how close to count as "same entity"
&lt;/span&gt;&lt;span class="n"&gt;MOVE_THRESHOLD&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mf"&gt;5.0&lt;/span&gt;          &lt;span class="c1"&gt;# minimum distance to classify as MOVED vs MODIFIED
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Three distinct thresholds for three distinct questions. The &lt;code&gt;move_threshold&lt;/code&gt; was also too low — entities with minor coordinate drift from re-export were classified as MOVED when they should have been MODIFIED. Raising it to 5.0 model units resolved the false positives.&lt;/p&gt;

&lt;p&gt;A separate bug: block ATTRIBs (text attributes on block references) weren't captured during comparison. Two otherwise-identical door blocks with different room number ATTRIBs looked identical to the diff engine. Now ATTRIB text is included in the entity fingerprint.&lt;/p&gt;

&lt;p&gt;PR #129 landed with 16 files changed. PR review caught a missing &lt;code&gt;&amp;lt;label&amp;gt;&lt;/code&gt; element for accessibility, an undocumented magic number, and a SIM103 lint warning. All fixed before merge.&lt;/p&gt;

&lt;h2&gt;
  
  
  j-rig-binary-eval: Zero to v0.2.8
&lt;/h2&gt;

&lt;p&gt;A brand new project went from &lt;code&gt;git init&lt;/code&gt; to v0.2.8 in a single day. Thirty-plus commits.&lt;/p&gt;

&lt;p&gt;j-rig-binary-eval is a calibration framework for evaluating Claude Code skill quality. Not "does this skill run without errors" — that's CI. Binary eval asks "does this skill produce output that a senior engineer would approve?" Judgment layers, calibration curves, scoring rubrics, experiment design.&lt;/p&gt;

&lt;p&gt;The day broke into phases.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Phase 1: Governance scaffolding.&lt;/strong&gt; CLAUDE.md, beads integration, doc-filing index, master build blueprint. Ten epic reference files (EPIC-01 through EPIC-10) defining the full system architecture before writing a line of application code.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Phase 2: Templates library.&lt;/strong&gt; Forty-seven reference files imported from the enterprise template collection. Immediately audited for bloat: 9 files deleted (~975 lines), 6 stale enterprise standards removed (~7,500 lines). The templates library was bigger than the project it was meant to serve.&lt;/p&gt;

&lt;p&gt;This is a pattern worth calling out. Enterprise template libraries grow monotonically. Nobody deletes templates. The audit removed files that hadn't been referenced in 6+ months and standards documents from a previous org structure. The remaining templates are the ones that actually get used.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Phase 3: Pattern A README.&lt;/strong&gt; The one-pager + operator-grade system analysis format. Title, tagline, badges, links, W5 table, stack table, key differentiators, then the full operator section. This README format exists so any engineer can understand the project in 90 seconds and operate it in 5 minutes.&lt;/p&gt;

&lt;p&gt;The project is pre-code. All 30+ commits are governance, architecture, and planning artifacts. No application logic yet. That's intentional. The binary eval framework needs to be right before it's fast. Getting the epic structure, scoring rubrics, and experiment design wrong means rebuilding the calibration pipeline later.&lt;/p&gt;

&lt;h2&gt;
  
  
  X Bug Triage Plugin: Six UX Enhancements
&lt;/h2&gt;

&lt;p&gt;The &lt;a href="https://github.com/jeremylongshore/x-bug-triage-plugin" rel="noopener noreferrer"&gt;x-bug-triage-plugin&lt;/a&gt; shipped v0.4.4 with six targeted UX improvements via PR #19. Yesterday was the &lt;a href="https://dev.to/posts/x-bug-triage-plugin-zero-to-v043-one-day/"&gt;zero-to-v0.4.3 sprint&lt;/a&gt;. Today was making it feel finished.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;E1: Action confirmation feedback.&lt;/strong&gt; All 11 review commands (approve, reject, defer, escalate, etc.) now emit a structured confirmation response. Before: the command ran silently. The user had to query the state to confirm it worked. After: immediate feedback with the action taken, the affected entity, and the new state.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;E2: Freshness assessment.&lt;/strong&gt; Source data ages. A tweet from 3 hours ago is fresh. A tweet from 3 weeks ago might reference a bug that's already fixed. The freshness system assigns &lt;code&gt;date_confidence&lt;/code&gt; bands — HIGH (&amp;lt; 24h), MEDIUM (1-7d), LOW (7-30d), STALE (&amp;gt; 30d) — and surfaces warnings when the triage pipeline processes stale data.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;E3: Source status header.&lt;/strong&gt; Aggregates a &lt;code&gt;DegradationReport&lt;/code&gt; per X API endpoint. If the tweets endpoint is returning 429s, the header shows it. If the search endpoint is slow, the header shows latency. Operators see API health at a glance instead of discovering it when a command fails.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;E4: File-based JSON cache.&lt;/strong&gt; Opt-in caching layer for X API responses. Development and testing no longer require live API calls. The cache writes JSON files with request fingerprints as filenames. &lt;code&gt;DEBUG_CACHE&lt;/code&gt; logging shows hit/miss rates.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;E5: Hybrid dedup.&lt;/strong&gt; The original dedup was exact-match on tweet ID. Duplicate &lt;em&gt;content&lt;/em&gt; across different tweets (retweets, quote tweets, copy-paste reports) passed through. The new hybrid approach combines character-trigram similarity with token-Jaccard scoring. Two tweets with 85%+ trigram overlap and 80%+ token-Jaccard get flagged as semantic duplicates before clustering.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;E6: Per-tier evidence counts.&lt;/strong&gt; The summary and detail views now show evidence counts broken down by severity tier. "3 CRITICAL, 7 HIGH, 12 MEDIUM" instead of just "22 bugs." Operators triage by tier, so the counts should match.&lt;/p&gt;

&lt;p&gt;Eight new files, 14 modifications, 70 new tests (348 total), zero breaking changes.&lt;/p&gt;

&lt;p&gt;PR review caught three things: magic numbers that should be extracted constants, fixed-width column padding that should be dynamic, and &lt;code&gt;DEBUG_CACHE&lt;/code&gt; logging that was always on instead of opt-in. All addressed before merge.&lt;/p&gt;

&lt;p&gt;The mermaid architecture diagram in the README was also replaced with a plain text flow diagram. Mermaid doesn't render on GitHub Pages. A tool that documents itself with diagrams nobody can see isn't documenting itself.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Pattern
&lt;/h2&gt;

&lt;p&gt;Three projects, same lesson. The CAD tool had real entity selection from day one — the highlight was just in the wrong place. The triage plugin had all 11 review commands — they just didn't confirm they worked. The binary eval framework had enterprise templates — most of them were dead weight.&lt;/p&gt;

&lt;p&gt;Functional is the first 80%. Usable is the remaining 80%.&lt;/p&gt;

&lt;p&gt;The common thread across all of today's work: removing the gap between "this technically works" and "this is pleasant to use." Correct bounding boxes. Color-coded selections. Confirmation messages. Freshness warnings. Evidence counts by tier. None of these are features. They're the absence of friction.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Related Posts:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;a href="https://dev.to/posts/x-bug-triage-plugin-zero-to-v043-one-day/"&gt;X Bug Triage Plugin: Zero to v0.4.3 in One Day&lt;/a&gt; — the bootstrap sprint this work builds on&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://dev.to/posts/pdf-extraction-sweep-day-42-commits/"&gt;PDF Extraction Bugs, Broadcast Persistence, and a 42-Commit Sweep Day&lt;/a&gt; — another heavy multi-repo day with CAD debugging&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://dev.to/posts/deterministic-dxf-comparison-engine-one-day-build/"&gt;Building a Deterministic DXF Comparison Engine in One Day&lt;/a&gt; — the original diff engine that today's MODIFIED/MOVED fix corrects&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>aiagents</category>
      <category>python</category>
      <category>testing</category>
      <category>architecture</category>
    </item>
    <item>
      <title>The Evolution of a Development Machine: 20 Days from Ubuntu Install to Production Powerhouse</title>
      <dc:creator>Jeremy Longshore</dc:creator>
      <pubDate>Wed, 25 Mar 2026 22:57:24 +0000</pubDate>
      <link>https://dev.to/jeremy_longshore/the-evolution-of-a-development-machine-20-days-from-ubuntu-install-to-production-powerhouse-2n2c</link>
      <guid>https://dev.to/jeremy_longshore/the-evolution-of-a-development-machine-20-days-from-ubuntu-install-to-production-powerhouse-2n2c</guid>
      <description>&lt;h2&gt;
  
  
  The Journey: From Fresh Ubuntu to Full-Stack Development Environment
&lt;/h2&gt;

&lt;p&gt;Today I want to share the incredible 20-day evolution of my Ubuntu development machine. Using file timestamps and git history, I've reconstructed the exact timeline of how a fresh Linux install became a production powerhouse running multiple SaaS projects, processing millions of data points, and generating content automatically.&lt;/p&gt;

&lt;h2&gt;
  
  
  Day 0: August 25, 2025 - The Beginning
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;5:00 PM&lt;/strong&gt;: Fresh Ubuntu installation&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;.bashrc&lt;/code&gt; and &lt;code&gt;.profile&lt;/code&gt; created&lt;/li&gt;
&lt;li&gt;User account established&lt;/li&gt;
&lt;li&gt;Basic system configuration&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The machine starts its life as a blank canvas.&lt;/p&gt;

&lt;h2&gt;
  
  
  Day 1: August 26 - Foundation Building
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Morning (8:00 AM - 12:00 PM)&lt;/strong&gt;:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Railway deployment platform configured&lt;/li&gt;
&lt;li&gt;Docker installation and setup&lt;/li&gt;
&lt;li&gt;Chrome profiles for testing environments&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Afternoon (2:00 PM - 6:00 PM)&lt;/strong&gt;:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;SSH keys generated for GitHub&lt;/li&gt;
&lt;li&gt;Git configuration&lt;/li&gt;
&lt;li&gt;Claude.ai integration setup&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Evening (8:00 PM - 11:00 PM)&lt;/strong&gt;:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Initial project directories created&lt;/li&gt;
&lt;li&gt;VS Code extensions installed&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Days 2-4: August 27-29 - Tool Arsenal Assembly
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;August 27&lt;/strong&gt;:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Netlify CLI installation&lt;/li&gt;
&lt;li&gt;Node.js environment setup&lt;/li&gt;
&lt;li&gt;Python virtual environments&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;August 28&lt;/strong&gt;:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Google Cloud SDK installation begins&lt;/li&gt;
&lt;li&gt;BigQuery CLI tools&lt;/li&gt;
&lt;li&gt;Cloud SQL proxy setup&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;August 29&lt;/strong&gt;:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Codex integration&lt;/li&gt;
&lt;li&gt;AI coding assistants configured&lt;/li&gt;
&lt;li&gt;First automated scripts written&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Day 6: August 31 - Docker Revolution
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;9:00 AM - 10:00 PM&lt;/strong&gt;:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Docker Compose configurations&lt;/li&gt;
&lt;li&gt;Multi-container development environments&lt;/li&gt;
&lt;li&gt;Database containers (PostgreSQL, Redis)&lt;/li&gt;
&lt;li&gt;First microservices architecture sketched&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Days 7-10: September 1-4 - The Project Explosion
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;September 1-2&lt;/strong&gt;: DiagnosticPro Birth&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;SvelteKit project initialized&lt;/li&gt;
&lt;li&gt;50+ components created&lt;/li&gt;
&lt;li&gt;Tailwind CSS integration&lt;/li&gt;
&lt;li&gt;First deployment to production&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;September 3&lt;/strong&gt;: Data Architecture&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;BigQuery schema design begins&lt;/li&gt;
&lt;li&gt;First 50 tables created&lt;/li&gt;
&lt;li&gt;Real-time data pipeline planned&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;September 4&lt;/strong&gt;:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Gemini API integration&lt;/li&gt;
&lt;li&gt;AI feature development&lt;/li&gt;
&lt;li&gt;226 RSS feeds configured&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Days 11-13: September 6-8 - The Great Scaling
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;September 6&lt;/strong&gt;: Content Pipeline&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;N8N workflow automation installed&lt;/li&gt;
&lt;li&gt;Daily content generation workflows&lt;/li&gt;
&lt;li&gt;Reddit scraper (500K+ posts collected)&lt;/li&gt;
&lt;li&gt;YouTube data extractor built&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;September 7&lt;/strong&gt;: Blog Platform Launch&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Hugo static site generator&lt;/li&gt;
&lt;li&gt;Two separate blogs created:

&lt;ul&gt;
&lt;li&gt;jeremylongshore.com (personal)&lt;/li&gt;
&lt;li&gt;startaitools.com (business)&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;li&gt;10 initial posts written&lt;/li&gt;

&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;September 8&lt;/strong&gt;: Cloud Native&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Google Cloud integration complete&lt;/li&gt;
&lt;li&gt;254 BigQuery tables in production&lt;/li&gt;
&lt;li&gt;Netlify auto-deployment&lt;/li&gt;
&lt;li&gt;CI/CD pipelines operational&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Days 14-17: September 9-12 - Enterprise Evolution
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;September 9&lt;/strong&gt;:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Project management structure created&lt;/li&gt;
&lt;li&gt;Handoff documentation system&lt;/li&gt;
&lt;li&gt;Active/Archive pattern established&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;September 10&lt;/strong&gt;: System Optimization&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Performance monitoring&lt;/li&gt;
&lt;li&gt;Log aggregation&lt;/li&gt;
&lt;li&gt;Error tracking&lt;/li&gt;
&lt;li&gt;Analytics dashboard&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;September 11&lt;/strong&gt;: DiagnosticPro Features&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Payment integration (Stripe)&lt;/li&gt;
&lt;li&gt;User authentication&lt;/li&gt;
&lt;li&gt;Real-time diagnostics&lt;/li&gt;
&lt;li&gt;AI-powered analysis&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;September 12&lt;/strong&gt;: Master Documentation&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;27,440-word master document created&lt;/li&gt;
&lt;li&gt;Complete system architecture documented&lt;/li&gt;
&lt;li&gt;API specifications written&lt;/li&gt;
&lt;li&gt;Database schemas finalized (266 tables!)&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Days 18-19: September 13 - The Great Reorganization
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;1:00 AM - 3:00 AM&lt;/strong&gt;: Directory Cleanup&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;29 empty directories removed&lt;/li&gt;
&lt;li&gt;7 old backups archived&lt;/li&gt;
&lt;li&gt;3 duplicate projects consolidated&lt;/li&gt;
&lt;li&gt;9 comprehensive READMEs created&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;10:00 AM - 2:00 PM&lt;/strong&gt;: Content Migration&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Blog migration from Archie to Hugo Book theme&lt;/li&gt;
&lt;li&gt;15 posts reorganized&lt;/li&gt;
&lt;li&gt;Learning paths created&lt;/li&gt;
&lt;li&gt;Glossary system implemented&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;3:00 PM - 11:00 PM&lt;/strong&gt;: Open Source Contributions&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Bob's Brain released to community&lt;/li&gt;
&lt;li&gt;AI documentation shared&lt;/li&gt;
&lt;li&gt;Multiple PRDs created&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Day 20: September 14 - The Professional Toolkit
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;12:00 AM - 4:00 AM&lt;/strong&gt;: AI Dev Tasks Creation&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;16 professional documentation templates&lt;/li&gt;
&lt;li&gt;8,465+ lines of documentation&lt;/li&gt;
&lt;li&gt;Complete SDLC coverage&lt;/li&gt;
&lt;li&gt;Open sourced with full attribution&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;The Current State&lt;/strong&gt;: A Production Powerhouse&lt;/p&gt;

&lt;h2&gt;
  
  
  By The Numbers (20 Days)
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Code &amp;amp; Development
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Lines of Code Written&lt;/strong&gt;: 150,000+&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Git Commits&lt;/strong&gt;: 500+&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Projects Active&lt;/strong&gt;: 7&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Projects Completed&lt;/strong&gt;: 3&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Open Source Contributions&lt;/strong&gt;: 2&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Data &amp;amp; Infrastructure
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;BigQuery Tables&lt;/strong&gt;: 266&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Data Processed&lt;/strong&gt;: 10GB+&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;RSS Feeds Monitored&lt;/strong&gt;: 226&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Reddit Posts Collected&lt;/strong&gt;: 500,000+&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;API Integrations&lt;/strong&gt;: 15+&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Content &amp;amp; Documentation
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Blog Posts Published&lt;/strong&gt;: 25+&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Documentation Pages&lt;/strong&gt;: 50+&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;PRDs Created&lt;/strong&gt;: 6&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Templates Developed&lt;/strong&gt;: 16&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Total Words Written&lt;/strong&gt;: 200,000+&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Automation &amp;amp; Efficiency
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;N8N Workflows&lt;/strong&gt;: 10+&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Automated Tasks&lt;/strong&gt;: 50+&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Time Saved Weekly&lt;/strong&gt;: 20+ hours&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Deployment Pipelines&lt;/strong&gt;: 5&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  The Directory Structure Evolution
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Day 1:  /home/jeremy/ (empty)
Day 5:  3 directories, 10 files
Day 10: 15 directories, 200+ files
Day 15: 30 directories, 1000+ files
Day 20: Organized hierarchy with clear purpose

Current:
/home/jeremy/
├── _ACTIVE_PROJECTS/     # The brain
├── _PROJECT_MANAGEMENT/  # The memory
├── projects/             # The workshop
├── research/             # The library
└── [tools &amp;amp; utilities]   # The toolkit
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Key Learnings
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Week 1: Foundation is Everything
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;Spend time on tooling setup&lt;/li&gt;
&lt;li&gt;Automate from day one&lt;/li&gt;
&lt;li&gt;Version control everything&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Week 2: Embrace the Chaos
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;Multiple projects accelerate learning&lt;/li&gt;
&lt;li&gt;Cross-pollination of ideas&lt;/li&gt;
&lt;li&gt;Document as you build&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Week 3: Organization Scales
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;Structure enables creativity&lt;/li&gt;
&lt;li&gt;Archives preserve knowledge&lt;/li&gt;
&lt;li&gt;Automation compounds value&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  The Philosophy That Emerged
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;"Every action should create value beyond its immediate purpose"&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Writing code? Generate documentation&lt;/li&gt;
&lt;li&gt;Solving problems? Create blog posts&lt;/li&gt;
&lt;li&gt;Building features? Design templates&lt;/li&gt;
&lt;li&gt;Learning tools? Share tutorials&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Tools That Made the Difference
&lt;/h2&gt;

&lt;h3&gt;
  
  
  The MVPs
&lt;/h3&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Claude/Cursor&lt;/strong&gt;: AI pair programming&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Hugo&lt;/strong&gt;: Lightning-fast static sites&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;N8N&lt;/strong&gt;: Workflow automation&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;BigQuery&lt;/strong&gt;: Massive data processing&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Docker&lt;/strong&gt;: Consistent environments&lt;/li&gt;
&lt;/ol&gt;

&lt;h3&gt;
  
  
  The Unsung Heroes
&lt;/h3&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;tmux&lt;/strong&gt;: Terminal multiplexing&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;ripgrep&lt;/strong&gt;: Fast searching&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;jq&lt;/strong&gt;: JSON processing&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;GitHub Actions&lt;/strong&gt;: CI/CD&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Netlify&lt;/strong&gt;: Instant deployments&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  What's Next: Days 21-30
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Planned Enhancements
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;Kubernetes orchestration&lt;/li&gt;
&lt;li&gt;Multi-region deployment&lt;/li&gt;
&lt;li&gt;ML model integration&lt;/li&gt;
&lt;li&gt;Real-time analytics dashboard&lt;/li&gt;
&lt;li&gt;Community platform launch&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Expected Metrics
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;500K+ daily data points&lt;/li&gt;
&lt;li&gt;50+ automated workflows&lt;/li&gt;
&lt;li&gt;100+ blog posts&lt;/li&gt;
&lt;li&gt;5 production applications&lt;/li&gt;
&lt;li&gt;1M+ users served&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  The Transformation
&lt;/h2&gt;

&lt;p&gt;In 20 days, this Ubuntu machine went from a fresh install to:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Development Hub&lt;/strong&gt;: 7 active projects&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Data Processor&lt;/strong&gt;: 266 BigQuery tables&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Content Factory&lt;/strong&gt;: 25+ blog posts&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Automation Center&lt;/strong&gt;: 50+ automated tasks&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Learning Platform&lt;/strong&gt;: Comprehensive documentation&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Open Source Contributor&lt;/strong&gt;: Multiple projects shared&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Conclusion: The Power of Momentum
&lt;/h2&gt;

&lt;p&gt;The most striking revelation from this timeline is the exponential nature of development momentum. Each day built upon the previous, each tool enabled new capabilities, and each project informed the next.&lt;/p&gt;

&lt;p&gt;What started as a blank Ubuntu installation became a sophisticated development environment processing millions of data points, running multiple production applications, and automatically generating content.&lt;/p&gt;

&lt;p&gt;The machine isn't just a tool anymore - it's a partner in creation, a repository of knowledge, and a launchpad for ideas.&lt;/p&gt;

&lt;h2&gt;
  
  
  Your Turn
&lt;/h2&gt;

&lt;p&gt;Want to build your own development powerhouse? Here's my advice:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Document everything&lt;/strong&gt; - Your future self will thank you&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Automate early&lt;/strong&gt; - Even simple tasks&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Organize often&lt;/strong&gt; - Don't wait for "later"&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Share freely&lt;/strong&gt; - Open source amplifies impact&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Build daily&lt;/strong&gt; - Momentum is everything&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;&lt;em&gt;This Ubuntu machine has been running for 20 days, 8 hours, and 30 minutes.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Total uptime: 99.8%&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Total value created: Immeasurable&lt;/em&gt;&lt;/p&gt;

</description>
      <category>ubuntu</category>
      <category>development</category>
      <category>devops</category>
      <category>journey</category>
    </item>
    <item>
      <title>Terraform for AI Infrastructure: Complete Learning Guide from Zero to Production</title>
      <dc:creator>Jeremy Longshore</dc:creator>
      <pubDate>Wed, 25 Mar 2026 22:57:13 +0000</pubDate>
      <link>https://dev.to/jeremy_longshore/terraform-for-ai-infrastructure-complete-learning-guide-from-zero-to-production-447g</link>
      <guid>https://dev.to/jeremy_longshore/terraform-for-ai-infrastructure-complete-learning-guide-from-zero-to-production-447g</guid>
      <description>&lt;h1&gt;
  
  
  Terraform for AI Infrastructure: Complete Learning Guide
&lt;/h1&gt;

&lt;p&gt;&lt;strong&gt;TL;DR&lt;/strong&gt;: Learn Infrastructure as Code with Terraform from beginner to advanced. Covers core concepts, state management, modules, best practices, and production deployment patterns for AI infrastructure.&lt;/p&gt;

&lt;h2&gt;
  
  
  What is Terraform?
&lt;/h2&gt;

&lt;p&gt;Terraform is an &lt;strong&gt;Infrastructure as Code (IaC)&lt;/strong&gt; tool that lets you define and provision cloud infrastructure using declarative configuration files instead of manual clicking in web consoles.&lt;/p&gt;

&lt;h3&gt;
  
  
  Key Benefits
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Declarative Configuration&lt;/strong&gt;: Describe &lt;em&gt;what&lt;/em&gt; you want, not &lt;em&gt;how&lt;/em&gt; to get there&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Plan Before Apply&lt;/strong&gt;: Preview all changes before execution&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Version Control&lt;/strong&gt;: Track infrastructure changes in Git&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Multi-Cloud&lt;/strong&gt;: Works with AWS, GCP, Azure, and 100+ providers&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Idempotent&lt;/strong&gt;: Safe to run multiple times without side effects&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  How It Works
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;1. Write Configuration (.tf files)
   ↓
2. terraform init (Download providers)
   ↓
3. terraform plan (Preview changes)
   ↓
4. terraform apply (Execute changes)
   ↓
5. Infrastructure Created/Updated
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Core Concepts
&lt;/h2&gt;

&lt;h3&gt;
  
  
  1. Providers
&lt;/h3&gt;

&lt;p&gt;Providers are plugins that interact with cloud platform APIs:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight hcl"&gt;&lt;code&gt;&lt;span class="c1"&gt;# Configure the Google Cloud Provider&lt;/span&gt;
&lt;span class="nx"&gt;provider&lt;/span&gt; &lt;span class="s2"&gt;"google"&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;project&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"my-ai-project"&lt;/span&gt;
  &lt;span class="nx"&gt;region&lt;/span&gt;  &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"us-central1"&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="c1"&gt;# Configure AWS Provider&lt;/span&gt;
&lt;span class="nx"&gt;provider&lt;/span&gt; &lt;span class="s2"&gt;"aws"&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;region&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"us-west-2"&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  2. Resources
&lt;/h3&gt;

&lt;p&gt;Resources describe infrastructure objects:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight hcl"&gt;&lt;code&gt;&lt;span class="c1"&gt;# Create a Cloud Storage bucket for ML models&lt;/span&gt;
&lt;span class="nx"&gt;resource&lt;/span&gt; &lt;span class="s2"&gt;"google_storage_bucket"&lt;/span&gt; &lt;span class="s2"&gt;"ml_models"&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;name&lt;/span&gt;     &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"my-ai-project-models"&lt;/span&gt;
  &lt;span class="nx"&gt;location&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"US"&lt;/span&gt;

  &lt;span class="nx"&gt;versioning&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;enabled&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="nx"&gt;lifecycle_rule&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;action&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="nx"&gt;type&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"Delete"&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="nx"&gt;condition&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="nx"&gt;age&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;90&lt;/span&gt;  &lt;span class="c1"&gt;# Delete old models after 90 days&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="c1"&gt;# Create Compute Instance for training&lt;/span&gt;
&lt;span class="nx"&gt;resource&lt;/span&gt; &lt;span class="s2"&gt;"google_compute_instance"&lt;/span&gt; &lt;span class="s2"&gt;"training_vm"&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;name&lt;/span&gt;         &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"ml-training-vm"&lt;/span&gt;
  &lt;span class="nx"&gt;machine_type&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"n1-standard-8"&lt;/span&gt;
  &lt;span class="nx"&gt;zone&lt;/span&gt;         &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"us-central1-a"&lt;/span&gt;

  &lt;span class="nx"&gt;boot_disk&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;initialize_params&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="nx"&gt;image&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"deeplearning-platform-release/tf2-gpu-2-11-cu113"&lt;/span&gt;
      &lt;span class="nx"&gt;size&lt;/span&gt;  &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;100&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;

  &lt;span class="nx"&gt;network_interface&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;network&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"default"&lt;/span&gt;
    &lt;span class="nx"&gt;access_config&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;h3&gt;
  
  
  3. Variables
&lt;/h3&gt;

&lt;p&gt;Make configurations reusable:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight hcl"&gt;&lt;code&gt;&lt;span class="c1"&gt;# variables.tf&lt;/span&gt;
&lt;span class="nx"&gt;variable&lt;/span&gt; &lt;span class="s2"&gt;"project_id"&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;description&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"GCP Project ID"&lt;/span&gt;
  &lt;span class="nx"&gt;type&lt;/span&gt;        &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;string&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="nx"&gt;variable&lt;/span&gt; &lt;span class="s2"&gt;"environment"&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;description&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"Environment (dev, staging, prod)"&lt;/span&gt;
  &lt;span class="nx"&gt;type&lt;/span&gt;        &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;string&lt;/span&gt;

  &lt;span class="nx"&gt;validation&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;condition&lt;/span&gt;     &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;contains&lt;/span&gt;&lt;span class="p"&gt;([&lt;/span&gt;&lt;span class="s2"&gt;"dev"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;"staging"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;"prod"&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt; &lt;span class="nx"&gt;var&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;environment&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="nx"&gt;error_message&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"Environment must be dev, staging, or prod"&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="nx"&gt;variable&lt;/span&gt; &lt;span class="s2"&gt;"ml_model_bucket"&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;description&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"Bucket for ML models"&lt;/span&gt;
  &lt;span class="nx"&gt;type&lt;/span&gt;        &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;string&lt;/span&gt;
  &lt;span class="nx"&gt;default&lt;/span&gt;     &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"ml-models"&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="c1"&gt;# Use variables&lt;/span&gt;
&lt;span class="nx"&gt;resource&lt;/span&gt; &lt;span class="s2"&gt;"google_storage_bucket"&lt;/span&gt; &lt;span class="s2"&gt;"models"&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;name&lt;/span&gt;     &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"${var.project_id}-${var.environment}-${var.ml_model_bucket}"&lt;/span&gt;
  &lt;span class="nx"&gt;location&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"US"&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  4. Outputs
&lt;/h3&gt;

&lt;p&gt;Expose information about your infrastructure:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight hcl"&gt;&lt;code&gt;&lt;span class="c1"&gt;# outputs.tf&lt;/span&gt;
&lt;span class="nx"&gt;output&lt;/span&gt; &lt;span class="s2"&gt;"bucket_url"&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;description&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"URL of ML models bucket"&lt;/span&gt;
  &lt;span class="nx"&gt;value&lt;/span&gt;       &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;google_storage_bucket&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;models&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;url&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="nx"&gt;output&lt;/span&gt; &lt;span class="s2"&gt;"training_vm_ip"&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;description&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"Public IP of training VM"&lt;/span&gt;
  &lt;span class="nx"&gt;value&lt;/span&gt;       &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;google_compute_instance&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;training_vm&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;network_interface&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;].&lt;/span&gt;&lt;span class="nx"&gt;access_config&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;].&lt;/span&gt;&lt;span class="nx"&gt;nat_ip&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  5. Data Sources
&lt;/h3&gt;

&lt;p&gt;Query existing infrastructure:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight hcl"&gt;&lt;code&gt;&lt;span class="c1"&gt;# Get current project info&lt;/span&gt;
&lt;span class="nx"&gt;data&lt;/span&gt; &lt;span class="s2"&gt;"google_project"&lt;/span&gt; &lt;span class="s2"&gt;"current"&lt;/span&gt; &lt;span class="p"&gt;{}&lt;/span&gt;

&lt;span class="c1"&gt;# Get latest GPU-enabled image&lt;/span&gt;
&lt;span class="nx"&gt;data&lt;/span&gt; &lt;span class="s2"&gt;"google_compute_image"&lt;/span&gt; &lt;span class="s2"&gt;"gpu_image"&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;family&lt;/span&gt;  &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"pytorch-latest-gpu"&lt;/span&gt;
  &lt;span class="nx"&gt;project&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"deeplearning-platform-release"&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="c1"&gt;# Use in resource&lt;/span&gt;
&lt;span class="nx"&gt;resource&lt;/span&gt; &lt;span class="s2"&gt;"google_compute_instance"&lt;/span&gt; &lt;span class="s2"&gt;"gpu_vm"&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;name&lt;/span&gt;         &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"gpu-training"&lt;/span&gt;
  &lt;span class="nx"&gt;machine_type&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"n1-standard-8"&lt;/span&gt;

  &lt;span class="nx"&gt;boot_disk&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;initialize_params&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="nx"&gt;image&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;google_compute_image&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;gpu_image&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;self_link&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;h2&gt;
  
  
  State Management
&lt;/h2&gt;

&lt;p&gt;Terraform tracks infrastructure state in a &lt;code&gt;.tfstate&lt;/code&gt; file.&lt;/p&gt;

&lt;h3&gt;
  
  
  Local State (Development)
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight hcl"&gt;&lt;code&gt;&lt;span class="c1"&gt;# Default: state stored locally&lt;/span&gt;
&lt;span class="c1"&gt;# terraform.tfstate in current directory&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Remote State (Production)
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;Google Cloud Storage:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight hcl"&gt;&lt;code&gt;&lt;span class="c1"&gt;# backend.tf&lt;/span&gt;
&lt;span class="nx"&gt;terraform&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;backend&lt;/span&gt; &lt;span class="s2"&gt;"gcs"&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;bucket&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"my-terraform-state"&lt;/span&gt;
    &lt;span class="nx"&gt;prefix&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"ai-infrastructure/state"&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;&lt;strong&gt;AWS S3:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight hcl"&gt;&lt;code&gt;&lt;span class="nx"&gt;terraform&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;backend&lt;/span&gt; &lt;span class="s2"&gt;"s3"&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;bucket&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"my-terraform-state"&lt;/span&gt;
    &lt;span class="nx"&gt;key&lt;/span&gt;    &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"ai-infra/terraform.tfstate"&lt;/span&gt;
    &lt;span class="nx"&gt;region&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"us-west-2"&lt;/span&gt;

    &lt;span class="nx"&gt;dynamodb_table&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"terraform-locks"&lt;/span&gt;
    &lt;span class="nx"&gt;encrypt&lt;/span&gt;        &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  State Management Commands
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# View state&lt;/span&gt;
terraform show

&lt;span class="c"&gt;# List resources in state&lt;/span&gt;
terraform state list

&lt;span class="c"&gt;# Remove resource from state (doesn't delete resource)&lt;/span&gt;
terraform state &lt;span class="nb"&gt;rm &lt;/span&gt;google_storage_bucket.example

&lt;span class="c"&gt;# Move resource in state (rename)&lt;/span&gt;
terraform state &lt;span class="nb"&gt;mv &lt;/span&gt;google_storage_bucket.old google_storage_bucket.new

&lt;span class="c"&gt;# Refresh state from real infrastructure&lt;/span&gt;
terraform refresh
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Modules: Reusable Infrastructure Components
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Module Structure
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;terraform/
├── modules/
│   └── ml-training-vm/
│       ├── main.tf       # Resources
│       ├── variables.tf  # Input variables
│       └── outputs.tf    # Output values
└── environments/
    ├── dev/
    │   └── main.tf      # Uses modules
    └── prod/
        └── main.tf
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Creating a Module
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;modules/ml-training-vm/main.tf:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight hcl"&gt;&lt;code&gt;&lt;span class="nx"&gt;resource&lt;/span&gt; &lt;span class="s2"&gt;"google_compute_instance"&lt;/span&gt; &lt;span class="s2"&gt;"training_vm"&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;name&lt;/span&gt;         &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"${var.name_prefix}-training-vm"&lt;/span&gt;
  &lt;span class="nx"&gt;machine_type&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;var&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;machine_type&lt;/span&gt;
  &lt;span class="nx"&gt;zone&lt;/span&gt;         &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;var&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;zone&lt;/span&gt;

  &lt;span class="nx"&gt;boot_disk&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;initialize_params&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="nx"&gt;image&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;var&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;boot_image&lt;/span&gt;
      &lt;span class="nx"&gt;size&lt;/span&gt;  &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;var&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;disk_size_gb&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;

  &lt;span class="nx"&gt;network_interface&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;network&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;var&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;network&lt;/span&gt;
    &lt;span class="nx"&gt;access_config&lt;/span&gt; &lt;span class="p"&gt;{}&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;

  &lt;span class="nx"&gt;metadata&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;environment&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;var&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;environment&lt;/span&gt;
    &lt;span class="nx"&gt;managed_by&lt;/span&gt;  &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"terraform"&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;&lt;strong&gt;modules/ml-training-vm/variables.tf:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight hcl"&gt;&lt;code&gt;&lt;span class="nx"&gt;variable&lt;/span&gt; &lt;span class="s2"&gt;"name_prefix"&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;description&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"Prefix for resource names"&lt;/span&gt;
  &lt;span class="nx"&gt;type&lt;/span&gt;        &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;string&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="nx"&gt;variable&lt;/span&gt; &lt;span class="s2"&gt;"machine_type"&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;description&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"Machine type for VM"&lt;/span&gt;
  &lt;span class="nx"&gt;type&lt;/span&gt;        &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;string&lt;/span&gt;
  &lt;span class="nx"&gt;default&lt;/span&gt;     &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"n1-standard-8"&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="nx"&gt;variable&lt;/span&gt; &lt;span class="s2"&gt;"zone"&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;description&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"GCP zone"&lt;/span&gt;
  &lt;span class="nx"&gt;type&lt;/span&gt;        &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;string&lt;/span&gt;
  &lt;span class="nx"&gt;default&lt;/span&gt;     &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"us-central1-a"&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="nx"&gt;variable&lt;/span&gt; &lt;span class="s2"&gt;"environment"&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;description&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"Environment name"&lt;/span&gt;
  &lt;span class="nx"&gt;type&lt;/span&gt;        &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;string&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Using a Module
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;environments/prod/main.tf:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight hcl"&gt;&lt;code&gt;&lt;span class="nx"&gt;module&lt;/span&gt; &lt;span class="s2"&gt;"training_vm"&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;source&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"../../modules/ml-training-vm"&lt;/span&gt;

  &lt;span class="nx"&gt;name_prefix&lt;/span&gt;  &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"prod-ml"&lt;/span&gt;
  &lt;span class="nx"&gt;machine_type&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"n1-highmem-16"&lt;/span&gt;
  &lt;span class="nx"&gt;zone&lt;/span&gt;         &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"us-central1-a"&lt;/span&gt;
  &lt;span class="nx"&gt;environment&lt;/span&gt;  &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"production"&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="c1"&gt;# Access module outputs&lt;/span&gt;
&lt;span class="nx"&gt;output&lt;/span&gt; &lt;span class="s2"&gt;"training_vm_ip"&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;value&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;module&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;training_vm&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;instance_ip&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Best Practices
&lt;/h2&gt;

&lt;h3&gt;
  
  
  1. Project Structure
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;terraform/
├── environments/
│   ├── dev/
│   │   ├── main.tf
│   │   ├── variables.tf
│   │   ├── terraform.tfvars
│   │   └── backend.tf
│   ├── staging/
│   └── prod/
├── modules/
│   ├── compute/
│   ├── networking/
│   └── storage/
└── shared/
    ├── providers.tf
    └── versions.tf
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  2. Naming Conventions
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight hcl"&gt;&lt;code&gt;&lt;span class="c1"&gt;# Resource naming: project-environment-service-resource&lt;/span&gt;
&lt;span class="nx"&gt;resource&lt;/span&gt; &lt;span class="s2"&gt;"google_storage_bucket"&lt;/span&gt; &lt;span class="s2"&gt;"ml_models"&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;name&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"${var.project_id}-${var.environment}-ml-models"&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="c1"&gt;# Variable naming: descriptive and clear&lt;/span&gt;
&lt;span class="nx"&gt;variable&lt;/span&gt; &lt;span class="s2"&gt;"ml_training_instance_tier"&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;description&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"Machine tier for ML training instance"&lt;/span&gt;
  &lt;span class="nx"&gt;type&lt;/span&gt;        &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;string&lt;/span&gt;
  &lt;span class="nx"&gt;default&lt;/span&gt;     &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"n1-standard-8"&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  3. Version Constraints
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight hcl"&gt;&lt;code&gt;&lt;span class="c1"&gt;# versions.tf&lt;/span&gt;
&lt;span class="nx"&gt;terraform&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;required_version&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"&amp;gt;= 1.0"&lt;/span&gt;

  &lt;span class="nx"&gt;required_providers&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;google&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="nx"&gt;source&lt;/span&gt;  &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"hashicorp/google"&lt;/span&gt;
      &lt;span class="nx"&gt;version&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"~&amp;gt; 5.0"&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="nx"&gt;aws&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="nx"&gt;source&lt;/span&gt;  &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"hashicorp/aws"&lt;/span&gt;
      &lt;span class="nx"&gt;version&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"~&amp;gt; 5.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;h3&gt;
  
  
  4. Resource Tagging/Labeling
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight hcl"&gt;&lt;code&gt;&lt;span class="c1"&gt;# Consistent labeling across all resources&lt;/span&gt;
&lt;span class="nx"&gt;locals&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;common_labels&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;environment&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;var&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;environment&lt;/span&gt;
    &lt;span class="nx"&gt;project&lt;/span&gt;     &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;var&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;project_name&lt;/span&gt;
    &lt;span class="nx"&gt;managed_by&lt;/span&gt;  &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"terraform"&lt;/span&gt;
    &lt;span class="nx"&gt;team&lt;/span&gt;        &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;var&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;team_name&lt;/span&gt;
    &lt;span class="nx"&gt;cost_center&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;var&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;cost_center&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="nx"&gt;resource&lt;/span&gt; &lt;span class="s2"&gt;"google_storage_bucket"&lt;/span&gt; &lt;span class="s2"&gt;"ml_models"&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;name&lt;/span&gt;   &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;var&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;bucket_name&lt;/span&gt;
  &lt;span class="nx"&gt;labels&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;local&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;common_labels&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="nx"&gt;resource&lt;/span&gt; &lt;span class="s2"&gt;"google_compute_instance"&lt;/span&gt; &lt;span class="s2"&gt;"training"&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;name&lt;/span&gt;   &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;var&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;instance_name&lt;/span&gt;
  &lt;span class="nx"&gt;labels&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;local&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;common_labels&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  5. Secrets Management
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight hcl"&gt;&lt;code&gt;&lt;span class="c1"&gt;# Don't store secrets in .tf files!&lt;/span&gt;
&lt;span class="nx"&gt;variable&lt;/span&gt; &lt;span class="s2"&gt;"database_password"&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;description&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"Database password"&lt;/span&gt;
  &lt;span class="nx"&gt;type&lt;/span&gt;        &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;string&lt;/span&gt;
  &lt;span class="nx"&gt;sensitive&lt;/span&gt;   &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;  &lt;span class="c1"&gt;# Marks as sensitive in outputs&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="c1"&gt;# Use random provider for generated secrets&lt;/span&gt;
&lt;span class="nx"&gt;resource&lt;/span&gt; &lt;span class="s2"&gt;"random_password"&lt;/span&gt; &lt;span class="s2"&gt;"db_password"&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="mi"&gt;32&lt;/span&gt;
  &lt;span class="nx"&gt;special&lt;/span&gt;          &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;
  &lt;span class="nx"&gt;override_special&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"!#$%&amp;amp;*()-_=+[]{}&amp;lt;&amp;gt;:?"&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="c1"&gt;# Store in Secret Manager&lt;/span&gt;
&lt;span class="nx"&gt;resource&lt;/span&gt; &lt;span class="s2"&gt;"google_secret_manager_secret"&lt;/span&gt; &lt;span class="s2"&gt;"db_password"&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;secret_id&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"db-password"&lt;/span&gt;

  &lt;span class="nx"&gt;replication&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;automatic&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="nx"&gt;resource&lt;/span&gt; &lt;span class="s2"&gt;"google_secret_manager_secret_version"&lt;/span&gt; &lt;span class="s2"&gt;"db_password_version"&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;secret&lt;/span&gt;      &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;google_secret_manager_secret&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;db_password&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;id&lt;/span&gt;
  &lt;span class="nx"&gt;secret_data&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;random_password&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;db_password&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;result&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Advanced Topics
&lt;/h2&gt;

&lt;h3&gt;
  
  
  1. Terraform Workspaces
&lt;/h3&gt;

&lt;p&gt;Manage multiple environments with one configuration:&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;# Create workspace&lt;/span&gt;
terraform workspace new staging

&lt;span class="c"&gt;# List workspaces&lt;/span&gt;
terraform workspace list

&lt;span class="c"&gt;# Switch workspace&lt;/span&gt;
terraform workspace &lt;span class="k"&gt;select &lt;/span&gt;prod

&lt;span class="c"&gt;# Current workspace&lt;/span&gt;
terraform workspace show
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Use in configuration:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight hcl"&gt;&lt;code&gt;&lt;span class="nx"&gt;locals&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;environment&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;terraform&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;workspace&lt;/span&gt;

  &lt;span class="nx"&gt;machine_types&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;dev&lt;/span&gt;     &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"n1-standard-2"&lt;/span&gt;
    &lt;span class="nx"&gt;staging&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"n1-standard-4"&lt;/span&gt;
    &lt;span class="nx"&gt;prod&lt;/span&gt;    &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"n1-standard-8"&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;

  &lt;span class="nx"&gt;machine_type&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;local&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;machine_types&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;terraform&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;workspace&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="nx"&gt;resource&lt;/span&gt; &lt;span class="s2"&gt;"google_compute_instance"&lt;/span&gt; &lt;span class="s2"&gt;"app"&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;name&lt;/span&gt;         &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"${terraform.workspace}-app-server"&lt;/span&gt;
  &lt;span class="nx"&gt;machine_type&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;local&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;machine_type&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  2. Dependencies
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;Implicit dependency (recommended):&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight hcl"&gt;&lt;code&gt;&lt;span class="nx"&gt;resource&lt;/span&gt; &lt;span class="s2"&gt;"google_storage_bucket"&lt;/span&gt; &lt;span class="s2"&gt;"ml_data"&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;name&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"ml-training-data"&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="nx"&gt;resource&lt;/span&gt; &lt;span class="s2"&gt;"google_storage_bucket_object"&lt;/span&gt; &lt;span class="s2"&gt;"dataset"&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;bucket&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;google_storage_bucket&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;ml_data&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;name&lt;/span&gt;  &lt;span class="c1"&gt;# Implicit dependency&lt;/span&gt;
  &lt;span class="nx"&gt;name&lt;/span&gt;   &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"dataset.csv"&lt;/span&gt;
  &lt;span class="nx"&gt;source&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"data/dataset.csv"&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Explicit dependency:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight hcl"&gt;&lt;code&gt;&lt;span class="nx"&gt;resource&lt;/span&gt; &lt;span class="s2"&gt;"google_compute_instance"&lt;/span&gt; &lt;span class="s2"&gt;"training_vm"&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;name&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"training-vm"&lt;/span&gt;

  &lt;span class="nx"&gt;depends_on&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
    &lt;span class="nx"&gt;google_storage_bucket&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;ml_data&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="nx"&gt;google_storage_bucket&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;ml_models&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;h3&gt;
  
  
  3. Import Existing Infrastructure
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# Import existing bucket&lt;/span&gt;
terraform import google_storage_bucket.existing my-existing-bucket

&lt;span class="c"&gt;# Then write configuration to match&lt;/span&gt;
resource &lt;span class="s2"&gt;"google_storage_bucket"&lt;/span&gt; &lt;span class="s2"&gt;"existing"&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
  name     &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"my-existing-bucket"&lt;/span&gt;
  location &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"US"&lt;/span&gt;
&lt;span class="o"&gt;}&lt;/span&gt;

&lt;span class="c"&gt;# Verify import&lt;/span&gt;
terraform plan  &lt;span class="c"&gt;# Should show no changes&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Real-World Example: ML Training Infrastructure
&lt;/h2&gt;

&lt;p&gt;Complete setup for machine learning training:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight hcl"&gt;&lt;code&gt;&lt;span class="c1"&gt;# main.tf&lt;/span&gt;
&lt;span class="nx"&gt;terraform&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;required_version&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"&amp;gt;= 1.0"&lt;/span&gt;

  &lt;span class="nx"&gt;backend&lt;/span&gt; &lt;span class="s2"&gt;"gcs"&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;bucket&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"my-terraform-state"&lt;/span&gt;
    &lt;span class="nx"&gt;prefix&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"ml-infrastructure/state"&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;

  &lt;span class="nx"&gt;required_providers&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;google&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="nx"&gt;source&lt;/span&gt;  &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"hashicorp/google"&lt;/span&gt;
      &lt;span class="nx"&gt;version&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"~&amp;gt; 5.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;span class="nx"&gt;provider&lt;/span&gt; &lt;span class="s2"&gt;"google"&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;project&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;var&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;project_id&lt;/span&gt;
  &lt;span class="nx"&gt;region&lt;/span&gt;  &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;var&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;region&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="c1"&gt;# Locals for common values&lt;/span&gt;
&lt;span class="nx"&gt;locals&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;common_labels&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;environment&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;var&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;environment&lt;/span&gt;
    &lt;span class="nx"&gt;project&lt;/span&gt;     &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"ml-training"&lt;/span&gt;
    &lt;span class="nx"&gt;managed_by&lt;/span&gt;  &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"terraform"&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="c1"&gt;# Storage buckets&lt;/span&gt;
&lt;span class="nx"&gt;resource&lt;/span&gt; &lt;span class="s2"&gt;"google_storage_bucket"&lt;/span&gt; &lt;span class="s2"&gt;"training_data"&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;name&lt;/span&gt;          &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"${var.project_id}-${var.environment}-training-data"&lt;/span&gt;
  &lt;span class="nx"&gt;location&lt;/span&gt;      &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;var&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;region&lt;/span&gt;
  &lt;span class="nx"&gt;force_destroy&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="kc"&gt;false&lt;/span&gt;

  &lt;span class="nx"&gt;labels&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;local&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;common_labels&lt;/span&gt;

  &lt;span class="nx"&gt;versioning&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;enabled&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="nx"&gt;lifecycle_rule&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;action&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="nx"&gt;type&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"Delete"&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="nx"&gt;condition&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="nx"&gt;age&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;90&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;resource&lt;/span&gt; &lt;span class="s2"&gt;"google_storage_bucket"&lt;/span&gt; &lt;span class="s2"&gt;"ml_models"&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;name&lt;/span&gt;          &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"${var.project_id}-${var.environment}-models"&lt;/span&gt;
  &lt;span class="nx"&gt;location&lt;/span&gt;      &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;var&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;region&lt;/span&gt;
  &lt;span class="nx"&gt;force_destroy&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="kc"&gt;false&lt;/span&gt;

  &lt;span class="nx"&gt;labels&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;local&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;common_labels&lt;/span&gt;

  &lt;span class="nx"&gt;versioning&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;enabled&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="c1"&gt;# Training VM with GPU&lt;/span&gt;
&lt;span class="nx"&gt;resource&lt;/span&gt; &lt;span class="s2"&gt;"google_compute_instance"&lt;/span&gt; &lt;span class="s2"&gt;"gpu_training"&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;name&lt;/span&gt;         &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"${var.environment}-gpu-training"&lt;/span&gt;
  &lt;span class="nx"&gt;machine_type&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;var&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;gpu_machine_type&lt;/span&gt;
  &lt;span class="nx"&gt;zone&lt;/span&gt;         &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;var&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;zone&lt;/span&gt;

  &lt;span class="nx"&gt;labels&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;local&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;common_labels&lt;/span&gt;

  &lt;span class="nx"&gt;boot_disk&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;initialize_params&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="nx"&gt;image&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"deeplearning-platform-release/pytorch-latest-gpu"&lt;/span&gt;
      &lt;span class="nx"&gt;size&lt;/span&gt;  &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;100&lt;/span&gt;
      &lt;span class="nx"&gt;type&lt;/span&gt;  &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"pd-ssd"&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;

  &lt;span class="nx"&gt;guest_accelerator&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;type&lt;/span&gt;  &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"nvidia-tesla-t4"&lt;/span&gt;
    &lt;span class="nx"&gt;count&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="nx"&gt;scheduling&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;on_host_maintenance&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"TERMINATE"&lt;/span&gt;  &lt;span class="c1"&gt;# Required for GPU instances&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;

  &lt;span class="nx"&gt;network_interface&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;network&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"default"&lt;/span&gt;
    &lt;span class="nx"&gt;access_config&lt;/span&gt; &lt;span class="p"&gt;{}&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;

  &lt;span class="nx"&gt;service_account&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;scopes&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;"cloud-platform"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;

  &lt;span class="nx"&gt;metadata_startup_script&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;file&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;"${path.module}/scripts/startup.sh"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="c1"&gt;# BigQuery dataset for analytics&lt;/span&gt;
&lt;span class="nx"&gt;resource&lt;/span&gt; &lt;span class="s2"&gt;"google_bigquery_dataset"&lt;/span&gt; &lt;span class="s2"&gt;"ml_analytics"&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;dataset_id&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"${var.environment}_ml_analytics"&lt;/span&gt;
  &lt;span class="nx"&gt;location&lt;/span&gt;   &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;var&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;region&lt;/span&gt;

  &lt;span class="nx"&gt;labels&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;local&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;common_labels&lt;/span&gt;

  &lt;span class="nx"&gt;access&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;role&lt;/span&gt;          &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"OWNER"&lt;/span&gt;
    &lt;span class="nx"&gt;user_by_email&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;var&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;admin_email&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="c1"&gt;# Outputs&lt;/span&gt;
&lt;span class="nx"&gt;output&lt;/span&gt; &lt;span class="s2"&gt;"training_data_bucket"&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;value&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;google_storage_bucket&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;training_data&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;name&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="nx"&gt;output&lt;/span&gt; &lt;span class="s2"&gt;"models_bucket"&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;value&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;google_storage_bucket&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;ml_models&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;name&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="nx"&gt;output&lt;/span&gt; &lt;span class="s2"&gt;"gpu_vm_ip"&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;value&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;google_compute_instance&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;gpu_training&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;network_interface&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;].&lt;/span&gt;&lt;span class="nx"&gt;access_config&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;].&lt;/span&gt;&lt;span class="nx"&gt;nat_ip&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="nx"&gt;output&lt;/span&gt; &lt;span class="s2"&gt;"bigquery_dataset"&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;value&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;google_bigquery_dataset&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;ml_analytics&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;dataset_id&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;variables.tf:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight hcl"&gt;&lt;code&gt;&lt;span class="nx"&gt;variable&lt;/span&gt; &lt;span class="s2"&gt;"project_id"&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;description&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"GCP Project ID"&lt;/span&gt;
  &lt;span class="nx"&gt;type&lt;/span&gt;        &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;string&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="nx"&gt;variable&lt;/span&gt; &lt;span class="s2"&gt;"region"&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;description&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"GCP region"&lt;/span&gt;
  &lt;span class="nx"&gt;type&lt;/span&gt;        &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;string&lt;/span&gt;
  &lt;span class="nx"&gt;default&lt;/span&gt;     &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"us-central1"&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="nx"&gt;variable&lt;/span&gt; &lt;span class="s2"&gt;"zone"&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;description&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"GCP zone"&lt;/span&gt;
  &lt;span class="nx"&gt;type&lt;/span&gt;        &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;string&lt;/span&gt;
  &lt;span class="nx"&gt;default&lt;/span&gt;     &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"us-central1-a"&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="nx"&gt;variable&lt;/span&gt; &lt;span class="s2"&gt;"environment"&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;description&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"Environment name"&lt;/span&gt;
  &lt;span class="nx"&gt;type&lt;/span&gt;        &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;string&lt;/span&gt;

  &lt;span class="nx"&gt;validation&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;condition&lt;/span&gt;     &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;contains&lt;/span&gt;&lt;span class="p"&gt;([&lt;/span&gt;&lt;span class="s2"&gt;"dev"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;"staging"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;"prod"&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt; &lt;span class="nx"&gt;var&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;environment&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="nx"&gt;error_message&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"Must be dev, staging, or prod"&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="nx"&gt;variable&lt;/span&gt; &lt;span class="s2"&gt;"gpu_machine_type"&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;description&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"Machine type for GPU training"&lt;/span&gt;
  &lt;span class="nx"&gt;type&lt;/span&gt;        &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;string&lt;/span&gt;
  &lt;span class="nx"&gt;default&lt;/span&gt;     &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"n1-standard-8"&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="nx"&gt;variable&lt;/span&gt; &lt;span class="s2"&gt;"admin_email"&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;description&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"Admin email for BigQuery access"&lt;/span&gt;
  &lt;span class="nx"&gt;type&lt;/span&gt;        &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;string&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;terraform.tfvars:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight hcl"&gt;&lt;code&gt;&lt;span class="nx"&gt;project_id&lt;/span&gt;       &lt;span class="err"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"my-ai-project"&lt;/span&gt;
&lt;span class="nx"&gt;region&lt;/span&gt;           &lt;span class="err"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"us-central1"&lt;/span&gt;
&lt;span class="nx"&gt;zone&lt;/span&gt;             &lt;span class="err"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"us-central1-a"&lt;/span&gt;
&lt;span class="nx"&gt;environment&lt;/span&gt;      &lt;span class="err"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"prod"&lt;/span&gt;
&lt;span class="nx"&gt;gpu_machine_type&lt;/span&gt; &lt;span class="err"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"n1-standard-8"&lt;/span&gt;
&lt;span class="nx"&gt;admin_email&lt;/span&gt;      &lt;span class="err"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"admin@example.com"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Common Pitfalls &amp;amp; Solutions
&lt;/h2&gt;

&lt;h3&gt;
  
  
  1. State File Conflicts
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;Problem&lt;/strong&gt;: Multiple team members modifying infrastructure simultaneously&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Solution&lt;/strong&gt;: Use remote state with locking&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight hcl"&gt;&lt;code&gt;&lt;span class="nx"&gt;terraform&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;backend&lt;/span&gt; &lt;span class="s2"&gt;"gcs"&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;bucket&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"terraform-state"&lt;/span&gt;
    &lt;span class="nx"&gt;prefix&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"prod"&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;h3&gt;
  
  
  2. Hardcoded Values
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;Problem&lt;/strong&gt;: Credentials and secrets in code&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Solution&lt;/strong&gt;: Use variables and secret management&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight hcl"&gt;&lt;code&gt;&lt;span class="c1"&gt;# Bad&lt;/span&gt;
&lt;span class="nx"&gt;resource&lt;/span&gt; &lt;span class="s2"&gt;"google_sql_database_instance"&lt;/span&gt; &lt;span class="s2"&gt;"db"&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;root_password&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"hardcoded-password-123"&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="c1"&gt;# Good&lt;/span&gt;
&lt;span class="nx"&gt;resource&lt;/span&gt; &lt;span class="s2"&gt;"google_sql_database_instance"&lt;/span&gt; &lt;span class="s2"&gt;"db"&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;root_password&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;var&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;db_password&lt;/span&gt;  &lt;span class="c1"&gt;# From env var or secret manager&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  3. No Version Constraints
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;Problem&lt;/strong&gt;: Provider updates break infrastructure&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Solution&lt;/strong&gt;: Pin provider versions&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight hcl"&gt;&lt;code&gt;&lt;span class="nx"&gt;terraform&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;required_providers&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;google&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="nx"&gt;source&lt;/span&gt;  &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"hashicorp/google"&lt;/span&gt;
      &lt;span class="nx"&gt;version&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"~&amp;gt; 5.0"&lt;/span&gt;  &lt;span class="c1"&gt;# Allow 5.x updates, not 6.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;h2&gt;
  
  
  Essential Commands
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# Initialize working directory&lt;/span&gt;
terraform init

&lt;span class="c"&gt;# Validate configuration&lt;/span&gt;
terraform validate

&lt;span class="c"&gt;# Format code&lt;/span&gt;
terraform &lt;span class="nb"&gt;fmt&lt;/span&gt; &lt;span class="nt"&gt;-recursive&lt;/span&gt;

&lt;span class="c"&gt;# Preview changes&lt;/span&gt;
terraform plan

&lt;span class="c"&gt;# Apply changes&lt;/span&gt;
terraform apply

&lt;span class="c"&gt;# Destroy infrastructure&lt;/span&gt;
terraform destroy

&lt;span class="c"&gt;# Show current state&lt;/span&gt;
terraform show

&lt;span class="c"&gt;# List resources&lt;/span&gt;
terraform state list

&lt;span class="c"&gt;# Import existing resource&lt;/span&gt;
terraform import &amp;lt;resource_type&amp;gt;.&amp;lt;name&amp;gt; &amp;lt;&lt;span class="nb"&gt;id&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;

&lt;span class="c"&gt;# Refresh state&lt;/span&gt;
terraform refresh

&lt;span class="c"&gt;# Output values&lt;/span&gt;
terraform output
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



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

&lt;h3&gt;
  
  
  Official Documentation
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://www.terraform.io/docs" rel="noopener noreferrer"&gt;Terraform Documentation&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://registry.terraform.io/" rel="noopener noreferrer"&gt;Terraform Registry&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://registry.terraform.io/providers/hashicorp/google/latest/docs" rel="noopener noreferrer"&gt;Google Provider Docs&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Best Practices
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://www.terraform-best-practices.com/" rel="noopener noreferrer"&gt;Terraform Best Practices&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://cloud.google.com/architecture" rel="noopener noreferrer"&gt;Google Cloud Architecture Center&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Tutorials
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://learn.hashicorp.com/terraform" rel="noopener noreferrer"&gt;HashiCorp Learn&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://cloud.google.com/docs/terraform" rel="noopener noreferrer"&gt;Google Cloud Terraform Tutorials&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Key Takeaways
&lt;/h2&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Infrastructure as Code&lt;/strong&gt; - Treat infrastructure like software&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;State Management&lt;/strong&gt; - Always use remote state in production&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Modules&lt;/strong&gt; - Build reusable components&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Version Control&lt;/strong&gt; - Track all infrastructure changes in Git&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Plan First&lt;/strong&gt; - Always review &lt;code&gt;terraform plan&lt;/code&gt; before applying&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Security&lt;/strong&gt; - Never commit secrets, use Secret Manager&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Consistency&lt;/strong&gt; - Use naming conventions and labels&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  What's Next
&lt;/h2&gt;

&lt;ol&gt;
&lt;li&gt;Set up remote state backend&lt;/li&gt;
&lt;li&gt;Create reusable modules for common patterns&lt;/li&gt;
&lt;li&gt;Implement CI/CD for infrastructure changes&lt;/li&gt;
&lt;li&gt;Add automated testing with &lt;code&gt;terraform validate&lt;/code&gt; and &lt;code&gt;tflint&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Explore advanced features like &lt;code&gt;for_each&lt;/code&gt; and dynamic blocks&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;&lt;strong&gt;Questions or feedback&lt;/strong&gt;: &lt;a href="mailto:jeremy@intentsolutions.io"&gt;jeremy@intentsolutions.io&lt;/a&gt;&lt;br&gt;
&lt;strong&gt;GitHub&lt;/strong&gt;: &lt;a href="https://github.com/jeremylongshore" rel="noopener noreferrer"&gt;@jeremylongshore&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Educational resource from Intent Solutions for AI infrastructure and DevOps practitioners.&lt;/em&gt;&lt;/p&gt;

</description>
      <category>terraform</category>
      <category>infrastructureascode</category>
      <category>devops</category>
      <category>cloud</category>
    </item>
    <item>
      <title>Self-Hosting n8n on Contabo VPS: Enterprise Automation for $0/Month</title>
      <dc:creator>Jeremy Longshore</dc:creator>
      <pubDate>Wed, 25 Mar 2026 22:51:50 +0000</pubDate>
      <link>https://dev.to/jeremy_longshore/self-hosting-n8n-on-contabo-vps-enterprise-automation-for-0month-2j01</link>
      <guid>https://dev.to/jeremy_longshore/self-hosting-n8n-on-contabo-vps-enterprise-automation-for-0month-2j01</guid>
      <description>&lt;p&gt;We just deployed a production-ready n8n instance at &lt;code&gt;n8n.intentsolutions.io&lt;/code&gt; with zero monthly software costs. Here's the complete technical architecture and deployment process.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Business Case
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Before:&lt;/strong&gt; Paying $20+/month for n8n Cloud&lt;br&gt;
&lt;strong&gt;After:&lt;/strong&gt; $0/month on existing Contabo VPS&lt;br&gt;
&lt;strong&gt;Setup Time:&lt;/strong&gt; 2 hours&lt;br&gt;
&lt;strong&gt;ROI:&lt;/strong&gt; Infinite (one-time setup, zero recurring cost)&lt;/p&gt;
&lt;h2&gt;
  
  
  Architecture Overview
&lt;/h2&gt;


&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;┌─────────────────────────────────────┐
│   n8n.intentsolutions.io (DNS)      │
│   ↓                                  │
│   194.113.67.242:443 (HTTPS)        │
│   ↓                                  │
│   Caddy Reverse Proxy               │
│   - Auto SSL (Let's Encrypt)        │
│   - Port 443 (HTTPS)                │
│   ↓                                  │
│   Docker Container: n8n             │
│   - Port 5678 (internal)            │
│   - SQLite database                 │
│   - Persistent data: ./data         │
│   - Backups: ./backups              │
└─────────────────────────────────────┘
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;h2&gt;
  
  
  Technical Challenge: Port Conflicts
&lt;/h2&gt;

&lt;p&gt;The server was already running:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Port 80&lt;/strong&gt;: Apache2 (existing web server)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Port 443&lt;/strong&gt;: Needed for n8n HTTPS&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Port 8080&lt;/strong&gt;: Caddy file browser&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Solution:&lt;/strong&gt; Configure Caddy with &lt;code&gt;auto_https disable_redirects&lt;/code&gt; to handle HTTPS only without requiring port 80:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;{
    auto_https disable_redirects
}

n8n.intentsolutions.io:443 {
    reverse_proxy localhost:5678
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Docker Configuration
&lt;/h2&gt;

&lt;p&gt;Key decisions for production stability:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Network Mode:&lt;/strong&gt; &lt;code&gt;host&lt;/code&gt; (avoids port mapping conflicts)&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;services&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;n8n&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;image&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;n8nio/n8n:latest&lt;/span&gt;
    &lt;span class="na"&gt;network_mode&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;host"&lt;/span&gt;
    &lt;span class="na"&gt;environment&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;N8N_HOST=n8n.intentsolutions.io&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;N8N_PROTOCOL=https&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;WEBHOOK_URL=https://n8n.intentsolutions.io/&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Database:&lt;/strong&gt; SQLite (perfect for single-user/small team)&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;environment&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;DB_TYPE=sqlite&lt;/span&gt;
  &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;DB_SQLITE_VACUUM_ON_STARTUP=true&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Workflow Import Process
&lt;/h2&gt;

&lt;p&gt;Imported 15 workflows using n8n CLI:&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;# Prepare workflows (remove problematic fields)&lt;/span&gt;
jq &lt;span class="s1"&gt;'del(.id, .versionId, .tags) | . + {active: false}'&lt;/span&gt; workflow.json &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; clean.json

&lt;span class="c"&gt;# Copy to container&lt;/span&gt;
docker &lt;span class="nb"&gt;cp &lt;/span&gt;workflows/ n8n:/tmp/

&lt;span class="c"&gt;# Import via CLI&lt;/span&gt;
docker &lt;span class="nb"&gt;exec&lt;/span&gt; &lt;span class="nt"&gt;-u&lt;/span&gt; node n8n n8n import:workflow &lt;span class="nt"&gt;--separate&lt;/span&gt; &lt;span class="nt"&gt;--input&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;/tmp/workflows/
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Result:&lt;/strong&gt; Successfully imported:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Daily Energizer Article Generator V4&lt;/li&gt;
&lt;li&gt;Tech/AI News Pipeline&lt;/li&gt;
&lt;li&gt;Lead Follow-up System (with Bland.ai integration)&lt;/li&gt;
&lt;li&gt;AI Blog Journalist&lt;/li&gt;
&lt;li&gt;Upwork Proposal Generator&lt;/li&gt;
&lt;li&gt;Disposable Marketplace&lt;/li&gt;
&lt;li&gt;Gmail Drive Organizer&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  SSL Certificate Automation
&lt;/h2&gt;

&lt;p&gt;Caddy automatically obtains SSL certificates from Let's Encrypt on first HTTPS request. No manual certificate management required.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Certificate Location:&lt;/strong&gt; &lt;code&gt;/var/lib/caddy/.local/share/caddy/certificates/&lt;/code&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Security Hardening
&lt;/h2&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Basic Authentication:&lt;/strong&gt; Enabled with secure password&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;HTTPS Enforced:&lt;/strong&gt; All traffic over SSL&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Firewall:&lt;/strong&gt; UFW configured for port 443&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Environment Variables:&lt;/strong&gt; Secrets stored in &lt;code&gt;.env&lt;/code&gt; (not committed)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;API Token:&lt;/strong&gt; Generated for programmatic access&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  Performance &amp;amp; Monitoring
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Resource Usage:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Memory: ~200MB (n8n container)&lt;/li&gt;
&lt;li&gt;CPU: Minimal (idle workflows)&lt;/li&gt;
&lt;li&gt;Storage: SSD on Contabo VPS&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Monitoring Commands:&lt;/strong&gt;&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;# Container health&lt;/span&gt;
docker ps | &lt;span class="nb"&gt;grep &lt;/span&gt;n8n

&lt;span class="c"&gt;# Logs&lt;/span&gt;
docker logs &lt;span class="nt"&gt;-f&lt;/span&gt; n8n

&lt;span class="c"&gt;# Disk usage&lt;/span&gt;
&lt;span class="nb"&gt;du&lt;/span&gt; &lt;span class="nt"&gt;-sh&lt;/span&gt; data/ backups/
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Cost Comparison
&lt;/h2&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Option&lt;/th&gt;
&lt;th&gt;Monthly Cost&lt;/th&gt;
&lt;th&gt;Setup Time&lt;/th&gt;
&lt;th&gt;Maintenance&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;n8n Cloud&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;$20-50+&lt;/td&gt;
&lt;td&gt;5 minutes&lt;/td&gt;
&lt;td&gt;Zero&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Self-Hosted&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;$0&lt;/td&gt;
&lt;td&gt;2 hours&lt;/td&gt;
&lt;td&gt;Minimal&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Annual Savings&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;$240-600&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;One-time&lt;/td&gt;
&lt;td&gt;Backups only&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;h2&gt;
  
  
  Backup Strategy
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# Weekly backup script&lt;/span&gt;
&lt;span class="nb"&gt;tar&lt;/span&gt; &lt;span class="nt"&gt;-czf&lt;/span&gt; &lt;span class="s2"&gt;"n8n-backup-&lt;/span&gt;&lt;span class="si"&gt;$(&lt;/span&gt;&lt;span class="nb"&gt;date&lt;/span&gt; +%Y%m%d&lt;span class="si"&gt;)&lt;/span&gt;&lt;span class="s2"&gt;.tar.gz"&lt;/span&gt; ./data
&lt;span class="nb"&gt;mv &lt;/span&gt;n8n-backup-&lt;span class="k"&gt;*&lt;/span&gt;.tar.gz ./backups/

&lt;span class="c"&gt;# Cleanup old backups (30 days)&lt;/span&gt;
find ./backups/ &lt;span class="nt"&gt;-name&lt;/span&gt; &lt;span class="s2"&gt;"n8n-backup-*.tar.gz"&lt;/span&gt; &lt;span class="nt"&gt;-mtime&lt;/span&gt; +30 &lt;span class="nt"&gt;-delete&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Lessons Learned
&lt;/h2&gt;

&lt;h3&gt;
  
  
  1. Use Existing Infrastructure
&lt;/h3&gt;

&lt;p&gt;We had Caddy already running - adding n8n was just one more config block. Don't deploy redundant services.&lt;/p&gt;

&lt;h3&gt;
  
  
  2. Network Mode Matters
&lt;/h3&gt;

&lt;p&gt;&lt;code&gt;host&lt;/code&gt; networking avoided all port mapping complexity. Sometimes the simple solution is best.&lt;/p&gt;

&lt;h3&gt;
  
  
  3. SQLite is Underrated
&lt;/h3&gt;

&lt;p&gt;For single-user or small team, SQLite is perfect. No PostgreSQL overhead needed.&lt;/p&gt;

&lt;h3&gt;
  
  
  4. CLI Import &amp;gt; API
&lt;/h3&gt;

&lt;p&gt;The n8n CLI handled workflow imports reliably. The API had validation issues with exported JSON structure.&lt;/p&gt;

&lt;h2&gt;
  
  
  Related Posts
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://dev.to/posts/waygate-mcp-v2-1-0-forensic-analysis-to-production-enterprise-server/"&gt;Waygate MCP v2.1.0: Forensic Analysis to Production Enterprise Server&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://dev.to/posts/when-commands-dont-work-debugging-journey-through-automated-content-systems/"&gt;When Commands Don't Work: Debugging Journey Through Automated Content Systems&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Next Steps
&lt;/h2&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Migrate to PostgreSQL&lt;/strong&gt; (if team grows beyond 5 users)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Set up CI/CD&lt;/strong&gt; for workflow version control&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Configure monitoring/alerts&lt;/strong&gt; for workflow failures&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Implement automated backups&lt;/strong&gt; to cloud storage&lt;/li&gt;
&lt;/ol&gt;

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

&lt;p&gt;Self-hosting n8n on existing infrastructure eliminated $240-600/year in SaaS costs while maintaining full control over data and workflows. The 2-hour setup investment pays for itself in the first month.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Business Impact:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;✅ Zero recurring automation costs&lt;/li&gt;
&lt;li&gt;✅ Complete data ownership&lt;/li&gt;
&lt;li&gt;✅ Custom domain with SSL&lt;/li&gt;
&lt;li&gt;✅ 15 production workflows migrated&lt;/li&gt;
&lt;li&gt;✅ Enterprise-grade infrastructure&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Repository:&lt;/strong&gt; &lt;a href="https://github.com/jeremylongshore/n8n-workflows" rel="noopener noreferrer"&gt;n8n-workflows&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Want to eliminate your SaaS costs while maintaining enterprise capabilities? Let's talk about self-hosted automation architecture for your business.&lt;/em&gt;&lt;/p&gt;

</description>
      <category>n8n</category>
      <category>selfhosting</category>
      <category>automation</category>
      <category>devops</category>
    </item>
    <item>
      <title>Scaling AI Batch Processing: Enhancing 235 Plugins with Vertex AI Gemini on the Free Tier</title>
      <dc:creator>Jeremy Longshore</dc:creator>
      <pubDate>Wed, 25 Mar 2026 22:51:36 +0000</pubDate>
      <link>https://dev.to/jeremy_longshore/scaling-ai-batch-processing-enhancing-235-plugins-with-vertex-ai-gemini-on-the-free-tier-4d50</link>
      <guid>https://dev.to/jeremy_longshore/scaling-ai-batch-processing-enhancing-235-plugins-with-vertex-ai-gemini-on-the-free-tier-4d50</guid>
      <description>&lt;h2&gt;
  
  
  The Problem: 235 Plugins Need Comprehensive Documentation
&lt;/h2&gt;

&lt;p&gt;I maintain &lt;a href="https://github.com/jeremylongshore/claude-code-plugins" rel="noopener noreferrer"&gt;claude-code-plugins&lt;/a&gt;, a marketplace with 235 plugins for Claude Code. Each plugin needed enhanced SKILL.md files (8,000-14,000 bytes) following Anthropic's Agent Skills standards. Doing this manually would take weeks.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The goal:&lt;/strong&gt; Process all 235 plugins overnight using Vertex AI Gemini 2.0 Flash - entirely on the free tier.&lt;/p&gt;

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

&lt;ul&gt;
&lt;li&gt;Must stay within Vertex AI free tier limits&lt;/li&gt;
&lt;li&gt;Need 100% success rate (no corrupted files)&lt;/li&gt;
&lt;li&gt;Require full audit trail for compliance&lt;/li&gt;
&lt;li&gt;Zero tolerance for API quota violations&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  The Journey: From Ultra-Conservative to Optimized
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Phase 1: Initial System Design
&lt;/h3&gt;

&lt;p&gt;I built &lt;code&gt;overnight-plugin-enhancer.py&lt;/code&gt; with these core components:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="c1"&gt;# Ultra-conservative rate limiting
&lt;/span&gt;&lt;span class="n"&gt;RATE_LIMIT_DELAY&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mf"&gt;90.0&lt;/span&gt;  &lt;span class="c1"&gt;# 90 seconds base delay
&lt;/span&gt;&lt;span class="n"&gt;RATE_LIMIT_RANDOMNESS&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mf"&gt;30.0&lt;/span&gt;  &lt;span class="c1"&gt;# Add 0-30 seconds random
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Why so slow?&lt;/strong&gt; I wanted to ensure we stayed well under the Vertex AI free tier limits:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;1,500 requests/day&lt;/li&gt;
&lt;li&gt;235 plugins = 470 API calls (analysis + generation per plugin)&lt;/li&gt;
&lt;li&gt;At 90-120s per plugin: ~15 plugins/hour = Safe&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The system included:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;SQLite Audit Database&lt;/strong&gt;
&lt;/li&gt;
&lt;/ol&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;init_database&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="n"&gt;cursor&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;execute&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;'''&lt;/span&gt;&lt;span class="s"&gt;
        CREATE TABLE IF NOT EXISTS enhancements (
            id INTEGER PRIMARY KEY AUTOINCREMENT,
            timestamp TEXT NOT NULL,
            plugin_name TEXT NOT NULL,
            plugin_path TEXT NOT NULL,
            enhancement_type TEXT NOT NULL,
            status TEXT NOT NULL,
            processing_time_seconds REAL
        )
    &lt;/span&gt;&lt;span class="sh"&gt;'''&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Automatic Backups Before Changes&lt;/strong&gt;
&lt;/li&gt;
&lt;/ol&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;backup_plugin&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;plugin&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="n"&gt;timestamp&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;datetime&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;now&lt;/span&gt;&lt;span class="p"&gt;().&lt;/span&gt;&lt;span class="nf"&gt;strftime&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;%Y%m%d_%H%M%S&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;backup_dir&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;BACKUP_DIR&lt;/span&gt; &lt;span class="o"&gt;/&lt;/span&gt; &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;plugin-backups&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt; &lt;span class="o"&gt;/&lt;/span&gt; &lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;plugin&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;name&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s"&gt;_&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;timestamp&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;
    &lt;span class="n"&gt;shutil&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;copytree&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;plugin_path&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;backup_dir&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Two-Phase AI Generation&lt;/strong&gt;
&lt;/li&gt;
&lt;/ol&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="c1"&gt;# Phase 1: Analyze and create enhancement plan
&lt;/span&gt;&lt;span class="n"&gt;plan&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;generate_enhancement_plan&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;plugin&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="c1"&gt;# Phase 2: Generate comprehensive SKILL.md
&lt;/span&gt;&lt;span class="n"&gt;skill_content&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;generate_skill_md&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;plugin&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;plan&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Phase 2: Testing and Timeout Issues
&lt;/h3&gt;

&lt;p&gt;First test run on 10 plugins:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;timeout &lt;/span&gt;120 python3 overnight-plugin-enhancer.py &lt;span class="nt"&gt;--limit&lt;/span&gt; 10
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Problem:&lt;/strong&gt; Process appeared stuck - no output for minutes.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Diagnosis:&lt;/strong&gt; Python output buffering. The script was working but output wasn't showing in real-time.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Fix:&lt;/strong&gt; Unbuffered output flag&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;python3 &lt;span class="nt"&gt;-u&lt;/span&gt; overnight-plugin-enhancer.py
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Result:&lt;/strong&gt; Real-time log streaming confirmed the system was working perfectly. Each plugin took 90-100 seconds as designed.&lt;/p&gt;

&lt;h3&gt;
  
  
  Phase 3: Expanding to All Categories
&lt;/h3&gt;

&lt;p&gt;Initially, I only processed 7 plugin categories (testing). Time to go all in:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="c1"&gt;# Before (testing with 7 categories)
&lt;/span&gt;&lt;span class="n"&gt;CATEGORIES&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
    &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;productivity&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;security&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;testing&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;packages&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;examples&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;community&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;mcp&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;
&lt;span class="p"&gt;]&lt;/span&gt;

&lt;span class="c1"&gt;# After (all 17 categories - full 235 plugins)
&lt;/span&gt;&lt;span class="n"&gt;CATEGORIES&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
    &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;productivity&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;security&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;testing&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;packages&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;examples&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;community&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;mcp&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;ai-agency&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;ai-ml&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;api-development&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;crypto&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;database&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;devops&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;fairdb-operations-kit&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;finance&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;performance&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;skill-enhancers&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;
&lt;span class="p"&gt;]&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Started the overnight batch:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;nohup &lt;/span&gt;python3 &lt;span class="nt"&gt;-u&lt;/span&gt; scripts/overnight-plugin-enhancer.py &lt;span class="o"&gt;&amp;gt;&amp;gt;&lt;/span&gt; overnight-enhancement-all-plugins.log 2&amp;gt;&amp;amp;1 &amp;amp;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Phase 4: Disaster Recovery Planning
&lt;/h3&gt;

&lt;p&gt;Mid-batch, user concern: &lt;strong&gt;"What if I get locked out of GitHub?"&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;This is a legitimate fear when you have 235 production plugins and rely on GitHub for everything. I needed an off-site backup solution immediately.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Enter Turso:&lt;/strong&gt; Edge SQLite database with free tier (500 databases, 9GB storage).&lt;/p&gt;

&lt;p&gt;Built &lt;code&gt;turso-plugin-backup.sh&lt;/code&gt; in 30 minutes:&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;# Creates comprehensive backup with integrity checks&lt;/span&gt;
create_plugins_archive&lt;span class="o"&gt;()&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
    &lt;span class="nb"&gt;local &lt;/span&gt;&lt;span class="nv"&gt;archive_name&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"plugins-&lt;/span&gt;&lt;span class="si"&gt;$(&lt;/span&gt;&lt;span class="nb"&gt;date&lt;/span&gt; +%Y%m%d-%H%M%S&lt;span class="si"&gt;)&lt;/span&gt;&lt;span class="s2"&gt;.tar.gz"&lt;/span&gt;
    &lt;span class="nb"&gt;tar&lt;/span&gt; &lt;span class="nt"&gt;-czf&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$archive_path&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="nt"&gt;-C&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$PLUGINS_DIR&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="nb"&gt;.&lt;/span&gt;

    &lt;span class="c"&gt;# Calculate SHA256 hash for integrity&lt;/span&gt;
    &lt;span class="nb"&gt;local hash&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="si"&gt;$(&lt;/span&gt;&lt;span class="nb"&gt;sha256sum&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$archive_path&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="s1"&gt;' '&lt;/span&gt; &lt;span class="nt"&gt;-f1&lt;/span&gt;&lt;span class="si"&gt;)&lt;/span&gt;
&lt;span class="o"&gt;}&lt;/span&gt;

&lt;span class="c"&gt;# Store metadata in Turso&lt;/span&gt;
upload_to_turso&lt;span class="o"&gt;()&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
    turso db shell &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$TURSO_DB_NAME&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&amp;lt;&lt;/span&gt;&lt;span class="no"&gt;EOF&lt;/span&gt;&lt;span class="sh"&gt;
    INSERT INTO backup_history (timestamp, version, plugin_count,
                                archive_size, backup_metadata)
    VALUES ('&lt;/span&gt;&lt;span class="nv"&gt;$timestamp&lt;/span&gt;&lt;span class="sh"&gt;', '&lt;/span&gt;&lt;span class="nv"&gt;$version&lt;/span&gt;&lt;span class="sh"&gt;', &lt;/span&gt;&lt;span class="nv"&gt;$plugin_count&lt;/span&gt;&lt;span class="sh"&gt;,
            &lt;/span&gt;&lt;span class="nv"&gt;$archive_size&lt;/span&gt;&lt;span class="sh"&gt;, '&lt;/span&gt;&lt;span class="nv"&gt;$metadata&lt;/span&gt;&lt;span class="sh"&gt;');
&lt;/span&gt;&lt;span class="no"&gt;EOF
&lt;/span&gt;&lt;span class="o"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;The backup system includes:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;All 235 plugins (tar.gz compressed)&lt;/li&gt;
&lt;li&gt;Enhancement SQLite database&lt;/li&gt;
&lt;li&gt;Plugin inventory JSON&lt;/li&gt;
&lt;li&gt;SHA256 integrity hashes&lt;/li&gt;
&lt;li&gt;Turso metadata for queryability&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Recovery time objective:&lt;/strong&gt; &amp;lt; 30 minutes to restore complete repository from Turso.&lt;/p&gt;

&lt;p&gt;Related: &lt;a href="https://startaitools.com/posts/building-production-testing-suite-playwright-github-actions-survey-automation/" rel="noopener noreferrer"&gt;Building Production Testing Suite with Playwright&lt;/a&gt; covers similar disaster recovery planning.&lt;/p&gt;

&lt;h3&gt;
  
  
  Phase 5: Speed Optimization Request
&lt;/h3&gt;

&lt;p&gt;At 12:53 PM (after 12 hours): 157/235 plugins complete (66%)&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;User:&lt;/strong&gt; "Let's speed it up - we have room."&lt;/p&gt;

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

&lt;ul&gt;
&lt;li&gt;Only using 7-14% of Vertex AI free tier quota&lt;/li&gt;
&lt;li&gt;Success rate: 100%&lt;/li&gt;
&lt;li&gt;Could safely cut delays in half&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Optimization:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="c1"&gt;# Old: Ultra-conservative
&lt;/span&gt;&lt;span class="n"&gt;RATE_LIMIT_DELAY&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mf"&gt;90.0&lt;/span&gt;
&lt;span class="n"&gt;RATE_LIMIT_RANDOMNESS&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mf"&gt;30.0&lt;/span&gt;

&lt;span class="c1"&gt;# New: Conservative but 2x faster
&lt;/span&gt;&lt;span class="n"&gt;RATE_LIMIT_DELAY&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mf"&gt;45.0&lt;/span&gt;
&lt;span class="n"&gt;RATE_LIMIT_RANDOMNESS&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mf"&gt;15.0&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



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

&lt;ul&gt;
&lt;li&gt;Before: ~15 plugins/hour → Completion: 5:30 AM&lt;/li&gt;
&lt;li&gt;After: ~30 plugins/hour → Completion: 2:30 AM (saved 3 hours!)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Killed the old process and restarted:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;kill &lt;/span&gt;876147
&lt;span class="nb"&gt;nohup &lt;/span&gt;python3 &lt;span class="nt"&gt;-u&lt;/span&gt; scripts/overnight-plugin-enhancer.py &lt;span class="o"&gt;&amp;gt;&amp;gt;&lt;/span&gt; overnight-enhancement-all-plugins.log 2&amp;gt;&amp;amp;1 &amp;amp;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The system intelligently skips already-enhanced plugins:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="n"&gt;skill_path&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;plugin_path&lt;/span&gt; &lt;span class="o"&gt;/&lt;/span&gt; &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;skills&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt; &lt;span class="o"&gt;/&lt;/span&gt; &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;skill-adapter&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt; &lt;span class="o"&gt;/&lt;/span&gt; &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;SKILL.md&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;
&lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;skill_path&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;exists&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="ow"&gt;and&lt;/span&gt; &lt;span class="nf"&gt;len&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;skill_path&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;read_text&lt;/span&gt;&lt;span class="p"&gt;())&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="mi"&gt;8000&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;  ⏭️  SKILL.md already comprehensive (&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nf"&gt;len&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;content&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s"&gt; bytes)&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="c1"&gt;# Skip AI generation, just backup and validate
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  The Technical Architecture
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Rate Limiting Strategy
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;apply_rate_limit&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;idx&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;total&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="sh"&gt;"""&lt;/span&gt;&lt;span class="s"&gt;Apply intelligent rate limiting&lt;/span&gt;&lt;span class="sh"&gt;"""&lt;/span&gt;
    &lt;span class="c1"&gt;# Base delay with randomness
&lt;/span&gt;    &lt;span class="n"&gt;base_delay&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;RATE_LIMIT_DELAY&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="n"&gt;random&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;uniform&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="n"&gt;RATE_LIMIT_RANDOMNESS&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;time&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;sleep&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;base_delay&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="c1"&gt;# Extra rest every 10 plugins
&lt;/span&gt;    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;idx&lt;/span&gt; &lt;span class="o"&gt;%&lt;/span&gt; &lt;span class="mi"&gt;10&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="n"&gt;extra_delay&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;random&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;uniform&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;30&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;60&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;  ⏸️  Extra rest break: &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;extra_delay&lt;/span&gt;&lt;span class="si"&gt;:&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="n"&gt;f&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s"&gt;s...&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="n"&gt;time&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;sleep&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;extra_delay&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Why this works:&lt;/strong&gt;&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Randomness prevents patterns&lt;/strong&gt; that might trigger rate limits&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Extra breaks every 10 plugins&lt;/strong&gt; ensure long-term sustainability&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Configurable delays&lt;/strong&gt; allow real-time optimization without code changes&lt;/li&gt;
&lt;/ol&gt;

&lt;h3&gt;
  
  
  Smart Processing Logic
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;process_plugin&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;plugin&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="sh"&gt;"""&lt;/span&gt;&lt;span class="s"&gt;Process single plugin with comprehensive enhancement&lt;/span&gt;&lt;span class="sh"&gt;"""&lt;/span&gt;
    &lt;span class="k"&gt;try&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="c1"&gt;# Always backup first (disaster recovery)
&lt;/span&gt;        &lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;backup_plugin&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;plugin&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

        &lt;span class="c1"&gt;# Generate enhancement plan
&lt;/span&gt;        &lt;span class="n"&gt;plan&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;generate_enhancement_plan&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;plugin&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

        &lt;span class="c1"&gt;# Generate or validate SKILL.md
&lt;/span&gt;        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;needs_generation&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
            &lt;span class="n"&gt;skill_content&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;generate_skill_md&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;plugin&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;plan&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="k"&gt;else&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
            &lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;  ⏭️  SKILL.md already comprehensive&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

        &lt;span class="c1"&gt;# Create bundled resource directories
&lt;/span&gt;        &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;resource_type&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;scripts&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;references&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;assets&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;]:&lt;/span&gt;
            &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;plan&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;bundled_resources_needed&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;][&lt;/span&gt;&lt;span class="n"&gt;resource_type&lt;/span&gt;&lt;span class="p"&gt;]:&lt;/span&gt;
                &lt;span class="nf"&gt;create_resource_directory&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;resource_type&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

        &lt;span class="c1"&gt;# Log to SQLite audit trail
&lt;/span&gt;        &lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log_enhancement&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;plugin&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;success&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;changes&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="k"&gt;except&lt;/span&gt; &lt;span class="nb"&gt;Exception&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="n"&gt;e&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log_enhancement&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;plugin&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;failed&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;error&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="nf"&gt;str&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;e&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
        &lt;span class="k"&gt;raise&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Monitoring and Observability
&lt;/h3&gt;

&lt;p&gt;Real-time progress tracking:&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;# Check current status&lt;/span&gt;
&lt;span class="nb"&gt;tail&lt;/span&gt; &lt;span class="nt"&gt;-f&lt;/span&gt; overnight-enhancement-all-plugins.log

&lt;span class="c"&gt;# Query database for metrics&lt;/span&gt;
sqlite3 backups/plugin-enhancements/enhancements.db &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="s2"&gt;"SELECT COUNT(*) FROM enhancements WHERE status = 'success';"&lt;/span&gt;

&lt;span class="c"&gt;# Get processing time stats&lt;/span&gt;
sqlite3 backups/plugin-enhancements/enhancements.db &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="s2"&gt;"SELECT AVG(processing_time_seconds), MAX(processing_time_seconds)
   FROM enhancements WHERE status = 'success';"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  The Results
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Final Metrics (as of 11:30 PM):&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Plugins processed:&lt;/strong&gt; 163/235 (69% complete)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Success rate:&lt;/strong&gt; 100%&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Average SKILL.md size:&lt;/strong&gt; 10,617 bytes&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Processing time:&lt;/strong&gt; ~60-100 seconds per plugin&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;API calls used:&lt;/strong&gt; ~326 of 1,500 daily limit (22%)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Estimated completion:&lt;/strong&gt; 2:30-3:00 AM&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Quality metrics:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;All SKILL.md files follow Anthropic Agent Skills standards&lt;/li&gt;
&lt;li&gt;Comprehensive documentation (8,000-14,000 bytes each)&lt;/li&gt;
&lt;li&gt;Proper YAML frontmatter&lt;/li&gt;
&lt;li&gt;Bundled resource directories created&lt;/li&gt;
&lt;li&gt;Complete backup trail in SQLite&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Lessons Learned
&lt;/h2&gt;

&lt;h3&gt;
  
  
  1. Start Conservative, Optimize Later
&lt;/h3&gt;

&lt;p&gt;Initial 90-120s delays seemed wasteful, but they ensured:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;No quota violations&lt;/li&gt;
&lt;li&gt;100% success rate&lt;/li&gt;
&lt;li&gt;Confidence to optimize&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Once we had data proving safety margins, cutting to 45-60s was an easy decision.&lt;/p&gt;

&lt;h3&gt;
  
  
  2. Real-Time Observability is Critical
&lt;/h3&gt;

&lt;p&gt;The unbuffered output fix was crucial. Without seeing real-time progress:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Can't identify stuck processes&lt;/li&gt;
&lt;li&gt;Can't calculate accurate completion times&lt;/li&gt;
&lt;li&gt;Can't debug issues as they happen&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  3. Disaster Recovery Before Production
&lt;/h3&gt;

&lt;p&gt;Building the Turso backup system mid-batch was the right call. Production systems need:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Off-site backups (not just local)&lt;/li&gt;
&lt;li&gt;Integrity verification (SHA256 hashes)&lt;/li&gt;
&lt;li&gt;Fast recovery (&amp;lt; 30 minutes)&lt;/li&gt;
&lt;li&gt;Queryable metadata (Turso SQLite)&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  4. SQLite for Audit Trails
&lt;/h3&gt;

&lt;p&gt;Using SQLite for enhancement tracking provided:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Complete history of every change&lt;/li&gt;
&lt;li&gt;Easy querying for metrics&lt;/li&gt;
&lt;li&gt;Backup-friendly (just copy the .db file)&lt;/li&gt;
&lt;li&gt;No external dependencies&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Related: &lt;a href="https://startaitools.com/posts/building-254-table-bigquery-schema-72-hours/" rel="noopener noreferrer"&gt;Building 254 BigQuery Schemas in 72 Hours&lt;/a&gt; shows similar database-driven automation patterns.&lt;/p&gt;

&lt;h3&gt;
  
  
  5. Smart Skipping Saves Money
&lt;/h3&gt;

&lt;p&gt;The system automatically skips already-enhanced plugins:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Saves API quota&lt;/li&gt;
&lt;li&gt;Reduces processing time&lt;/li&gt;
&lt;li&gt;Allows safe restarts after failures&lt;/li&gt;
&lt;li&gt;Enables incremental improvements&lt;/li&gt;
&lt;/ul&gt;

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

&lt;p&gt;Full implementation: &lt;a href="https://github.com/jeremylongshore/claude-code-plugins/blob/main/scripts/overnight-plugin-enhancer.py" rel="noopener noreferrer"&gt;claude-code-plugins/scripts/overnight-plugin-enhancer.py&lt;/a&gt;&lt;/p&gt;

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

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;overnight-plugin-enhancer.py&lt;/code&gt; - Main batch processor&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;turso-plugin-backup.sh&lt;/code&gt; - Disaster recovery system&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;TURSO-BACKUP-GUIDE.md&lt;/code&gt; - Recovery procedures&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;enhancements.db&lt;/code&gt; - SQLite audit trail&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  What's Next
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Immediate (tonight):&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;[x] Complete batch processing (163/235 done)&lt;/li&gt;
&lt;li&gt;[ ] Run Turso backup after completion&lt;/li&gt;
&lt;li&gt;[ ] Release v1.2.0 with 235 enhanced plugins&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Short-term (this week):&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;[ ] Generate analytics on enhancement quality&lt;/li&gt;
&lt;li&gt;[ ] Spot-check 10 random SKILL.md files&lt;/li&gt;
&lt;li&gt;[ ] Deploy marketplace website with new content&lt;/li&gt;
&lt;li&gt;[ ] Set up automated weekly backups to Turso&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Long-term (future releases):&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;[ ] Build &lt;code&gt;turso-plugin-restore.sh&lt;/code&gt; for automated recovery&lt;/li&gt;
&lt;li&gt;[ ] Add Turso backup to release checklist&lt;/li&gt;
&lt;li&gt;[ ] Implement progressive enhancement (update existing SKILL.md files)&lt;/li&gt;
&lt;li&gt;[ ] A/B test different SKILL.md structures for effectiveness&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Try It Yourself
&lt;/h2&gt;

&lt;p&gt;The enhancement system is open source and works with any plugin repository:&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;# Clone the repo&lt;/span&gt;
git clone https://github.com/jeremylongshore/claude-code-plugins
&lt;span class="nb"&gt;cd &lt;/span&gt;claude-code-plugins

&lt;span class="c"&gt;# Configure Vertex AI&lt;/span&gt;
gcloud auth application-default login

&lt;span class="c"&gt;# Test on single plugin&lt;/span&gt;
python3 scripts/overnight-plugin-enhancer.py &lt;span class="nt"&gt;--plugin&lt;/span&gt; overnight-dev

&lt;span class="c"&gt;# Run batch on 10 plugins&lt;/span&gt;
python3 scripts/overnight-plugin-enhancer.py &lt;span class="nt"&gt;--limit&lt;/span&gt; 10

&lt;span class="c"&gt;# Full overnight batch&lt;/span&gt;
&lt;span class="nb"&gt;nohup &lt;/span&gt;python3 &lt;span class="nt"&gt;-u&lt;/span&gt; scripts/overnight-plugin-enhancer.py &lt;span class="o"&gt;&amp;gt;&amp;gt;&lt;/span&gt; batch.log 2&amp;gt;&amp;amp;1 &amp;amp;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



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

&lt;ul&gt;
&lt;li&gt;Google Cloud account with Vertex AI enabled&lt;/li&gt;
&lt;li&gt;Python 3.12+&lt;/li&gt;
&lt;li&gt;Claude Code plugins repository structure&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Free tier limits:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;1,500 Vertex AI requests/day&lt;/li&gt;
&lt;li&gt;Process ~750 plugins/day (2 calls per plugin)&lt;/li&gt;
&lt;li&gt;Completely free for repositories under 1,000 plugins&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Related Reading
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;a href="https://startaitools.com/posts/building-ai-friendly-codebase-documentation-real-time-claude-md-creation-journey/" rel="noopener noreferrer"&gt;Building AI-Friendly Codebases&lt;/a&gt; - Documentation systems for AI tools&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://startaitools.com/posts/debugging-claude-code-slash-commands-silent-deployment-failures/" rel="noopener noreferrer"&gt;Automating Developer Workflows&lt;/a&gt; - Building slash commands and automation&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://startaitools.com/posts/building-production-testing-suite-playwright-github-actions-survey-automation/" rel="noopener noreferrer"&gt;Building Production Testing Suites&lt;/a&gt; - Automated testing at scale&lt;/li&gt;
&lt;/ul&gt;

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

&lt;p&gt;Batch processing 235 plugins with AI isn't just about throwing API calls at the problem. It requires:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Conservative rate limiting&lt;/strong&gt; that respects free tier limits&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Real-time observability&lt;/strong&gt; to catch issues immediately&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Disaster recovery planning&lt;/strong&gt; before you need it&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Smart optimization&lt;/strong&gt; based on real data&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Complete audit trails&lt;/strong&gt; for compliance and debugging&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;The overnight batch will complete around 2:30 AM with 100% success rate, entirely on the Vertex AI free tier. That's 235 plugins × 10KB of AI-generated documentation = 2.3MB of high-quality content created overnight.&lt;/p&gt;

&lt;p&gt;Not bad for free.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Want to see the results?&lt;/strong&gt; Check out &lt;a href="https://claudecodeplugins.io/" rel="noopener noreferrer"&gt;claudecodeplugins.io&lt;/a&gt; to see the enhanced plugins in action, or explore the &lt;a href="https://github.com/jeremylongshore/claude-code-plugins" rel="noopener noreferrer"&gt;complete source code&lt;/a&gt; on GitHub.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Have questions about batch processing with Vertex AI?&lt;/strong&gt; Drop a comment or find me on X &lt;a href="https://twitter.com/AsphaltCowb0y" rel="noopener noreferrer"&gt;@AsphaltCowb0y&lt;/a&gt;.&lt;/p&gt;

</description>
      <category>vertexai</category>
      <category>gemini</category>
      <category>batchprocessing</category>
      <category>automation</category>
    </item>
    <item>
      <title>Perception Dashboard: Wiring Real Triggers, Topic Watchlists, and the BSL-1.1 Decision</title>
      <dc:creator>Jeremy Longshore</dc:creator>
      <pubDate>Wed, 25 Mar 2026 22:46:13 +0000</pubDate>
      <link>https://dev.to/jeremy_longshore/perception-dashboard-wiring-real-triggers-topic-watchlists-and-the-bsl-11-decision-55kn</link>
      <guid>https://dev.to/jeremy_longshore/perception-dashboard-wiring-real-triggers-topic-watchlists-and-the-bsl-11-decision-55kn</guid>
      <description>&lt;h2&gt;
  
  
  What Perception Does
&lt;/h2&gt;

&lt;p&gt;Perception is a media intelligence dashboard. It ingests articles from 128 RSS feeds, scores them by relevance and trending signals, and surfaces the ones that matter through a React dashboard backed by Firestore and a FastAPI MCP service.&lt;/p&gt;

&lt;p&gt;The dashboard existed before February. What didn't exist: real data ingestion, interactive topic management, or auto-ingestion on login. The dashboard was running on mock data. This month, the plumbing got real.&lt;/p&gt;

&lt;h2&gt;
  
  
  Mock to Real: The store_articles Trigger
&lt;/h2&gt;

&lt;p&gt;The mock &lt;code&gt;store_articles&lt;/code&gt; function returned fake results. The real implementation uses Firestore batch writes with URL-based deduplication:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="c1"&gt;# Document ID: art-{sha256(url)[:16]}
&lt;/span&gt;&lt;span class="n"&gt;doc_id&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;art-&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;hashlib&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;sha256&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;url&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;encode&lt;/span&gt;&lt;span class="p"&gt;()).&lt;/span&gt;&lt;span class="nf"&gt;hexdigest&lt;/span&gt;&lt;span class="p"&gt;()[&lt;/span&gt;&lt;span class="si"&gt;:&lt;/span&gt;&lt;span class="mi"&gt;16&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;

&lt;span class="c1"&gt;# Batch writes with merge=True (preserves AI-enriched fields)
&lt;/span&gt;&lt;span class="n"&gt;batch&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;db&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;batch&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;article&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;articles&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="c1"&gt;# Firestore batch limit
&lt;/span&gt;    &lt;span class="n"&gt;ref&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;db&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;collection&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;articles&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;document&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;doc_id&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;batch&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;set&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;ref&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;article_data&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;merge&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="bp"&gt;True&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;batch&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;commit&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The &lt;code&gt;merge=True&lt;/code&gt; strategy is deliberate. When the ingestion pipeline re-processes an article, it preserves any AI-enriched fields (summaries, tags, relevance scores) that were added after the initial store. New raw data merges in without overwriting analysis results.&lt;/p&gt;

&lt;p&gt;The orchestration endpoint (&lt;code&gt;POST /trigger/ingestion&lt;/code&gt;) loads all 128 RSS sources from a YAML config, fetches feeds with a 10-item semaphore for concurrency control, stores articles in 500-doc batches, and upserts author metadata. It returns 202 immediately and runs the pipeline in a background task, updating a Firestore &lt;code&gt;ingestion_runs&lt;/code&gt; document with live phase progress.&lt;/p&gt;

&lt;p&gt;Idempotency is built in: if a run is already active, the endpoint returns 409. Stale runs (older than 10 minutes) get auto-cleaned. Success evaluation checks that articles were actually stored, fewer than 50% of sources failed, and the run completed within 5 minutes.&lt;/p&gt;

&lt;h2&gt;
  
  
  Auto-Ingestion on Login
&lt;/h2&gt;

&lt;p&gt;The login flow now redirects to &lt;code&gt;/dashboard&lt;/code&gt; instead of the feed page. On mount, a &lt;code&gt;useAutoIngestion&lt;/code&gt; hook fires once per browser session:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;SESSION_KEY&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;perception-auto-ingestion-fired&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="nf"&gt;useEffect&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;sessionStorage&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getItem&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;SESSION_KEY&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt; &lt;span class="k"&gt;return&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nx"&gt;sessionStorage&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;setItem&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;SESSION_KEY&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;1&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

  &lt;span class="nf"&gt;fetch&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;MCP_URL&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;/trigger/ingestion`&lt;/span&gt;&lt;span class="p"&gt;,&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="s1"&gt;POST&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;body&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;stringify&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;auto&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;time_window_hours&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;24&lt;/span&gt; &lt;span class="p"&gt;}),&lt;/span&gt;
  &lt;span class="p"&gt;});&lt;/span&gt;
&lt;span class="p"&gt;},&lt;/span&gt; &lt;span class="p"&gt;[]);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Fire-and-forget. The hook dispatches a custom event that the &lt;code&gt;IngestionButton&lt;/code&gt; component listens for, showing live progress phases: "Loading sources..." → "Fetching feeds..." → "Storing articles..." → "Done." The button polls every 3 seconds and detects stuck runs with a 5-minute timeout.&lt;/p&gt;

&lt;p&gt;The user experience: log in, immediately see fresh articles loading in the background. No manual trigger needed.&lt;/p&gt;

&lt;h2&gt;
  
  
  Interactive Topic Watchlist
&lt;/h2&gt;

&lt;p&gt;The Topic Watchlist moved from a static display to full CRUD with real-time Firestore sync:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Add topics&lt;/strong&gt; with keyword input, category dropdown (16 categories), and source search with autocomplete&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Multi-select source assignment&lt;/strong&gt; per topic — pick which of the 128 feeds should match&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Delete with hover-reveal&lt;/strong&gt; button — no accidental deletions&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Live metadata&lt;/strong&gt; showing "N sources available" and "M topics watching"
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// Firestore write on topic creation&lt;/span&gt;
&lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;setDoc&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;doc&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;db&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;topics_to_monitor&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;topicId&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="na"&gt;keyword&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;topicKeyword&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;category&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;selectedCategory&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;sources&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;selectedSources&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;created_at&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="na"&gt;user_id&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;auth&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;currentUser&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;uid&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 source search filters against the full 128-source catalog with max 10 results, so the dropdown stays usable. Categories include the obvious ones (ai, engineering, infrastructure) plus niche feeds (hn-popular, crypto, automotive).&lt;/p&gt;

&lt;h2&gt;
  
  
  Per-Category Trending
&lt;/h2&gt;

&lt;p&gt;The Articles page now shows the top 3 trending articles per category, scored by a simple algorithm:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;TrendingScore = RelevanceScore + RecencyBoost + HNBoost

RecencyBoost: 5 points at &amp;lt; 1 hour, decaying to 0 over 24 hours
HNBoost: Every 100 Hacker News points = 1 trending point (capped at 5)
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The HN integration fetches the top 30 stories and matches by URL against the article database. Articles that are both recent and popular on HN bubble to the top. Articles older than 24 hours lose their recency boost entirely, keeping the trending view fresh.&lt;/p&gt;

&lt;p&gt;Category filters show per-category article counts. The Articles page became the default home route — the first thing you see after login is what's trending right now across your monitored topics.&lt;/p&gt;

&lt;h2&gt;
  
  
  The BSL-1.1 Decision
&lt;/h2&gt;

&lt;p&gt;Perception moved from Apache 2.0 to Business Source License 1.1. The terms:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Change Date&lt;/strong&gt;: Four years from publication&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Change License&lt;/strong&gt;: Converts to Apache 2.0 automatically&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Permitted&lt;/strong&gt;: Non-production use (testing, development, research, academic)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Restricted&lt;/strong&gt;: Commercial repackaging as a competing service&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This wasn't a philosophical decision. It was practical. Perception has a real competitive advantage in its RSS source curation (128 feeds), its trending algorithm, and its MCP service architecture. Apache 2.0 lets anyone fork and deploy a competing hosted version immediately. BSL-1.1 gives a four-year commercial window while keeping the code source-available for developers who want to learn from it, build on it, or use it in their own non-competing products.&lt;/p&gt;

&lt;p&gt;After four years, it converts to Apache 2.0 automatically. No renewal, no decision needed.&lt;/p&gt;

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

&lt;p&gt;&lt;strong&gt;Mock-to-real is where architecture gets tested.&lt;/strong&gt; The mock ingestion worked because it returned the exact shape the dashboard expected. Real Firestore writes, concurrent feed fetching, and batch limits exposed timing issues and data shape mismatches that mocks hide by design.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Fire-and-forget with visibility.&lt;/strong&gt; The auto-ingestion hook doesn't wait for completion. It fires the trigger and the IngestionButton shows progress independently. This pattern works because the user doesn't need to wait — they can browse existing articles while fresh ones load.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Session guards prevent duplicate work.&lt;/strong&gt; Without the &lt;code&gt;sessionStorage&lt;/code&gt; guard, every React re-render would fire another ingestion trigger. The guard ensures one trigger per browser session, and the server-side idempotency guard (409 Conflict) catches anything the client misses.&lt;/p&gt;

</description>
      <category>firebase</category>
      <category>firestore</category>
      <category>mcp</category>
      <category>dashboard</category>
    </item>
    <item>
      <title>OKLCH: Why Your CSS Color System Is Lying to You</title>
      <dc:creator>Jeremy Longshore</dc:creator>
      <pubDate>Wed, 25 Mar 2026 22:46:02 +0000</pubDate>
      <link>https://dev.to/jeremy_longshore/oklch-why-your-css-color-system-is-lying-to-you-4j7a</link>
      <guid>https://dev.to/jeremy_longshore/oklch-why-your-css-color-system-is-lying-to-you-4j7a</guid>
      <description>&lt;p&gt;Pick two colors in HSL. Same saturation, same lightness, different hues. They should look equally bright. They don't.&lt;/p&gt;

&lt;p&gt;This is the core lie of HSL. And it bit me during a full visual facelift of the claude-code-plugins marketplace.&lt;/p&gt;

&lt;h2&gt;
  
  
  The HSL Problem
&lt;/h2&gt;

&lt;p&gt;Try this yourself. Open a browser console:&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="nc"&gt;.yellow&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;hsl&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="m"&gt;60&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="m"&gt;100%&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="m"&gt;50%&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="nc"&gt;.blue&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;hsl&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="m"&gt;240&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="m"&gt;100%&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="m"&gt;50%&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;Both are &lt;code&gt;100%&lt;/code&gt; saturation, &lt;code&gt;50%&lt;/code&gt; lightness. Mathematically identical brightness. Put them side by side and yellow screams while blue recedes. Your eyes aren't broken — HSL is. It maps to a color model that doesn't account for how human vision actually perceives luminance.&lt;/p&gt;

&lt;p&gt;This matters when you're generating color palettes programmatically. If your primary, secondary, and accent colors share the same lightness value in HSL, they'll look inconsistent. Buttons will feel heavier in some hues than others. Hover states will shift perceived brightness depending on the color. Your design system lies to you at the API level.&lt;/p&gt;

&lt;h2&gt;
  
  
  OKLCH Fixes This
&lt;/h2&gt;

&lt;p&gt;OKLCH is a perceptually uniform color space. Same lightness value means same perceived brightness, regardless of hue. The "OK" refers to Björn Ottosson's improved version of the CIE Lab color model. Three channels:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;L&lt;/strong&gt; — Lightness (0 = black, 1 = white, perceptually linear)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;C&lt;/strong&gt; — Chroma (color intensity, like saturation but calibrated)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;H&lt;/strong&gt; — Hue (0-360 degrees, same as HSL)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The equivalent comparison:&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="nc"&gt;.yellow&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;oklch&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="m"&gt;0.9&lt;/span&gt; &lt;span class="m"&gt;0.2&lt;/span&gt; &lt;span class="m"&gt;100&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="nc"&gt;.blue&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;oklch&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="m"&gt;0.9&lt;/span&gt; &lt;span class="m"&gt;0.2&lt;/span&gt; &lt;span class="m"&gt;260&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;Same lightness, same chroma. These actually look equally bright. You can sweep through hues and get a coherent palette without manual per-color tweaking.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Marketplace Facelift
&lt;/h2&gt;

&lt;p&gt;PR #343 applied this across the entire claude-code-plugins marketplace. Every page. Homepage, explore, blog, verification badges, individual skill pages. One PR, one coherent visual system.&lt;/p&gt;

&lt;p&gt;The OKLCH adoption wasn't just a color swap. It was part of a broader typography and layout overhaul:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Color system&lt;/strong&gt; — All color values moved to OKLCH, including shadow values&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Typography&lt;/strong&gt; — New font stack and size scale for better hierarchy&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Navigation&lt;/strong&gt; — Restructured nav for cleaner information architecture&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Footer&lt;/strong&gt; — Updated links, layout, and dynamic copyright year&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;PR review caught inline styles that needed extraction and OKLCH shadow values that were hardcoded instead of using CSS custom properties. Good catches. The kind of feedback that turns a facelift into a maintainable system.&lt;/p&gt;

&lt;h2&gt;
  
  
  When to Adopt OKLCH
&lt;/h2&gt;

&lt;p&gt;Browser support is there. Every modern browser handles &lt;code&gt;oklch()&lt;/code&gt; natively. If you're building a design system, generating theme colors from code, or maintaining a dark/light mode toggle, OKLCH eliminates an entire class of "why does this color look wrong" bugs.&lt;/p&gt;

&lt;p&gt;If you're tweaking three hex values on a landing page, HSL is fine. But the moment your color system becomes programmatic — tokens, theme generation, dynamic palettes — OKLCH is the correct abstraction.&lt;/p&gt;

&lt;h2&gt;
  
  
  Related Posts
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://dev.to/posts/verified-plugins-program-quality-signal-for-the-marketplace/"&gt;Verified Plugins Program: Building a Quality Signal for the Marketplace&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://dev.to/posts/three-projects-two-reverts-one-day/"&gt;Three Projects, Two Reverts, One Day&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://dev.to/posts/react-native-mobile-app-one-session/"&gt;React Native Mobile App in One Session&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>webdev</category>
      <category>architecture</category>
      <category>css</category>
    </item>
    <item>
      <title>Applying Universal Directory Standards to a Prompt Engineering Repository</title>
      <dc:creator>Jeremy Longshore</dc:creator>
      <pubDate>Wed, 25 Mar 2026 22:40:40 +0000</pubDate>
      <link>https://dev.to/jeremy_longshore/applying-universal-directory-standards-to-a-prompt-engineering-repository-2fmo</link>
      <guid>https://dev.to/jeremy_longshore/applying-universal-directory-standards-to-a-prompt-engineering-repository-2fmo</guid>
      <description>&lt;h2&gt;
  
  
  The Problem: Inconsistent File Naming Across Master Systems
&lt;/h2&gt;

&lt;p&gt;I had a prompt engineering repository with 150+ templates organized in a logical category structure, but the &lt;code&gt;000-master-systems/&lt;/code&gt; directory had inconsistent file naming. Some files used &lt;code&gt;CATEGORY-###-description-MMDDYY.md&lt;/code&gt;, others didn't. I needed to apply universal directory standards without breaking the existing structure.&lt;/p&gt;

&lt;p&gt;The repository: &lt;a href="https://github.com/jeremylongshore/prompts-intent-solutions" rel="noopener noreferrer"&gt;prompts-intent-solutions&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  The Challenge: Two Different Naming Conventions
&lt;/h2&gt;

&lt;p&gt;Here's where it got interesting. I initially thought the pattern was:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;CATEGORY-###-description-MMDDYY.md
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Looking at existing files in &lt;code&gt;000-master-systems/github/&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;GITHUB-001-master-repo-audit-092825.md
GITHUB-002-master-repo-chore-092825.md
GITHUB-003-master-repo-release-092825.md
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;But the MASTER DIRECTORY STANDARDS document specified a different format:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;NNN-abv-description.ext
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Where:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;NNN&lt;/code&gt; = zero-padded sequence number (001, 002, 003...)&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;abv&lt;/code&gt; = approved abbreviation (tsk, gde, rel, aud, etc.)&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;description&lt;/code&gt; = kebab-case description&lt;/li&gt;
&lt;li&gt;No date suffix for reference documents&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;The confusion:&lt;/strong&gt; Number first or category first?&lt;/p&gt;

&lt;h2&gt;
  
  
  The Iterative Solution Process
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Attempt 1: Following the Existing Pattern
&lt;/h3&gt;

&lt;p&gt;I created new TaskWarrior files matching the github directory pattern:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;TASKWARRIOR-001-mandatory-integration-protocol-100825.md
TASKWARRIOR-002-complete-usage-guide-100825.md
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This seemed right because it matched what I saw.&lt;/p&gt;

&lt;h3&gt;
  
  
  Attempt 2: Examining the Standards Document
&lt;/h3&gt;

&lt;p&gt;Then I re-read the MASTER DIRECTORY STANDARDS more carefully:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight markdown"&gt;&lt;code&gt;&lt;span class="gu"&gt;## DOCS FILE NAMING STANDARD&lt;/span&gt;

&lt;span class="gu"&gt;### Format&lt;/span&gt;
NNN-abv-short-description.ext
&lt;span class="p"&gt;
-&lt;/span&gt; NNN = Zero-padded sequence number (chronology enforced)
&lt;span class="p"&gt;-&lt;/span&gt; abv = Approved abbreviation from table below
&lt;span class="p"&gt;-&lt;/span&gt; short-description = 1–4 words, kebab-case
&lt;span class="p"&gt;-&lt;/span&gt; ext = File extension
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;The realization:&lt;/strong&gt; The existing &lt;code&gt;github/&lt;/code&gt; directory was using an old pattern. The standard called for number-first, not category-first.&lt;/p&gt;

&lt;h3&gt;
  
  
  Attempt 3: The Correct Approach
&lt;/h3&gt;

&lt;p&gt;Renamed to proper format:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;001-tsk-mandatory-integration-protocol.md
002-gde-complete-usage-guide.md
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Using approved abbreviations:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;tsk&lt;/code&gt; = Task Breakdown/List&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;gde&lt;/code&gt; = User Guide/Handbook&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Key insight:&lt;/strong&gt; Reference documents don't need date suffixes. Dates are for versioned files like &lt;code&gt;deployment-config-2024-10-05-v2.json&lt;/code&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  What We Built: Five Master System Documents
&lt;/h2&gt;

&lt;h3&gt;
  
  
  1. TaskWarrior Integration Protocol (&lt;code&gt;001-tsk-mandatory-integration-protocol.md&lt;/code&gt;)
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;Purpose:&lt;/strong&gt; Enforce TaskWarrior lifecycle tracking for all code-related tasks.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The Four-Phase Mandate:&lt;/strong&gt;&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Task Decomposition&lt;/strong&gt; - Break work into discrete tasks before coding&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Task Activation&lt;/strong&gt; - Start time tracking with &lt;code&gt;task &amp;lt;ID&amp;gt; start&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Code Execution&lt;/strong&gt; - Implement with progress annotations&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Task Completion&lt;/strong&gt; - Mark done and review time spent&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;&lt;strong&gt;Required Attributes:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;task add &lt;span class="s2"&gt;"Build authentication system"&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  project:WebDev &lt;span class="se"&gt;\&lt;/span&gt;
  priority:H &lt;span class="se"&gt;\&lt;/span&gt;
  due:today &lt;span class="se"&gt;\&lt;/span&gt;
  +coding +security
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Every task must include:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;project:&lt;/code&gt; - Categorization&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;priority:&lt;/code&gt; - Urgency (H/M/L)&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;due:&lt;/code&gt; - Realistic deadline&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;tags:&lt;/code&gt; - Minimum 2 relevant tags&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;The Validation Checklist:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;[ ] All discrete work units captured as tasks&lt;/li&gt;
&lt;li&gt;[ ] Dependencies properly linked&lt;/li&gt;
&lt;li&gt;[ ] Priority and due dates set&lt;/li&gt;
&lt;li&gt;[ ] Task started before code execution&lt;/li&gt;
&lt;li&gt;[ ] Time tracking active&lt;/li&gt;
&lt;li&gt;[ ] Task marked done after completion&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  2. TaskWarrior Usage Guide (&lt;code&gt;002-gde-complete-usage-guide.md&lt;/code&gt;)
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;Purpose:&lt;/strong&gt; Practical examples for every usage scenario.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Pattern Catalog:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Simple single-task&lt;/strong&gt;: Create → start → code → done&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Complex multi-step&lt;/strong&gt;: Parent task with dependent subtasks&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Debugging&lt;/strong&gt;: High priority, annotation of findings, resolution notes&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Recurring maintenance&lt;/strong&gt;: &lt;code&gt;recur:weekly&lt;/code&gt; for ongoing tasks&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Troubleshooting Section:&lt;/strong&gt;&lt;br&gt;
Common issues and solutions:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Claude jumps to code without creating tasks → Re-emphasize mandate&lt;/li&gt;
&lt;li&gt;Tasks lack proper attributes → Specify required fields&lt;/li&gt;
&lt;li&gt;No time tracking → Verify &lt;code&gt;task active&lt;/code&gt; output&lt;/li&gt;
&lt;li&gt;Tasks not completed → Explicit completion command&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Customization Examples:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Team collaboration mode&lt;/li&gt;
&lt;li&gt;Custom urgency weights&lt;/li&gt;
&lt;li&gt;Project-specific tag vocabularies&lt;/li&gt;
&lt;li&gt;Minimal mode for rapid prototyping&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;
  
  
  3. Streamlined GitHub Release Workflow (&lt;code&gt;001-rel-master-repo-release.md&lt;/code&gt;)
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;Purpose:&lt;/strong&gt; Complete release pipeline without handoff bloat.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The Problem We Solved:&lt;/strong&gt;&lt;br&gt;
The original release system had:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Chore handoff files&lt;/li&gt;
&lt;li&gt;Manual script orchestration&lt;/li&gt;
&lt;li&gt;Complex initialization phases&lt;/li&gt;
&lt;li&gt;Handoff between audit → chore → release&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;The Solution - 8-Phase Linear Pipeline:&lt;/strong&gt;&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Verification&lt;/strong&gt; - Close issues, run tests, check clean state&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Version Management&lt;/strong&gt; - Determine bump type, increment, commit&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Changelog Generation&lt;/strong&gt; - Build entry (newest first), commit&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Documentation Sync&lt;/strong&gt; - Update README, docs, commit&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Tag &amp;amp; Release&lt;/strong&gt; - Annotated tag, push, GitHub release&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Deployment&lt;/strong&gt; - NPM/Docker/Actions based on repo type&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Announcement&lt;/strong&gt; - Issue, pin, discussion post&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Archive &amp;amp; Schedule&lt;/strong&gt; - Save artifacts, schedule next audit&lt;/li&gt;
&lt;/ol&gt;

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

&lt;ul&gt;
&lt;li&gt;Sequential correctness - proper semantic versioning&lt;/li&gt;
&lt;li&gt;Consistency - all references match current release&lt;/li&gt;
&lt;li&gt;Audit trail - archived artifacts with linked milestones&lt;/li&gt;
&lt;li&gt;Automation ready - standalone or end-to-end execution&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Practical Implementation:&lt;/strong&gt;&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;# Version bump&lt;/span&gt;
npm version patch &lt;span class="nt"&gt;-m&lt;/span&gt; &lt;span class="s2"&gt;"chore: bump version to %s"&lt;/span&gt;

&lt;span class="c"&gt;# Changelog update&lt;/span&gt;
&lt;span class="nb"&gt;cat&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&amp;gt;&lt;/span&gt; CHANGELOG.md &lt;span class="o"&gt;&amp;lt;&amp;lt;&lt;/span&gt; &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="no"&gt;EOF&lt;/span&gt;&lt;span class="sh"&gt;'
## [X.Y.Z] - YYYY-MM-DD
### Added
- New features
### Fixed
- Bug fixes
&lt;/span&gt;&lt;span class="no"&gt;EOF

&lt;/span&gt;&lt;span class="c"&gt;# Create tag and release&lt;/span&gt;
git tag &lt;span class="nt"&gt;-a&lt;/span&gt; vX.Y.Z &lt;span class="nt"&gt;-m&lt;/span&gt; &lt;span class="s2"&gt;"Release vX.Y.Z"&lt;/span&gt;
git push origin vX.Y.Z
gh release create vX.Y.Z &lt;span class="nt"&gt;--generate-notes&lt;/span&gt; &lt;span class="nt"&gt;--latest&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  4. Multi-Repo Workflow Installation (&lt;code&gt;002-aut-install-release-workflow-all-repos.md&lt;/code&gt;)
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;Purpose:&lt;/strong&gt; Install standardized release workflow across entire GitHub organization.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The Mega Prompt Approach:&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Discovery Phase:&lt;/strong&gt;&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;# Scan organizations&lt;/span&gt;
gh repo list &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$ORG&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="nt"&gt;--limit&lt;/span&gt; 1000 &lt;span class="nt"&gt;--json&lt;/span&gt; name,owner,isArchived,isFork

&lt;span class="c"&gt;# Filter repos&lt;/span&gt;
&lt;span class="nb"&gt;grep&lt;/span&gt; &lt;span class="nt"&gt;-E&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$INCLUDE_REGEX&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;-Ev&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$EXCLUDE_REGEX&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



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

&lt;ul&gt;
&lt;li&gt;Auto-detect version bump from commits (BREAKING CHANGE, feat:, or patch)&lt;/li&gt;
&lt;li&gt;Generate changelog from commit history since last tag&lt;/li&gt;
&lt;li&gt;Update version files (package.json, version.txt, README.md)&lt;/li&gt;
&lt;li&gt;Create annotated Git tags&lt;/li&gt;
&lt;li&gt;Generate GitHub releases&lt;/li&gt;
&lt;li&gt;Dry run mode for testing&lt;/li&gt;
&lt;/ul&gt;

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

&lt;ul&gt;
&lt;li&gt;Concurrency control&lt;/li&gt;
&lt;li&gt;Test validation before release&lt;/li&gt;
&lt;li&gt;Clean state verification&lt;/li&gt;
&lt;li&gt;No destructive operations&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Output:&lt;/strong&gt;&lt;br&gt;
CSV summary with status for each repo:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csvs"&gt;&lt;code&gt;&lt;span class="k"&gt;repo&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="k"&gt;status&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="k"&gt;branch&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="k"&gt;commit&lt;/span&gt;&lt;span class="err"&gt;_&lt;/span&gt;&lt;span class="k"&gt;or&lt;/span&gt;&lt;span class="err"&gt;_&lt;/span&gt;&lt;span class="k"&gt;pr&lt;/span&gt;
&lt;span class="k"&gt;jeremylongshore&lt;/span&gt;&lt;span class="err"&gt;/&lt;/span&gt;&lt;span class="k"&gt;prompts&lt;/span&gt;&lt;span class="err"&gt;-&lt;/span&gt;&lt;span class="k"&gt;intent&lt;/span&gt;&lt;span class="err"&gt;-&lt;/span&gt;&lt;span class="k"&gt;solutions&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="k"&gt;pr&lt;/span&gt;&lt;span class="err"&gt;-&lt;/span&gt;&lt;span class="k"&gt;opened&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="k"&gt;main&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="nv"&gt;https:&lt;/span&gt;&lt;span class="c1"&gt;//github.com/.../pull/42&lt;/span&gt;
&lt;span class="k"&gt;jeremylongshore&lt;/span&gt;&lt;span class="err"&gt;/&lt;/span&gt;&lt;span class="k"&gt;bobs&lt;/span&gt;&lt;span class="err"&gt;-&lt;/span&gt;&lt;span class="k"&gt;brain&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="k"&gt;pr&lt;/span&gt;&lt;span class="err"&gt;-&lt;/span&gt;&lt;span class="k"&gt;opened&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="k"&gt;main&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="nv"&gt;https:&lt;/span&gt;&lt;span class="c1"&gt;//github.com/.../pull/15&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  5. Universal Web-App QA Framework (&lt;code&gt;001-qap-universal-webapp-qa-mega-prompt.md&lt;/code&gt;)
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;Purpose:&lt;/strong&gt; Complete end-to-end QA suite for any web application.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The Non-Negotiables:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;✅ Preserve every existing test (only add or refactor)&lt;/li&gt;
&lt;li&gt;✅ Idempotent runs (no destructive ops)&lt;/li&gt;
&lt;li&gt;✅ Gate optional suites behind capability checks&lt;/li&gt;
&lt;li&gt;✅ Report after each phase&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;6-Phase Workflow:&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Phase 0 - Detect &amp;amp; Plan:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Auto-detect framework (React/Next/Vue/Svelte/Static)&lt;/li&gt;
&lt;li&gt;Auto-detect host (Netlify/Vercel/Cloudflare/AWS)&lt;/li&gt;
&lt;li&gt;Select adapters for submission verification and email&lt;/li&gt;
&lt;li&gt;Print plan summary and wait for approval&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Phase 1 - Structure &amp;amp; Scripts:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;tests/
├── playwright/
├── cypress/
├── helpers/
├── adapters/
├── scripts/
└── artifacts/&amp;lt;YYYY-MM-DD_HHMM&amp;gt;/
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Capability-guarded npm scripts:&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="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"test:core"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Playwright E2E"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"test:accessibility"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"axe/pa11y"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"test:performance"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Lighthouse"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"test:visual"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"BackstopJS"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"test:security"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"headers, XSS/SQLi"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"test:complete"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"everything available"&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;strong&gt;Phase 3 - Test Coverage Matrix (11 categories):&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;A. &lt;strong&gt;Manual-equivalent E2E:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Form happy path with submission verification&lt;/li&gt;
&lt;li&gt;Dashboard/API verification&lt;/li&gt;
&lt;li&gt;Email notification receipt and content check&lt;/li&gt;
&lt;li&gt;Error handling, network fail, offline messaging&lt;/li&gt;
&lt;li&gt;Rate-limit/spam, honeypot/reCAPTCHA checks&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;B. &lt;strong&gt;Validation &amp;amp; Edge Cases:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Empty, partial, invalid formats&lt;/li&gt;
&lt;li&gt;Boundary lengths, unicode, RTL, emojis&lt;/li&gt;
&lt;li&gt;XSS payloads (confirm no alert/injection)&lt;/li&gt;
&lt;li&gt;Rapid multi-submit and idempotency&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;C. &lt;strong&gt;Cross-browser &amp;amp; Devices:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Chromium, Firefox, WebKit&lt;/li&gt;
&lt;li&gt;Mobile (iPhone/Android), tablet, desktop&lt;/li&gt;
&lt;li&gt;Private mode and ad-block&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;D. &lt;strong&gt;Accessibility (WCAG 2.1 AA):&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Keyboard-only flow, tab order, focus visible&lt;/li&gt;
&lt;li&gt;Labels, roles, ARIA announcements&lt;/li&gt;
&lt;li&gt;Color contrast, 200% zoom without horizontal scroll&lt;/li&gt;
&lt;li&gt;Live regions for status updates&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;E. &lt;strong&gt;Performance:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Lighthouse thresholds: Performance ≥70, Accessibility ≥90, Best-Practices ≥90, SEO ≥90&lt;/li&gt;
&lt;li&gt;Web-Vitals sampling&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;F. &lt;strong&gt;Visual Regression:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Key routes and form states&lt;/li&gt;
&lt;li&gt;Mismatch threshold ≤ 0.1%&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;G. &lt;strong&gt;Security Sanity:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;HTTPS redirect, HSTS, X-Frame-Options, X-Content-Type-Options&lt;/li&gt;
&lt;li&gt;CSP, Referrer-Policy&lt;/li&gt;
&lt;li&gt;XSS/SQLi probes must not leak stack traces&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;H. &lt;strong&gt;Networking &amp;amp; Observability:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Console error-free&lt;/li&gt;
&lt;li&gt;Network POST status 2xx/3xx&lt;/li&gt;
&lt;li&gt;Server/app logs captured&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;I. &lt;strong&gt;Load/Soak (optional):&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;1-5 rps warmup, 10 rps sustain, 20 rps spike&lt;/li&gt;
&lt;li&gt;Track p95 latency and error rate&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;J. &lt;strong&gt;Internationalization:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Locale switch, date/number formats, RTL&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;K. &lt;strong&gt;Cookies/Storage/Auth:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;CSRF token presence and rotation&lt;/li&gt;
&lt;li&gt;SameSite, Secure flags&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Phase 5 - Evidence Pack:&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Complete audit trail in &lt;code&gt;tests/artifacts/&amp;lt;timestamp&amp;gt;/&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;├── reports/              # HTML, JSON, JUnit
├── screenshots/          # Visual evidence
├── videos/               # Test recordings
├── traces/               # Playwright traces
├── lighthouse/           # Performance reports
├── accessibility/        # a11y results
├── security/             # Security scans
├── visual/               # Regression diffs
├── evidence/             # IDs, headers, payloads
└── SUMMARY.md            # Executive summary
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Exit Criteria:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;All core E2E pass&lt;/li&gt;
&lt;li&gt;WCAG 2.1 AA violations = 0 (or documented waivers)&lt;/li&gt;
&lt;li&gt;Lighthouse thresholds met (or ticketed)&lt;/li&gt;
&lt;li&gt;Security headers present (or ticketed)&lt;/li&gt;
&lt;li&gt;Visual diffs approved&lt;/li&gt;
&lt;li&gt;Evidence pack complete&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Adapter Pattern:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// tests/adapters/submission-verifier.netlify.js&lt;/span&gt;
&lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;NetlifySubmissionVerifier&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="nf"&gt;verifySubmission&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;submissionId&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;expectedData&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;response&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;axios&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
      &lt;span class="s2"&gt;`https://api.netlify.com/api/v1/sites/&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;siteId&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;/submissions/&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;submissionId&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="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="na"&gt;Authorization&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;`Bearer &lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;authToken&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="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="na"&gt;found&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="na"&gt;matches&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;Object&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;keys&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;expectedData&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;every&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
        &lt;span class="nx"&gt;key&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;response&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;key&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="nx"&gt;expectedData&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;key&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
      &lt;span class="p"&gt;),&lt;/span&gt;
      &lt;span class="na"&gt;submissionId&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;response&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="na"&gt;timestamp&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;response&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;created_at&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;h2&gt;
  
  
  Applying the Directory Standards
&lt;/h2&gt;

&lt;h3&gt;
  
  
  The Adaptation Challenge
&lt;/h3&gt;

&lt;p&gt;The repository isn't a traditional code project - it's a &lt;strong&gt;prompt library&lt;/strong&gt;. The standard structure assumes:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;02-Src/      # Source code
03-Tests/    # Test suites
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;But our core product is:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;prompts/     # 150+ prompt templates
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Decision:&lt;/strong&gt; Adapt the standards while preserving the product structure.&lt;/p&gt;

&lt;h3&gt;
  
  
  Final Structure
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;prompts-intent-solutions/
├── .github/                    # Workflows, templates
├── 000-master-systems/         # Master automation (DO NOT MODIFY)
│   ├── taskwarrior/            # TaskWarrior protocols
│   ├── directory/              # Directory standards
│   ├── github/                 # GitHub workflows
│   ├── content/                # Content systems
│   ├── debug/                  # Debug workflows
│   ├── tracking/               # Time tracking
│   ├── testing/                # QA frameworks
│   └── legacy/                 # Archive
├── 01-Docs/                    # Project docs (FLAT)
├── prompts/                    # Core prompt library
│   ├── development/            # Dev prompts
│   ├── business/               # Business prompts
│   └── specialized/            # Advanced prompts
├── tools/                      # Validation &amp;amp; automation
├── 99-Archive/                 # Deprecated content
├── .directory-standards.md     # Standards reference
├── README.md                   # Updated with standards
├── CLAUDE.md                   # Updated with standards
├── CHANGELOG.md                # Newest-first format
└── LICENSE
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Updated Documentation
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;README.md addition:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight markdown"&gt;&lt;code&gt;&lt;span class="gu"&gt;## Directory Standards&lt;/span&gt;

This project follows the &lt;span class="gs"&gt;**MASTER DIRECTORY STANDARDS**&lt;/span&gt;.
&lt;span class="p"&gt;
-&lt;/span&gt; &lt;span class="gs"&gt;**Structure**&lt;/span&gt;: See &lt;span class="sb"&gt;`.directory-standards.md`&lt;/span&gt; for details
&lt;span class="p"&gt;-&lt;/span&gt; &lt;span class="gs"&gt;**Documentation**&lt;/span&gt;: All docs in &lt;span class="sb"&gt;`01-Docs/`&lt;/span&gt; using &lt;span class="sb"&gt;`NNN-abv-description.ext`&lt;/span&gt;
&lt;span class="p"&gt;-&lt;/span&gt; &lt;span class="gs"&gt;**Prompts**&lt;/span&gt;: Core library organized in &lt;span class="sb"&gt;`prompts/`&lt;/span&gt; by category
&lt;span class="p"&gt;-&lt;/span&gt; &lt;span class="gs"&gt;**File Naming**&lt;/span&gt;: kebab-case files, PascalCase directories
&lt;span class="p"&gt;-&lt;/span&gt; &lt;span class="gs"&gt;**Chronology**&lt;/span&gt;: Documentation in strict chronological order
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;CLAUDE.md addition:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight markdown"&gt;&lt;code&gt;&lt;span class="gu"&gt;## Directory Standards&lt;/span&gt;

Follow &lt;span class="sb"&gt;`.directory-standards.md`&lt;/span&gt; for structure and file naming.

&lt;span class="gu"&gt;### Key Standards&lt;/span&gt;
&lt;span class="p"&gt;-&lt;/span&gt; Store all docs in &lt;span class="sb"&gt;`01-Docs/`&lt;/span&gt; using &lt;span class="sb"&gt;`NNN-abv-description.ext`&lt;/span&gt;
&lt;span class="p"&gt;-&lt;/span&gt; Maintain strict chronological order
&lt;span class="p"&gt;-&lt;/span&gt; Prompts directory is core product - organize by category
&lt;span class="p"&gt;-&lt;/span&gt; File naming: kebab-case, PascalCase for main directories
&lt;span class="p"&gt;-&lt;/span&gt; CHANGELOG.md: Newest entries on TOP
&lt;span class="p"&gt;-&lt;/span&gt; 000-master-systems/: DO NOT modify without permission
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;CHANGELOG.md format:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight markdown"&gt;&lt;code&gt;&lt;span class="gh"&gt;# Changelog&lt;/span&gt;

Format: Newest entries on TOP (reverse chronological order).

&lt;span class="gu"&gt;## [Unreleased]&lt;/span&gt;
&lt;span class="gu"&gt;### Changed&lt;/span&gt;
&lt;span class="p"&gt;-&lt;/span&gt; Applied MASTER DIRECTORY STANDARDS
&lt;span class="p"&gt;-&lt;/span&gt; Updated README.md and CLAUDE.md with standards references
&lt;span class="p"&gt;-&lt;/span&gt; Reorganized CHANGELOG.md to newest-first

&lt;span class="gu"&gt;## [1.0.1] - 2025-10-02&lt;/span&gt;
...
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  The Abbreviation Table Discovery
&lt;/h2&gt;

&lt;p&gt;The MASTER DIRECTORY STANDARDS includes 120+ approved abbreviations organized by category:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Product &amp;amp; Planning:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;prd, pln, rmp, brd, frd, sow, kpi, okr&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Architecture &amp;amp; Technical:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;adr, tad, dsg, api, sdk, int, dia&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Testing &amp;amp; Quality:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;tst, tsc, qap, bug, perf, sec, pen&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Operations &amp;amp; Deployment:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;ops, dep, inf, cfg, env, rel, chg, inc, pst&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Project Management:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;tsk, bkl, spr, ret, stb, rsk, iss&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Documentation &amp;amp; Reference:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;ref, gde, man, faq, gls, sop, tmp, chk&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;After Action:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;aar, lsn, pmi&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Workflows &amp;amp; Automation:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;wfl, n8n, aut, hok&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This standardization means every document type has a consistent, recognizable abbreviation across all projects.&lt;/p&gt;

&lt;h2&gt;
  
  
  Lessons Learned
&lt;/h2&gt;

&lt;h3&gt;
  
  
  1. Examine Before You Name
&lt;/h3&gt;

&lt;p&gt;Don't assume the existing pattern is correct. Check the authoritative standards document first.&lt;/p&gt;

&lt;h3&gt;
  
  
  2. Numbers vs Categories
&lt;/h3&gt;

&lt;p&gt;The sequence number comes &lt;strong&gt;first&lt;/strong&gt; (&lt;code&gt;001-tsk-&lt;/code&gt;), not the category (&lt;code&gt;TASKWARRIOR-001-&lt;/code&gt;). This enforces chronological order and makes sorting work correctly.&lt;/p&gt;

&lt;h3&gt;
  
  
  3. Date Suffixes Are Optional
&lt;/h3&gt;

&lt;p&gt;Reference documents don't need dates. Reserve &lt;code&gt;-MMDDYY&lt;/code&gt; suffix for versioned files that change over time.&lt;/p&gt;

&lt;h3&gt;
  
  
  4. Flat Structures for Documentation
&lt;/h3&gt;

&lt;p&gt;Both &lt;code&gt;01-Docs/&lt;/code&gt; and &lt;code&gt;claudes-docs/&lt;/code&gt; should be flat - no subdirectories. Use the numbering and abbreviation system for organization.&lt;/p&gt;

&lt;h3&gt;
  
  
  5. CHANGELOG Newest-First
&lt;/h3&gt;

&lt;p&gt;Modern practice: newest entries on top (reverse chronological). Users want to see what's new immediately.&lt;/p&gt;

&lt;h3&gt;
  
  
  6. Adaptation Over Rigidity
&lt;/h3&gt;

&lt;p&gt;The standards are universal, but implementation must adapt to the repository type. A prompt library isn't a code project, so preserve the product structure while applying the naming standards.&lt;/p&gt;

&lt;h3&gt;
  
  
  7. Protection of Master Systems
&lt;/h3&gt;

&lt;p&gt;The &lt;code&gt;000-master-systems/&lt;/code&gt; directory is protected. It contains the source-of-truth automation workflows that shouldn't be modified without explicit approval.&lt;/p&gt;

&lt;h2&gt;
  
  
  Related Resources
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;a href="https://startaitools.com/posts/ai-dev-transformation-part-2-enterprise-library/" rel="noopener noreferrer"&gt;AI Dev Transformation Part 2: Enterprise Library&lt;/a&gt; - Building comprehensive prompt libraries&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://startaitools.com/posts/ai-assisted-technical-writing-automation-workflows/" rel="noopener noreferrer"&gt;AI-Assisted Technical Writing Automation Workflows&lt;/a&gt; - Documentation automation systems&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/jeremylongshore/prompts-intent-solutions/blob/main/.directory-standards.md" rel="noopener noreferrer"&gt;GitHub Repository Organization Master Standards&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Key Takeaways
&lt;/h2&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;File naming matters&lt;/strong&gt; - Consistent naming enables automation, sorting, and discovery&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Standards require interpretation&lt;/strong&gt; - Adapt universal standards to repository type&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Chronology is enforced&lt;/strong&gt; - Sequential numbering creates a timeline of development&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Abbreviations reduce cognitive load&lt;/strong&gt; - 120+ standard abbreviations mean instant recognition&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;CHANGELOG format evolved&lt;/strong&gt; - Newest-first is modern best practice&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Master systems need protection&lt;/strong&gt; - Source-of-truth workflows shouldn't be casually modified&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Iteration is normal&lt;/strong&gt; - We went through 3 attempts to get the naming right&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;The result: A professionally organized prompt engineering repository with universal directory standards, comprehensive automation workflows, and complete documentation.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Repository:&lt;/strong&gt; &lt;a href="https://github.com/jeremylongshore/prompts-intent-solutions" rel="noopener noreferrer"&gt;prompts-intent-solutions&lt;/a&gt;&lt;/p&gt;

</description>
      <category>directorystructure</category>
      <category>organization</category>
      <category>standards</category>
      <category>automation</category>
    </item>
  </channel>
</rss>
