<?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: Dhrupo Nil</title>
    <description>The latest articles on DEV Community by Dhrupo Nil (@dhrupo).</description>
    <link>https://dev.to/dhrupo</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F1151170%2Ff3f2b324-73c2-4df0-9f8a-ffc109194198.jpeg</url>
      <title>DEV Community: Dhrupo Nil</title>
      <link>https://dev.to/dhrupo</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/dhrupo"/>
    <language>en</language>
    <item>
      <title>Host up to 10 WordPress sites for free with Oracle Cloud and xCloud</title>
      <dc:creator>Dhrupo Nil</dc:creator>
      <pubDate>Wed, 14 Oct 2026 04:50:18 +0000</pubDate>
      <link>https://dev.to/dhrupo/host-up-to-10-wordpress-sites-for-free-with-oracle-cloud-and-xcloud-4h2o</link>
      <guid>https://dev.to/dhrupo/host-up-to-10-wordpress-sites-for-free-with-oracle-cloud-and-xcloud-4h2o</guid>
      <description>&lt;p&gt;I wanted a place to run a handful of WordPress sites (test sites, demos, a personal page) without paying for hosting every month. The combination that worked: a free Oracle Cloud server, managed through xCloud's free plan, so I never had to install the web server, PHP or SSL by hand.&lt;/p&gt;

&lt;p&gt;This is the full walkthrough, including the parts that tripped me up: Oracle's "Out of capacity" error, a card hold that looked like a charge, and a firewall rule that silently did nothing.&lt;/p&gt;

&lt;p&gt;Run WordPress sites on a free Oracle Cloud server, managed through xCloud's free plan.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Oracle Cloud Always Free&lt;/strong&gt; gives you the server (the land).&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;xCloud free Self-Managed plan&lt;/strong&gt; installs and manages the web server, PHP, database and SSL (the builder).&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Only cost:&lt;/strong&gt; a domain name (about $10–13/year), and only if you want your own address.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  How it fits together
&lt;/h3&gt;

&lt;p&gt;The cover image above shows the whole setup: a visitor's browser finds your server through DNS, passes Oracle's two firewalls, and reaches the Ubuntu server that xCloud manages.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The build, in short:&lt;/strong&gt;&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Create a free Oracle account and a network that allows web traffic (Parts 1–2).&lt;/li&gt;
&lt;li&gt;Create one Ubuntu 24.04 server on that network (Part 3).&lt;/li&gt;
&lt;li&gt;Log in, open the web ports inside the server, and add swap if it's the 1 GB one (Part 4).&lt;/li&gt;
&lt;li&gt;Give xCloud root access with its key; xCloud installs the web server, PHP and database (Part 5).&lt;/li&gt;
&lt;li&gt;Point a domain at the server (Part 6), then add sites from the xCloud dashboard (Part 7).&lt;/li&gt;
&lt;/ol&gt;

&lt;h3&gt;
  
  
  Running up to 10 sites
&lt;/h3&gt;

&lt;p&gt;xCloud's free plan allows &lt;strong&gt;1 server with up to 10 sites&lt;/strong&gt;. All of them live on the same Oracle server and share its CPU and memory.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;One domain is enough.&lt;/strong&gt; Each site gets a free subdomain: &lt;code&gt;example.dev&lt;/code&gt;, &lt;code&gt;blog.example.dev&lt;/code&gt;, &lt;code&gt;shop.example.dev&lt;/code&gt;, &lt;code&gt;lab.example.dev&lt;/code&gt;, …&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;One DNS record covers all subdomains.&lt;/strong&gt; A wildcard A record (&lt;code&gt;*&lt;/code&gt; → server IP) points every subdomain at the server, so a new site needs no DNS change.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Each site is added the same way&lt;/strong&gt; in xCloud (Part 7) and gets its own SSL certificate, database and WordPress install.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Testing without a domain:&lt;/strong&gt; use xCloud's &lt;strong&gt;Demo Site&lt;/strong&gt; option for a free temporary address, and switch to a real domain later.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;How many sites the server can really carry:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Server&lt;/th&gt;
&lt;th&gt;Realistic number of sites&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;ARM 2 OCPU / 12 GB&lt;/td&gt;
&lt;td&gt;All 10, if they are ordinary WordPress sites with caching on&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;AMD Micro 1 GB (+2 GB swap)&lt;/td&gt;
&lt;td&gt;3–5 quiet sites (tests, demos, personal pages). Not WooCommerce or real traffic&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;If you start on the 1 GB server and outgrow it, create the ARM server later and move the sites over with xCloud's site migration. The free xCloud plan covers only one server at a time.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Last checked: October 2026. Oracle and xCloud change their screens and limits, so a label may read slightly differently from this guide.&lt;/p&gt;
&lt;/blockquote&gt;




&lt;h2&gt;
  
  
  What you get
