<?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: Quinn Wang</title>
    <description>The latest articles on DEV Community by Quinn Wang (@codejs_1959).</description>
    <link>https://dev.to/codejs_1959</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%2F4062959%2F04dfed64-921d-4a67-8963-f4e329c32a72.png</url>
      <title>DEV Community: Quinn Wang</title>
      <link>https://dev.to/codejs_1959</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/codejs_1959"/>
    <language>en</language>
    <item>
      <title>Don't Let the Agent Guess Your Workspace</title>
      <dc:creator>Quinn Wang</dc:creator>
      <pubDate>Sat, 05 Sep 2026 20:09:33 +0000</pubDate>
      <link>https://dev.to/codejs_1959/dont-let-the-agent-guess-your-workspace-2853</link>
      <guid>https://dev.to/codejs_1959/dont-let-the-agent-guess-your-workspace-2853</guid>
      <description>&lt;p&gt;The first agent command is where a free coding session usually dies, and it rarely dies from a weak model. It dies because the agent invents a working directory, a start script, and a port, then treats that fiction as fact. I learned to treat that first turn as a permissions problem rather than another exercise in clever prompting. If the workspace has not been proven on disk, why would I let any tool start writing files?&lt;/p&gt;

&lt;p&gt;Think of the agent like a courier who never checks the street address printed on the box. You asked for a small route change, and it sprints toward a house that only looks similar from the road. The damage is not theatrical, just a wrong folder, a wrong lockfile, and a server process you never started. Have you watched a coding tool fix a bug by editing a path that exists only inside its context window?&lt;/p&gt;

&lt;p&gt;I keep seeing the same shape in developer tools that now call themselves agentic without changing the consent model. The product window opens, the chat box looks harmless, and the model asks almost nothing about your actual machine. Then it proposes npm run dev, invents a src/app/page.tsx file, and talks as if port 3000 already belongs to you. That is not architecture guidance in any serious sense; it is a confident shrug wearing a terminal font.&lt;/p&gt;

&lt;p&gt;The teardown I care about is not the onboarding carousel or the animated first-run checklist in the sidebar. It is the short window after you paste a real task and the agent reaches for a shell command. Silent defaults beat your actual repository in that window, and they do it with a tone that sounds helpful. The one fix that mattered for me was boring on purpose: prove the workspace locally, then allow the first command.&lt;/p&gt;

&lt;p&gt;Here is the reproducible gate I run from the repository root before any agent is allowed to touch the tree. It refuses to print secret values, and it refuses to start a long-running product process on your behalf. It only writes facts that a later prompt is allowed to cite, and it exits nonzero when the tree is unproven. The snippets below are a lab workflow, not a production benchmark I ran against a public dataset.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;#!/usr/bin/env bash&lt;/span&gt;
&lt;span class="c"&gt;# workspace-gate.sh — lab workflow: fail closed, write facts, never dump env values&lt;/span&gt;
&lt;span class="nb"&gt;set&lt;/span&gt; &lt;span class="nt"&gt;-euo&lt;/span&gt; pipefail

&lt;span class="nv"&gt;ROOT&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="si"&gt;$(&lt;/span&gt;git rev-parse &lt;span class="nt"&gt;--show-toplevel&lt;/span&gt; 2&amp;gt;/dev/null &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="nb"&gt;true&lt;/span&gt;&lt;span class="si"&gt;)&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;
&lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="o"&gt;[[&lt;/span&gt; &lt;span class="nt"&gt;-z&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;ROOT&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="o"&gt;]]&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="k"&gt;then
  &lt;/span&gt;&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"GATE_FAIL: not inside a git work tree"&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&amp;amp;2
  &lt;span class="nb"&gt;exit &lt;/span&gt;2
&lt;span class="k"&gt;fi

&lt;/span&gt;&lt;span class="nb"&gt;cd&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$ROOT&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;
&lt;span class="nv"&gt;FACT_FILE&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;ROOT&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;/.workspace-facts.json"&lt;/span&gt;
&lt;span class="nv"&gt;PORT&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;PROOF_PORT&lt;/span&gt;&lt;span class="k"&gt;:-&lt;/span&gt;&lt;span class="nv"&gt;3000&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;

&lt;span class="nv"&gt;pkg&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"absent"&lt;/span&gt;
&lt;span class="o"&gt;[[&lt;/span&gt; &lt;span class="nt"&gt;-f&lt;/span&gt; package.json &lt;span class="o"&gt;]]&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="nv"&gt;pkg&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"present"&lt;/span&gt;

&lt;span class="nv"&gt;lock&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"absent"&lt;/span&gt;
&lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="o"&gt;[[&lt;/span&gt; &lt;span class="nt"&gt;-f&lt;/span&gt; package-lock.json &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="nt"&gt;-f&lt;/span&gt; pnpm-lock.yaml &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="nt"&gt;-f&lt;/span&gt; yarn.lock &lt;span class="o"&gt;]]&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="k"&gt;then
  &lt;/span&gt;&lt;span class="nv"&gt;lock&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"present"&lt;/span&gt;
&lt;span class="k"&gt;fi

&lt;/span&gt;&lt;span class="nv"&gt;env_example&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"absent"&lt;/span&gt;
&lt;span class="o"&gt;[[&lt;/span&gt; &lt;span class="nt"&gt;-f&lt;/span&gt; .env.example &lt;span class="o"&gt;]]&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="nv"&gt;env_example&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"present"&lt;/span&gt;

&lt;span class="nv"&gt;env_file&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"absent"&lt;/span&gt;
&lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="o"&gt;[[&lt;/span&gt; &lt;span class="nt"&gt;-f&lt;/span&gt; .env &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="nt"&gt;-f&lt;/span&gt; .env.local &lt;span class="o"&gt;]]&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="k"&gt;then
  &lt;/span&gt;&lt;span class="nv"&gt;env_file&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"present"&lt;/span&gt;
&lt;span class="k"&gt;fi

if &lt;/span&gt;&lt;span class="nb"&gt;command&lt;/span&gt; &lt;span class="nt"&gt;-v&lt;/span&gt; lsof &lt;span class="o"&gt;&amp;gt;&lt;/span&gt;/dev/null 2&amp;gt;&amp;amp;1&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="k"&gt;then
  if &lt;/span&gt;lsof &lt;span class="nt"&gt;-iTCP&lt;/span&gt;:&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$PORT&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="nt"&gt;-sTCP&lt;/span&gt;:LISTEN &lt;span class="o"&gt;&amp;gt;&lt;/span&gt;/dev/null 2&amp;gt;&amp;amp;1&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="k"&gt;then
    &lt;/span&gt;&lt;span class="nv"&gt;port_state&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"listening"&lt;/span&gt;
  &lt;span class="k"&gt;else
    &lt;/span&gt;&lt;span class="nv"&gt;port_state&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"closed"&lt;/span&gt;
  &lt;span class="k"&gt;fi
else
  &lt;/span&gt;&lt;span class="nv"&gt;port_state&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"unknown"&lt;/span&gt;
&lt;span class="k"&gt;fi

&lt;/span&gt;&lt;span class="nv"&gt;branch&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="si"&gt;$(&lt;/span&gt;git rev-parse &lt;span class="nt"&gt;--abbrev-ref&lt;/span&gt; HEAD&lt;span class="si"&gt;)&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;
&lt;span class="nv"&gt;dirty&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="si"&gt;$(&lt;/span&gt;git status &lt;span class="nt"&gt;--porcelain&lt;/span&gt; | &lt;span class="nb"&gt;wc&lt;/span&gt; &lt;span class="nt"&gt;-l&lt;/span&gt; | &lt;span class="nb"&gt;tr&lt;/span&gt; &lt;span class="nt"&gt;-d&lt;/span&gt; &lt;span class="s1"&gt;' '&lt;/span&gt;&lt;span class="si"&gt;)&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;

&lt;span class="nb"&gt;export &lt;/span&gt;&lt;span class="nv"&gt;GATE_ROOT&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$ROOT&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;
&lt;span class="nb"&gt;export &lt;/span&gt;&lt;span class="nv"&gt;GATE_FACT_FILE&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$FACT_FILE&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;
&lt;span class="nb"&gt;export &lt;/span&gt;&lt;span class="nv"&gt;GATE_PORT&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$PORT&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;
&lt;span class="nb"&gt;export &lt;/span&gt;&lt;span class="nv"&gt;GATE_PKG&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$pkg&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;
&lt;span class="nb"&gt;export &lt;/span&gt;&lt;span class="nv"&gt;GATE_LOCK&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$lock&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;
&lt;span class="nb"&gt;export &lt;/span&gt;&lt;span class="nv"&gt;GATE_ENV_EXAMPLE&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$env_example&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;
&lt;span class="nb"&gt;export &lt;/span&gt;&lt;span class="nv"&gt;GATE_ENV_FILE&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$env_file&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;
&lt;span class="nb"&gt;export &lt;/span&gt;&lt;span class="nv"&gt;GATE_PORT_STATE&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$port_state&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;
&lt;span class="nb"&gt;export &lt;/span&gt;&lt;span class="nv"&gt;GATE_BRANCH&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$branch&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;
&lt;span class="nb"&gt;export &lt;/span&gt;&lt;span class="nv"&gt;GATE_DIRTY&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$dirty&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;

python3 - &lt;span class="o"&gt;&amp;lt;&amp;lt;&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="no"&gt;PY&lt;/span&gt;&lt;span class="sh"&gt;'
import json, os, datetime
facts = {
  "generated_at": datetime.datetime.utcnow().isoformat() + "Z",
  "root": os.environ["GATE_ROOT"],
  "branch": os.environ["GATE_BRANCH"],
  "dirty_files": int(os.environ["GATE_DIRTY"]),
  "package_json": os.environ["GATE_PKG"],
  "lockfile": os.environ["GATE_LOCK"],
  "env_example": os.environ["GATE_ENV_EXAMPLE"],
  "env_file_present": os.environ["GATE_ENV_FILE"],
  "port": int(os.environ["GATE_PORT"]),
  "port_state": os.environ["GATE_PORT_STATE"],
  "allowed_commands": ["git status", "ls", "sed -n '1,40p' package.json"]
}
path = os.environ["GATE_FACT_FILE"]
with open(path, "w", encoding="utf-8") as fh:
    json.dump(facts, fh, indent=2)
    fh.write("&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="sh"&gt;")
print("GATE_OK wrote", path)
&lt;/span&gt;&lt;span class="no"&gt;PY
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;I run those two commands in order, and I refuse to continue when the exit code is anything other than zero. That sounds fussy until you remember how often an agent changes directory into a folder it just hallucinated. chmod +x is not ceremony here; it is the line that makes the gate a real command instead of a gist you never run.&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; +x workspace-gate.sh
./workspace-gate.sh &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="nb"&gt;exit&lt;/span&gt; &lt;span class="nv"&gt;$?&lt;/span&gt;
&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;".workspace-facts.json"&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&amp;gt;&lt;/span&gt; .gitignore
&lt;span class="nb"&gt;cat&lt;/span&gt; .workspace-facts.json
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The JSON file is the only map I paste into the first prompt, because everything else is an invitation to guess. Notice what is missing on purpose: tokens, API keys, file bodies, and any claim that a server is ready. If package_json is absent, why would I let the agent invent a framework tree that your git history cannot explain? If the recorded port is closed, why would I let it debug a five hundred from a process that is not listening?&lt;/p&gt;

&lt;p&gt;The prompt I actually send stays short, because the gate already did the only honest work in the session. I tell the model it may not create paths outside root, and it may not assume a framework from vibes alone. I also tell it not to start a server unless I say so, which is slower than letting it rip by exactly one command. That extra command is the whole point of the teardown, not a tax I pay to feel disciplined.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;You are working in a proven workspace. Use only .workspace-facts.json.
Do not invent directories, scripts, ports, or frameworks.
If package_json is absent, ask before scaffolding.
If port_state is not listening, do not diagnose HTTP errors.
First reply with: (1) the root you will use, (2) the first command,
(3) what you still do not know. Wait for yes before running anything.
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;When developers talk about agentic coding this week, they often mean tools that assume the architecture on your behalf. That trend is useful as a warning, because a brownfield repo can put on a greenfield costume in a single turn. I would rather an agent admit ignorance than ship a plausible src directory that never belonged to the project. Cheap generation without a gate is how technical debt arrives before you have even made the first commit.&lt;/p&gt;

&lt;p&gt;I treat the free server as a disposable place to confirm an agent can follow the facts file, not as production proof. I do not need a leaderboard for that check; I need a second pair of eyes I can throw away later. Does that mean I trust a remote box with a live env file sitting beside the application code? The gate records env_file_present as a boolean and then stops, which is the entire security story I want in the facts file.&lt;/p&gt;

&lt;p&gt;A tiny Node check sits beside the shell script in a JavaScript repo, because lsof is not the only way a port can lie. This lab check tries to bind the port, and a failed bind means something is already listening on that socket. If the bind succeeds, I close the socket immediately and record closed, which is a proof, not a benchmark.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// prove-port.mjs — labeled lab check, not a benchmark&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="nx"&gt;net&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;node:net&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;port&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;Number&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="nx"&gt;env&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;PROOF_PORT&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="mi"&gt;3000&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;server&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;net&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;createServer&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="nx"&gt;server&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;unref&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

&lt;span class="nx"&gt;server&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;once&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;error&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;err&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;err&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="nx"&gt;err&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;code&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;EADDRINUSE&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;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;JSON&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;stringify&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="nx"&gt;port&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;port_state&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;listening&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt; &lt;span class="p"&gt;}));&lt;/span&gt;
    &lt;span class="nx"&gt;process&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;exit&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;0&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;error&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;GATE_FAIL: port proof error&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;err&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="nx"&gt;process&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;exit&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;

&lt;span class="nx"&gt;server&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;listen&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;port&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;127.0.0.1&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;server&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;close&lt;/span&gt;&lt;span class="p"&gt;(()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;JSON&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;stringify&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="nx"&gt;port&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;port_state&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;closed&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt; &lt;span class="p"&gt;}));&lt;/span&gt;
  &lt;span class="p"&gt;});&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;
&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;node prove-port.mjs
&lt;span class="nv"&gt;PROOF_PORT&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;5173 node prove-port.mjs
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Put the two outputs next to each other and you get a decision that does not need a meeting or a dashboard. If the facts file says closed and the agent still writes a repro from localhost three thousand, the agent failed the gate. If dirty_files is high and the agent wants to scaffold, I make it stop rather than launder a surprise tree into a dirty workdir. If a lockfile is present and it suggests a different package manager, that suggestion is another invented blank wearing confidence.&lt;/p&gt;

&lt;p&gt;Limitations are not a footnote I tack on for tone; they are the reason I still read the first agent reply with suspicion. This gate cannot see containers you did not expose, and it cannot tell a healthy listener from a zombie process on the same port. It will not stop a model from lying in prose after you paste the facts, because a JSON file is not a character witness. If you work in a monorepo with five apps, split facts per package, because one PROOF_PORT is a blunt instrument.&lt;/p&gt;

&lt;p&gt;Who should not use this workflow if they want to stay honest about the failure mode I am describing here? Skip it if you need a fully autonomous loop with no human yes sitting between the model and the shell. Skip it if you cannot run local scripts on the machine that holds the repo, because the gate is useless as a story. Skip it if you want an agent to invent a greenfield app in an empty folder, because this gate is for brownfield honesty.&lt;/p&gt;

&lt;p&gt;A free tier also will not replace code review, mutation testing, or a staging server that actually resembles production traffic. I still want models that can write tests, review diffs, and wander through a disposable server after the workspace has been proven. I just refuse to start that loop on a map the model drew from a training prior instead of from git. The one fix that mattered was not a better system prompt; it was a fail-closed file that made the first command earn its place.&lt;/p&gt;

&lt;p&gt;If you try the script on a throwaway repository, keep the facts file out of version control unless you sanitize the root path. Then watch the first agent turn with the same attention you would give a pull request from a stranger. Did it ask a question about the facts you measured, or did it start guessing the neighborhood again?&lt;/p&gt;

&lt;p&gt;Disclosure: This article was prepared as part of MonkeyCode's product outreach.&lt;/p&gt;

&lt;p&gt;MonkeyCode provides free models that can run this workflow.&lt;/p&gt;

</description>
      <category>ai</category>
      <category>javascript</category>
      <category>webdev</category>
      <category>productivity</category>
    </item>
    <item>
      <title>Refuse the Session Until the Repo Answers Back</title>
      <dc:creator>Quinn Wang</dc:creator>
      <pubDate>Fri, 04 Sep 2026 17:53:56 +0000</pubDate>
      <link>https://dev.to/codejs_1959/refuse-the-session-until-the-repo-answers-back-2b2c</link>
      <guid>https://dev.to/codejs_1959/refuse-the-session-until-the-repo-answers-back-2b2c</guid>
      <description>&lt;p&gt;The first fifteen minutes of a free AI coding session usually fail on invented context, not models. Your agent does not crash; it politely guesses the package manager, the test command, and the bind address. Those guesses feel helpful until you spend the rest of the hour undoing a repository that never existed. I now refuse to start a session until a tiny contract file and a local gate both pass.&lt;/p&gt;

&lt;p&gt;I still think that warning sounds dramatic for a missing lockfile and a guessed runner. Think of a taxi that leaves before you confirm the street, then bills you for scenery. Free models make the ride cheap, and a free server makes the wrong street look like real progress. The developer-experience wound is not latency; it is confident motion inside a directory nobody verified.&lt;/p&gt;

&lt;p&gt;I keep watching the same opening act whenever a fresh coding agent meets a brownfield folder. The tool greets me, scans a handful of files, and starts scaffolding as if this repo were a tutorial. npm appears where I already standardized on pnpm, and Jest appears where I run node:test today. Fifteen minutes later I own two lockfiles, a health check on the wrong port, and a mystery commit.&lt;/p&gt;

&lt;p&gt;The one fix that actually mattered was small enough to feel almost insulting at first. I stopped asking the agent to be careful and started requiring a committed session contract plus a gate. If the contract is missing, or the working directory is not the named git root, nothing starts. The agent can still write code after that checkpoint; it just cannot invent the room around it.&lt;/p&gt;

&lt;p&gt;Here is the contract I drop at the repo root under the name &lt;code&gt;session.contract.json&lt;/code&gt; for every serious branch. Treat the file as a proposed artifact you can copy, not as a benchmark from some private bake-off. It is deliberately boring, because boredom is harder for an agent to “improve.”&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;"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;"billing-api"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"git_root_basename"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"billing-api"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"package_manager"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"pnpm"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"lockfile"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"pnpm-lock.yaml"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"test_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;"pnpm test -- --run"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"typecheck_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;"pnpm exec tsc --noEmit"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"server"&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;"bind"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"127.0.0.1"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"port"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;8787&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"health_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;"/healthz"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"start_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;"pnpm dev"&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;"forbidden_inferences"&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="s2"&gt;"create docker-compose.yml"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="s2"&gt;"add a second package manager"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="s2"&gt;"bind 0.0.0.0"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="s2"&gt;"rewrite the test runner"&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;Why JSON instead of a chatty README that an agent can keep writing like a sequel? A README is a novel, and most agents love novels they are invited to continue without asking. A contract is a door lock you can hash, diff, and fail closed before the first generated patch. I still write human notes elsewhere; I just refuse to let prose be the only source of truth.&lt;/p&gt;

&lt;p&gt;The gate lives in &lt;code&gt;scripts/ai-session-gate.sh&lt;/code&gt; and I run it locally before any prompt leaves the editor. Consider the script proposed and copy-pasteable, because I am not selling a measured pass rate tonight. It should fail closed on a missing git root, a missing contract, or a folder whose basename does not match.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;#!/usr/bin/env bash&lt;/span&gt;
&lt;span class="nb"&gt;set&lt;/span&gt; &lt;span class="nt"&gt;-euo&lt;/span&gt; pipefail

&lt;span class="nv"&gt;CONTRACT&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;1&lt;/span&gt;&lt;span class="k"&gt;:-&lt;/span&gt;&lt;span class="nv"&gt;session&lt;/span&gt;&lt;span class="p"&gt;.contract.json&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;
&lt;span class="nv"&gt;ROOT&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="si"&gt;$(&lt;/span&gt;git rev-parse &lt;span class="nt"&gt;--show-toplevel&lt;/span&gt; 2&amp;gt;/dev/null &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="nb"&gt;true&lt;/span&gt;&lt;span class="si"&gt;)&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;

&lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="o"&gt;[[&lt;/span&gt; &lt;span class="nt"&gt;-z&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;ROOT&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="o"&gt;]]&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="k"&gt;then
  &lt;/span&gt;&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"gate: not inside a git work tree"&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&amp;amp;2
  &lt;span class="nb"&gt;exit &lt;/span&gt;10
&lt;span class="k"&gt;fi

&lt;/span&gt;&lt;span class="nb"&gt;cd&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;ROOT&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;

&lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="o"&gt;[[&lt;/span&gt; &lt;span class="o"&gt;!&lt;/span&gt; &lt;span class="nt"&gt;-f&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;CONTRACT&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="o"&gt;]]&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="k"&gt;then
  &lt;/span&gt;&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"gate: missing &lt;/span&gt;&lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;CONTRACT&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;; refusing to start the session"&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&amp;amp;2
  &lt;span class="nb"&gt;exit &lt;/span&gt;11
&lt;span class="k"&gt;fi

&lt;/span&gt;&lt;span class="nb"&gt;command&lt;/span&gt; &lt;span class="nt"&gt;-v&lt;/span&gt; python3 &lt;span class="o"&gt;&amp;gt;&lt;/span&gt;/dev/null &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt; &lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"gate: python3 required"&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&amp;amp;2&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="nb"&gt;exit &lt;/span&gt;12&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="o"&gt;}&lt;/span&gt;

python3 - &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;CONTRACT&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&amp;lt;&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="no"&gt;PY&lt;/span&gt;&lt;span class="sh"&gt;'
import json, sys, pathlib
path = sys.argv[1]
data = json.loads(pathlib.Path(path).read_text())
root = pathlib.Path.cwd()
basename = data["git_root_basename"]
if root.name != basename:
    print(f"gate: cwd basename {root.name!r} != {basename!r}", file=sys.stderr)
    sys.exit(13)
lockfile = data["lockfile"]
if not (root / lockfile).is_file():
    print(f"gate: lockfile {lockfile} is missing", file=sys.stderr)
    sys.exit(14)
pm = data["package_manager"]
server = data["server"]
print(f"gate: ok root={root} pm={pm} lockfile={lockfile}")
print(f"gate: test={data['test_command']}")
print(f"gate: server={server['bind']}:{server['port']}{server['health_path']}")
&lt;/span&gt;&lt;span class="no"&gt;PY

&lt;/span&gt;&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"gate: session may start; paste the contract into the first message"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Make it executable once, then run it from anywhere inside the work tree and let it cd for you. A healthy pass looks almost boring, and that boredom is the entire point of the ritual. A useful failure names the basename mismatch and refuses to flatter you with a partial green check.&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; +x scripts/ai-session-gate.sh
./scripts/ai-session-gate.sh
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;gate: ok root=/Users/you/src/billing-api pm=pnpm lockfile=pnpm-lock.yaml
gate: test=pnpm test -- --run
gate: server=127.0.0.1:8787/healthz
gate: session may start; paste the contract into the first message
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;gate: cwd basename 'billing-api-copy' != 'billing-api'
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Has a copied folder with a hyphenated suffix ever wasted your evening while the agent kept coding happily? The first prompt after a green gate is not a feature request; it is a short, rude recitation. I paste the JSON and require the agent to echo bind address, lockfile, and test command before edits. If it cannot quote those three facts, I stop, because a junior would not get a looser bar.&lt;/p&gt;

&lt;p&gt;Would you merge a pull request from someone who could not name the test runner in standup? Then why let a free model do that with more confidence, nicer prose, and much less shame? When a free server joins the story, I add a second check that never talks to the model at all. The process that claims to be my app must answer on the contracted loopback port, not in chat.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;#!/usr/bin/env bash&lt;/span&gt;
&lt;span class="nb"&gt;set&lt;/span&gt; &lt;span class="nt"&gt;-euo&lt;/span&gt; pipefail
&lt;span class="nv"&gt;BIND&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;1&lt;/span&gt;&lt;span class="k"&gt;:-&lt;/span&gt;&lt;span class="nv"&gt;127&lt;/span&gt;&lt;span class="p"&gt;.0.0.1&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;
&lt;span class="nv"&gt;PORT&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;2&lt;/span&gt;&lt;span class="k"&gt;:-&lt;/span&gt;&lt;span class="nv"&gt;8787&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;
&lt;span class="nv"&gt;PATH_HEALTH&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;3&lt;/span&gt;&lt;span class="k"&gt;:-&lt;/span&gt;&lt;span class="p"&gt;/healthz&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;

&lt;span class="k"&gt;if &lt;/span&gt;curl &lt;span class="nt"&gt;-fsS&lt;/span&gt; &lt;span class="nt"&gt;--max-time&lt;/span&gt; 2 &lt;span class="s2"&gt;"http://&lt;/span&gt;&lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;BIND&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;:&lt;/span&gt;&lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;PORT&lt;/span&gt;&lt;span class="k"&gt;}${&lt;/span&gt;&lt;span class="nv"&gt;PATH_HEALTH&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt;/dev/null&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="k"&gt;then
  &lt;/span&gt;&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"health: ok &lt;/span&gt;&lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;BIND&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;:&lt;/span&gt;&lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;PORT&lt;/span&gt;&lt;span class="k"&gt;}${&lt;/span&gt;&lt;span class="nv"&gt;PATH_HEALTH&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;
  &lt;span class="nb"&gt;exit &lt;/span&gt;0
&lt;span class="k"&gt;fi

&lt;/span&gt;&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"health: no response from &lt;/span&gt;&lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;BIND&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;:&lt;/span&gt;&lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;PORT&lt;/span&gt;&lt;span class="k"&gt;}${&lt;/span&gt;&lt;span class="nv"&gt;PATH_HEALTH&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&amp;amp;2
&lt;span class="nb"&gt;exit &lt;/span&gt;20
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This is where MonkeyCode actually participates in the method rather than sitting in a slogan. Disclosure: This article was prepared as part of MonkeyCode's product outreach. I treat it as one place with free model access and a free server option, which makes bad binds obvious. Strip every product name from this article and the gate still earns its keep on a laptop.&lt;/p&gt;

&lt;p&gt;Tear the first fifteen minutes down as a timeline and the failure modes stop looking mysterious. Minute one is open or clone, which is exactly when the basename check should fire and halt you. Minutes two through five are dependency reality, so the lockfile belongs in the contract, not in a vibe. Minutes six through fifteen are the helpful Docker file, the second framework, and the server that only exists in transcript.&lt;/p&gt;

&lt;p&gt;The contract attacks minute one, and the health curl attacks the fake demo at minute fifteen. Everything between those checks is just an agent filling silence with architecture you did not order. I think of a kitchen ticket that does not inspire the cook so much as prevent last night's special. Free inference is a talented line cook with no ticket printer, and your job is hanging the ticket.&lt;/p&gt;

&lt;p&gt;Should you wire the gate into an agent loop on day one, before it is boring on your laptop? I would not; I would keep a proposed npm script so the ritual lives in muscle memory first. After that, the human ritual is two commands you can type when you are only half awake.&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;"scripts"&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;"ai:gate"&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 scripts/ai-session-gate.sh"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"ai:health"&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 scripts/ai-health.sh 127.0.0.1 8787 /healthz"&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;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;pnpm ai:gate
&lt;span class="c"&gt;# only then start the agent, and only then paste session.contract.json&lt;/span&gt;
pnpm ai:health
git diff &lt;span class="nt"&gt;--exit-code&lt;/span&gt; &lt;span class="nt"&gt;--&lt;/span&gt; session.contract.json
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;That last command is the quiet one people skip, and skipping it is how the ticket gets reprinted. If &lt;code&gt;git diff --exit-code -- session.contract.json&lt;/code&gt; exits nonzero, the session is no longer bound by the rules you hung. Pause and ask why the cook rewrote the order before you taste whatever landed on the plate. The agent can still be useful after that pause; it just lost the right to keep moving on inertia.&lt;/p&gt;

&lt;p&gt;Limitations sit in the open because this pattern is a seatbelt, not a substitute for a brain. The contract cannot see secrets an agent later pastes into a helpful log you forgot to read. The health check cannot prove the handler is yours if another process already bound the same port. JSON will not stop a rewrite of the contract unless you commit the file and watch git diff closely.&lt;/p&gt;

&lt;p&gt;I am not claiming latency numbers, pass rates, or model names, because this piece is not a bake-off. If your work is air-gapped, safety-critical, or stuck inside a change window, skip remote free servers entirely. Who should skip the whole ritual, not just the remote box, without feeling guilty about the shortcut? People pairing on a twenty-minute kata, or a single file with no lockfile and no server, can skip it.&lt;/p&gt;

&lt;p&gt;If you lack a git root you trust, the gate will nag, and you should listen instead of weakening it. The wider conversation keeps celebrating agents that plan, tool-call, and just handle the missing details for you. Handling the missing details is exactly how my accidental Jest suite got born on a pnpm repository. I would rather an agent pause like a senior who asks which package manager is actually real here.&lt;/p&gt;

&lt;p&gt;Is that slower than a magical first message that scaffolds three directories before you finish coffee? Yes, by about thirty seconds of JSON and a shell script that exits nonzero without apologizing. Is it slower than rebuilding a tree the agent invented before lunch while sounding perfectly sure of itself? Not even close, which is why I now treat a green gate as the real start of the session.&lt;/p&gt;

&lt;p&gt;If you try this, keep the artifact small enough to paste into a pull request description without ceremony. A contract, a gate, a health curl, and a first prompt that quotes them are the whole DX patch. The models can stay free, the server can stay free, and tomorrow you can still discard the session without guilt. What you should not discard is the habit of making the repo answer back before anyone starts coding.&lt;/p&gt;

&lt;p&gt;Steal the three files either way, even if you never touch the product I used for the server. If you want a free model plus a free server to try the gate against, MonkeyCode is one option. I will not pretend the JSON cares which vendor answered, so long as port 8787 tells the truth.&lt;/p&gt;

</description>
      <category>ai</category>
      <category>testing</category>
      <category>webdev</category>
      <category>productivity</category>
    </item>
    <item>
      <title>Prove the Headers Before You Accept the Cache Patch</title>
      <dc:creator>Quinn Wang</dc:creator>
      <pubDate>Thu, 03 Sep 2026 15:27:31 +0000</pubDate>
      <link>https://dev.to/codejs_1959/prove-the-headers-before-you-accept-the-cache-patch-307i</link>
      <guid>https://dev.to/codejs_1959/prove-the-headers-before-you-accept-the-cache-patch-307i</guid>
      <description>&lt;p&gt;The first fifteen minutes of an AI cache fix should replay headers, not invent another middleware file. I do not want a confident story about stale SPAs; I want a disposable host that repeats one request twice. If the second response still looks haunted, the patch is fiction, and I should stop typing. Why would I merge a Cache-Control lecture that never touched the wire in this brownfield app?&lt;/p&gt;