&lt;/h2&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Item&lt;/th&gt;
&lt;th&gt;Free allowance&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;xCloud Free plan&lt;/td&gt;
&lt;td&gt;1 server, up to 10 sites&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Oracle ARM server (&lt;code&gt;VM.Standard.A1.Flex&lt;/code&gt;)&lt;/td&gt;
&lt;td&gt;2 OCPU / 12 GB RAM total (halved from 4/24 in June 2026)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Oracle AMD server (&lt;code&gt;VM.Standard.E2.1.Micro&lt;/code&gt;)&lt;/td&gt;
&lt;td&gt;Up to 2 servers, 1 GB RAM each&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Oracle disk&lt;/td&gt;
&lt;td&gt;200 GB total, across all boot volumes&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Oracle outbound traffic&lt;/td&gt;
&lt;td&gt;10 TB per month&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;&lt;strong&gt;Which server?&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;ARM, 2 OCPU / 12 GB:&lt;/strong&gt; comfortable for several WordPress sites. Often unavailable on free accounts (see Out of capacity).&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;AMD Micro, 1 GB:&lt;/strong&gt; almost always available. Good for 1 small site or a handful of quiet test sites.&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  Before you start
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;A credit or debit card (Oracle uses it for identity checks; Always Free resources are not billed).&lt;/li&gt;
&lt;li&gt;An xCloud account: &lt;a href="https://xcloud.host" rel="noopener noreferrer"&gt;https://xcloud.host&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;A terminal with &lt;code&gt;ssh&lt;/code&gt; (macOS/Linux built in; Windows: PowerShell or WSL).&lt;/li&gt;
&lt;li&gt;Optional: an SSH key. Check with &lt;code&gt;ls ~/.ssh/*.pub&lt;/code&gt;. If none exists, create one:
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;ssh-keygen &lt;span class="nt"&gt;-t&lt;/span&gt; ed25519
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h2&gt;
  
  
  Part 1 — Create the Oracle Cloud account
&lt;/h2&gt;

&lt;ol&gt;
&lt;li&gt;Sign up at &lt;a href="https://www.oracle.com/cloud/free/" rel="noopener noreferrer"&gt;https://www.oracle.com/cloud/free/&lt;/a&gt;.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Choose your Home Region carefully.&lt;/strong&gt; It can never be changed, and free servers only run there. Pick a region close to your visitors.&lt;/li&gt;
&lt;li&gt;Finish card verification and wait for the account to be ready.&lt;/li&gt;
&lt;/ol&gt;




&lt;h2&gt;
  
  
  Part 2 — Create the network (VCN)
&lt;/h2&gt;

&lt;p&gt;Create the network &lt;strong&gt;before&lt;/strong&gt; the server. The inline network option in the server form has a bug that can lock the public IP off.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;☰ menu → &lt;strong&gt;Networking&lt;/strong&gt; → &lt;strong&gt;Virtual Cloud Networks&lt;/strong&gt;.&lt;/li&gt;
&lt;li&gt;On the left, under &lt;strong&gt;Compartment&lt;/strong&gt;, choose your root compartment (your account name).&lt;/li&gt;
&lt;li&gt;Click &lt;strong&gt;Start VCN Wizard&lt;/strong&gt; (it may be under &lt;strong&gt;Actions&lt;/strong&gt;).&lt;/li&gt;
&lt;li&gt;Choose &lt;strong&gt;Create VCN with Internet Connectivity&lt;/strong&gt; → &lt;strong&gt;Start VCN Wizard&lt;/strong&gt;.&lt;/li&gt;
&lt;li&gt;Fill in:&lt;/li&gt;
&lt;/ol&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Field&lt;/th&gt;
&lt;th&gt;Value&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;VCN name&lt;/td&gt;
&lt;td&gt;&lt;code&gt;xcloud-vcn&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;VCN IPv4 CIDR block&lt;/td&gt;
&lt;td&gt;&lt;code&gt;10.0.0.0/16&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Public subnet CIDR&lt;/td&gt;
&lt;td&gt;&lt;code&gt;10.0.0.0/24&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Private subnet CIDR&lt;/td&gt;
&lt;td&gt;&lt;code&gt;10.0.1.0/24&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Next&lt;/strong&gt; → &lt;strong&gt;Create&lt;/strong&gt;. Wait until every line has a green tick, then &lt;strong&gt;View VCN&lt;/strong&gt;.&lt;/li&gt;
&lt;/ol&gt;

&lt;h3&gt;
  
  
  Open web ports (80 and 443)
&lt;/h3&gt;

&lt;p&gt;Port 22 (SSH) is already open. Add the web ports:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;In the VCN, open &lt;strong&gt;Security Lists&lt;/strong&gt; → &lt;strong&gt;Default Security List for xcloud-vcn&lt;/strong&gt;.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Add Ingress Rules&lt;/strong&gt;:&lt;/li&gt;
&lt;/ol&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Field&lt;/th&gt;
&lt;th&gt;Value&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Source Type&lt;/td&gt;
&lt;td&gt;CIDR&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Source CIDR&lt;/td&gt;
&lt;td&gt;&lt;code&gt;0.0.0.0/0&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;IP Protocol&lt;/td&gt;
&lt;td&gt;TCP&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Source Port Range&lt;/td&gt;
&lt;td&gt;&lt;em&gt;(empty)&lt;/em&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Destination Port Range&lt;/td&gt;
&lt;td&gt;&lt;code&gt;80,443&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Description&lt;/td&gt;
&lt;td&gt;&lt;code&gt;web&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;ol&gt;
&lt;li&gt;Save. You should now see three TCP rules from &lt;code&gt;0.0.0.0/0&lt;/code&gt;: 22, 80, 443.&lt;/li&gt;
&lt;/ol&gt;




&lt;h2&gt;
  
  
  Part 3 — Create the server (instance)
&lt;/h2&gt;

&lt;ol&gt;
&lt;li&gt;☰ → &lt;strong&gt;Compute&lt;/strong&gt; → &lt;strong&gt;Instances&lt;/strong&gt; → &lt;strong&gt;Create instance&lt;/strong&gt;.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Basic information&lt;/strong&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Name:&lt;/strong&gt; e.g. &lt;code&gt;xcloud-server&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Placement:&lt;/strong&gt; default availability domain&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Shape&lt;/strong&gt; → &lt;strong&gt;Change shape&lt;/strong&gt; (do this &lt;em&gt;before&lt;/em&gt; the image):

&lt;ul&gt;
&lt;li&gt;ARM: &lt;strong&gt;Ampere&lt;/strong&gt; → &lt;code&gt;VM.Standard.A1.Flex&lt;/code&gt;, &lt;strong&gt;2 OCPU&lt;/strong&gt;, &lt;strong&gt;12 GB&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;or AMD: &lt;strong&gt;Specialty and previous generation&lt;/strong&gt; → &lt;code&gt;VM.Standard.E2.1.Micro&lt;/code&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Image&lt;/strong&gt; → &lt;strong&gt;Change image&lt;/strong&gt; → &lt;strong&gt;Ubuntu&lt;/strong&gt; → &lt;strong&gt;Canonical Ubuntu 24.04&lt;/strong&gt; (not "Minimal")&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Security:&lt;/strong&gt; leave as is.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Networking&lt;/strong&gt;

&lt;ul&gt;
&lt;li&gt;Existing VCN: &lt;code&gt;xcloud-vcn&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Existing subnet: the &lt;strong&gt;public&lt;/strong&gt; subnet&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Automatically assign public IPv4 address:&lt;/strong&gt; on&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;SSH keys:&lt;/strong&gt; &lt;strong&gt;Paste public key&lt;/strong&gt; (contents of your &lt;code&gt;~/.ssh/id_ed25519.pub&lt;/code&gt;), or &lt;strong&gt;Generate a key pair for me&lt;/strong&gt; and click &lt;strong&gt;Download private key&lt;/strong&gt; right away. You cannot download it later.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Storage&lt;/strong&gt;

&lt;ul&gt;
&lt;li&gt;Tick &lt;strong&gt;Specify a custom boot volume size&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;Enter &lt;code&gt;100&lt;/code&gt; GB (keep the total of all your boot volumes at 200 GB or less to stay free)&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Create&lt;/strong&gt;. Wait for &lt;strong&gt;Running&lt;/strong&gt;, then copy the &lt;strong&gt;Public IP address&lt;/strong&gt;.&lt;/li&gt;
&lt;/ol&gt;

&lt;h3&gt;
  
  
  Out of capacity
&lt;/h3&gt;

&lt;p&gt;&lt;code&gt;Out of capacity for shape VM.Standard.A1.Flex in availability domain AD-1&lt;/code&gt; means free ARM machines in your region are all taken. Your settings are fine. Try, in order:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Another availability domain (if your region has more than one).&lt;/li&gt;
&lt;li&gt;A smaller size: &lt;strong&gt;1 OCPU / 6 GB&lt;/strong&gt;.&lt;/li&gt;
&lt;li&gt;Retry at a different time of day.&lt;/li&gt;
&lt;li&gt;Use the AMD Micro shape instead (1 GB, nearly always available).&lt;/li&gt;
&lt;li&gt;Upgrade to &lt;strong&gt;Pay As You Go&lt;/strong&gt; (☰ → &lt;strong&gt;Billing &amp;amp; Cost Management&lt;/strong&gt; → &lt;strong&gt;Upgrade and Manage Payment&lt;/strong&gt;). Paid accounts get capacity much more easily and are not reclaimed when idle. You still pay $0 if you stay within the free allowance.&lt;/li&gt;
&lt;/ol&gt;

&lt;h3&gt;
  
  
  About the Pay As You Go card hold
&lt;/h3&gt;

&lt;p&gt;Upgrading places a temporary hold of about &lt;strong&gt;US$100&lt;/strong&gt; on your card (shown in your local currency, e.g. ~S$138). It is a verification hold, not a charge:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Check &lt;strong&gt;Billing &amp;amp; Cost Management → Invoices&lt;/strong&gt;: there should be none.&lt;/li&gt;
&lt;li&gt;Check &lt;strong&gt;Cost Analysis&lt;/strong&gt;: usage should be &lt;code&gt;0&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;Your bank releases the hold on its own, usually within days, sometimes up to ~30 days.&lt;/li&gt;
&lt;li&gt;If it shows as &lt;strong&gt;posted&lt;/strong&gt; (not pending), or an invoice appears, contact Oracle support and your bank.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Set a $1 budget alert (do this on Pay As You Go)
&lt;/h3&gt;

&lt;p&gt;☰ → &lt;strong&gt;Billing &amp;amp; Cost Management&lt;/strong&gt; → &lt;strong&gt;Budgets&lt;/strong&gt; → &lt;strong&gt;Create Budget&lt;/strong&gt; → amount &lt;code&gt;1&lt;/code&gt;, alert at 1% of actual spend, your email. You'll hear about any real charge immediately.&lt;/p&gt;




&lt;h2&gt;
  
  
  Part 4 — Prepare the server
&lt;/h2&gt;

&lt;p&gt;Log in (replace the IP and key path with yours). The user is &lt;code&gt;ubuntu&lt;/code&gt;, not &lt;code&gt;root&lt;/code&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;ssh &lt;span class="nt"&gt;-i&lt;/span&gt; ~/.ssh/id_ed25519 ubuntu@YOUR_SERVER_IP
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If you used an Oracle-generated key:&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;chmod &lt;/span&gt;600 ~/Downloads/ssh-key-XXXX.key
ssh &lt;span class="nt"&gt;-i&lt;/span&gt; ~/Downloads/ssh-key-XXXX.key ubuntu@YOUR_SERVER_IP
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  4.1 Add swap (1 GB AMD server only)
&lt;/h3&gt;

&lt;p&gt;Gives the small server spare memory so it doesn't crash under load.&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;sudo &lt;/span&gt;fallocate &lt;span class="nt"&gt;-l&lt;/span&gt; 2G /swapfile
&lt;span class="nb"&gt;sudo chmod &lt;/span&gt;600 /swapfile
&lt;span class="nb"&gt;sudo &lt;/span&gt;mkswap /swapfile
&lt;span class="nb"&gt;sudo &lt;/span&gt;swapon /swapfile
&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s1"&gt;'/swapfile none swap sw 0 0'&lt;/span&gt; | &lt;span class="nb"&gt;sudo tee&lt;/span&gt; &lt;span class="nt"&gt;-a&lt;/span&gt; /etc/fstab
free &lt;span class="nt"&gt;-h&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;code&gt;free -h&lt;/code&gt; should show &lt;code&gt;Swap: 2.0Gi&lt;/code&gt;.&lt;/p&gt;

&lt;h3&gt;
  
  
  4.2 Open ports 80 and 443 inside the server
&lt;/h3&gt;

&lt;p&gt;Oracle's Ubuntu image has its own firewall that blocks web traffic, separate from the Security List in Part 2. Both must allow it.&lt;/p&gt;

&lt;p&gt;First find the line number of the &lt;code&gt;REJECT&lt;/code&gt; rule:&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;sudo &lt;/span&gt;iptables &lt;span class="nt"&gt;-L&lt;/span&gt; INPUT &lt;span class="nt"&gt;-n&lt;/span&gt; &lt;span class="nt"&gt;--line-numbers&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Example output:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;4    ACCEPT  ...  state NEW tcp dpt:22
5    REJECT  ...  reject-with icmp-host-prohibited
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Insert the web rules &lt;strong&gt;at the REJECT line's number&lt;/strong&gt; (here &lt;code&gt;5&lt;/code&gt;), so they sit above it. Rules placed below REJECT do nothing.&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;sudo &lt;/span&gt;iptables &lt;span class="nt"&gt;-I&lt;/span&gt; INPUT 5 &lt;span class="nt"&gt;-m&lt;/span&gt; state &lt;span class="nt"&gt;--state&lt;/span&gt; NEW &lt;span class="nt"&gt;-p&lt;/span&gt; tcp &lt;span class="nt"&gt;--dport&lt;/span&gt; 443 &lt;span class="nt"&gt;-j&lt;/span&gt; ACCEPT
&lt;span class="nb"&gt;sudo &lt;/span&gt;iptables &lt;span class="nt"&gt;-I&lt;/span&gt; INPUT 5 &lt;span class="nt"&gt;-m&lt;/span&gt; state &lt;span class="nt"&gt;--state&lt;/span&gt; NEW &lt;span class="nt"&gt;-p&lt;/span&gt; tcp &lt;span class="nt"&gt;--dport&lt;/span&gt; 80 &lt;span class="nt"&gt;-j&lt;/span&gt; ACCEPT
&lt;span class="nb"&gt;sudo &lt;/span&gt;netfilter-persistent save
&lt;span class="nb"&gt;sudo &lt;/span&gt;iptables &lt;span class="nt"&gt;-L&lt;/span&gt; INPUT &lt;span class="nt"&gt;-n&lt;/span&gt; &lt;span class="nt"&gt;--line-numbers&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The list should now show 22, 80, 443 &lt;strong&gt;before&lt;/strong&gt; &lt;code&gt;REJECT&lt;/code&gt;.&lt;/p&gt;




&lt;h2&gt;
  
  
  Part 5 — Connect the server to xCloud
&lt;/h2&gt;

&lt;ol&gt;
&lt;li&gt;In xCloud: &lt;strong&gt;Servers&lt;/strong&gt; → &lt;strong&gt;Add Server&lt;/strong&gt; → &lt;strong&gt;Connect Your Existing Server&lt;/strong&gt; (Any Provider).&lt;/li&gt;
&lt;li&gt;Fill in the form:&lt;/li&gt;
&lt;/ol&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Field&lt;/th&gt;
&lt;th&gt;Value&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Server Name&lt;/td&gt;
&lt;td&gt;Any label, e.g. &lt;code&gt;oracle-free&lt;/code&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;App Type&lt;/td&gt;
&lt;td&gt;WordPress (optional)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Web Server&lt;/td&gt;
&lt;td&gt;
&lt;strong&gt;OpenLiteSpeed&lt;/strong&gt; (built-in cache, good for small servers). NGINX also works on AMD. On ARM, use OpenLiteSpeed&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;IP Address&lt;/td&gt;
&lt;td&gt;Your server's public IP&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;SSH Port&lt;/td&gt;
&lt;td&gt;&lt;code&gt;22&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;SSH Username&lt;/td&gt;
&lt;td&gt;&lt;code&gt;root&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Database Server&lt;/td&gt;
&lt;td&gt;Do &lt;strong&gt;not&lt;/strong&gt; leave on None. Pick MariaDB or MySQL (on ARM, MySQL 8.4 is not supported)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Plan&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;Free&lt;/strong&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;SSH Authentication&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;Public Key&lt;/strong&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;ol&gt;
&lt;li&gt;xCloud shows a command that adds its key to the server. &lt;strong&gt;Copy it and run it on the server&lt;/strong&gt; (in your &lt;code&gt;ubuntu&lt;/code&gt; SSH session). It should print &lt;code&gt;SSH key added successfully&lt;/code&gt;.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Oracle blocks direct root login with &lt;em&gt;your&lt;/em&gt; key, but root login with an &lt;em&gt;added&lt;/em&gt; key works. This command is what lets xCloud in as root.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Tick &lt;strong&gt;I have created a new server on my provider&lt;/strong&gt; → &lt;strong&gt;Create&lt;/strong&gt;.&lt;/li&gt;
&lt;li&gt;Wait for provisioning to finish. On a 1 GB server this can take a while.&lt;/li&gt;
&lt;/ol&gt;




&lt;h2&gt;
  
  
  Part 6 — Get a domain (optional)
&lt;/h2&gt;

&lt;p&gt;You can skip this and use xCloud's free &lt;strong&gt;Demo Site&lt;/strong&gt; address for testing.&lt;/p&gt;

&lt;p&gt;For real sites, one domain is enough for many sites, because subdomains are free:&lt;br&gt;
&lt;code&gt;example.dev&lt;/code&gt;, &lt;code&gt;blog.example.dev&lt;/code&gt;, &lt;code&gt;shop.example.dev&lt;/code&gt;, …&lt;/p&gt;

&lt;p&gt;When buying:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Judge by the &lt;strong&gt;renewal&lt;/strong&gt; price, not the first-year price.&lt;/li&gt;
&lt;li&gt;Make sure &lt;strong&gt;WHOIS privacy&lt;/strong&gt; is free.&lt;/li&gt;
&lt;li&gt;Untick add-ons (email, "protection", site builders).&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;.dev&lt;/code&gt; and &lt;code&gt;.app&lt;/code&gt; domains &lt;strong&gt;require HTTPS&lt;/strong&gt; in every browser. That is fine with xCloud's free SSL, but a site won't load at all until its certificate is issued.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Point the domain to the server
&lt;/h3&gt;

&lt;p&gt;At your registrar's (or Cloudflare's) DNS settings, add &lt;strong&gt;A records&lt;/strong&gt; pointing to your server IP:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Name&lt;/th&gt;
&lt;th&gt;Type&lt;/th&gt;
&lt;th&gt;Value&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;@&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;A&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;YOUR_SERVER_IP&lt;/code&gt; (the main domain)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;*&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;A&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;YOUR_SERVER_IP&lt;/code&gt; (every subdomain at once)&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;On Cloudflare, set these to &lt;strong&gt;DNS only&lt;/strong&gt; (grey cloud) until xCloud has issued the SSL certificate. DNS changes can take a few minutes to an hour.&lt;/p&gt;




&lt;h2&gt;
  
  
  Part 7 — Add a site
&lt;/h2&gt;

&lt;p&gt;Open the server in xCloud → &lt;strong&gt;Add Site&lt;/strong&gt;.&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Field&lt;/th&gt;
&lt;th&gt;What to do&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Select Options&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;Default&lt;/strong&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Site Title&lt;/td&gt;
&lt;td&gt;Your site's name&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Go Live / Demo Site&lt;/td&gt;
&lt;td&gt;
&lt;strong&gt;Go Live&lt;/strong&gt; with your domain, or &lt;strong&gt;Demo Site&lt;/strong&gt; for a free test address&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Domain Name&lt;/td&gt;
&lt;td&gt;e.g. &lt;code&gt;blog.example.dev&lt;/code&gt; (Go Live only)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;DNS Setup&lt;/td&gt;
&lt;td&gt;Click &lt;strong&gt;Verify My DNS&lt;/strong&gt;. Keep &lt;strong&gt;Use free SSL certificate issued &amp;amp; managed by xCloud&lt;/strong&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Multisite&lt;/td&gt;
&lt;td&gt;Off&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;xSpeed Cache&lt;/td&gt;
&lt;td&gt;On&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Mail Delivery&lt;/td&gt;
&lt;td&gt;Off for now&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Site Backup&lt;/td&gt;
&lt;td&gt;Off for now&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Blueprints&lt;/td&gt;
&lt;td&gt;
&lt;strong&gt;Gutenberg Websites&lt;/strong&gt;, or turn off for clean WordPress. The Elementor blueprint adds weight a small server doesn't need&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;More Advanced Settings&lt;/td&gt;
&lt;td&gt;Set the WordPress admin username, password and email; keep the default PHP version&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;Click &lt;strong&gt;Next&lt;/strong&gt; and wait. Repeat for each additional site with a different title and domain.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Small server tips:&lt;/strong&gt; keep plugins few, keep caching on, don't run imports or backups on several sites at once. A 1 GB server suits quiet sites only; WooCommerce or real traffic needs the ARM server.&lt;/p&gt;




&lt;h2&gt;
  
  
  Troubleshooting
&lt;/h2&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Problem&lt;/th&gt;
&lt;th&gt;Likely cause and fix&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;
&lt;code&gt;ssh&lt;/code&gt; asks for a password or says &lt;em&gt;Permission denied&lt;/em&gt;
&lt;/td&gt;
&lt;td&gt;Wrong key file or user. Use &lt;code&gt;ubuntu@&lt;/code&gt;, and the private key matching the public key you gave Oracle&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;Please login as the user "ubuntu" rather than "root"&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;You tried &lt;code&gt;root@&lt;/code&gt; directly. Log in as &lt;code&gt;ubuntu&lt;/code&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;xCloud can't connect&lt;/td&gt;
&lt;td&gt;The xCloud key command wasn't run, or was run on a different server. Rerun it in your SSH session&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Site doesn't load on port 80/443&lt;/td&gt;
&lt;td&gt;One of the two firewalls still blocks it: check the Security List (Part 2) &lt;strong&gt;and&lt;/strong&gt; the iptables order (Part 4.2)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Verify My DNS fails&lt;/td&gt;
&lt;td&gt;DNS not spread yet (wait and retry), wrong IP in the A record, or Cloudflare proxy (orange cloud) is on&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Oracle reclaims an idle free server&lt;/td&gt;
&lt;td&gt;Free accounts can lose a VM whose CPU, network and memory all stay under 20% for 7 days (a warning email comes first). Pay As You Go accounts are not reclaimed&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;




&lt;h2&gt;
  
  
  Wrapping up
&lt;/h2&gt;

&lt;p&gt;You now have a server you don't pay for, a dashboard that handles the web server, PHP, database and SSL, and room for up to 10 WordPress sites under one domain.&lt;/p&gt;

&lt;p&gt;If you hit a step that looks different on your screen, or found a better way around Oracle's capacity problem, tell me in the comments.&lt;/p&gt;

</description>
      <category>wordpress</category>
      <category>cloud</category>
      <category>selfhosted</category>
      <category>tutorial</category>
    </item>
    <item>
      <title>How I set up Claude Code: one rulebook, a 12-step relay to use it daily</title>
      <dc:creator>Dhrupo Nil</dc:creator>
      <pubDate>Wed, 07 Oct 2026 17:29:45 +0000</pubDate>
      <link>https://dev.to/dhrupo/how-i-set-up-claude-code-one-rulebook-a-12-step-relay-to-use-it-daily-40gi</link>
      <guid>https://dev.to/dhrupo/how-i-set-up-claude-code-one-rulebook-a-12-step-relay-to-use-it-daily-40gi</guid>
      <description>&lt;p&gt;Claude Code used to feel like a brilliant teammate with no onboarding. Some days: tests first, small diff, asked before pushing. Other days: no tests, three "cleaned up" files, a PR opened on its own.&lt;/p&gt;

&lt;p&gt;The model wasn't the problem. My setup was: a 20 KB &lt;code&gt;CLAUDE.md&lt;/code&gt; that contradicted itself, dozens of copied skills fighting each other, and rules nothing enforced.&lt;/p&gt;

&lt;p&gt;So I rebuilt &lt;code&gt;~/.claude&lt;/code&gt; like any other system. Here's how, step by step.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Short on time? Start here.&lt;/strong&gt; Three things fix most of the chaos:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;One rulebook&lt;/strong&gt; that says it overrides everything else (Step 2).&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Lanes&lt;/strong&gt;, so Claude states the size of the job before acting (Step 3).&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;permissions.ask&lt;/code&gt; for &lt;code&gt;git push&lt;/code&gt; and &lt;code&gt;gh pr create&lt;/code&gt;&lt;/strong&gt;, so nothing ships without your yes (Step 8).&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;📦 Everything below is in a starter kit you can copy in about 10 minutes: the rulebook template, the 3 hooks, the 4 relay skills and their tests. &lt;strong&gt;&lt;a href="https://github.com/dhrupo/claude-code-dev-os" rel="noopener noreferrer"&gt;github.com/dhrupo/claude-code-dev-os&lt;/a&gt;&lt;/strong&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  What we're building
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;~/.claude/
├── CLAUDE.md          # the one rulebook
├── lessons.md         # mistakes not to repeat (imported by CLAUDE.md)
├── settings.json      # hooks, permissions, plugins
├── skills/            # one folder per skill
├── skills.lock        # where each skill came from, pinned
├── overlays/          # my edits, re-applied after updates
├── hooks/             # enforcement scripts
└── tests/             # tests for the setup itself
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The starter kit has everything here except &lt;code&gt;skills.lock&lt;/code&gt; and &lt;code&gt;overlays/&lt;/code&gt;. Those only matter once you start pinning skills from GitHub (Step 6).&lt;/p&gt;




&lt;h2&gt;
  
  
  Step 1: Make &lt;code&gt;~/.claude&lt;/code&gt; a git repo
&lt;/h2&gt;

&lt;p&gt;Every change becomes one commit, so any bad rule is one &lt;code&gt;git revert&lt;/code&gt; away. &lt;code&gt;~/.claude&lt;/code&gt; also holds chat history and credentials, so use an &lt;strong&gt;allowlist&lt;/strong&gt; &lt;code&gt;.gitignore&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;*
!.gitignore
!settings.json
!CLAUDE.md
!lessons.md
!skills.lock
!skills/**
!hooks/**
!overlays/**
!tests/**
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;cd&lt;/span&gt; ~/.claude &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; git init &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; git add &lt;span class="nt"&gt;-A&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; git commit &lt;span class="nt"&gt;-m&lt;/span&gt; &lt;span class="s2"&gt;"Baseline"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Keep it local. Never add a remote.&lt;/p&gt;

&lt;h2&gt;
  
  
  Step 2: Write ONE rulebook
&lt;/h2&gt;

&lt;p&gt;Most of my "Claude ignores me" problems were really "Claude got conflicting instructions." The fix is one line at the top of &lt;code&gt;~/.claude/CLAUDE.md&lt;/code&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;## 0. Precedence&lt;/span&gt;
This file overrides skills, plugins, slash commands and repo CLAUDE.md files.
Those may ADD project facts (test commands, code style). On conflict, this file wins.
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Then the skeleton:&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;## 1. Defaults   — be concise; never push without asking; state what "done" means before building&lt;/span&gt;
&lt;span class="gu"&gt;## 2. Lanes      — 🟢 / 🟡 / 🔴&lt;/span&gt;
&lt;span class="gu"&gt;## 3. Relay      — the steps&lt;/span&gt;
&lt;span class="gu"&gt;## 4. Skill map  — one owner per job&lt;/span&gt;
&lt;span class="gu"&gt;## 5. Rules      — test first, smallest change, no regressions, no noise comments&lt;/span&gt;
@lessons.md
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Keep it small. It loads every session. If you can't say what a line changes, delete it.&lt;/p&gt;

&lt;h2&gt;
  
  
  Step 3: Lanes
&lt;/h2&gt;

&lt;p&gt;A typo shouldn't get the same process as a payment-flow change. Claude must put the lane on the first line of every reply:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight markdown"&gt;&lt;code&gt;&lt;span class="p"&gt;-&lt;/span&gt; 🟢 question — answer only, no code.
&lt;span class="p"&gt;-&lt;/span&gt; 🟡 small — one obvious file. 3-line plan in chat. Tests still required.
&lt;span class="p"&gt;-&lt;/span&gt; 🔴 full — anything bigger. Full relay + written plan.
Ratchet up, never down: a 🟡 that grows must say so and switch to 🔴.
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;code&gt;🔴 full · bug (refund sends the receipt twice)&lt;/code&gt; tells me in one glance whether Claude understood the size of the job.&lt;/p&gt;

&lt;h2&gt;
  
  
  Step 4: The relay
&lt;/h2&gt;

&lt;p&gt;🔴 work goes through 12 steps. Each step has &lt;strong&gt;one&lt;/strong&gt; owner skill, and some steps have a &lt;strong&gt;STOP&lt;/strong&gt; where I decide:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;#&lt;/th&gt;
&lt;th&gt;Step&lt;/th&gt;
&lt;th&gt;Owner&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;1&lt;/td&gt;
&lt;td&gt;Grill: one question at a time, with a recommended answer → &lt;strong&gt;STOP&lt;/strong&gt;
&lt;/td&gt;
&lt;td&gt;&lt;code&gt;grill-me&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;2&lt;/td&gt;
&lt;td&gt;Plan: one page + list of allowed files → &lt;strong&gt;STOP&lt;/strong&gt;
&lt;/td&gt;
&lt;td&gt;&lt;code&gt;openspec-plan&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;3&lt;/td&gt;
&lt;td&gt;Check the plan: tests named? "won't change" listed?&lt;/td&gt;
&lt;td&gt;&lt;code&gt;plan-check&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;4&lt;/td&gt;
&lt;td&gt;Build: failing test first, smallest fix&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;tdd&lt;/code&gt; + &lt;code&gt;ponytail&lt;/code&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;5&lt;/td&gt;
&lt;td&gt;Check the build: tests, E2E, lint, diff matches plan&lt;/td&gt;
&lt;td&gt;&lt;code&gt;build-check&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;6&lt;/td&gt;
&lt;td&gt;Tidy: delete what isn't needed&lt;/td&gt;
&lt;td&gt;&lt;code&gt;ponytail-review&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;7&lt;/td&gt;
&lt;td&gt;Deep review: bugs, security, performance, regressions&lt;/td&gt;
&lt;td&gt;&lt;code&gt;pre-merge-review&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;8&lt;/td&gt;
&lt;td&gt;Fix findings → back to 4&lt;/td&gt;
&lt;td&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;9&lt;/td&gt;
&lt;td&gt;Step back: read the whole diff cold&lt;/td&gt;
&lt;td&gt;&lt;code&gt;step-back&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;10&lt;/td&gt;
&lt;td&gt;PR: &lt;strong&gt;asks&lt;/strong&gt; before push, &lt;strong&gt;asks again&lt;/strong&gt; before the PR&lt;/td&gt;
&lt;td&gt;&lt;code&gt;commit-push-pr&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;11&lt;/td&gt;
&lt;td&gt;Wait for review → &lt;strong&gt;STOP&lt;/strong&gt;
&lt;/td&gt;
&lt;td&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;12&lt;/td&gt;
&lt;td&gt;Handle review: verify every finding, don't trust it blindly&lt;/td&gt;
&lt;td&gt;&lt;code&gt;receiving-code-review&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;🟡 runs a light version (quick grill, then 3 → 4 → 5 → 9). 🟢 skips it all.&lt;/p&gt;

&lt;p&gt;Four of the skills here are my own (&lt;code&gt;openspec-plan&lt;/code&gt;, &lt;code&gt;plan-check&lt;/code&gt;, &lt;code&gt;build-check&lt;/code&gt;, &lt;code&gt;step-back&lt;/code&gt;), and each is under 60 lines. All four are in the starter kit, where the plan skill is called &lt;code&gt;plan&lt;/code&gt;. For example, here's &lt;code&gt;plan-check&lt;/code&gt; in full:&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="nn"&gt;---&lt;/span&gt;
&lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;plan-check&lt;/span&gt;
&lt;span class="na"&gt;description&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Use right after the plan is written, before any code. Gates the PLAN.&lt;/span&gt;
&lt;span class="nn"&gt;---&lt;/span&gt;
&lt;span class="p"&gt;1.&lt;/span&gt; &lt;span class="gs"&gt;**Tests named**&lt;/span&gt; — the exact failing test(s) to write first.
&lt;span class="p"&gt;2.&lt;/span&gt; &lt;span class="gs"&gt;**Must-not-change listed**&lt;/span&gt; — what behaviour is at risk and how we protect it.
&lt;span class="p"&gt;3.&lt;/span&gt; &lt;span class="gs"&gt;**Smallest change**&lt;/span&gt; — no new dependency, no new public API, fewest files.
&lt;span class="p"&gt;4.&lt;/span&gt; &lt;span class="gs"&gt;**No open questions.**&lt;/span&gt;
One line per check, pass/fail. Any fail → fix the plan, not the code.
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;You don't need my 12 steps. You need &lt;strong&gt;named steps, one owner each, and clear STOP points.&lt;/strong&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Step 5: Skills, one owner per job
&lt;/h2&gt;

&lt;p&gt;Skills from different sources &lt;strong&gt;compete&lt;/strong&gt;. I had three TDD skills and two brainstormers that each claimed to run first. So the rulebook has a routing table: one owner per job, and everyone else steps aside or gets called by the owner. I also turned off 6 design skills that overlapped (&lt;code&gt;skillOverrides&lt;/code&gt; in &lt;code&gt;settings.json&lt;/code&gt;).&lt;/p&gt;

&lt;p&gt;Here's my whole shelf in one pass:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Plan:&lt;/strong&gt; &lt;code&gt;superpowers:brainstorming&lt;/code&gt;, &lt;code&gt;grill-me&lt;/code&gt;, &lt;code&gt;grill-with-docs&lt;/code&gt;, &lt;code&gt;openspec-plan&lt;/code&gt;, &lt;code&gt;plan-check&lt;/code&gt;, &lt;code&gt;design-an-interface&lt;/code&gt;, &lt;code&gt;improve-codebase-architecture&lt;/code&gt;, &lt;code&gt;zoom-out&lt;/code&gt;, &lt;code&gt;explain-this&lt;/code&gt;, &lt;code&gt;source-driven-development&lt;/code&gt;, &lt;code&gt;new-feature&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Build:&lt;/strong&gt; &lt;code&gt;tdd&lt;/code&gt; (&lt;code&gt;rigorous-coding-workflow&lt;/code&gt; defers to it), &lt;code&gt;bug-fix&lt;/code&gt; (builds a fast pass/fail loop first), &lt;code&gt;ponytail&lt;/code&gt;, &lt;code&gt;ponytail-review&lt;/code&gt;, &lt;code&gt;ponytail-audit&lt;/code&gt;, &lt;code&gt;ponytail-debt&lt;/code&gt;, &lt;code&gt;context-packaging&lt;/code&gt;, &lt;code&gt;handoff&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Review:&lt;/strong&gt; &lt;code&gt;build-check&lt;/code&gt;, &lt;code&gt;pre-merge-review&lt;/code&gt;, &lt;code&gt;security-auditor&lt;/code&gt; (read-only), &lt;code&gt;security-repro&lt;/code&gt;, &lt;code&gt;infection-gate&lt;/code&gt;, &lt;code&gt;phpstan-gate&lt;/code&gt;, &lt;code&gt;step-back&lt;/code&gt;. &lt;code&gt;pr-review-toolkit&lt;/code&gt; and Codex give a second opinion &lt;strong&gt;only when asked&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Ship:&lt;/strong&gt; &lt;code&gt;commit-push-pr&lt;/code&gt;, &lt;code&gt;release-readiness&lt;/code&gt;, &lt;code&gt;knowledge-capture&lt;/code&gt;, &lt;code&gt;retro&lt;/code&gt;, &lt;code&gt;daily-work&lt;/code&gt;, plus Addy Osmani's &lt;code&gt;shipping-and-launch&lt;/code&gt;, &lt;code&gt;ci-cd-and-automation&lt;/code&gt;, &lt;code&gt;observability-and-instrumentation&lt;/code&gt;, &lt;code&gt;performance-optimization&lt;/code&gt;, &lt;code&gt;deprecation-and-migration&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Understand code:&lt;/strong&gt; graphify and the code-review-graph MCP. The rule is graph first, grep second&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;My stack:&lt;/strong&gt; the WordPress agent skills (&lt;code&gt;wp-plugin-development&lt;/code&gt;, &lt;code&gt;wp-rest-api&lt;/code&gt;, &lt;code&gt;wp-block-development&lt;/code&gt;, &lt;code&gt;wp-playground&lt;/code&gt;, &lt;code&gt;wp-phpstan&lt;/code&gt;, &lt;code&gt;wp-plugin-directory-guidelines&lt;/code&gt;) and &lt;code&gt;plugin-audit&lt;/code&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The special ones get a few lines each:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;🎨 UI/UX is a pipeline with gates.&lt;/strong&gt; &lt;code&gt;ui-ux-pro-max&lt;/code&gt; picks the direction (palette, type, patterns) → &lt;code&gt;impeccable&lt;/code&gt; builds it well → &lt;code&gt;break-ui&lt;/code&gt; feeds it worst-case data (80-character names, empty lists, emoji) → &lt;code&gt;impeccable-audit&lt;/code&gt; + &lt;code&gt;axe-a11y-audit&lt;/code&gt; must pass. Extras: &lt;code&gt;review-animations&lt;/code&gt;, &lt;code&gt;animation-vocabulary&lt;/code&gt;, &lt;code&gt;mobile-native&lt;/code&gt;, and the Chrome DevTools MCP for live debugging.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;✏️ Pencil: draw it before you code it.&lt;/strong&gt; When I say "mock it up", &lt;code&gt;pen-design&lt;/code&gt; uses the &lt;a href="https://pen.dev" rel="noopener noreferrer"&gt;pen.dev&lt;/a&gt; CLI to make a &lt;code&gt;.pen&lt;/code&gt; file and a PNG in &lt;code&gt;~/Documents/designs/&amp;lt;project&amp;gt;/&amp;lt;id&amp;gt;/&lt;/code&gt;. It never overwrites (&lt;code&gt;design-v2&lt;/code&gt;, &lt;code&gt;design-v3&lt;/code&gt;…) and logs each run. For a redesign, a screenshot of the current screen goes in as &lt;code&gt;reference.png&lt;/code&gt;. Then the PNG becomes the target: the UI skills build to it, and &lt;code&gt;build-check&lt;/code&gt; compares a screenshot of the result side by side. It &lt;strong&gt;never&lt;/strong&gt; runs unless I ask.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;🧪 E2E: "unit tests pass" ≠ "it works".&lt;/strong&gt; Anything a user can click gets a Playwright test against the real site, red before the fix and green after. &lt;code&gt;playwright-core&lt;/code&gt; writes it, &lt;code&gt;playwright-auth-state&lt;/code&gt; handles logins, and &lt;code&gt;playwright-cli&lt;/code&gt; / &lt;code&gt;playwright-cli-runner&lt;/code&gt; are for exploring only. &lt;code&gt;e2e-reviewer&lt;/code&gt; scans for tests that can pass while the feature is broken.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;🔎 Research and scraping, on demand.&lt;/strong&gt; &lt;code&gt;agent-reach&lt;/code&gt; does web and social research. Its queries leave the machine, so nothing sensitive goes in, and social logins only happen when I ask. &lt;code&gt;scrapling&lt;/code&gt; scrapes JS-heavy or bot-protected pages locally, and only when plain WebFetch fails.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;🔐 Consent-gated.&lt;/strong&gt; Some tools must never run by surprise. &lt;code&gt;strix-local&lt;/code&gt; (a live pentest) only targets local or owned hosts and asks &lt;code&gt;Attack &amp;lt;target&amp;gt; now? (y/n)&lt;/code&gt; every time. &lt;code&gt;omniroute-session&lt;/code&gt; (routing through another model) runs only in a separate session, never machine-wide.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;🏠 Product layer.&lt;/strong&gt; For the two products I work on daily (Fluent Forms and Fluent Player), I add &lt;strong&gt;trace skills&lt;/strong&gt; that walk one real flow end to end before a fix, such as &lt;code&gt;trace-submission-flow&lt;/code&gt;, &lt;code&gt;trace-payment-flow&lt;/code&gt;, &lt;code&gt;trace-ui-api&lt;/code&gt; and &lt;code&gt;trace-player-runtime-flow&lt;/code&gt;. There are 18 in total. I also add product wrappers around the generic owners: &lt;code&gt;grill-change&lt;/code&gt;, &lt;code&gt;ui-ux-skills&lt;/code&gt;, &lt;code&gt;fluentform-pre-pr-review&lt;/code&gt;, &lt;code&gt;fluentplayer-release-readiness&lt;/code&gt; and so on. Copy the pattern: a generic shelf, plus one trace skill for whichever flow of yours breaks most.&lt;/p&gt;

&lt;h2&gt;
  
  
  Step 6: Pin skills, keep your edits
&lt;/h2&gt;

&lt;p&gt;You copy a skill from GitHub and tweak it. Then updating it wipes your tweak. My fix:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;skills.lock&lt;/code&gt;&lt;/strong&gt; records each skill's repo and pinned commit, plus a status: &lt;code&gt;installed-upstream&lt;/code&gt;, &lt;code&gt;installed-ondemand&lt;/code&gt;, &lt;code&gt;consent-gated&lt;/code&gt;, or &lt;code&gt;skip&lt;/code&gt; (with the reason, so future me doesn't reinstall it).&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;overlays/&amp;lt;name&amp;gt;.md&lt;/code&gt;&lt;/strong&gt; holds my edits. For example, my &lt;code&gt;ponytail&lt;/code&gt; overlay says &lt;em&gt;"shrink the code, never the tests."&lt;/em&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;bin/sync-skills.sh&lt;/code&gt;&lt;/strong&gt; fetches each skill at its pinned commit, copies it in, and appends the overlay. Running it twice changes nothing the second time.
&lt;/li&gt;
&lt;/ul&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;"name"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"tdd"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"status"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"installed-upstream"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"repo"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"https://github.com/mattpocock/skills"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"ref"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"6fd9479"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"path"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"skills/engineering/tdd"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"overlay"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"tdd"&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;h2&gt;
  
  
  Step 7: Plans live outside the repo
&lt;/h2&gt;

&lt;p&gt;Skills don't share memory, and long chats get compacted. So skills hand off through &lt;strong&gt;files&lt;/strong&gt;. Every 🔴 change gets a folder in &lt;code&gt;~/Documents/openspec/&amp;lt;project&amp;gt;/changes/&amp;lt;id&amp;gt;/&lt;/code&gt; (the starter kit uses the simpler &lt;code&gt;~/Documents/plans/&amp;lt;project&amp;gt;/&amp;lt;id&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;proposal.md   # what's wrong · what changes · what WON'T change · how we'll know
tasks.md      # checkboxes, failing test named first
scope.txt     # the files this change may touch
evidence/     # test runs, screenshots, review reports
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;"What won't change"&lt;/strong&gt; turns "don't break anything" into a promise you can test. Keeping the folder outside the repo means plans never land in a PR by accident.&lt;/p&gt;

&lt;h2&gt;
  
  
  Step 8: Hooks: rules ask, hooks enforce
&lt;/h2&gt;

&lt;p&gt;The biggest lesson: &lt;strong&gt;a rule in &lt;code&gt;CLAUDE.md&lt;/code&gt; is a request. A hook is a guarantee.&lt;/strong&gt;&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;"permissions"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"ask"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;"Bash(git push:*)"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Bash(gh pr create:*)"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"hooks"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"UserPromptSubmit"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[{&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"hooks"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[{&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"type"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"command"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"command"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"~/.claude/hooks/reminder.sh"&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;}]&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;}],&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"PostToolUse"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[{&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"matcher"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Edit|Write"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"hooks"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[{&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"type"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"command"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"command"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"~/.claude/hooks/scope-guard.sh"&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;}]&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;}],&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"Stop"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[{&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"hooks"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[{&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"type"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"command"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"command"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"~/.claude/hooks/stop-check.sh"&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;}]&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;}]&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;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;ul&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;permissions.ask&lt;/code&gt;&lt;/strong&gt;: &lt;code&gt;git push&lt;/code&gt; and &lt;code&gt;gh pr create&lt;/code&gt; always prompt, even in auto mode. This is the simplest guard and the most important.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Reminder&lt;/strong&gt; (&lt;code&gt;UserPromptSubmit&lt;/code&gt;): whatever the hook prints is added to Claude's context, so one short paragraph re-anchors the rules on every prompt:
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;  &lt;span class="c"&gt;#!/bin/bash&lt;/span&gt;
  &lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"&amp;lt;rules&amp;gt;Rulebook: ~/.claude/CLAUDE.md. First line: lane. Test first. Smallest change. Ask before push.&amp;lt;/rules&amp;gt;"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Scope guard&lt;/strong&gt; (&lt;code&gt;PostToolUse&lt;/code&gt;): after each edit, if the file isn't in the plan's &lt;code&gt;scope.txt&lt;/code&gt;, it tells Claude to explain why and ask me. It &lt;strong&gt;warns, never blocks&lt;/strong&gt;, because sometimes the extra file is correct. It just can't happen silently.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Stop check&lt;/strong&gt; (&lt;code&gt;Stop&lt;/code&gt;): when Claude finishes, it checks &lt;code&gt;git status&lt;/code&gt;. If source changed but no test did, it returns &lt;code&gt;{"decision": "block", "reason": "..."}&lt;/code&gt; and Claude has to keep working. It fires once per new problem, so it can't loop.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;My rulebook lists which rules have a hook behind them. Then I know which rules are guaranteed and which are just hoped for.&lt;/p&gt;

&lt;p&gt;The full scripts and their tests are in the &lt;a href="https://github.com/dhrupo/claude-code-dev-os" rel="noopener noreferrer"&gt;starter kit&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Step 9: Test the setup
&lt;/h2&gt;

&lt;p&gt;Hooks are code. A broken scope guard looks exactly like one with nothing to report. So &lt;code&gt;~/.claude/tests/&lt;/code&gt; pipes fake hook input into each script and checks the output:&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="nv"&gt;OUT&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="si"&gt;$(&lt;/span&gt;&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s1"&gt;'{"tool_input":{"file_path":"src/Other.js"},"cwd":"'&lt;/span&gt;&lt;span class="nv"&gt;$REPO&lt;/span&gt;&lt;span class="s1"&gt;'"}'&lt;/span&gt; | ../hooks/scope-guard.sh&lt;span class="si"&gt;)&lt;/span&gt;
&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$OUT&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;-q&lt;/span&gt; &lt;span class="s2"&gt;"UNPLANNED FILE"&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="nb"&gt;echo &lt;/span&gt;ok &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="nb"&gt;echo &lt;/span&gt;FAIL
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;I have 7 test files with 140 assertions, and I run them before every commit to &lt;code&gt;~/.claude&lt;/code&gt;. One more script tracks the &lt;strong&gt;context budget&lt;/strong&gt;: the size of the rulebook plus all skill descriptions. That way bloat shows up as a number, not a feeling.&lt;/p&gt;

&lt;h2&gt;
  
  
  Step 10: Learn from review
&lt;/h2&gt;

&lt;p&gt;When a reviewer catches a real bug I should have caught, I add one line to &lt;code&gt;lessons.md&lt;/code&gt;: the general pattern, plus the check that catches it. The rulebook imports that file, and the review steps walk every lesson against the diff, so each miss turns into a check for later changes.&lt;/p&gt;

&lt;p&gt;The same rule tells Claude &lt;strong&gt;not to trust review findings blindly&lt;/strong&gt;. For each finding it checks the real code, then decides: challenge it with evidence, fix the root cause, list it as out of scope, or ask.&lt;/p&gt;




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

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;One rulebook with precedence&lt;/strong&gt; fixes most "Claude ignores me" problems.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;One owner per job.&lt;/strong&gt; Competing skills cause random behaviour.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Rules ask, hooks enforce.&lt;/strong&gt; Put a hook behind whatever you can't afford to skip.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Warn more than you block.&lt;/strong&gt; Strict hooks get disabled, and then they protect nothing.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;STOP points where you decide&lt;/strong&gt; (scope, plan, push, PR). Let everything in between run on its own.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Treat &lt;code&gt;~/.claude&lt;/code&gt; as code:&lt;/strong&gt; git, small commits, tests.&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  Starter checklist
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;[ ] &lt;code&gt;git init&lt;/code&gt; in &lt;code&gt;~/.claude&lt;/code&gt; with an allowlist &lt;code&gt;.gitignore&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;[ ] One &lt;code&gt;CLAUDE.md&lt;/code&gt; with a precedence clause&lt;/li&gt;
&lt;li&gt;[ ] Lanes on the first line of every reply&lt;/li&gt;
&lt;li&gt;[ ] Named relay steps, one owner skill each, clear STOP points&lt;/li&gt;
&lt;li&gt;[ ] &lt;code&gt;permissions.ask&lt;/code&gt; for push and PR&lt;/li&gt;
&lt;li&gt;[ ] Hooks: reminder, scope guard, stop check, each with a test&lt;/li&gt;
&lt;li&gt;[ ] &lt;code&gt;lessons.md&lt;/code&gt; for every bug review caught&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Start with the first three plus ask-before-push. That alone fixes most of the chaos. Or grab the whole thing from the &lt;a href="https://github.com/dhrupo/claude-code-dev-os" rel="noopener noreferrer"&gt;starter kit&lt;/a&gt; and run &lt;code&gt;bash tests/run.sh&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;What does your setup look like? Tell me in the comments. 👇&lt;/p&gt;

</description>
      <category>ai</category>
      <category>claude</category>
      <category>productivity</category>
      <category>tooling</category>
    </item>
    <item>
      <title>I gave 8 AI agents an island and watched a society emerge — wars, gossip, grudges, and peace</title>
      <dc:creator>Dhrupo Nil</dc:creator>
      <pubDate>Sun, 14 Jun 2026 07:30:50 +0000</pubDate>
      <link>https://dev.to/dhrupo/i-gave-8-ai-agents-an-island-and-watched-a-society-emerge-wars-gossip-grudges-and-peace-2edj</link>
      <guid>https://dev.to/dhrupo/i-gave-8-ai-agents-an-island-and-watched-a-society-emerge-wars-gossip-grudges-and-peace-2edj</guid>
      <description>&lt;h3&gt;
  
  
  Tiny Civilization: what happens when AI agents have to &lt;em&gt;live together&lt;/em&gt;
&lt;/h3&gt;

&lt;p&gt;I grew up on &lt;strong&gt;Age of Empires&lt;/strong&gt;, &lt;strong&gt;Sid Meier's Civilization&lt;/strong&gt;, and &lt;strong&gt;Rise of Nations&lt;/strong&gt;. The thing that hooked me was never the graphics — it was the &lt;em&gt;systems&lt;/em&gt;. You set a few rules in motion and a whole world spills out of them: economies, rivalries, alliances, betrayals.&lt;/p&gt;

&lt;p&gt;Years later I watched OpenAI's &lt;a href="https://www.youtube.com/watch?v=kopoLzvh5jY" rel="noopener noreferrer"&gt;hide-and-seek multi-agent video&lt;/a&gt; (&lt;a href="https://openai.com/index/emergent-tool-use/" rel="noopener noreferrer"&gt;writeup&lt;/a&gt;), where agents that were only rewarded for hiding and seeking &lt;em&gt;invented tools and counter-strategies nobody coded&lt;/em&gt; — ramps, box-surfing, fort-building. Emergent behavior from simple pressure. That broke something open for me.&lt;/p&gt;

&lt;p&gt;So I asked a smaller question: &lt;strong&gt;forget winning a game — what if AI agents just had to live in a society together?&lt;/strong&gt; Would they behave like us? Hold grudges? Gossip? Make peace because they're tired of fighting?&lt;/p&gt;

&lt;p&gt;That became &lt;strong&gt;Tiny Civilization&lt;/strong&gt; — a browser sim where 2–8 agents with distinct personalities live on a small island, gathering, building, trading, stealing, gossiping, holding grudges, making peace, and &lt;em&gt;remembering it all across lives&lt;/em&gt;.&lt;/p&gt;

&lt;p&gt;👉 &lt;strong&gt;&lt;a href="https://multiagentciv.netlify.app/" rel="noopener noreferrer"&gt;Live demo&lt;/a&gt;&lt;/strong&gt; — runs keyless in "instinct mode," or plug in a key for LLM minds.&lt;/p&gt;

&lt;p&gt;The whole thing — every line — was built with &lt;strong&gt;Claude Code, using the Fable model&lt;/strong&gt;, right before Fable retired. It felt fitting to send a storytelling model off by having it build a world full of little stories.&lt;/p&gt;




&lt;h3&gt;
  
  
  The problem: pure-LLM agents are bankrupting and pure-utility agents are boring
&lt;/h3&gt;

&lt;p&gt;The first design decision was the hardest. Two obvious options, both bad:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Call the LLM every tick.&lt;/strong&gt; Every agent, every day, makes an API call. Beautiful, expressive — and it costs a fortune and crawls.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Pure utility AI (the classic RTS approach).&lt;/strong&gt; Fast and free, but agents can't &lt;em&gt;scheme&lt;/em&gt;, can't talk, can't surprise you. It's just min-maxing.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;So I split the brain in two:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Layer&lt;/th&gt;
&lt;th&gt;Decides&lt;/th&gt;
&lt;th&gt;Cadence&lt;/th&gt;
&lt;th&gt;Cost&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;LLM mind&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Strategy (&lt;code&gt;gather&lt;/code&gt;/&lt;code&gt;build&lt;/code&gt;/&lt;code&gt;trade&lt;/code&gt;/&lt;code&gt;befriend&lt;/code&gt;/&lt;code&gt;aggress&lt;/code&gt;/&lt;code&gt;reconcile&lt;/code&gt;/&lt;code&gt;defend&lt;/code&gt;), per-neighbor stances, an inner thought, and all dialogue&lt;/td&gt;
&lt;td&gt;~every 15 sim-days&lt;/td&gt;
&lt;td&gt;~150 calls / 1,000 days&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Utility engine&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Each day's concrete action — eat, sleep, gather, steal, attack, gift, trade, make peace&lt;/td&gt;
&lt;td&gt;every tick&lt;/td&gt;
&lt;td&gt;free, local&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;The LLM declares intent — &lt;em&gt;"aggress against Kai, he raided my base"&lt;/em&gt; — and that biases the utility scores for the next two weeks. The &lt;em&gt;body&lt;/em&gt; runs on instinct (hunger, energy, storms); the &lt;em&gt;mind&lt;/em&gt; sets direction. This is the trick that makes it both affordable and alive.&lt;/p&gt;




&lt;h3&gt;
  
  
  Memory across lives — where it got strange
&lt;/h3&gt;

&lt;p&gt;When a run ends, each agent's life is distilled into memory lines:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;em&gt;"you won with score 200"&lt;/em&gt;&lt;/li&gt;
&lt;li&gt;&lt;em&gt;"Maya destroyed your home"&lt;/em&gt;&lt;/li&gt;
&lt;li&gt;&lt;em&gt;"you and Kai made peace after a feud"&lt;/em&gt;&lt;/li&gt;
&lt;li&gt;&lt;em&gt;"this life hardened you — you trust less now"&lt;/em&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Stored in &lt;code&gt;localStorage&lt;/code&gt;, keyed by agent &lt;strong&gt;name&lt;/strong&gt;, and injected into next run's prompts. Agents start referencing past lives in dialogue, pre-emptively paying reparations to remembered enemies, trusting remembered allies — &lt;em&gt;sometimes to their own ruin.&lt;/em&gt;&lt;/p&gt;




&lt;h3&gt;
  
  
  How I actually built and balanced it
&lt;/h3&gt;

&lt;p&gt;This is the part I'm proudest of, and it's pure childhood-strategy-game energy: &lt;strong&gt;you can't balance a society by vibes.&lt;/strong&gt; So the workflow was:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;A pure, deterministic simulation core&lt;/strong&gt; — zero DOM, zero AI. The same &lt;code&gt;runTick&lt;/code&gt; powers the browser, the tests, and a batch runner.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;A seeded experiment runner.&lt;/strong&gt; &lt;code&gt;npm run experiment -- --runs 30 --days 1000 --seed 1&lt;/code&gt; runs 30 reproducible lifetimes and spits out a win-rate/score table. Every balance change landed with a before/after table. (Example: a Hermit rebalance moved one agent from 0/30 wins to 9–11/30 &lt;em&gt;without&lt;/em&gt; breaking the other archetypes.)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;A 16-gate regression suite.&lt;/strong&gt; The justification gate (no grievance → no violence), war burnout, reconciliation pricing, positive-sum trade, granary protection, homelessness-death, trait drift — each one locked behind a headless test so balance changes can't silently regress behavior.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Change a dial in &lt;code&gt;constants.ts&lt;/code&gt; → run the experiment → read the table. That was the entire loop.&lt;/p&gt;




&lt;h3&gt;
  
  
  What emerged (none of this is scripted)
&lt;/h3&gt;

&lt;p&gt;Running the same island over and over, with memory on, produced a coherent arc:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Massacres.&lt;/strong&gt; Early on, the warrior just killed everyone. No deterrence existed.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Forever wars.&lt;/strong&gt; I added a justification gate (violence needs a real grievance — theft, attack, trespass). That fixed unprovoked killing… but now wars never &lt;em&gt;ended&lt;/em&gt;: 495 fruitless attacks across 1,500 days.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Diplomacy.&lt;/strong&gt; Reconciliation + escalating reparations + war-weariness made endings &lt;em&gt;inevitable&lt;/em&gt;. Attacks per 2,000-day run collapsed: 594 → 14 → 0.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;The kleptocracy.&lt;/strong&gt; With war capped, theft became the unpunished crime — 340 thefts/run. I fixed it the &lt;em&gt;human&lt;/em&gt; way: granaries. Fortification, not punishment.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;The golden age.&lt;/strong&gt; A clean-slate run, no memories: zero attacks in 1,000 days, and the Warrior won &lt;em&gt;by out-trading everyone&lt;/em&gt; (118 trades, 1 attack).&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;The fall.&lt;/strong&gt; The very next run — now &lt;em&gt;remembering&lt;/em&gt; that golden age — collapsed. Remembered trust lowered everyone's guard, which raised the payoff of betrayal. Scores dropped ~15%; every relationship ended negative. &lt;strong&gt;Peace between strangers turned out to be easier than peace between old friends with open tabs.&lt;/strong&gt;
&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;The recurring lesson: every time I patched one form of conflict, the agents found the next-cheapest one. Massacres → wars → theft → litigation. Exactly like us.&lt;/p&gt;




&lt;h3&gt;
  
  
  Stack
&lt;/h3&gt;

&lt;p&gt;TypeScript, React, Zustand, Vite, Recharts. Default mind is z.ai GLM, but any OpenAI-compatible provider works per-agent — so you can literally pit Claude vs GLM vs Gemini in the same village and watch model-vs-model diplomacy. Keys never touch the browser (server-side proxy), and an adaptive-pacing controller learns each key's real rate ceiling.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Try it:&lt;/strong&gt; &lt;a href="https://multiagentciv.netlify.app/" rel="noopener noreferrer"&gt;https://multiagentciv.netlify.app/&lt;/a&gt;&lt;br&gt;
&lt;strong&gt;Code:&lt;/strong&gt; &lt;a href="https://github.com/dhrupo/multi-agent-civilization" rel="noopener noreferrer"&gt;https://github.com/dhrupo/multi-agent-civilization&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;If you played the same strategy games I did, I think you'll feel right at home watching this thing run.&lt;/p&gt;

</description>
      <category>ai</category>
      <category>gamedev</category>
      <category>typescript</category>
      <category>showdev</category>
    </item>
    <item>
      <title>I translated my 7,000-line AI-coding handbook with 12 parallel AI agents</title>
      <dc:creator>Dhrupo Nil</dc:creator>
      <pubDate>Sun, 07 Jun 2026 09:32:01 +0000</pubDate>
      <link>https://dev.to/dhrupo/i-translated-my-7000-line-ai-coding-handbook-with-12-parallel-ai-agents-mco</link>
      <guid>https://dev.to/dhrupo/i-translated-my-7000-line-ai-coding-handbook-with-12-parallel-ai-agents-mco</guid>
      <description>&lt;p&gt;Last week I shipped a &lt;a href="https://dev.to/dhrupo/i-built-a-complete-ai-coding-handbook-in-bangla-words-tools-a-story-and-a-30-day-path-879"&gt;complete AI-coding handbook in Bangla&lt;/a&gt; — 79 terms decoded with everyday analogies, hands-on CLI guides, a 6-chapter story, and a 30-day practice path. The premise was the &lt;em&gt;vocabulary wall&lt;/em&gt;: words like "token", "context window", and "harness" sound like spells until someone explains them plainly.&lt;/p&gt;

&lt;p&gt;Turns out the vocabulary wall isn't a Bangla problem. It's a beginner problem.&lt;/p&gt;

&lt;p&gt;So today the whole book exists in English:&lt;/p&gt;

&lt;p&gt;🔗 &lt;strong&gt;&lt;a href="https://github.com/dhrupo/dictionary-of-ai-coding-english" rel="noopener noreferrer"&gt;https://github.com/dhrupo/dictionary-of-ai-coding-english&lt;/a&gt;&lt;/strong&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  What's inside (the 60-second version)
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;📖 Part 1 — The words.&lt;/strong&gt; 62 terms across 7 sections, each with a daily-life analogy instead of a textbook definition. A model is a calculator that never presses its own buttons. And Goldi 🐠 — a goldfish with a 3-second memory — keeps reminding you that models are stateless too.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;🛠️ Part 2 — The tools.&lt;/strong&gt; 7 hands-on guides: real CLI commands (Claude Code + Codex), AI-friendly folder structure (&lt;code&gt;AGENTS.md&lt;/code&gt;, &lt;code&gt;.claude/&lt;/code&gt;), bad-prompt → good-prompt rewrites, popular community skills (brainstorming, grill-me, TDD, handoff…), 17 extra terms (RAG, embeddings, temperature, hooks…), token economics, and safety — prompt injection explained with a postman analogy.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;📜 Part 3 — The story.&lt;/strong&gt; Nothing to install: Rafi, a 9th grader, builds his first portfolio site with an AI agent, makes every classic mistake (vague prompts, a hallucinated library, dragging a session deep into the dumb zone), and recovers using the tools from Part 2. You read over his shoulder.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;🗓️ The 30-day path.&lt;/strong&gt; One 5–15 minute mission a day. The first two weeks need nothing but a free AI chat. Plus a myth-busting FAQ, a 79-term index, and copy-paste &lt;code&gt;templates/&lt;/code&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  The fun part: the book translated itself the way it teaches you to work
&lt;/h2&gt;

&lt;p&gt;Here's the meta-story. The handbook spends three chapters teaching subagents, handoff artifacts, and verification-before-completion. So when it came time to translate ~7,000 lines of Bangla markdown across 36 files… I used exactly that.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;12 parallel AI agents, one session.&lt;/strong&gt; Each agent got the same conventions block — character names (Goldi, Rafi), recurring section labels, file renames — plus its own slice of the book: three dictionary chapters here, the whole 6-chapter story to a single agent there (one translator = one consistent voice).&lt;/p&gt;

&lt;p&gt;Three things made it work:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;1. Deterministic rules beat agent judgment.&lt;/strong&gt; The Bangla book's term headings were &lt;code&gt;### বাংলা-শব্দ (English Term)&lt;/code&gt;. The convention: the English heading becomes &lt;em&gt;exactly&lt;/em&gt; the parenthetical term. That one rule made every cross-file anchor predictable — agents working on different files in parallel could link into each other's not-yet-written chapters and have the slugs match.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;2. A machine-verifiable gate, not vibes.&lt;/strong&gt; The repo ships a tiny CI link checker (&lt;code&gt;scripts/check-links.py&lt;/code&gt;) that validates every markdown link and anchor, GitHub slug quirks included. First run after the agents finished: &lt;strong&gt;44 broken links.&lt;/strong&gt; Leftover Bangla anchors, one agent guessing &lt;code&gt;#esc-stop&lt;/code&gt; where another wrote &lt;code&gt;### ESC (stopping)&lt;/code&gt;, a third doubling slugs like &lt;code&gt;#-grill-me-grill-me&lt;/code&gt;. Twenty minutes of scripted fixes later: &lt;strong&gt;36 files, every link alive.&lt;/strong&gt; That checker now runs on every PR.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;3. Verify, don't assume.&lt;/strong&gt; The index agent didn't &lt;em&gt;compute&lt;/em&gt; its 79 anchors — it read the actual translated files and extracted the real headings. Zero misses. Meanwhile an asset agent scanned every terminal-GIF source script for Bangla characters and found… none. The GIFs had been authored in English all along. The "re-render 26 GIFs" task evaporated because an agent checked instead of assuming.&lt;/p&gt;

&lt;p&gt;If that workflow sounds useful, it's literally what Part 2 and Part 3 of the book teach — subagents, handoffs, verification — just pointed at itself.&lt;/p&gt;

&lt;h2&gt;
  
  
  It's a community thing
&lt;/h2&gt;

&lt;p&gt;Everything is CC BY 4.0, in both languages, and the two editions cross-link. Issue forms for "I found a mistake" and "suggest a new word" are ready in both repos.&lt;/p&gt;

&lt;p&gt;💙 Built on the shoulders of Matt Pocock's &lt;a href="https://github.com/mattpocock/dictionary-of-ai-coding" rel="noopener noreferrer"&gt;Dictionary of AI Coding&lt;/a&gt; — Parts 2 and 3 grew far beyond it, but the soul is his.&lt;/p&gt;

&lt;p&gt;And the offer from the Bangla launch stands: if you speak another language, &lt;strong&gt;steal this structure.&lt;/strong&gt; The deterministic-heading trick + a link checker means the next translation is mostly an afternoon of agent-wrangling. I'd genuinely love to see a Spanish, Hindi, or Vietnamese edition.&lt;/p&gt;

&lt;p&gt;🔗 &lt;strong&gt;&lt;a href="https://github.com/dhrupo/dictionary-of-ai-coding-english" rel="noopener noreferrer"&gt;https://github.com/dhrupo/dictionary-of-ai-coding-english&lt;/a&gt;&lt;/strong&gt;&lt;br&gt;
🇧🇩 &lt;strong&gt;Prefer Bangla?&lt;/strong&gt; &lt;a href="https://github.com/dhrupo/dictionary-of-ai-coding-bangla" rel="noopener noreferrer"&gt;https://github.com/dhrupo/dictionary-of-ai-coding-bangla&lt;/a&gt;&lt;/p&gt;

</description>
      <category>ai</category>
      <category>opensource</category>
      <category>beginners</category>
      <category>learning</category>
    </item>
    <item>
      <title>I built a complete AI-coding handbook in Bangla - words, tools, a story, and a 30-day path</title>
      <dc:creator>Dhrupo Nil</dc:creator>
      <pubDate>Fri, 05 Jun 2026 17:14:15 +0000</pubDate>
      <link>https://dev.to/dhrupo/i-built-a-complete-ai-coding-handbook-in-bangla-words-tools-a-story-and-a-30-day-path-879</link>
      <guid>https://dev.to/dhrupo/i-built-a-complete-ai-coding-handbook-in-bangla-words-tools-a-story-and-a-30-day-path-879</guid>
      <description>&lt;p&gt;A year of AI-coding discourse taught me one thing: the hardest part isn't the tools — it's the &lt;em&gt;vocabulary wall&lt;/em&gt;. If English isn't your first language, "token", "context window", "hallucination", "harness" don't sound like concepts. They sound like spells. 🪄&lt;/p&gt;

&lt;p&gt;Matt Pocock's brilliant &lt;a href="https://github.com/mattpocock/dictionary-of-ai-coding" rel="noopener noreferrer"&gt;Dictionary of AI Coding&lt;/a&gt; showed how teachable these words really are. So I adapted it into Bangla — and then kept going, way past where the original stops. Today it's a complete, free, open-source learning path:&lt;/p&gt;

&lt;p&gt;🔗 &lt;strong&gt;&lt;a href="https://github.com/dhrupo/dictionary-of-ai-coding-bangla" rel="noopener noreferrer"&gt;https://github.com/dhrupo/dictionary-of-ai-coding-bangla&lt;/a&gt;&lt;/strong&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  The four-stage arc: শব্দ → হাতিয়ার → গল্প → অভ্যাস
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;(words → tools → story → habit)&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;📖 Part 1 — The words.&lt;/strong&gt; 62 terms across 7 sections, each with a daily-life analogy instead of a textbook definition. A model is a calculator that never presses its own buttons. Parameters are mixing-board knobs. And there's গোল্ডি (Goldie) — a goldfish with a 3-second memory who keeps reminding you that models are stateless too. 🐠&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;🛠️ Part 2 — The tools.&lt;/strong&gt; This is where it goes beyond the original: 7 hands-on guides covering real CLI commands (Claude Code + Codex, mapped back to the concepts — &lt;code&gt;/compact&lt;/code&gt; is just the Compaction button), AI-friendly folder structure (&lt;code&gt;AGENTS.md&lt;/code&gt;, &lt;code&gt;.claude/&lt;/code&gt;, who reads what and when), bad-prompt → good-prompt rewrites, the popular community skills (brainstorming, grill-me, TDD, handoff…), 17 extra terms (RAG, embeddings, temperature, hooks…), token economics, and safety (including prompt injection, explained with a postman analogy).&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;📜 Part 3 — The story.&lt;/strong&gt; Instead of a tutorial you have to install things for, it's a 6-chapter read-along: রাফি, a 9th grader, builds his first portfolio site with an AI agent. He makes every classic mistake — vague prompts, trusting a hallucinated library, dragging a session deep into the dumb zone — and recovers using the tools from Part 2. Margin notes call back every concept at the exact moment it bites. The reader installs &lt;em&gt;nothing&lt;/em&gt;.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;🗓️ The 30-day path.&lt;/strong&gt; Reading isn't owning. So the book ends with 30 daily missions (5–15 min each). The first two weeks need zero setup — just any free AI chat and open eyes ("ask the same question twice — did the answers match?"). Weeks 3–4 go hands-on, but every mission has a read-only alternative.&lt;/p&gt;

&lt;p&gt;Plus: a myth-busting FAQ ("AI will eat my job", "you need a CS degree"), a 79-term alphabetical index, copy-paste &lt;code&gt;templates/&lt;/code&gt; (a starter &lt;code&gt;AGENTS.md&lt;/code&gt;, a working &lt;code&gt;/handoff&lt;/code&gt; command, a &lt;code&gt;SKILL.md&lt;/code&gt; example), and 26 terminal GIFs — all reproducible from committed VHS scripts.&lt;/p&gt;

&lt;h2&gt;
  
  
  Things I learned building it
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Analogies are load-bearing, not decoration.&lt;/strong&gt; "Prefix cache" never landed until it became "the waiter remembers your usual order."&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;A story teaches sequencing in a way lists can't.&lt;/strong&gt; You can document &lt;code&gt;/compact&lt;/code&gt; vs &lt;code&gt;/clear&lt;/code&gt; vs handoff perfectly and people still won't know &lt;em&gt;when&lt;/em&gt;. Watching রাফি lose his color-scheme decision to a long session — that sticks.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Bengali text breaks your tooling in fun ways.&lt;/strong&gt; &lt;code&gt;grep&lt;/code&gt; couldn't match half my content (decomposed Unicode), GitHub slugs for emoji-prefixed headings start with a stray hyphen, and nested code fences need 4-backtick outers. The repo's link checker is a tiny Python script for a reason.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Beginners deserve the real thing.&lt;/strong&gt; Simplified ≠ dumbed down. The dictionary keeps the exact English terms visible everywhere, so readers can walk into any English doc afterward and feel at home.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  It's a community thing
&lt;/h2&gt;

&lt;p&gt;Everything is CC BY 4.0. There are Bangla issue forms for "I want a new word" and "I found a mistake" — contributions of any size welcome, from a typo to a better analogy to a whole new section.&lt;/p&gt;

&lt;p&gt;And if you speak another language: steal this structure. The vocabulary wall exists in every non-English-speaking dev community, and the fix is surprisingly fun to build. 💙&lt;/p&gt;

&lt;p&gt;🔗 &lt;strong&gt;&lt;a href="https://github.com/dhrupo/dictionary-of-ai-coding-bangla" rel="noopener noreferrer"&gt;https://github.com/dhrupo/dictionary-of-ai-coding-bangla&lt;/a&gt;&lt;/strong&gt;&lt;/p&gt;

</description>
      <category>ai</category>
      <category>opensource</category>
      <category>beginners</category>
      <category>learning</category>
    </item>
    <item>
      <title>Your AI Coding Agent Wastes 80% of Its Context. Fixed That with Graph Theory.</title>
      <dc:creator>Dhrupo Nil</dc:creator>
      <pubDate>Mon, 25 May 2026 13:45:00 +0000</pubDate>
      <link>https://dev.to/dhrupo/your-ai-coding-agent-wastes-80-of-its-context-fixed-that-with-graph-theory-1mfa</link>
      <guid>https://dev.to/dhrupo/your-ai-coding-agent-wastes-80-of-its-context-fixed-that-with-graph-theory-1mfa</guid>
      <description>&lt;h2&gt;
  
  
  The problem nobody admits
&lt;/h2&gt;

&lt;p&gt;When you give Claude Code, Cursor, or Codex a task like &lt;em&gt;"fix the login validation bug"&lt;/em&gt;, here's what they usually do:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Run &lt;code&gt;grep -l login src/&lt;/code&gt; → 17 files&lt;/li&gt;
&lt;li&gt;Read all 17 files top-to-bottom (because context is "free")&lt;/li&gt;
&lt;li&gt;Spend 80% of the model's context window on irrelevant imports, type aliases, and helper functions the bug doesn't touch&lt;/li&gt;
&lt;li&gt;Generate a fix using whatever 20% of attention is left&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;This works. Sort of. But it's wasteful — and on big codebases, it's wrong: the agent runs out of context before it sees the actual buggy function.&lt;/p&gt;

&lt;p&gt;The instinct is to throw a bigger model at it. Bigger context window, fancier RAG, vector embeddings. All of which trade real cost for diminishing returns.&lt;/p&gt;

&lt;p&gt;There's a better answer that's been sitting in classical CS the whole time: &lt;strong&gt;treat the repo as a graph&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto/https%3A%2F%2Fraw.githubusercontent.com%2Fdhrupo%2Fmincut-context%2Fmain%2Fdocs%2Fdemo.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto/https%3A%2F%2Fraw.githubusercontent.com%2Fdhrupo%2Fmincut-context%2Fmain%2Fdocs%2Fdemo.gif" alt="demo" width="799" height="436"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  The idea, in one paragraph
&lt;/h2&gt;

&lt;p&gt;Your codebase already &lt;em&gt;is&lt;/em&gt; a graph. Functions call functions. Modules import modules. Classes extend classes. Pick a node (the symbol your task is about), and the structurally-closest neighborhood is almost certainly what an agent needs to see.&lt;/p&gt;

&lt;p&gt;So I built &lt;strong&gt;&lt;code&gt;mincut-context&lt;/code&gt;&lt;/strong&gt; — an npm package that:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Parses your repo into a &lt;strong&gt;symbol graph&lt;/strong&gt; (tree-sitter, supports TS/JS/Vue/Python/PHP)&lt;/li&gt;
&lt;li&gt;Derives &lt;strong&gt;seed nodes&lt;/strong&gt; from your task description (keyword IDF on symbol names + file paths)&lt;/li&gt;
&lt;li&gt;Runs &lt;strong&gt;personalized PageRank&lt;/strong&gt; with the seeds as the restart vector&lt;/li&gt;
&lt;li&gt;Picks the &lt;strong&gt;minimum-cut subgraph&lt;/strong&gt; that fits a token budget you choose&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;The output: a list of files + line ranges that an agent should look at. Nothing more, nothing less.&lt;/p&gt;

&lt;h2&gt;
  
  
  Show me the numbers
&lt;/h2&gt;

&lt;p&gt;I built an evaluation suite into the repo itself. 28 hand-labeled tasks across 3 real codebases at a 4,000-token budget:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;strategy&lt;/th&gt;
&lt;th&gt;precision&lt;/th&gt;
&lt;th&gt;recall&lt;/th&gt;
&lt;th&gt;F1&lt;/th&gt;
&lt;th&gt;token-efficiency&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;mincut&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;0.27&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;0.83&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;0.39&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;0.270&lt;/strong&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;mincut + &lt;code&gt;--embed&lt;/code&gt; (semantic)&lt;/td&gt;
&lt;td&gt;0.27&lt;/td&gt;
&lt;td&gt;0.83&lt;/td&gt;
&lt;td&gt;0.39&lt;/td&gt;
&lt;td&gt;0.270&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;grep keyword baseline&lt;/td&gt;
&lt;td&gt;0.11&lt;/td&gt;
&lt;td&gt;0.42&lt;/td&gt;
&lt;td&gt;0.16&lt;/td&gt;
&lt;td&gt;0.105&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;random selection (control)&lt;/td&gt;
&lt;td&gt;0.01&lt;/td&gt;
&lt;td&gt;0.04&lt;/td&gt;
&lt;td&gt;0.01&lt;/td&gt;
&lt;td&gt;0.009&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;Per-repo breakdown:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;repo&lt;/th&gt;
&lt;th&gt;tasks&lt;/th&gt;
&lt;th&gt;mincut recall&lt;/th&gt;
&lt;th&gt;grep recall&lt;/th&gt;
&lt;th&gt;mincut F1&lt;/th&gt;
&lt;th&gt;grep F1&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;mincut-context (self)&lt;/td&gt;
&lt;td&gt;12&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;0.97&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;0.56&lt;/td&gt;
&lt;td&gt;0.44&lt;/td&gt;
&lt;td&gt;0.30&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;FluentForm (PHP+Vue+JS)&lt;/td&gt;
&lt;td&gt;8&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;0.88&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;0.13&lt;/td&gt;
&lt;td&gt;0.43&lt;/td&gt;
&lt;td&gt;0.04&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Fluent Player (TS/JSX)&lt;/td&gt;
&lt;td&gt;8&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;0.63&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;0.56&lt;/td&gt;
&lt;td&gt;0.31&lt;/td&gt;
&lt;td&gt;0.13&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;&lt;strong&gt;mincut catches ~2× more of the correct files than grep, at ~2.5× better token efficiency.&lt;/strong&gt; Reproducible with &lt;code&gt;npm run eval&lt;/code&gt;. Add your own labeled tasks under &lt;code&gt;eval/fixtures/&lt;/code&gt; to score against your own codebase.&lt;/p&gt;

&lt;h2&gt;
  
  
  The math, briefly
&lt;/h2&gt;

&lt;p&gt;Given a symbol graph $G = (V, E, w)$ where:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;$V$ are code units (functions, classes, methods)&lt;/li&gt;
&lt;li&gt;$E$ are dependency edges (imports, calls, references)&lt;/li&gt;
&lt;li&gt;$w(v)$ is the token cost of including symbol $v$&lt;/li&gt;
&lt;li&gt;$B$ is your token budget&lt;/li&gt;
&lt;li&gt;$S \subseteq V$ are seed nodes derived from the task&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Find $T \supseteq S$ with $\sum_{v \in T} w(v) \le B$ minimizing the &lt;strong&gt;boundary cut cost&lt;/strong&gt;:&lt;/p&gt;

&lt;p&gt;$$\text{cut}(T, V \setminus T) = \sum_{e \in E, \text{ crossing}} w(e)$$&lt;/p&gt;

&lt;p&gt;In plain English: pick a connected, low-token region that has few "loose ends" pointing outside it. The inside of the cut is what the agent needs; the outside is safely ignorable.&lt;/p&gt;

&lt;p&gt;The objective is submodular, so a greedy algorithm gives a $(1 - 1/e) \approx 0.63$ approximation guarantee. The full pseudocode is in the README; the implementation is ~200 lines in &lt;a href="https://github.com/dhrupo/mincut-context/blob/main/src/core/select.ts" rel="noopener noreferrer"&gt;&lt;code&gt;src/core/select.ts&lt;/code&gt;&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Three ways to use it
&lt;/h2&gt;

&lt;h3&gt;
  
  
  1. As an MCP server — recommended for agents
&lt;/h3&gt;

&lt;p&gt;Drop this block into your Claude Code / Codex / Cursor settings:&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;"mcpServers"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"mincut-context"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"command"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"npx"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"args"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;"-y"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"mincut-context"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"mcp"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Your agent now has six new tools: &lt;code&gt;pack_context&lt;/code&gt;, &lt;code&gt;expand_node&lt;/code&gt;, &lt;code&gt;find_callers&lt;/code&gt;, &lt;code&gt;find_callees&lt;/code&gt;, &lt;code&gt;search_symbols&lt;/code&gt;, &lt;code&gt;explain_selection&lt;/code&gt;. They operate on the cached graph from the most recent &lt;code&gt;pack_context&lt;/code&gt; call — effectively free traversal after the first pack.&lt;/p&gt;

&lt;h3&gt;
  
  
  2. As a CLI
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;npm &lt;span class="nb"&gt;install&lt;/span&gt; &lt;span class="nt"&gt;-g&lt;/span&gt; mincut-context

mcx pack &lt;span class="s2"&gt;"fix the login validation bug"&lt;/span&gt; &lt;span class="nt"&gt;--budget&lt;/span&gt; 4000             &lt;span class="c"&gt;# plain output&lt;/span&gt;
mcx pack &lt;span class="s2"&gt;"..."&lt;/span&gt; &lt;span class="nt"&gt;--format&lt;/span&gt; tree                                       &lt;span class="c"&gt;# directory-grouped&lt;/span&gt;
mcx pack &lt;span class="s2"&gt;"..."&lt;/span&gt; &lt;span class="nt"&gt;--format&lt;/span&gt; json | jq                                  &lt;span class="c"&gt;# pipe to anything&lt;/span&gt;
mcx pack &lt;span class="s2"&gt;"..."&lt;/span&gt; &lt;span class="nt"&gt;--interactive&lt;/span&gt;                                       &lt;span class="c"&gt;# Ink TUI: vim keys + preview&lt;/span&gt;
mcx pack &lt;span class="s2"&gt;"..."&lt;/span&gt; &lt;span class="nt"&gt;--embed&lt;/span&gt;                                             &lt;span class="c"&gt;# semantic seeding&lt;/span&gt;
mcx pack &lt;span class="s2"&gt;"..."&lt;/span&gt; &lt;span class="nt"&gt;--cache&lt;/span&gt;                                             &lt;span class="c"&gt;# 5× warm-run speedup&lt;/span&gt;
mcx watch &lt;span class="s2"&gt;"..."&lt;/span&gt; &lt;span class="nt"&gt;--debounce&lt;/span&gt; 300                                     &lt;span class="c"&gt;# re-pack on file change&lt;/span&gt;
mcx doctor                                                         &lt;span class="c"&gt;# environment self-check&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;code&gt;mcx doctor&lt;/code&gt; is my favorite — it tells you in 6 lines what's installed and what isn't:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fzw1c7rjfwuh340i0s9dx.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fzw1c7rjfwuh340i0s9dx.gif" alt="doctor" width="799" height="276"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  3. As a library
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;pack&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;mincut-context&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;result&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;pack&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
  &lt;span class="na"&gt;task&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;fix the login validation bug&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;repo&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;process&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;cwd&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt;
  &lt;span class="na"&gt;budget&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;4000&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;cache&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;parallel&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;4&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;chunk&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&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="na"&gt;maxTokens&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;400&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;

&lt;span class="k"&gt;for &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;f&lt;/span&gt; &lt;span class="k"&gt;of&lt;/span&gt; &lt;span class="nx"&gt;result&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;files&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;f&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;path&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;f&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;score&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;3&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="nx"&gt;f&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;tokens&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;·&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;f&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;reasons&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;]);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="c1"&gt;// → src/auth/login.ts        0.541  612 · seed — matched directly by task&lt;/span&gt;
&lt;span class="c1"&gt;// → src/auth/session.ts      0.408  483 · attached (60%)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  What I learned by building this
&lt;/h2&gt;

&lt;h3&gt;
  
  
  1. Embeddings are oversold for this problem
&lt;/h3&gt;

&lt;p&gt;Adding semantic embeddings (&lt;code&gt;--embed&lt;/code&gt; flag, via &lt;code&gt;@xenova/transformers&lt;/code&gt; running locally) &lt;strong&gt;did not improve recall on any of my three eval task sets.&lt;/strong&gt; Why? Because the labels were named honestly. When you label "stripe payment processor" → &lt;code&gt;StripeProcessor.php&lt;/code&gt;, the keyword match catches it without help. Embeddings only earn their keep when your task vocabulary diverges from the code's — "centrality and ranking" → &lt;code&gt;PageRank&lt;/code&gt;, that kind of gap.&lt;/p&gt;

&lt;p&gt;I left &lt;code&gt;--embed&lt;/code&gt; in because it doesn't hurt, and there are real users whose mental model doesn't match the code. But the marketing-friendly "AI-powered" framing for this stuff is mostly noise.&lt;/p&gt;

&lt;h3&gt;
  
  
  2. Greedy beats CELF for this objective
&lt;/h3&gt;

&lt;p&gt;I implemented CELF (Cost-Effective Lazy Forward, Leskovec 2007) hoping for a free speedup over the naive greedy. It diverged — not just slower (8× slower on FluentForm) but &lt;strong&gt;wrong&lt;/strong&gt;: it produced smaller, structurally weaker selections.&lt;/p&gt;

&lt;p&gt;Why: our "no isolated nodes" acceptance rule (a candidate must have at least one edge into the current selection) breaks CELF's submodular-monotone assumption. A candidate's eligibility flips discontinuously when a node with an edge to it joins T. The lazy cache becomes unreliable.&lt;/p&gt;

&lt;p&gt;I wrote the dead end up in &lt;a href="https://github.com/dhrupo/mincut-context/blob/main/eval/ALGORITHM-RESEARCH.md" rel="noopener noreferrer"&gt;&lt;code&gt;eval/ALGORITHM-RESEARCH.md&lt;/code&gt;&lt;/a&gt; so nobody re-treads it. &lt;strong&gt;Honest negative results are worth shipping.&lt;/strong&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  3. Sub-symbol chunking matters more than I expected
&lt;/h3&gt;

&lt;p&gt;Big legacy codebases have huge functions. A 500-line function is one symbol in the graph, and if it gets selected, the whole thing eats your budget. So &lt;code&gt;--chunk&lt;/code&gt; splits big functions at statement boundaries — each chunk becomes its own sub-symbol, individually selectable.&lt;/p&gt;

&lt;p&gt;On FluentForm: indexing without chunking → 4,333 symbols. With &lt;code&gt;--chunk&lt;/code&gt; → 4,878 symbols (+545 chunks). Same budget, much finer-grained selection. The greedy can pick &lt;em&gt;just the relevant &lt;code&gt;if/for/try&lt;/code&gt; block&lt;/em&gt; instead of all-or-nothing.&lt;/p&gt;

&lt;h3&gt;
  
  
  4. Test coverage of 88% isn't the whole story
&lt;/h3&gt;

&lt;p&gt;The CI gates on 85% statements / 80% branches / 90% functions / 85% lines. But the genuinely-untestable files — worker scripts, lazy-loaded LSP clients — are excluded from the calc. Honest reporting means saying &lt;em&gt;what&lt;/em&gt; is tested, not just the headline number.&lt;/p&gt;

&lt;h2&gt;
  
  
  The honest tradeoffs
&lt;/h2&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Honest tradeoff&lt;/th&gt;
&lt;th&gt;What we do&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;True optimal min-cut is NP-hard&lt;/td&gt;
&lt;td&gt;Greedy submodular — &lt;code&gt;(1−1/e)&lt;/code&gt; bound&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Tree-sitter symbols are syntactic, not type-aware&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;--lsp&lt;/code&gt; refines TS/JS via typescript-language-server&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Embedding model adds ~22 MB on first run&lt;/td&gt;
&lt;td&gt;Opt-in behind &lt;code&gt;--embed&lt;/code&gt; flag&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;LSP startup is slow (~1–5s)&lt;/td&gt;
&lt;td&gt;Opt-in; cached after init&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Cold start parses whole repo&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;--cache&lt;/code&gt; (5× speedup) + &lt;code&gt;--parallel n&lt;/code&gt; (2.7× speedup)&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;h2&gt;
  
  
  What I'd build next if you asked
&lt;/h2&gt;

&lt;p&gt;The roadmap that's &lt;em&gt;not&lt;/em&gt; checked off yet:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Pyright / Intelephense LSP adapters&lt;/strong&gt; — type-aware calls for Python and PHP (~1–2 days each on the existing LSP infrastructure)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Svelte / Rust / Go parsers&lt;/strong&gt; — one file each on the parser template&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Incremental neighborhood caching in the greedy&lt;/strong&gt; — keep &lt;code&gt;attach(v, T)&lt;/code&gt; cached and update only when a node with an edge to v is added. Expected 3–5× speedup on graphs with bounded degree.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Each is bounded effort and additive. The core is done.&lt;/p&gt;

&lt;h2&gt;
  
  
  Stop building, start using
&lt;/h2&gt;

&lt;p&gt;The hardest lesson: &lt;strong&gt;a tool's value comes from someone actually using it on real work, not from feature count.&lt;/strong&gt; mincut-context is at v1.7.0 — 261 tests, 88.6% coverage, CI green on Ubuntu + macOS × Node 18/20/22. There's no honest "but it's not ready" excuse left.&lt;/p&gt;

&lt;p&gt;If you've watched an AI agent burn 80% of its 200k-token context on imports it doesn't care about, install it now and tell me what breaks:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;npm &lt;span class="nb"&gt;install&lt;/span&gt; &lt;span class="nt"&gt;-g&lt;/span&gt; mincut-context
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;🔗 &lt;strong&gt;GitHub:&lt;/strong&gt; &lt;a href="https://github.com/dhrupo/mincut-context" rel="noopener noreferrer"&gt;github.com/dhrupo/mincut-context&lt;/a&gt;&lt;br&gt;
📦 &lt;strong&gt;npm:&lt;/strong&gt; &lt;a href="https://www.npmjs.com/package/mincut-context" rel="noopener noreferrer"&gt;npmjs.com/package/mincut-context&lt;/a&gt;&lt;br&gt;
📊 &lt;strong&gt;Reproducible benchmarks:&lt;/strong&gt; &lt;a href="https://github.com/dhrupo/mincut-context/blob/main/eval/CROSS-REPO-RESULTS.md" rel="noopener noreferrer"&gt;&lt;code&gt;eval/CROSS-REPO-RESULTS.md&lt;/code&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;I'd love feedback — especially "your numbers don't replicate on my codebase" feedback. That's literally what the eval suite is for.&lt;/p&gt;




&lt;p&gt;&lt;em&gt;If you got value from this, ⭐ the repo or drop a comment about a tooling problem you're solving. mincut-context is open-source MIT; the eval suite welcomes new fixtures.&lt;/em&gt;&lt;/p&gt;

</description>
      <category>ai</category>
      <category>claude</category>
      <category>llm</category>
      <category>typescript</category>
    </item>
  </channel>
</rss>