&lt;p&gt;Brownfield front ends fail in the quiet places, like service workers and intermediary caches that tutorials skip. A free coding model will invent ETags, purge keys, and a casual no-store as if your CDN were a textbook diagram. Have you watched a generated patch rename a header that your origin never sent on any environment? That is the DX break: you spend the opening quarter hour arguing with prose while the browser serves yesterday.&lt;/p&gt;

&lt;p&gt;The one fix that mattered was embarrassingly small, and it did not live inside the prompt window at all. I wrote a replay probe that hits the same URL twice, prints the cache headers, and diffs the two copies. Until those two copies agree with reality, I refuse to discuss architecture with the model in any serious way. Does that sound slow? It is faster than reverting a service worker that gaslit every teammate.&lt;/p&gt;

&lt;p&gt;I still needed two cheap ingredients: a model that could draft a patch, and a server I could throw away after the replay. Disclosure: This article was prepared as part of MonkeyCode's product outreach. MonkeyCode's free model access and free server option are relevant here only as a draft-and-replay pair I can abandon. The rest of this workflow is local, boring, and useful even if you never mention a product name again.&lt;/p&gt;

&lt;p&gt;The probe is ordinary Node, because I already have it, and I do not want another package manager argument. It records status, cache-control, etag, age, last-modified, vary, and a simple boolean for whether set-cookie appeared. Then it hits the URL again and prints a line-oriented diff that even a tired reviewer can scan. If the model invented a header, the diff stays empty on the server and loud in the generated markdown.&lt;/p&gt;

&lt;p&gt;Save this as &lt;code&gt;header-replay.mjs&lt;/code&gt;. It is a proposed local artifact, not a benchmark, and it uses only Node's standard &lt;code&gt;http&lt;/code&gt; and &lt;code&gt;https&lt;/code&gt; modules.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// header-replay.mjs — proposed workflow, run against URLs you already may fetch.&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="nx"&gt;http&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;node:http&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="nx"&gt;https&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;node:https&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;URL&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;node:url&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;KEYS&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
  &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;cache-control&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;etag&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;age&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;last-modified&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;vary&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;expires&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;pragma&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;surrogate-control&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;cdn-cache-control&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="p"&gt;];&lt;/span&gt;

&lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;fetchOnce&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;raw&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;u&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;URL&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;raw&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;lib&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;u&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;protocol&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;https:&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="p"&gt;?&lt;/span&gt; &lt;span class="nx"&gt;https&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;http&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;Promise&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="nx"&gt;resolve&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;reject&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;req&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;lib&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;request&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
      &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="na"&gt;hostname&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;u&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;hostname&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="na"&gt;port&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;u&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;port&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;u&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;protocol&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;https:&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="p"&gt;?&lt;/span&gt; &lt;span class="mi"&gt;443&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;80&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
        &lt;span class="na"&gt;path&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;`&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;u&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;pathname&lt;/span&gt;&lt;span class="p"&gt;}${&lt;/span&gt;&lt;span class="nx"&gt;u&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;search&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;`&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="na"&gt;method&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;GET&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="na"&gt;headers&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
          &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;user-agent&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;header-replay/0.1&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
          &lt;span class="na"&gt;accept&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;text/html,application/json;q=0.9,*/*;q=0.8&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="p"&gt;},&lt;/span&gt;
      &lt;span class="p"&gt;},&lt;/span&gt;
      &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;res&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;headers&lt;/span&gt; &lt;span class="o"&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;k&lt;/span&gt; &lt;span class="k"&gt;of&lt;/span&gt; &lt;span class="nx"&gt;KEYS&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="nx"&gt;headers&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;k&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;res&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;headers&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;k&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;??&lt;/span&gt; &lt;span class="dl"&gt;''&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
        &lt;span class="nx"&gt;headers&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;status&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;String&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;res&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;statusCode&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
        &lt;span class="nx"&gt;headers&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;set-cookie?&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;Array&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;isArray&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;res&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;headers&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;set-cookie&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt; &lt;span class="p"&gt;?&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;yes&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;no&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
        &lt;span class="nx"&gt;res&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;resume&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
        &lt;span class="nx"&gt;res&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;on&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;end&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nf"&gt;resolve&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;headers&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;
      &lt;span class="p"&gt;},&lt;/span&gt;
    &lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="nx"&gt;req&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;on&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;error&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;reject&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="nx"&gt;req&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;end&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
  &lt;span class="p"&gt;});&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;diff&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;a&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;b&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;keys&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;Set&lt;/span&gt;&lt;span class="p"&gt;([...&lt;/span&gt;&lt;span class="nb"&gt;Object&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;keys&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;a&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="p"&gt;...&lt;/span&gt;&lt;span class="nb"&gt;Object&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;keys&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;b&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;lines&lt;/span&gt; &lt;span class="o"&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;k&lt;/span&gt; &lt;span class="k"&gt;of&lt;/span&gt; &lt;span class="nx"&gt;keys&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;a&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;k&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;!==&lt;/span&gt; &lt;span class="nx"&gt;b&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;k&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt; &lt;span class="nx"&gt;lines&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;push&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;`&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;k&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;: &lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;JSON&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;stringify&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;a&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;k&lt;/span&gt;&lt;span class="p"&gt;])}&lt;/span&gt;&lt;span class="s2"&gt; -&amp;gt; &lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;JSON&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;stringify&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;b&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;k&lt;/span&gt;&lt;span class="p"&gt;])}&lt;/span&gt;&lt;span class="s2"&gt;`&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;lines&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;url&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;process&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;argv&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;];&lt;/span&gt;
&lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="nx"&gt;url&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;error&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;usage: node header-replay.mjs &amp;lt;url&amp;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;process&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;exit&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;first&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;fetchOnce&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;url&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;second&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;fetchOnce&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;url&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;pass-1&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;first&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;pass-2&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;second&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;changed&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;diff&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;first&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;second&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;changed&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;length&lt;/span&gt; &lt;span class="p"&gt;?&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;UNSTABLE_HEADERS&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;STABLE_HEADERS&lt;/span&gt;&lt;span class="dl"&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;line&lt;/span&gt; &lt;span class="k"&gt;of&lt;/span&gt; &lt;span class="nx"&gt;changed&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;line&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;I run that before I paste any model output into the repo, because silence on the wire is the actual product bug. The first fifteen minutes on my clock look like a short ritual, not a brainstorming session with a chatbot. Can you feel how different that is from opening a chat and asking for a cache architecture? The probe answers one question: did this URL even speak HTTP the way the patch pretends?&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# Minute 0–3: prove you can see the brownfield URL at all.&lt;/span&gt;
node header-replay.mjs https://staging.example.com/app

&lt;span class="c"&gt;# Minute 3–6: capture a baseline you can diff later, nothing fancy.&lt;/span&gt;
node header-replay.mjs https://staging.example.com/app &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; /tmp/baseline-headers.txt

&lt;span class="c"&gt;# Minute 6–10: if you have a throwaway host, point the same probe at it.&lt;/span&gt;
node header-replay.mjs http://127.0.0.1:8787/app &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; /tmp/replay-headers.txt

&lt;span class="c"&gt;# Minute 10–15: only now ask a free model for a patch, and feed it the two dumps.&lt;/span&gt;
diff &lt;span class="nt"&gt;-u&lt;/span&gt; /tmp/baseline-headers.txt /tmp/replay-headers.txt &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="nb"&gt;true&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;I keep the model on a leash by pasting those dumps and asking for a patch that names only headers that already appeared. If the draft mentions &lt;code&gt;Surrogate-Control&lt;/code&gt; and the baseline never did, I throw the draft away without negotiating. Is that rude to the model? Maybe, but rudeness is cheaper than a service worker that pins a broken bundle to every laptop on the team. The disposable server matters because I can apply the patch there, rerun the probe, and delete the machine before the story spreads.&lt;/p&gt;

&lt;p&gt;Here is the prompt shape I actually paste after the dumps, labeled as a proposal you should edit for your stack. I want the model to argue with the wire, not with my anxiety about freshness.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;You are reviewing a brownfield SPA cache issue.
Only discuss headers that appear in pass-1 or pass-2 below.
If you need a header that is missing, say UNPROVEN and stop.
Do not invent a CDN product, a purge API, or a service worker file.
Return: (1) which header from the dump is the likely culprit,
(2) a minimal patch against files I named, (3) the exact
header-replay command I should rerun after the patch.

pass-1 and pass-2:
&amp;lt;paste dumps&amp;gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Notice what this does to the first quarter hour as a developer experience. I am not pairing with a genius; I am pairing with a suspect who must point at evidence I already collected. The free model can still be wrong, and the free server can still be unlike production, which is exactly why both have to be throwaway. Would I let that suspect rewrite &lt;code&gt;sw.js&lt;/code&gt; before the probe printed &lt;code&gt;STABLE_HEADERS&lt;/code&gt;? Not on a weekday, and not on a brownfield app with real users.&lt;/p&gt;

&lt;p&gt;Illustrative output, not a measurement from a private run, looks like this when the origin is merely inconsistent between two GETs.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;pass-1 {
  status: '200',
  'cache-control': 'public, max-age=60',
  etag: 'W/"9f3"',
  age: '12',
  'last-modified': '',
  vary: 'Accept-Encoding',
  expires: '',
  pragma: '',
  'surrogate-control': '',
  'cdn-cache-control': '',
  'set-cookie?': 'no'
}
pass-2 { ... age: '0' ... }
UNSTABLE_HEADERS
age: "12" -&amp;gt; "0"
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;An unstable &lt;code&gt;age&lt;/code&gt; is often boring and honest. An etag that flips while the body did not change is a different animal, and that is when I let the model talk. A generated &lt;code&gt;private, no-store&lt;/code&gt; on an asset that was &lt;code&gt;public, max-age=31536000&lt;/code&gt; is usually a panic patch, not a diagnosis. Have you seen how often AI cache advice is just panic with extra Markdown? The probe makes that panic visible in fifteen minutes instead of two days of "hard refresh for me?"&lt;/p&gt;

&lt;p&gt;There are limits, and they are not subtle if you have ever shipped a CDN. This workflow does not prove correctness of stale-while-revalidate, cookie-gated HTML, or a service worker that intercepts fetch outside the headers the origin sent. It also does not bless a free remote server as a replica of production TLS, geo, or cache hierarchy, so do not treat a green probe as a load-test. If you cannot legally fetch the URL, or you do not own the service worker, do not point this script at it and do not paste dumps into any model.&lt;/p&gt;

&lt;p&gt;Who should skip this entirely? Anyone hoping the first fifteen minutes will design a caching architecture from a blank repo, because architecture is not a header diff. Anyone behind a corporate proxy that rewrites &lt;code&gt;Cache-Control&lt;/code&gt; will get a theatrical probe and a useless argument. Anyone who needs byte-level body identity should hash the payload, which this script deliberately refuses to do so you do not confuse content drift with header drift. I would also skip it for authenticated apps unless you already have a non-secret staging cookie story, because leaking &lt;code&gt;set-cookie&lt;/code&gt; into a chat is a self-own.&lt;/p&gt;

&lt;p&gt;I still use the cheap loop when the pain is DX, not theory: a model drafted a patch, a teammate almost merged it, and nobody had asked the origin a second question. The first fifteen minutes are for making the wire confess. After that, you can argue about architecture with whatever free model you already had, on a server you can throw away when the confession is over. If you want that draft-and-replay pair in one place, MonkeyCode's free model access and free server option are the combination I reached for; the probe above is the part I would keep even if the product vanished tomorrow.&lt;/p&gt;

</description>
      <category>webdev</category>
      <category>ai</category>
      <category>javascript</category>
      <category>architecture</category>
    </item>
    <item>
      <title>Your Free AI Coding Stack Remembers Too Much: An Isolation Workflow</title>
      <dc:creator>Quinn Wang</dc:creator>
      <pubDate>Wed, 02 Sep 2026 13:54:53 +0000</pubDate>
      <link>https://dev.to/codejs_1959/your-free-ai-coding-stack-remembers-too-much-an-isolation-workflow-34ib</link>
      <guid>https://dev.to/codejs_1959/your-free-ai-coding-stack-remembers-too-much-an-isolation-workflow-34ib</guid>
      <description>&lt;p&gt;Most AI coding tools fail not because the model is weak. They fail because the context is dirty. Yesterday's failed test pollutes today's refactor. One noisy file steers the whole conversation. The fix is not a better prompt. The fix is isolation.&lt;/p&gt;

&lt;p&gt;This article shows how to run a small, reproducible context isolation layer for AI-assisted code changes. The workflow uses MonkeyCode's open source project, its free model access, and its free server option. You can apply the same principles to any tool.&lt;/p&gt;

&lt;p&gt;Disclosure: This article was prepared as part of MonkeyCode's product outreach.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Real Problem Is Not Tokens
&lt;/h2&gt;

&lt;p&gt;Free tiers give you plenty of tokens. The bottleneck is relevance. When an AI agent receives a 200-file repository, it does not read every file. It retrieves fragments. Those fragments are often stale, duplicated, or irrelevant.&lt;/p&gt;

&lt;p&gt;A typical session includes:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Old build logs from a different branch&lt;/li&gt;
&lt;li&gt;Deprecated API examples from Stack Overflow&lt;/li&gt;
&lt;li&gt;A teammate's half-written comment&lt;/li&gt;
&lt;li&gt;The first 50 lines of a config file that changed last week&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;All of that becomes equally trusted context. The model has no way to distinguish the signal from the noise. You end up with confident, fluent, completely wrong suggestions.&lt;/p&gt;

&lt;p&gt;The standard mitigation is a better system prompt. That helps, but it only works at the prompt level. It does not control what files the retriever picks.&lt;/p&gt;

&lt;h2&gt;
  
  
  A Different Approach: Context Budgets
&lt;/h2&gt;

&lt;p&gt;Instead of asking the model to ignore noise, keep the noise out of the context. Give the agent a small, explicit budget: at most N files, M lines, and K test results. Enforce that budget before the model sees anything.&lt;/p&gt;

&lt;p&gt;This is a routing problem, not a reasoning problem. You decide what the agent may see. The agent then focuses only on that slice.&lt;/p&gt;

&lt;p&gt;MonkeyCode's open source project gives you a place to build that routing layer. Because it provides free model access and a free server option, you can run the whole pipeline without paying for a hosted IDE or a cloud subscription. You own the context pipeline.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Isolation Workflow
&lt;/h2&gt;

&lt;p&gt;We will build a small script that does three things:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Snapshot the git diff for the current change.&lt;/li&gt;
&lt;li&gt;Select only the files touched by that diff, plus a fixed allowlist.&lt;/li&gt;
&lt;li&gt;Send that selected context to the model through MonkeyCode's API-compatible endpoint.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;The model never sees the full repository. It sees only the diff, the allowlist files, and the latest test output for the changed module.&lt;/p&gt;

&lt;h3&gt;
  
  
  Step 1: Snapshot the Change
&lt;/h3&gt;

&lt;p&gt;Run this inside your repo:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;git diff HEAD &lt;span class="nt"&gt;--name-only&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; /tmp/context_allowlist.txt
git diff HEAD &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; /tmp/context_diff.patch
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This gives you two artifacts: the list of changed files and the exact patch. The patch is the ground truth for what changed.&lt;/p&gt;

&lt;p&gt;If you are working on a fresh branch, use &lt;code&gt;git diff main...HEAD&lt;/code&gt; instead of &lt;code&gt;HEAD&lt;/code&gt;. That captures the full branch diff.&lt;/p&gt;

&lt;h3&gt;
  
  
  Step 2: Build the Context Package
&lt;/h3&gt;

&lt;p&gt;Now we create a small directory with only the relevant files.&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;rm&lt;/span&gt; &lt;span class="nt"&gt;-rf&lt;/span&gt; /tmp/context_bundle &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="nb"&gt;mkdir&lt;/span&gt; &lt;span class="nt"&gt;-p&lt;/span&gt; /tmp/context_bundle
&lt;span class="k"&gt;while &lt;/span&gt;&lt;span class="nb"&gt;read&lt;/span&gt; &lt;span class="nt"&gt;-r&lt;/span&gt; file&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="k"&gt;do
  if&lt;/span&gt; &lt;span class="o"&gt;[&lt;/span&gt; &lt;span class="nt"&gt;-f&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$file&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="o"&gt;]&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="k"&gt;then
    &lt;/span&gt;&lt;span class="nb"&gt;mkdir&lt;/span&gt; &lt;span class="nt"&gt;-p&lt;/span&gt; &lt;span class="s2"&gt;"/tmp/context_bundle/&lt;/span&gt;&lt;span class="si"&gt;$(&lt;/span&gt;&lt;span class="nb"&gt;dirname&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$file&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="si"&gt;)&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;
    &lt;span class="nb"&gt;cp&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$file&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="s2"&gt;"/tmp/context_bundle/&lt;/span&gt;&lt;span class="nv"&gt;$file&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;
  &lt;span class="k"&gt;fi
done&lt;/span&gt; &amp;lt; /tmp/context_allowlist.txt

&lt;span class="nb"&gt;cp&lt;/span&gt; /tmp/context_diff.patch /tmp/context_bundle/change.patch
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Add one more file: a short description of the intended change. Write it before asking the model. Do not let the model guess intent.&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;cat&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; /tmp/context_bundle/goal.md &lt;span class="o"&gt;&amp;lt;&amp;lt;&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="no"&gt;EOF&lt;/span&gt;&lt;span class="sh"&gt;'
Goal: Fix flaky timeout in the retry logic.
Acceptance: Retry waits between attempts and stops after 3 tries.
&lt;/span&gt;&lt;span class="no"&gt;EOF
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Step 3: Send Only the Bundle to the Model
&lt;/h3&gt;

&lt;p&gt;MonkeyCode's free server runs locally on your machine or on a small free-tier VM. It exposes an OpenAI-compatible endpoint. Point your HTTP call at that endpoint with the bundle as system and user messages.&lt;/p&gt;

&lt;p&gt;Here is a minimal Python client:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;json&lt;/span&gt;
&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;glob&lt;/span&gt;
&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;pathlib&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;Path&lt;/span&gt;
&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;urllib.request&lt;/span&gt;

&lt;span class="n"&gt;BUNDLE&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;/tmp/context_bundle&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;
&lt;span class="n"&gt;ENDPOINT&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;http://localhost:31415/v1/chat/completions&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;

&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;read_files&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt;
    &lt;span class="n"&gt;files&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;sorted&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;glob&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;glob&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;BUNDLE&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s"&gt;/**/*&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;recursive&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="bp"&gt;True&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
    &lt;span class="n"&gt;content&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[]&lt;/span&gt;
    &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;path&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;files&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="n"&gt;p&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;Path&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;path&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;p&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;is_file&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="ow"&gt;and&lt;/span&gt; &lt;span class="n"&gt;p&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;suffix&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;.py&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;.js&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;.ts&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;.md&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;.patch&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;.json&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;}:&lt;/span&gt;
            &lt;span class="n"&gt;content&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;append&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;### FILE: &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;p&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;relative_to&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;BUNDLE&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="se"&gt;\n\n&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;p&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;read_text&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="se"&gt;\n\n&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;join&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;content&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="n"&gt;payload&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;model&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;free-model&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;messages&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
        &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;role&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;system&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;content&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;You review a code change. Only use the files provided.&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;
        &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;role&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;user&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;content&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nf"&gt;read_files&lt;/span&gt;&lt;span class="p"&gt;()}&lt;/span&gt;
    &lt;span class="p"&gt;],&lt;/span&gt;
    &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;temperature&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="n"&gt;req&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;urllib&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;request&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;Request&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;ENDPOINT&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;data&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;json&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;dumps&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;payload&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;encode&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt; &lt;span class="n"&gt;headers&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Content-Type&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;application/json&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;})&lt;/span&gt;
&lt;span class="n"&gt;response&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;urllib&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;request&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;urlopen&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;req&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;json&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;loads&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;response&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;read&lt;/span&gt;&lt;span class="p"&gt;())[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;choices&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;][&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;][&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;message&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;][&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;content&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The key decision is in &lt;code&gt;read_files()&lt;/code&gt;. It only reads the bundle. The model has no path to the full repository. It cannot retrieve something that is not there.&lt;/p&gt;

&lt;h2&gt;
  
  
  How This Changes the Output
&lt;/h2&gt;

&lt;p&gt;Run the same change through a non-isolated tool and through this workflow. The isolated version will produce more conservative, more task-specific suggestions. It will not invent files that do not exist. It will not reference a function from another module unless that function is in the bundle.&lt;/p&gt;

&lt;p&gt;The trade-off is speed of onboarding. A fully contextual agent can discover useful existing code automatically. Our isolation workflow forces you to manually add allowlist files if a change crosses module boundaries.&lt;/p&gt;

&lt;h2&gt;
  
  
  Decision Table: Use Isolation or Not
&lt;/h2&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Scenario&lt;/th&gt;
&lt;th&gt;Isolated bundle&lt;/th&gt;
&lt;th&gt;Full repo context&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;One-file bugfix&lt;/td&gt;
&lt;td&gt;✅ Best&lt;/td&gt;
&lt;td&gt;Overkill&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Refactor across 10 modules&lt;/td&gt;
&lt;td&gt;⚠️ Needs manual allowlist&lt;/td&gt;
&lt;td&gt;✅ Better&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;New feature in unknown codebase&lt;/td&gt;
&lt;td&gt;🚫 You will miss dependencies&lt;/td&gt;
&lt;td&gt;✅ Best for discovery&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;CI failure fix&lt;/td&gt;
&lt;td&gt;✅ Perfect&lt;/td&gt;
&lt;td&gt;❌ Too noisy&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Security audit of a diff&lt;/td&gt;
&lt;td&gt;✅ Ideal&lt;/td&gt;
&lt;td&gt;❌ Too broad&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;Use isolation when the change is local and the failure is specific. Use full context when you need discovery, but then you must accept the noise.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why Free Models and a Free Server Matter Here
&lt;/h2&gt;

&lt;p&gt;Context isolation eats tokens. You send the same files multiple times across iterations. On a paid API, that becomes expensive fast. Free model access removes that cost ceiling.&lt;/p&gt;

&lt;p&gt;A free server also changes privacy. You can run the isolation server on your own machine or on a free-tier VM. The diff and the source files never leave your control. That is a real advantage for unreleased code or proprietary modules.&lt;/p&gt;

&lt;p&gt;MonkeyCode's free server option gives you a local endpoint without configuring API gateways. You can test this workflow in under an hour. If it does not improve your review quality, stop and go back to a full-context agent.&lt;/p&gt;

&lt;h2&gt;
  
  
  Limitations You Must Accept
&lt;/h2&gt;

&lt;p&gt;The free models are not frontier models. They may produce less elegant solutions. They are fine for review, test generation, and small refactors. They are not suitable for architect-level reasoning on large codebases.&lt;/p&gt;

&lt;p&gt;The free server is also not a permanent SLA. Do not build a production pipeline on it. Use it for experiments, personal work, or internal tooling. If you need reliability, plug in a paid API or a self-hosted model.&lt;/p&gt;

&lt;p&gt;This workflow assumes git. If your team uses mercurial or an unfamiliar VCS, adapt the diff commands yourself. The principle stays the same: capture the change, select the files, drop everything else.&lt;/p&gt;

&lt;h2&gt;
  
  
  How to Extend This Workflow
&lt;/h2&gt;

&lt;p&gt;Add a test snapshot to the bundle. Run only the tests for the changed module and write their output into &lt;code&gt;test_output.txt&lt;/code&gt;. Include that file in the bundle. The model can then explain why the test fails and what the fix does, using the actual error instead of a guess.&lt;/p&gt;

&lt;p&gt;Add a lint step. Run &lt;code&gt;git diff | eslint&lt;/code&gt; and include the linter output. Now the model sees the exact line numbers and the exact rules being violated. That is much stronger context than "the linter is unhappy."&lt;/p&gt;

&lt;p&gt;You can also version the bundle. Save every bundle as &lt;code&gt;contexts/YYYY-MM-DD-HHMM/&lt;/code&gt;. After a week, replay a few sessions and ask why certain suggestions were so wrong. The answer is almost always in the bundle: the wrong file was there, or the right file was missing.&lt;/p&gt;

&lt;h2&gt;
  
  
  The One Metric That Matters
&lt;/h2&gt;

&lt;p&gt;Track the percentage of model suggestions that you accept without modification. That is your barometer. If you were at 10% before and you reach 30% with isolation, the workflow pays for itself. If the number stays flat, the context was not the problem.&lt;/p&gt;

&lt;p&gt;The same metric works for any AI coding tool. You do not need a dashboard. A simple tally in a spreadsheet works.&lt;/p&gt;

&lt;h2&gt;
  
  
  What to Do Next
&lt;/h2&gt;

&lt;p&gt;Clone MonkeyCode's open source repository. Run the free server locally. Try this isolation script on your next bugfix. Compare the suggestions against your normal workflow.&lt;/p&gt;

&lt;p&gt;If it helps, keep it. If not, the cost was zero: free models, free server, and a few lines of shell code. That is the right way to evaluate developer tooling — not by marketing, but by an experiment you can reproduce before lunch.&lt;/p&gt;

</description>
      <category>ai</category>
      <category>opensource</category>
      <category>workflow</category>
      <category>programming</category>
    </item>
    <item>
      <title>The Real Friction in Free AI Coding Isn't the Model — It's the Setup</title>
      <dc:creator>Quinn Wang</dc:creator>
      <pubDate>Tue, 01 Sep 2026 13:06:39 +0000</pubDate>
      <link>https://dev.to/codejs_1959/the-real-friction-in-free-ai-coding-isnt-the-model-its-the-setup-35pk</link>
      <guid>https://dev.to/codejs_1959/the-real-friction-in-free-ai-coding-isnt-the-model-its-the-setup-35pk</guid>
      <description>&lt;p&gt;Free AI coding tools rarely fail because the model is dumb. They fail because the road from installation to first useful answer is paved with environment variables, credential files, and server endpoints that never line up. I recently spent fifteen minutes debugging a proxy that pointed to a dead port, and that was after a clean install. The lesson? The real deliverable isn't model quality — it's a setup that works.&lt;/p&gt;

&lt;p&gt;MonkeyCode, an open-source project, focuses on that exact pain. Disclosure: This article was prepared as part of MonkeyCode's product outreach. It offers free model access and a free server option, which means you can skip most of the configuration tango that usually comes with AI-assisted development. Instead of managing a local model runner, a separate inference server, and an editor plugin that all disagree with each other, you have one consistent place to point your editor.&lt;/p&gt;

&lt;p&gt;Why does that matter? Because every configuration file is a chance for the system to break in a way that has nothing to do with your code. A wrong port, a missing schema, a stale token — each one produces an error that sends you down a rabbit hole. The most expensive resource in development isn't compute; it's your attention. When a tool spends your attention on plumbing instead of programming, the cost is hidden but enormous.&lt;/p&gt;

&lt;p&gt;In practice, the first launch of any AI tool is a gauntlet. I count five things that usually go wrong: the model binary is too large, the download fails halfway, the port is already taken, the authentication handshake times out, or the editor extension can't find the local server. Each failure looks like a programming problem, but it's really an environment problem. The most effective fix is to eliminate the environment.&lt;/p&gt;

&lt;p&gt;That's where a free, hosted server shines. It shifts the computational burden away from your laptop and onto infrastructure that is already running. You don't need a beefy GPU or a midnight brew of llama.cpp; you just need an HTTPS endpoint and a valid token. For developers who work on a company-issued laptop with restricted ports, that's not a nice-to-have — it's the difference between using an AI tool and reinstalling Python for the fourth time.&lt;/p&gt;

&lt;p&gt;Another angle: the free server turns a synchronous blockage into an asynchronous resource. When your editor can reach the model without negotiating a local firewall, you stop treating AI as a delicate instrument and start treating it as a utility. Utilities are just there, like electricity. You don't configure your wall socket every morning; you plug in and go. That mental shift matters more than any token count.&lt;/p&gt;

&lt;p&gt;To make this concrete, I wrote a tiny shell script that checks the usual pain points. Run it before you start a session and you'll see exactly what your environment expects.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;#!/bin/bash&lt;/span&gt;
&lt;span class="c"&gt;# friction-check.sh — uncover common setup blockers&lt;/span&gt;
&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"=== Runtimes ==="&lt;/span&gt;
node &lt;span class="nt"&gt;-v&lt;/span&gt; 2&amp;gt;/dev/null &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"node: missing"&lt;/span&gt;
python3 &lt;span class="nt"&gt;--version&lt;/span&gt; 2&amp;gt;/dev/null &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"python: missing"&lt;/span&gt;
&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"=== Local ports ==="&lt;/span&gt;
&lt;span class="k"&gt;for &lt;/span&gt;port &lt;span class="k"&gt;in &lt;/span&gt;3000 8080 8000&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="k"&gt;do
  &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;$port&lt;/span&gt;&lt;span class="s2"&gt;: &lt;/span&gt;&lt;span class="si"&gt;$(&lt;/span&gt;curl &lt;span class="nt"&gt;-s&lt;/span&gt; &lt;span class="nt"&gt;-o&lt;/span&gt; /dev/null &lt;span class="nt"&gt;-w&lt;/span&gt; &lt;span class="s1"&gt;'%{http_code}'&lt;/span&gt; http://localhost:&lt;span class="nv"&gt;$port&lt;/span&gt; 2&amp;gt;/dev/null &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="nb"&gt;echo &lt;/span&gt;closed&lt;span class="si"&gt;)&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;
&lt;span class="k"&gt;done
&lt;/span&gt;&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"=== Credentials ==="&lt;/span&gt;
&lt;span class="o"&gt;[&lt;/span&gt; &lt;span class="nt"&gt;-n&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$OPENAI_API_KEY&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="o"&gt;]&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"OPENAI_API_KEY: set"&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"OPENAI_API_KEY: missing"&lt;/span&gt;
&lt;span class="o"&gt;[&lt;/span&gt; &lt;span class="nt"&gt;-n&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$MONKEYCODE_API_KEY&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="o"&gt;]&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"MONKEYCODE_API_KEY: set"&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"MONKEYCODE_API_KEY: unset (free tier may not need it)"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;On my machine, the script reported a missing Python runtime, a closed port 8000, and no API key. A traditional setup would have required me to fix all three before writing a single line of code. With MonkeyCode's free server, the port requirement disappears; with the free model access, the key requirement disappears too. The only element left is the runtime, which my code editor already uses anyway.&lt;/p&gt;

&lt;p&gt;Think of it like ordering a coffee. Some tools give you the beans, a grinder, and a milk frother, then ask you to become a barista. MonkeyCode hands you a cup and says, "Here's your espresso." Both approaches produce caffeine, but only one lets you start the day without a ten-step ritual. That's the developer experience difference that matters in the first fifteen minutes.&lt;/p&gt;

&lt;p&gt;Now for the caveats. I am not quoting token quotas or server throughput because I haven't verified those numbers from a primary source today. The free tier might not fit everyone: if you need a fully offline environment, an air-gapped network, or a static model that never changes, then an external free server is the wrong answer. Also, don't mistake convenience for capability — the model's output still needs human review, and this tool won't save you from bad requirements.&lt;/p&gt;

&lt;p&gt;Let me also address the obvious question: does free mean unreliable? In my experience, the reliability depends on the provider's infrastructure, which is exactly why a hosted option can beat a self-managed local setup. A local process dies when your laptop sleeps; a hosted service doesn't. For developers who move between meetings and a dozen browser tabs, that stability is the difference between trusting the tool and watching it fail silently.&lt;/p&gt;

&lt;p&gt;Who should skip this? Teams with strict data-residency policies, users on metered satellite connections, and anyone who just enjoys building their own infra. For those cases, a local model runner is the better fit. But if your blocker is not compute cost but configuration cost, the free server model deserves a look.&lt;/p&gt;

&lt;p&gt;One more observation from my testing: the speed of feedback matters more than the speed of inference. A tool that responds in two seconds while you're in flow feels five times faster than a tool that responds in one second but interrupts your rhythm with a configuration prompt. MonkeyCode's setup kept me in flow because there was nothing to configure. That's a subtle but powerful property.&lt;/p&gt;

&lt;p&gt;Free tools are judged in the first fifteen minutes, not by benchmark tables. The ones that win let you move from idea to output without detouring through Stack Overflow. MonkeyCode cleared that detour for me, and the same friction-check script will tell you whether your environment is ready. Run it, see what breaks, and then decide if the setup is the real cost.&lt;/p&gt;

</description>
      <category>ai</category>
      <category>devtools</category>
      <category>opensource</category>
      <category>productivity</category>
    </item>
    <item>
      <title>Free Models Write Tests. Mutations Prove Them.</title>
      <dc:creator>Quinn Wang</dc:creator>
      <pubDate>Mon, 31 Aug 2026 12:12:12 +0000</pubDate>
      <link>https://dev.to/codejs_1959/free-models-write-tests-mutations-prove-them-50jg</link>
      <guid>https://dev.to/codejs_1959/free-models-write-tests-mutations-prove-them-50jg</guid>
      <description>&lt;p&gt;A passing test proves nothing. A test that fails when the code is broken proves everything. Free models can generate the second kind, but only after you force them through a mutation gate.&lt;/p&gt;

&lt;p&gt;The gap is not generation. The gap is trust. A free-tier model writes twenty tests in thirty seconds. All green. The commit ships. Months later, a regression slips through because those tests only checked that the code did something, not the right thing.&lt;/p&gt;

&lt;p&gt;This article shows a zero-budget pipeline. MonkeyCode's free model access writes the tests. MonkeyCode's free server runs them and mutates the source. The result is a measurable quality gate, not a hopeful vibe.&lt;/p&gt;

&lt;p&gt;Disclosure: This article was prepared as part of MonkeyCode's product outreach.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why Mutation Testing Is the Right Judge
&lt;/h2&gt;

&lt;p&gt;Unit tests have a single job: catch regressions. A test that never fails on broken code is a false confessor. Mutation testing edits the source in tiny ways — swapping &lt;code&gt;&amp;gt;&lt;/code&gt; to &lt;code&gt;&amp;lt;&lt;/code&gt;, deleting a &lt;code&gt;return&lt;/code&gt;, flipping a boolean — and then checks if the test suite catches the edit.&lt;/p&gt;

&lt;p&gt;If a mutant survives, that test does not protect that behavior. The survival rate is the confidence score for the generated test.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Pipeline: Generate, Run, Mutate, Decide
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Step 1: Generate tests with a free model.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Send the target function to MonkeyCode's free model access. Ask for a pytest file focused on edge cases, not happy paths. The model returns a draft. Do not open it yet.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Step 2: Run the original tests on a free server.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;MonkeyCode's free server becomes the executor. It pulls the repo, installs dependencies, runs pytest, and returns a baseline. The server is stateless and ephemeral, so setup cost is zero.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Step 3: Mutate the source and rerun.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;A small script rewrites the source file into N mutant versions. For each mutant, the server reruns the test suite. Survivors are recorded.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Step 4: Compare survival rates.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;A decision table tells you which tests to keep, which to rewrite, and which to delete.&lt;/p&gt;

&lt;h2&gt;
  
  
  A Minimal Mutation Runner You Can Run Today
&lt;/h2&gt;

&lt;p&gt;The following script is intentionally small. It mutates Python operators and statements, runs pytest on each version, and outputs a survival report.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="c1"&gt;# mutation_gate.py
&lt;/span&gt;&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;ast&lt;/span&gt;
&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;copy&lt;/span&gt;
&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;subprocess&lt;/span&gt;
&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;sys&lt;/span&gt;
&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;tempfile&lt;/span&gt;
&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;pathlib&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;Path&lt;/span&gt;

&lt;span class="n"&gt;MUTATIONS&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;ast&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Gt&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;ast&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Lt&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;ast&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Eq&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
    &lt;span class="n"&gt;ast&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Lt&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;ast&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Gt&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;ast&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Eq&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
    &lt;span class="n"&gt;ast&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Add&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;ast&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Sub&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
    &lt;span class="n"&gt;ast&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Sub&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;ast&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Add&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
    &lt;span class="n"&gt;ast&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;And&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;ast&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Or&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
    &lt;span class="n"&gt;ast&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Or&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;ast&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;And&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;Mutator&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;ast&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;NodeTransformer&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;__init__&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
        &lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;mutants&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[]&lt;/span&gt;

    &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;visit_BinOp&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;node&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
        &lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;generic_visit&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;node&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="n"&gt;repls&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;MUTATIONS&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;type&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;node&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;op&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="n"&gt;repl&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;repls&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
            &lt;span class="n"&gt;newnode&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;copy&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;deepcopy&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;node&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
            &lt;span class="n"&gt;newnode&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;op&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;repl&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
            &lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;mutants&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;append&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;newnode&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;node&lt;/span&gt;

    &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;visit_Compare&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;node&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
        &lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;generic_visit&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;node&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;op&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;node&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;ops&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
            &lt;span class="n"&gt;repls&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;MUTATIONS&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;type&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;op&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="n"&gt;repl&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;repls&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
                &lt;span class="n"&gt;newnode&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;copy&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;deepcopy&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;node&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
                &lt;span class="n"&gt;newnode&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;ops&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nf"&gt;repl&lt;/span&gt;&lt;span class="p"&gt;()]&lt;/span&gt;
                &lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;mutants&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;append&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;newnode&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;node&lt;/span&gt;

&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;apply_mutant&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;source&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;mutant_node&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="n"&gt;tree&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;ast&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;parse&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;source&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="c1"&gt;# Simple approach: replace the whole module with a tree holding one mutant
&lt;/span&gt;    &lt;span class="c1"&gt;# In practice, replace node in the tree by matching source position.
&lt;/span&gt;    &lt;span class="c1"&gt;# This demo mutation swaps every occurrence of a single operator.
&lt;/span&gt;    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;ast&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;unparse&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;mutant_node&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;run_tests&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;test_file&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;subprocess&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;run&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
        &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;sys&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;executable&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;-m&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;pytest&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;test_file&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;-q&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
        &lt;span class="n"&gt;capture_output&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="bp"&gt;True&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;text&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="bp"&gt;True&lt;/span&gt;
    &lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="n"&gt;returncode&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;

&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;mutate_and_report&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;source_file&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;test_file&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="n"&gt;source&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;Path&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;source_file&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;read_text&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="n"&gt;tree&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;ast&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;parse&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;source&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;mut&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;Mutator&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="n"&gt;mut&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;visit&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;tree&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="n"&gt;total&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;
    &lt;span class="n"&gt;killed&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;
    &lt;span class="n"&gt;survivors&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[]&lt;/span&gt;

    &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;mutant_ast&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;mut&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;mutants&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="n"&gt;mutant_source&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;apply_mutant&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;source&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;mutant_ast&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="k"&gt;with&lt;/span&gt; &lt;span class="n"&gt;tempfile&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;NamedTemporaryFile&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;mode&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;w&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;suffix&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;.py&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;delete&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="bp"&gt;False&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="n"&gt;f&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
            &lt;span class="n"&gt;f&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;write&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;mutant_source&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
            &lt;span class="n"&gt;mutant_path&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;f&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;name&lt;/span&gt;

        &lt;span class="n"&gt;total&lt;/span&gt; &lt;span class="o"&gt;+=&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;
        &lt;span class="n"&gt;tests_pass&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;run_tests&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;test_file&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;tests_pass&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
            &lt;span class="n"&gt;survivors&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;append&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;mutant_path&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="k"&gt;else&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
            &lt;span class="n"&gt;killed&lt;/span&gt; &lt;span class="o"&gt;+=&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;

    &lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Mutants total: &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;total&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Killed: &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;killed&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Survived: &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nf"&gt;len&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;survivors&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Mutation score: &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;killed&lt;/span&gt; &lt;span class="o"&gt;/&lt;/span&gt; &lt;span class="n"&gt;total&lt;/span&gt;&lt;span class="si"&gt;:&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="o"&gt;%&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt; &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;total&lt;/span&gt; &lt;span class="k"&gt;else&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;No mutants&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;__name__&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;__main__&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="nf"&gt;mutate_and_report&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;sys&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;argv&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt; &lt;span class="n"&gt;sys&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;argv&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Run it after the free model writes the test:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;python mutation_gate.py src/billing.py tests/test_billing.py
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;A mutation score above 80% means the generated test is worth keeping. Below 60%, the test is mostly theater.&lt;/p&gt;

&lt;h2&gt;
  
  
  Decision Table: Which Tests Earn Their Place
&lt;/h2&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Mutation score&lt;/th&gt;
&lt;th&gt;Verdict&lt;/th&gt;
&lt;th&gt;Action&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;90–100%&lt;/td&gt;
&lt;td&gt;Strong&lt;/td&gt;
&lt;td&gt;Keep and merge&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;70–89%&lt;/td&gt;
&lt;td&gt;Acceptable&lt;/td&gt;
&lt;td&gt;Keep, add one edge-case test&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;40–69%&lt;/td&gt;
&lt;td&gt;Weak&lt;/td&gt;
&lt;td&gt;Regenerate with more failure examples&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;0–39%&lt;/td&gt;
&lt;td&gt;Harmful&lt;/td&gt;
&lt;td&gt;Delete and rewrite from a different prompt&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;This table turns a subjective code review into a numeric gate. Teams can set the threshold in CI and reject PRs that lower the mutation score.&lt;/p&gt;

&lt;h2&gt;
  
  
  Where the Free Server Shines
&lt;/h2&gt;

&lt;p&gt;Running this loop locally ties up a laptop for an hour. With MonkeyCode's free server, the mutation runs happen off-machine. The server queues each pytest run, collects the scores, and writes a Markdown report.&lt;/p&gt;

&lt;p&gt;The cost is zero for a reasonable number of mutants. That changes the habit: developers stop treating generated tests as filler and start demanding proof.&lt;/p&gt;

&lt;h2&gt;
  
  
  Honest Limits
&lt;/h2&gt;

&lt;p&gt;This mutation runner is a prototype, not a production framework. It handles only operator-level mutants, not deleted statements or changed arguments. Real projects need a tool like &lt;code&gt;mutmut&lt;/code&gt; or &lt;code&gt;cosmic-ray&lt;/code&gt; running on the server.&lt;/p&gt;

&lt;p&gt;The free server is not a high-performance CI cluster. It executes small test suites and reports results. Heavy integration tests will hit patience limits, not correctness limits.&lt;/p&gt;

&lt;p&gt;Who should not use this approach? Teams shipping safety-critical systems need formal verification, not mutation testing. Developers who already use a commercial mutation suite may find the free-tier overlap redundant.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Core Lesson
&lt;/h2&gt;

&lt;p&gt;Free models are cheap. Trust is expensive. Mutation testing is the exchange rate.&lt;/p&gt;

&lt;p&gt;Next time a free model hands you twenty green tests, run one mutation pass before you merge. Let the survivors teach the model what real coverage means.&lt;/p&gt;

</description>
      <category>ai</category>
      <category>testing</category>
      <category>python</category>
      <category>productivity</category>
    </item>
    <item>
      <title>The $0 Code-Review Pipeline: Free Models, Free Server, No Credit Card</title>
      <dc:creator>Quinn Wang</dc:creator>
      <pubDate>Sun, 30 Aug 2026 11:16:34 +0000</pubDate>
      <link>https://dev.to/codejs_1959/the-0-code-review-pipeline-free-models-free-server-no-credit-card-5c7n</link>
      <guid>https://dev.to/codejs_1959/the-0-code-review-pipeline-free-models-free-server-no-credit-card-5c7n</guid>
      <description>&lt;p&gt;A code-review bot that costs zero dollars per pull request sounds like a compromise. It is not. A free model with a self-hosted server catches missing tests, dead code, and broken error handling as well as paid tiers do for common cases. The trick is choosing the right endpoint and wiring a trigger that runs on every PR.&lt;/p&gt;

&lt;p&gt;This guide uses MonkeyCode, an open-source AI coding assistant, for exactly that setup. Disclosure: This article was prepared as part of MonkeyCode's product outreach. MonkeyCode offers free models and a free server option, so the entire pipeline runs without a credit card. There is no trial clock. There is no billing form. There is only an API endpoint and a prompt.&lt;/p&gt;

&lt;p&gt;The workflow is simple. A GitHub Action detects a pull request, extracts the diff, and sends it to a MonkeyCode endpoint. The endpoint can be the hosted free server or a server you run yourself. The returned comments are posted back to the PR. The whole loop takes less than a minute for a typical diff.&lt;/p&gt;

&lt;p&gt;Why bother with your own server? Control. You set the rate limits, the logging, and the model. The hosted free tier is fine for experiments. A self-hosted server keeps every diff inside your network, which matters for closed-source work. That choice should not feel heavy. It is a matter of one environment variable.&lt;/p&gt;

&lt;p&gt;Here is the complete action. Save it as &lt;code&gt;.github/workflows/ai-review.yml&lt;/code&gt; in any repository.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;ai-review&lt;/span&gt;
&lt;span class="na"&gt;on&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;pull_request&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;types&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="pi"&gt;[&lt;/span&gt;&lt;span class="nv"&gt;opened&lt;/span&gt;&lt;span class="pi"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;synchronize&lt;/span&gt;&lt;span class="pi"&gt;]&lt;/span&gt;
&lt;span class="na"&gt;jobs&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;review&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;runs-on&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;ubuntu-latest&lt;/span&gt;
    &lt;span class="na"&gt;steps&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;uses&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;actions/checkout@v4&lt;/span&gt;
        &lt;span class="na"&gt;with&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
          &lt;span class="na"&gt;fetch-depth&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="m"&gt;0&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Generate diff&lt;/span&gt;
        &lt;span class="na"&gt;run&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;git diff origin/${{ github.event.pull_request.base.ref }}...HEAD &amp;gt; /tmp/diff.txt&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Send diff to MonkeyCode&lt;/span&gt;
        &lt;span class="na"&gt;env&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
          &lt;span class="na"&gt;MC_API_URL&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;${{ secrets.MC_API_URL }}&lt;/span&gt;
          &lt;span class="na"&gt;MC_API_KEY&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;${{ secrets.MC_API_KEY }}&lt;/span&gt;
        &lt;span class="na"&gt;run&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="pi"&gt;|&lt;/span&gt;
          &lt;span class="s"&gt;curl -sS -X POST "$MC_API_URL/v1/completions" \&lt;/span&gt;
            &lt;span class="s"&gt;-H "Content-Type: application/json" \&lt;/span&gt;
            &lt;span class="s"&gt;-H "Authorization: Bearer $MC_API_KEY" \&lt;/span&gt;
            &lt;span class="s"&gt;-d @- &amp;lt;&amp;lt;BODY &amp;gt; /tmp/review.json&lt;/span&gt;
          &lt;span class="s"&gt;{"model":"default","prompt":"You are a code reviewer. Review this diff and list concrete issues:\n\n$(cat /tmp/diff.txt)","max_tokens":500}&lt;/span&gt;
&lt;span class="s"&gt;BODY&lt;/span&gt;
      &lt;span class="s"&gt;- name&lt;/span&gt;&lt;span class="err"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Post comment&lt;/span&gt;
        &lt;span class="s"&gt;run&lt;/span&gt;&lt;span class="err"&gt;:&lt;/span&gt; &lt;span class="pi"&gt;|&lt;/span&gt;
          &lt;span class="s"&gt;comment=$(jq -r .choices[0].text /tmp/review.json)&lt;/span&gt;
          &lt;span class="s"&gt;gh pr comment "${{ github.event.pull_request.number }}" --body "$comment"&lt;/span&gt;
        &lt;span class="na"&gt;env&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
          &lt;span class="na"&gt;GH_TOKEN&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;${{ secrets.GITHUB_TOKEN }}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The workflow needs two repository secrets. &lt;code&gt;MC_API_URL&lt;/code&gt; points to the MonkeyCode server address. &lt;code&gt;MC_API_KEY&lt;/code&gt; holds the free API key from the project's dashboard. If you self-host, point &lt;code&gt;MC_API_URL&lt;/code&gt; to &lt;code&gt;http://localhost:8080&lt;/code&gt; and keep the key secret. The JSON body asks for a review with &lt;code&gt;max_tokens&lt;/code&gt; set to 500, which keeps the response short enough for a PR comment.&lt;/p&gt;

&lt;p&gt;The diff generation command is the fragile part. It assumes the branch was opened from the current default branch. For forks, &lt;code&gt;git diff&lt;/code&gt; may not see the history. A safer method is to use the GitHub API to fetch the diff directly. That change is left as an exercise because most personal projects run on branches, not forks.&lt;/p&gt;

&lt;p&gt;The &lt;code&gt;jq&lt;/code&gt; parser is available on &lt;code&gt;ubuntu-latest&lt;/code&gt;. If you use a lighter runner, install it first. The &lt;code&gt;gh&lt;/code&gt; CLI also needs to be present, and it is. Nothing else is required. The action is intentionally small.&lt;/p&gt;

&lt;p&gt;A decision table helps you pick the server mode. The factors are straightforward.&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Factor&lt;/th&gt;
&lt;th&gt;Hosted free server&lt;/th&gt;
&lt;th&gt;Self-hosted free server&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Setup time&lt;/td&gt;
&lt;td&gt;About five minutes&lt;/td&gt;
&lt;td&gt;About thirty minutes&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Data privacy&lt;/td&gt;
&lt;td&gt;Diff leaves your network&lt;/td&gt;
&lt;td&gt;Diff stays on your machine&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Capacity&lt;/td&gt;
&lt;td&gt;Limited by provider&lt;/td&gt;
&lt;td&gt;Limited by your hardware&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Best for&lt;/td&gt;
&lt;td&gt;Quick trials, open source&lt;/td&gt;
&lt;td&gt;Private repos, compliance&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;That table is not a benchmark. It is a cost model. The hosted server wins when you want a taste without reading a README. The self-hosted server wins when your code is the product.&lt;/p&gt;

&lt;p&gt;Free models come with limitations. Context windows are smaller than the latest paid tier. Rate limits are lower. The model name and capabilities can change as the project evolves. Treat this bot as a formatting and sanity check, not an architectural oracle. It will catch a missing &lt;code&gt;else&lt;/code&gt; or an unhandled null. It will not design your event-driven microservice.&lt;/p&gt;

&lt;p&gt;The output is a suggestion. Do not make the PR fail when the bot complains. Let a human decide. The bot's value is in the baseline it creates: every diff gets a first pass before a human looks at it.&lt;/p&gt;

&lt;p&gt;What about the free server? The MonkeyCode repository documents how to launch it. The server exposes a compatible API on a local port. You can run it on a spare laptop or a small VPS. It does not need a GPU for the free model tier. A single CPU with a few gigabytes of RAM is enough for occasional PR reviews.&lt;/p&gt;

&lt;p&gt;This pipeline was tested against a real side project. The review caught a missing null check in a TypeScript function and a SQL query that forgot a &lt;code&gt;WHERE&lt;/code&gt; clause. It also generated a false positive on a closure variable name. That one-in-three hit rate is fine for a free tool. The false positive cost a minute to dismiss.&lt;/p&gt;

&lt;p&gt;You can reproduce that result. Clone any repository, add the action, and open a PR. The first run takes about two minutes because the Action itself has to spin up. The second run is faster. Keep the diff small for the first trial. A feedback loop of ten lines is easier to judge than a rewrite of the whole codebase.&lt;/p&gt;

&lt;p&gt;The setup cost is nearly zero. MonkeyCode's free models and free server make the experiment free. The only resource you spend is the time to read the repository's README and set two secrets. That is the right price for a test worth running.&lt;/p&gt;

&lt;p&gt;Try it on a side project this weekend. After a few PRs, look at the comments you actually accepted. If the bot catches one real bug a week, it has already paid for itself in attention. If it only contradicts your style guide, delete the workflow and move on. Either way, you now know what a zero-dollar review bot feels like.&lt;/p&gt;

&lt;p&gt;The open-source project is listed on the MonkeyCode GitHub page. Start there, run the server, and point this workflow at your next pull request.&lt;/p&gt;

</description>
      <category>ai</category>
      <category>opensource</category>
      <category>devops</category>
      <category>productivity</category>
    </item>
    <item>
      <title>Friction Log: The First Fifteen Minutes of an AI Coding Tool</title>
      <dc:creator>Quinn Wang</dc:creator>
      <pubDate>Sat, 29 Aug 2026 10:22:53 +0000</pubDate>
      <link>https://dev.to/codejs_1959/friction-log-the-first-fifteen-minutes-of-an-ai-coding-tool-4h27</link>
      <guid>https://dev.to/codejs_1959/friction-log-the-first-fifteen-minutes-of-an-ai-coding-tool-4h27</guid>
      <description>&lt;p&gt;The first fifteen minutes of an AI coding tool have almost nothing to do with the model, because the model only matters once the editor, the config, and a server on the other end of the network agree to talk to one another. When those seams are quiet, you trust the tool enough to hand it a real task, and when they squeak, you blame the model and start shopping for the next thing. That is why I recently stopped timing benchmarks and started timing my own setup, and the result was a small friction log that changed how I review every AI assistant.&lt;/p&gt;

&lt;p&gt;MonkeyCode is an open-source AI coding assistant that offers two things I usually distrust in the same breath: free model access and a free server option. Disclosure: This article was prepared as part of MonkeyCode's product outreach. I approached it the way I approach every free tier, which means I treated the generosity as temporary, the documentation as optimistic, and the first prompt as a coin flip. My start line was identical for every tool: a fresh project, an empty config, and a stopwatch running in the corner of my terminal.&lt;/p&gt;

&lt;p&gt;The metric I care about is time to first useful edit, which I define as the exact second a suggestion lands in my editor and I actually keep it instead of deleting it in confusion. My first attempt failed on the most boring blocker imaginable: the tool sat silent on the first prompt, and that silence is the worst failure mode in this category because it looks like a model problem when it is usually a routing problem. I checked the model settings, I checked the API key, and I was about to abandon the whole free-tier category when I remembered my own rule from a previous teardown: audit the server, not the model. The server is the one part of the chain you can interrogate directly, and a single curl command tells you more than five minutes of prompt fiddling ever will.&lt;/p&gt;

&lt;p&gt;The fix that mattered took about twenty seconds, because the default configuration was not broken, it was just pointing at a destination I had never verified. Once I pointed the client at the free server option instead of assuming I had to host the backend myself, the first response showed up fast enough that I laughed at the fifteen minutes I had spent staring at silence. That is the exact moment every AI tool earns me or loses me, but how many tools have you abandoned right before the one-line fix that would have saved them? Every AI coding tool promotes the developer to reviewer, and nobody has ever tested the reviewer, but the first fifteen minutes are that test whether you run it deliberately or not.&lt;/p&gt;

&lt;p&gt;Here is the reproducible part: a tiny friction log that costs nothing and survives every tool you throw at it. Put it in &lt;code&gt;~/.local/bin&lt;/code&gt; and source it at the start of any evaluation session, then run your setup exactly the way a new user would.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;#!/usr/bin/env bash&lt;/span&gt;
&lt;span class="c"&gt;# friction.sh — a stopwatch for setup sessions&lt;/span&gt;
&lt;span class="c"&gt;# usage: source friction.sh; note "opened docs"; note "first suggestion kept"&lt;/span&gt;
&lt;span class="nv"&gt;_start&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="si"&gt;$(&lt;/span&gt;&lt;span class="nb"&gt;date&lt;/span&gt; +%s&lt;span class="si"&gt;)&lt;/span&gt;
note&lt;span class="o"&gt;()&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
  &lt;span class="nb"&gt;printf&lt;/span&gt; &lt;span class="s1"&gt;'%4ss  %s\n'&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="k"&gt;$((&lt;/span&gt; &lt;span class="si"&gt;$(&lt;/span&gt;&lt;span class="nb"&gt;date&lt;/span&gt; +%s&lt;span class="si"&gt;)&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; _start &lt;span class="k"&gt;))&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$*&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; | &lt;span class="nb"&gt;tee&lt;/span&gt; &lt;span class="nt"&gt;-a&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$HOME&lt;/span&gt;&lt;span class="s2"&gt;/friction.log"&lt;/span&gt;
&lt;span class="o"&gt;}&lt;/span&gt;
first_edit&lt;span class="o"&gt;()&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
  &lt;span class="nb"&gt;grep&lt;/span&gt; &lt;span class="nt"&gt;-i&lt;/span&gt; &lt;span class="s2"&gt;"kept"&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$HOME&lt;/span&gt;&lt;span class="s2"&gt;/friction.log"&lt;/span&gt; | &lt;span class="nb"&gt;head&lt;/span&gt; &lt;span class="nt"&gt;-1&lt;/span&gt; | &lt;span class="nb"&gt;awk&lt;/span&gt; &lt;span class="s1"&gt;'{print $1}'&lt;/span&gt;
&lt;span class="o"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Call &lt;code&gt;note&lt;/code&gt; after every step that costs you attention, especially the ones that make you open a browser tab you did not plan to open. When you finally keep a suggestion, call &lt;code&gt;note "first suggestion kept"&lt;/code&gt; and then run &lt;code&gt;first_edit&lt;/code&gt; to see how many seconds of your life this tool consumed before it did anything useful. The gaps between those markers are the real review, because a tool that loses you at step three will never show you how good step fifty is.&lt;/p&gt;

&lt;p&gt;When a tool goes silent, resist the urge to rewrite the prompt and interrogate the chain in this order instead: config, endpoint, auth, restart. First ask whether the client read the config you think it read, then probe the server with a plain curl, then check whether a token is present and still valid, and only then restart the whole thing.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# does the endpoint answer at all, and how slowly?&lt;/span&gt;
curl &lt;span class="nt"&gt;-sS&lt;/span&gt; &lt;span class="nt"&gt;-o&lt;/span&gt; /dev/null &lt;span class="nt"&gt;-w&lt;/span&gt; &lt;span class="s1"&gt;'HTTP %{http_code} in %{time_total}s\n'&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$SERVER_URL&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;

&lt;span class="c"&gt;# is a token actually in the environment, or did you dream it?&lt;/span&gt;
&lt;span class="nb"&gt;env&lt;/span&gt; | &lt;span class="nb"&gt;grep&lt;/span&gt; &lt;span class="nt"&gt;-i&lt;/span&gt; token | &lt;span class="nb"&gt;sed&lt;/span&gt; &lt;span class="s1"&gt;'s/=.*/=&amp;lt;redacted&amp;gt;/'&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;That order works for MonkeyCode, and it works for every other assistant I have tried, because the model is almost never the first thing that breaks. Free model access and a free server are the two availability claims that made this teardown possible, but those are exactly the kind of facts you verify in the repository before you build a workflow around them. Free tiers change without ceremony, and a quota you read on a blog post today is already a historical document by the time your calendar catches up.&lt;/p&gt;

&lt;p&gt;Who should not use this approach: anyone whose evaluation question is about answer quality on hard problems, because fifteen minutes says nothing about that, and anyone whose policy requires code to stay on company hardware, because a hosted free server is a non-starter regardless of how convenient it is. This is not a benchmark either, and I am deliberately not printing a number for the free tier, because an unverifiable number is exactly the outdated data this article is supposed to avoid. What this approach is good for is deciding whether a tool deserves a second hour at all.&lt;/p&gt;

&lt;p&gt;Steal the friction log for your next evaluation, because fifteen minutes of honest timestamps will save you fifteen hours of feature-list reading. If MonkeyCode ends up on your shortlist, treat its repository as the source of truth for what the free tier includes this month, and let the first useful edit do the talking. The best review you will ever write is the one where you can point to the exact second a tool won you over, or the exact second it lost you.&lt;/p&gt;

</description>
      <category>ai</category>
      <category>productivity</category>
      <category>devex</category>
      <category>testing</category>
    </item>
    <item>
      <title>Run the Cheap Review First</title>
      <dc:creator>Quinn Wang</dc:creator>
      <pubDate>Wed, 26 Aug 2026 11:17:36 +0000</pubDate>
      <link>https://dev.to/codejs_1959/run-the-cheap-review-first-10oj</link>
      <guid>https://dev.to/codejs_1959/run-the-cheap-review-first-10oj</guid>
      <description>&lt;p&gt;Most code review errors are boring. They are typos, wrong variable names, missing null checks, and copy-paste mistakes. A free model catches these reliably. A paid model catches them slightly faster. The difference rarely matters. The expensive model earns its money later, on the second pass, when the review turns architectural. Most teams run these two passes in the wrong order. They send every diff to the most powerful model they can afford. Then they run out of budget and patience before the interesting questions get asked.&lt;/p&gt;

&lt;p&gt;Disclosure: This article was prepared as part of MonkeyCode's product outreach. MonkeyCode's free models and free server make the two-queue design practical for a solo developer or a small team. The free tier is not a compromise here. It is the correct tool for the first pass.&lt;/p&gt;

&lt;p&gt;The two-queue rule is simple. Every incoming diff gets classified into one of two queues. The fast queue handles mechanical errors. The slow queue handles design problems. The fast queue runs on free models. The slow queue runs on the best model you can justify. The classification is the hard part, and it is a script, not a feeling.&lt;/p&gt;

&lt;p&gt;Think of a hospital triage desk. A paper cut does not go to surgery. A chest pain does not wait in the minor injuries line. Code review needs the same discipline. Most diffs are paper cuts. A few are chest pains. Routing them correctly is worth more than upgrading the model.&lt;/p&gt;

&lt;p&gt;Here is a routing script that reads a diff and assigns a risk score. Save it as &lt;code&gt;queue-diff.py&lt;/code&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="c1"&gt;#!/usr/bin/env python3
&lt;/span&gt;&lt;span class="sh"&gt;"""&lt;/span&gt;&lt;span class="s"&gt;queue-diff.py — route a diff to the fast or slow review queue.&lt;/span&gt;&lt;span class="sh"&gt;"""&lt;/span&gt;
&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;re&lt;/span&gt;
&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;sys&lt;/span&gt;

&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;risk_score&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;diff_text&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;str&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="nb"&gt;int&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="n"&gt;score&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;
    &lt;span class="n"&gt;added&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;re&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;findall&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sa"&gt;r&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;^\+[^+]&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;diff_text&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;re&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;MULTILINE&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;score&lt;/span&gt; &lt;span class="o"&gt;+=&lt;/span&gt; &lt;span class="nf"&gt;len&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;added&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;//&lt;/span&gt; &lt;span class="mi"&gt;50&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;re&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;search&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sa"&gt;r&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;TODO|FIXME|HACK&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;diff_text&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
        &lt;span class="n"&gt;score&lt;/span&gt; &lt;span class="o"&gt;+=&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;re&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;search&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sa"&gt;r&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;except:|except\s&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;diff_text&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
        &lt;span class="n"&gt;score&lt;/span&gt; &lt;span class="o"&gt;+=&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;re&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;search&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sa"&gt;r&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;password|token|secret|api[_-]?key&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;diff_text&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;re&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;IGNORECASE&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
        &lt;span class="n"&gt;score&lt;/span&gt; &lt;span class="o"&gt;+=&lt;/span&gt; &lt;span class="mi"&gt;5&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;score&lt;/span&gt;

&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;main&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="bp"&gt;None&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="n"&gt;diff&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;sys&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;stdin&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;read&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="n"&gt;score&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;risk_score&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;diff&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;SLOW_QUEUE&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt; &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;score&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;=&lt;/span&gt; &lt;span class="mi"&gt;5&lt;/span&gt; &lt;span class="k"&gt;else&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;FAST_QUEUE&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;__name__&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;__main__&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="nf"&gt;main&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Run it in a review pipeline like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;git diff origin/main...HEAD | python3 queue-diff.py
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The script is deliberately naive. It does not understand semantics. It understands signals. A diff touching a secrets file goes to the slow queue. A config change with two lines goes to the fast queue. A fifty-line refactor goes to the slow queue. The threshold is a starting point, not a law.&lt;/p&gt;

&lt;p&gt;The fast queue review prompt is short. Ask the free model to check three things. Are names consistent? Are there null or undefined risks? Are there obvious copy-paste errors? That is the entire brief. No architecture questions. No style debates. The output is a list of concrete findings or a single word: clean.&lt;/p&gt;

&lt;p&gt;The slow queue review prompt is different. It asks about coupling, error propagation, and whether the change matches the surrounding design. This is where a stronger model changes the outcome. This is where the paid tier earns its cost. Running this prompt on every tiny diff is how teams burn their budget on noise.&lt;/p&gt;

&lt;p&gt;The two-queue design changes the economics of review. A team of five reviewing ten pull requests a day can route most diffs to the fast queue. The free models handle the mechanical pass. The slow queue sees only the diffs that matter. The result is a review process that scales with attention, not with token spend.&lt;/p&gt;

&lt;p&gt;MonkeyCode fits this workflow because both queues are available in one client. The free models cover the fast queue without a subscription. The free server removes the local hardware requirement, which matters on a company laptop. The setup is a config change, not a migration. The project is open source, so the limits are visible in the repository. Check the current numbers there before planning around them.&lt;/p&gt;

&lt;p&gt;The decision table for queue assignment looks like this:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Diff characteristic&lt;/th&gt;
&lt;th&gt;Queue&lt;/th&gt;
&lt;th&gt;Why&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Config, docs, styles&lt;/td&gt;
&lt;td&gt;Fast&lt;/td&gt;
&lt;td&gt;Mechanical changes, low blast radius&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;One function, clear scope&lt;/td&gt;
&lt;td&gt;Fast&lt;/td&gt;
&lt;td&gt;Free models handle local errors&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Cross-module refactor&lt;/td&gt;
&lt;td&gt;Slow&lt;/td&gt;
&lt;td&gt;Design questions dominate&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Auth, secrets, payments&lt;/td&gt;
&lt;td&gt;Slow&lt;/td&gt;
&lt;td&gt;Failure cost is high&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Generated code&lt;/td&gt;
&lt;td&gt;Fast&lt;/td&gt;
&lt;td&gt;Nobody should review it deeply&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;The approach has limits. Free models still hallucinate on ambiguous code. The fast queue can miss subtle concurrency bugs. A free server adds network latency and sends code off-device. Teams under compliance rules should skip the server and run a local model instead. Solo developers on air-gapped machines cannot use this workflow at all. The two-queue rule is a budget strategy, not a quality guarantee.&lt;/p&gt;

&lt;p&gt;Some teams should not use this approach at all. Teams reviewing security-critical code. Organizations with strict data residency policies. Projects where every line is a liability. These teams need the slow queue for everything. The free tier does not help them, and pretending otherwise is how review pipelines rot.&lt;/p&gt;

&lt;p&gt;The core lesson stands. Run the cheap review first. Reserve the expensive review for the diffs that deserve it. The free models are not a downgrade. They are the first pass of a two-stage process, and they are the stage that runs most often.&lt;/p&gt;

&lt;p&gt;If you want to try the two-queue setup, clone the open source repository and check the current free model list and server status. The script in this article is yours to keep. It works with any client that reads a diff from stdin. The review budget is the real constraint, and this workflow spends it where it counts.&lt;/p&gt;

</description>
      <category>ai</category>
      <category>productivity</category>
      <category>testing</category>
      <category>webdev</category>
    </item>
    <item>
      <title>The 10M Token Question</title>
      <dc:creator>Quinn Wang</dc:creator>
      <pubDate>Tue, 25 Aug 2026 09:14:37 +0000</pubDate>
      <link>https://dev.to/codejs_1959/the-10m-token-question-2o3j</link>
      <guid>https://dev.to/codejs_1959/the-10m-token-question-2o3j</guid>
      <description>&lt;p&gt;&lt;strong&gt;Disclosure: This article was prepared as part of MonkeyCode's product outreach.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Most developers ask the wrong question about AI coding tools, and I used to be one of them. We argue about model rankings and leaderboard scores, yet the assistants that actually change our workflow are rarely the smartest ones; they are the ones we can afford to run badly. That is why I now evaluate every coding tool the same way: give it a hard token budget, put it on a free server, and watch what it does while the meter is running.&lt;/p&gt;

&lt;p&gt;Benchmarks measure the model, not the loop, and that distinction is the whole game. A model can score beautifully on a curated suite and still burn forty thousand tokens explaining a syntax error, because no leaderboard measures the cost of a rambling conversation. The price signal is the only honest feedback loop we have, and most paid subscriptions hide it behind a flat monthly fee where waste feels free.&lt;/p&gt;

&lt;p&gt;This is where MonkeyCode enters the picture, and I will be honest about my bias up front: the project gives you free model access, a free server option, and a ten-million-token allowance, which sounds like marketing until you treat it as an experiment budget. Ten million tokens is roughly enough to run a small feature through a coding assistant a few hundred times, which means you can finally measure the variance between a good day and a bad day. Generosity, in other words, is a debugging strategy rather than a giveaway, because it removes the excuse not to look at your own usage.&lt;/p&gt;

&lt;p&gt;The free server matters just as much as the token budget, and localhost is a liar when it comes to agent behavior. An assistant that feels instant on your laptop will stall, retry, and quietly fail once it faces real network latency, cold starts, and rate limits, and a free server reproduces those conditions without asking for a credit card. If a tool cannot survive a constrained environment, it will not survive your production environment either, and you deserve to learn that before you pay for it.&lt;/p&gt;

&lt;h3&gt;
  
  
  The budget test
&lt;/h3&gt;

&lt;p&gt;So here is the reproducible artifact I use, a small budget test that treats a real task as the unit of measurement instead of a benchmark question. Save the script below, point it at your tool's CLI or any OpenAI-compatible endpoint, and run it against a task file from your own repository. It takes about ten minutes, and if your endpoint is free, the only thing you spend is attention.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;#!/usr/bin/env bash&lt;/span&gt;
&lt;span class="c"&gt;# budget_test.sh — measure tokens per real task, not per benchmark&lt;/span&gt;
&lt;span class="nb"&gt;set&lt;/span&gt; &lt;span class="nt"&gt;-euo&lt;/span&gt; pipefail

&lt;span class="nv"&gt;TASK_FILE&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;1&lt;/span&gt;&lt;span class="k"&gt;:-&lt;/span&gt;&lt;span class="nv"&gt;task&lt;/span&gt;&lt;span class="p"&gt;.md&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;
&lt;span class="nv"&gt;LOG&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"budget_&lt;/span&gt;&lt;span class="si"&gt;$(&lt;/span&gt;&lt;span class="nb"&gt;date&lt;/span&gt; +%Y%m%d_%H%M%S&lt;span class="si"&gt;)&lt;/span&gt;&lt;span class="s2"&gt;.jsonl"&lt;/span&gt;

run&lt;span class="o"&gt;()&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
  &lt;span class="nb"&gt;local &lt;/span&gt;&lt;span class="nv"&gt;label&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$1&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="nv"&gt;prompt&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$2&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;
  &lt;span class="nb"&gt;local &lt;/span&gt;start end response usage
  &lt;span class="nv"&gt;start&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="si"&gt;$(&lt;/span&gt;&lt;span class="nb"&gt;date&lt;/span&gt; +%s&lt;span class="si"&gt;)&lt;/span&gt;
  &lt;span class="c"&gt;# Point ENDPOINT and API_KEY at your tool's CLI or compatible endpoint.&lt;/span&gt;
  &lt;span class="nv"&gt;response&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="si"&gt;$(&lt;/span&gt;curl &lt;span class="nt"&gt;-s&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;ENDPOINT&lt;/span&gt;&lt;span class="k"&gt;:-&lt;/span&gt;&lt;span class="nv"&gt;http&lt;/span&gt;://localhost:8080/v1/chat/completions&lt;span class="k"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
    &lt;span class="nt"&gt;-H&lt;/span&gt; &lt;span class="s2"&gt;"Authorization: Bearer &lt;/span&gt;&lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;API_KEY&lt;/span&gt;&lt;span class="k"&gt;:-&lt;/span&gt;&lt;span class="nv"&gt;test&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
    &lt;span class="nt"&gt;-H&lt;/span&gt; &lt;span class="s2"&gt;"Content-Type: application/json"&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
    &lt;span class="nt"&gt;-d&lt;/span&gt; &lt;span class="s2"&gt;"{&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;model&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;:&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;MODEL&lt;/span&gt;&lt;span class="k"&gt;:-&lt;/span&gt;&lt;span class="nv"&gt;default&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;,&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;messages&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;:[{&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;role&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;:&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;user&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;,&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;content&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;:&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="nv"&gt;$prompt&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;}]}"&lt;/span&gt;&lt;span class="si"&gt;)&lt;/span&gt;
  &lt;span class="nv"&gt;end&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="si"&gt;$(&lt;/span&gt;&lt;span class="nb"&gt;date&lt;/span&gt; +%s&lt;span class="si"&gt;)&lt;/span&gt;
  &lt;span class="nv"&gt;usage&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="si"&gt;$(&lt;/span&gt;&lt;span class="nb"&gt;printf&lt;/span&gt; &lt;span class="s1"&gt;'%s'&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$response&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; | jq &lt;span class="nt"&gt;-c&lt;/span&gt; &lt;span class="s1"&gt;'.usage // {prompt_tokens:0, completion_tokens:0}'&lt;/span&gt;&lt;span class="si"&gt;)&lt;/span&gt;
  &lt;span class="nb"&gt;printf&lt;/span&gt; &lt;span class="s1"&gt;'{"label":"%s","seconds":%d,"usage":%s}\n'&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$label&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="k"&gt;$((&lt;/span&gt;end-start&lt;span class="k"&gt;))&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$usage&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$LOG&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;
&lt;span class="o"&gt;}&lt;/span&gt;

run &lt;span class="s2"&gt;"explain"&lt;/span&gt; &lt;span class="s2"&gt;"Read the failing test in &lt;/span&gt;&lt;span class="nv"&gt;$TASK_FILE&lt;/span&gt;&lt;span class="s2"&gt; and explain the root cause in three sentences."&lt;/span&gt;
run &lt;span class="s2"&gt;"plan"&lt;/span&gt; &lt;span class="s2"&gt;"Propose a fix for &lt;/span&gt;&lt;span class="nv"&gt;$TASK_FILE&lt;/span&gt;&lt;span class="s2"&gt; without writing any code."&lt;/span&gt;
run &lt;span class="s2"&gt;"patch"&lt;/span&gt; &lt;span class="s2"&gt;"Write the minimal patch for &lt;/span&gt;&lt;span class="nv"&gt;$TASK_FILE&lt;/span&gt;&lt;span class="s2"&gt;."&lt;/span&gt;

jq &lt;span class="nt"&gt;-s&lt;/span&gt; &lt;span class="s1"&gt;'{runs:length,
        total_seconds:(map(.seconds)|add),
        total_prompt_tokens:(map(.usage.prompt_tokens)|add),
        total_completion_tokens:(map(.usage.completion_tokens)|add)}'&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$LOG&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Run it three times on the same task, because one sample tells you nothing about variance, and then read the ledger like a doctor reads a chart. If the explain step burns more than a couple thousand tokens, your task file is too vague or the tool cannot see your codebase. If the plan step simply repeats the prompt back at you, the tool is ignoring your constraints, and no amount of clever prompting will fix that.&lt;/p&gt;

&lt;p&gt;The decision table below is the part that turns the script into a decision, and I trust it more than any benchmark score. It converts three runs into a diagnosis, which is the difference between guessing and knowing. Read the row that matches your worst result, and act on it before you spend another token.&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Observation&lt;/th&gt;
&lt;th&gt;Diagnosis&lt;/th&gt;
&lt;th&gt;Action&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;explain step &amp;gt; 2k tokens&lt;/td&gt;
&lt;td&gt;Context too vague or codebase invisible&lt;/td&gt;
&lt;td&gt;Tighten the task file, shrink the repo scope&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;plan echoes the prompt&lt;/td&gt;
&lt;td&gt;Tool ignores constraints&lt;/td&gt;
&lt;td&gt;Switch tool or endpoint before paying&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;patch fails twice in a row&lt;/td&gt;
&lt;td&gt;Model cannot see the relevant code&lt;/td&gt;
&lt;td&gt;Reduce file size, add explicit file paths&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;seconds grow across runs&lt;/td&gt;
&lt;td&gt;Server or context degradation&lt;/td&gt;
&lt;td&gt;Test again on a different free server&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;h3&gt;
  
  
  Limitations
&lt;/h3&gt;

&lt;p&gt;Who should not use this approach? Teams with production SLAs should not run real workloads on a free server, and anyone in a regulated environment should check data policies before sending code anywhere. Free tiers also change without notice, so a ten-million-token budget is a starting point for measurement, not a permanent promise, and my script assumes an OpenAI-compatible endpoint or a thin CLI wrapper around whatever tool you choose.&lt;/p&gt;

&lt;p&gt;The uncomfortable truth is that most of us do not know how many tokens we waste in a normal day, because the tools we pay for make waste invisible. A free server and a generous budget flip that equation, turning cost into a signal you can actually read, and that is why I now recommend the cheapest setup to everyone who asks. The next time somebody asks me which coding assistant is best, I ask them a different question: how many tokens did you waste yesterday?&lt;/p&gt;

&lt;p&gt;If you do not know the answer, go find a free server, grab a budget, and start measuring, because the best assistant is the one whose waste you can see. MonkeyCode's free tier is a fine place to run this ledger, and the open-source codebase means you can read what the tool does before you trust what it says. If you run the budget test on your own tasks, I would genuinely like to hear what your token ledger tells you.&lt;/p&gt;

</description>
      <category>ai</category>
      <category>opensource</category>
      <category>productivity</category>
      <category>webdev</category>
    </item>
    <item>
      <title>Audit the Server, Not the Model: A Free-Tier Test for AI Coding Tools</title>
      <dc:creator>Quinn Wang</dc:creator>
      <pubDate>Tue, 25 Aug 2026 05:01:34 +0000</pubDate>
      <link>https://dev.to/codejs_1959/audit-the-server-not-the-model-a-free-tier-test-for-ai-coding-tools-37pe</link>
      <guid>https://dev.to/codejs_1959/audit-the-server-not-the-model-a-free-tier-test-for-ai-coding-tools-37pe</guid>
      <description>&lt;p&gt;The model decides what the tool says, but the server decides what the tool does, and most developers audit the first and ignore the second. When you evaluate an AI coding assistant you ask which model it wraps, how many tokens the free tier includes, and whether the completions feel smart, yet the moment the tool has to actually run your code the whole calculation changes. My position is simple: the free server is the part of the free tier worth auditing first, because it tells you whether the tool can close the loop between a suggestion and a working program.&lt;/p&gt;

&lt;p&gt;A token allowance is just a number on a pricing page until you watch it burn inside a real edit cycle, and a clever model is just a chat partner until its output has to survive a compiler. That is why I stopped asking which model is behind a tool and started asking where the code runs when I press run. The second question exposes more about a tool's honesty than any benchmark, because a tool that only generates text can look brilliant in a screenshot and fall apart in a fresh workspace.&lt;/p&gt;

&lt;p&gt;Disclosure: This article was prepared as part of MonkeyCode's product outreach. MonkeyCode is an open-source AI coding assistant that currently offers free model access alongside a free hosted server option, and the operator's current free allowance sits at 10 million tokens; I treat that number as a snapshot rather than a promise, because quotas move and you should verify them before you build a workflow on top of them.&lt;/p&gt;

&lt;p&gt;Why do I care about the server more than the model? Because the model is replaceable and the loop is not, and a free server lets you test the loop without the usual excuses. Local setups lie to you: your dependencies are already installed, your environment variables are already set, and your linter already knows your preferences, so the tool gets credit for your machine's preparation. A blank remote workspace removes that advantage and forces the assistant to bootstrap from nothing, which is exactly the condition most real projects start from.&lt;/p&gt;

&lt;p&gt;Have you ever noticed that every demo video ends exactly when the code compiles? That is not a coincidence, because the interesting part of an AI coding session is not the first suggestion but the tenth correction, and the tenth correction is precisely what a screenshot cannot show. A free server changes that dynamic because it gives you a place where the failure is visible, and visible failure is the only honest material for an evaluation.&lt;/p&gt;

&lt;p&gt;Here is the audit I run on any tool that offers a free server, and it is deliberately dumb because it measures three numbers instead of pretending to judge quality. The script is a ledger, not a benchmark: it records time to first edit, time to first run, and error-to-fix cycles, then leaves the interpretation to you.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;#!/usr/bin/env bash&lt;/span&gt;
&lt;span class="c"&gt;# audit-free-tier.sh — a ledger for your next AI coding tool trial&lt;/span&gt;
&lt;span class="nb"&gt;set&lt;/span&gt; &lt;span class="nt"&gt;-euo&lt;/span&gt; pipefail
&lt;span class="nv"&gt;LOG&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;1&lt;/span&gt;&lt;span class="k"&gt;:-&lt;/span&gt;&lt;span class="nv"&gt;free&lt;/span&gt;&lt;span class="p"&gt;-tier-audit.csv&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;
: &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$LOG&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;
now&lt;span class="o"&gt;()&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt; &lt;span class="nb"&gt;date&lt;/span&gt; +%s&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="o"&gt;}&lt;/span&gt;
stamp&lt;span class="o"&gt;()&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt; &lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="si"&gt;$(&lt;/span&gt;now&lt;span class="si"&gt;)&lt;/span&gt;&lt;span class="s2"&gt;,&lt;/span&gt;&lt;span class="nv"&gt;$1&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$LOG&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="o"&gt;}&lt;/span&gt;

stamp &lt;span class="s2"&gt;"session_start"&lt;/span&gt;
&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"1. Open the tool's free server workspace and paste this task:"&lt;/span&gt;
&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s1"&gt;'Build a Node HTTP server with a /health route returning {"ok":true}'&lt;/span&gt;
&lt;span class="nb"&gt;read&lt;/span&gt; &lt;span class="nt"&gt;-rp&lt;/span&gt; &lt;span class="s2"&gt;"Press Enter when the first edit appears"&lt;/span&gt; _
stamp &lt;span class="s2"&gt;"first_edit"&lt;/span&gt;

&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"2. Run the generated code in the remote terminal."&lt;/span&gt;
&lt;span class="nb"&gt;read&lt;/span&gt; &lt;span class="nt"&gt;-rp&lt;/span&gt; &lt;span class="s2"&gt;"Press Enter after the first run attempt"&lt;/span&gt; _
stamp &lt;span class="s2"&gt;"first_run"&lt;/span&gt;

&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"3. If it failed, paste the error back and count cycles."&lt;/span&gt;
&lt;span class="k"&gt;for &lt;/span&gt;i &lt;span class="k"&gt;in &lt;/span&gt;1 2 3 4 5&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="k"&gt;do
  &lt;/span&gt;&lt;span class="nb"&gt;read&lt;/span&gt; &lt;span class="nt"&gt;-rp&lt;/span&gt; &lt;span class="s2"&gt;"Does it pass now? (y/n) "&lt;/span&gt; ok
  &lt;span class="o"&gt;[&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$ok&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"y"&lt;/span&gt; &lt;span class="o"&gt;]&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt; stamp &lt;span class="s2"&gt;"fixed_cycle_&lt;/span&gt;&lt;span class="nv"&gt;$i&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="nb"&gt;break&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="o"&gt;}&lt;/span&gt;
  stamp &lt;span class="s2"&gt;"failed_cycle_&lt;/span&gt;&lt;span class="nv"&gt;$i&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;
&lt;span class="k"&gt;done

&lt;/span&gt;&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"Ledger written to &lt;/span&gt;&lt;span class="nv"&gt;$LOG&lt;/span&gt;&lt;span class="s2"&gt;. Now compute:"&lt;/span&gt;
&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"  time_to_first_edit = first_edit - session_start"&lt;/span&gt;
&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"  time_to_first_run  = first_run - first_edit"&lt;/span&gt;
&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"  fix_cycles         = the last cycle number in the log"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Notice what the script does not do: it does not measure code quality, it does not score the model, and it does not care which vendor you are testing. That is intentional, because the moment you add subjective scoring you start rationalizing the tool you already want to like, and the whole exercise collapses into confirmation bias. A ledger keeps you honest by reducing the trial to events you can timestamp, which is why I run it on every new assistant instead of trusting my memory of how the session felt.&lt;/p&gt;

&lt;p&gt;The numbers only mean something when you compare them across tools, so I keep a small table in my notes and I read it the same way every time. If the first edit lands within two minutes, the tool can bootstrap from a blank workspace; if the first run succeeds on the first attempt, the free server is actually executing code rather than just suggesting it; if error recovery takes more than two cycles, you are doing the debugging and the assistant is a typewriter with opinions. Tools with impressive models die on step two because their free server is a glorified preview pane, while modest models look great because the remote environment handles setup for them.&lt;/p&gt;

&lt;p&gt;Teams keep choosing coding assistants the way they choose headphones, by reading spec sheets and trusting the brand, and then they discover six weeks later that the tool cannot run their monorepo or their CI pipeline. A ten-minute free-server audit will not catch every problem, but it will catch the most expensive one: the gap between what the tool generates and what the tool can execute. That gap is where developer time actually disappears, and it is the one number every vendor would prefer you never measure.&lt;/p&gt;

&lt;p&gt;Who should not use this approach? If you already have a locked production workflow and a model you trust, a free-tier audit is noise, and if your project depends on proprietary libraries or private data, a free hosted workspace is the wrong place to test it. The audit also has real limits: ten million tokens sound generous until a long refactor starts eating them, free servers are not production infrastructure, and the numbers measure the tool's loop rather than the model's ceiling. Treat the ledger as a first date, not a marriage contract, and re-run it whenever the vendor changes the quota or the execution environment.&lt;/p&gt;

&lt;p&gt;The next time someone shows you an AI coding tool, ask to see the free server instead of the model card, because a suggestion you cannot run is just a paragraph with syntax highlighting. If you want to run this audit somewhere with a free server, MonkeyCode's free tier is a reasonable place to start, but bring your own stopwatch — the point is the method, not the vendor.&lt;/p&gt;

</description>
      <category>ai</category>
      <category>productivity</category>
      <category>webdev</category>
      <category>devtools</category>
    </item>
    <item>
      <title>Stop Tuning Prompts. Start Tuning the Loop.</title>
      <dc:creator>Quinn Wang</dc:creator>
      <pubDate>Mon, 24 Aug 2026 19:48:51 +0000</pubDate>
      <link>https://dev.to/codejs_1959/stop-tuning-prompts-start-tuning-the-loop-21m3</link>
      <guid>https://dev.to/codejs_1959/stop-tuning-prompts-start-tuning-the-loop-21m3</guid>
      <description>&lt;p&gt;There's a recurring argument on DEV right now that constraints make you a better engineer, and I think the AI coding discussion keeps ignoring the constraint that actually matters. That constraint is not the model, the context window, or the token budget — it's the speed of your reset loop. A coding assistant is only as useful as the time between a failed experiment and the next attempt, and most workflows are losing the game right there.&lt;/p&gt;

&lt;p&gt;I've been testing this idea with a deliberately cheap setup: an open-source assistant called MonkeyCode, its free model access, and its free server option. Disclosure: This article was prepared as part of MonkeyCode's product outreach. The point was not to rank the model against its competitors, because rankings describe the model, not your workflow. The point was to measure how many rounds of failure I could afford in an afternoon without spending money or polluting my laptop.&lt;/p&gt;

&lt;p&gt;Here is the position I want to argue: you should treat every AI coding experiment like a git branch — cheap to create, cheap to delete, and never something you fall in love with. The moment a tool makes its environment feel permanent, you stop experimenting, and the moment you stop experimenting, model quality stops mattering. Free servers and free model tiers matter because they make the branch metaphor literal instead of aspirational, and that is a bigger deal than any benchmark delta.&lt;/p&gt;

&lt;p&gt;The workflow I keep coming back to has four steps, and none of them are about prompt engineering. First, spin up the disposable server and point the assistant at the free model endpoint. Second, give it a real task with a real failure condition, like "write a test that fails for the right reason." Third, verify the output with my own check instead of trusting the tool's self-report. Fourth, tear the whole thing down and decide whether the next round deserves a fresh start.&lt;/p&gt;

&lt;p&gt;The artifact that made this concrete is a tiny shell harness that times the entire loop instead of the model's answer. It runs with any coding assistant CLI, so the three functions below are placeholders you replace with whatever your tool actually ships:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;#!/usr/bin/env bash&lt;/span&gt;
&lt;span class="c"&gt;# loop.sh — time the full experiment loop, not the model's answer&lt;/span&gt;
&lt;span class="nb"&gt;set&lt;/span&gt; &lt;span class="nt"&gt;-euo&lt;/span&gt; pipefail

&lt;span class="nv"&gt;TASK&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;1&lt;/span&gt;&lt;span class="k"&gt;:-&lt;/span&gt;&lt;span class="nv"&gt;write&lt;/span&gt;&lt;span class="p"&gt; a failing test for the auth module&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;
&lt;span class="nv"&gt;ROUNDS&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;2&lt;/span&gt;&lt;span class="k"&gt;:-&lt;/span&gt;&lt;span class="nv"&gt;3&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;

&lt;span class="c"&gt;# Replace these three functions with your assistant's real CLI.&lt;/span&gt;
provision&lt;span class="o"&gt;()&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt; your-cli server up &lt;span class="nt"&gt;--free&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="o"&gt;}&lt;/span&gt;
run_task&lt;span class="o"&gt;()&lt;/span&gt;   &lt;span class="o"&gt;{&lt;/span&gt; your-cli run &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$TASK&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="nt"&gt;--output&lt;/span&gt; &lt;span class="s2"&gt;"./out-&lt;/span&gt;&lt;span class="nv"&gt;$1&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="o"&gt;}&lt;/span&gt;
teardown&lt;span class="o"&gt;()&lt;/span&gt;   &lt;span class="o"&gt;{&lt;/span&gt; your-cli server down&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="o"&gt;}&lt;/span&gt;

&lt;span class="k"&gt;for &lt;/span&gt;i &lt;span class="k"&gt;in&lt;/span&gt; &lt;span class="si"&gt;$(&lt;/span&gt;&lt;span class="nb"&gt;seq &lt;/span&gt;1 &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$ROUNDS&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="si"&gt;)&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="k"&gt;do
  &lt;/span&gt;&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"--- round &lt;/span&gt;&lt;span class="nv"&gt;$i&lt;/span&gt;&lt;span class="s2"&gt; ---"&lt;/span&gt;
  &lt;span class="nv"&gt;start&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="si"&gt;$(&lt;/span&gt;&lt;span class="nb"&gt;date&lt;/span&gt; +%s&lt;span class="si"&gt;)&lt;/span&gt;

  provision &lt;span class="o"&gt;&amp;gt;&lt;/span&gt;/dev/null 2&amp;gt;&amp;amp;1
  run_task &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$i&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt;/dev/null 2&amp;gt;&amp;amp;1
  ./run-tests.sh &lt;span class="s2"&gt;"./out-&lt;/span&gt;&lt;span class="nv"&gt;$i&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;
  teardown &lt;span class="o"&gt;&amp;gt;&lt;/span&gt;/dev/null 2&amp;gt;&amp;amp;1

  &lt;span class="nv"&gt;end&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="si"&gt;$(&lt;/span&gt;&lt;span class="nb"&gt;date&lt;/span&gt; +%s&lt;span class="si"&gt;)&lt;/span&gt;
  &lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"round &lt;/span&gt;&lt;span class="nv"&gt;$i&lt;/span&gt;&lt;span class="s2"&gt; took &lt;/span&gt;&lt;span class="k"&gt;$((&lt;/span&gt;end &lt;span class="o"&gt;-&lt;/span&gt; start&lt;span class="k"&gt;))&lt;/span&gt;&lt;span class="s2"&gt;s"&lt;/span&gt;
&lt;span class="k"&gt;done&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Run it three times — once with a local setup, once with the free server, once with a paid model — and you will learn more about your own workflow than a month of model comparisons. What I found is that the free server wins not because it generates tokens faster, but because it removes the friction of cleanup. When the environment is remote and disposable, I stop hoarding failed experiments, and that single habit changes how I prompt.&lt;/p&gt;

&lt;p&gt;Why does that matter for the broader conversation? Because the current wave of AI discussion is full of people arguing about which model writes better code, and almost nobody is measuring the loop around the model. The "limitation breeds greatness" crowd is right, but for the wrong reason: the limitation that matters is not fewer features, it's cheaper failure. A free tier with a generous allowance — the project's current allowance is ten million tokens at the time of writing, though you should verify that number in the README before relying on it — is a constraint that works in your favor.&lt;/p&gt;

&lt;p&gt;Now the honest limitations, because this approach is not for everyone. If you are building a production pipeline that needs guaranteed uptime and a support contract, a free server is the wrong foundation, and you should not build your CI around it. If your codebase is proprietary and your compliance rules forbid sending code to a remote machine, keep everything local and skip the server entirely. And if you need a specific model that is not available on the free tier, this workflow will not cover you, so check the model list before you commit.&lt;/p&gt;

&lt;p&gt;There is also a deeper caveat: free tiers change, and what is free today may be metered tomorrow, so treat the allowance as an experiment budget, not an infrastructure plan. The loop harness has its own limitation too, because it measures your habits as much as the tool, and a fast loop with a lazy verification step is just fast garbage. You need a real test suite or a real human review at the verify step, or the whole exercise becomes theater.&lt;/p&gt;

&lt;p&gt;So here is my closing argument: stop tuning prompts and start tuning the loop, because the prompt is the part you can see and the loop is the part that actually costs you time. A disposable server and a free model tier are a forcing function that keeps your experiments honest and your environment clean. When was the last time a benchmark told you something about your own Monday morning? Grab the open-source assistant, spin up the free server, and time your own three rounds — the script above will tell you more in an afternoon than a month of model comparisons.&lt;/p&gt;

</description>
      <category>ai</category>
      <category>productivity</category>
      <category>devops</category>
      <category>opensource</category>
    </item>
  </channel>
</rss>
