<?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: Nico Acosta</title>
    <description>The latest articles on DEV Community by Nico Acosta (@nico_acosta_bc7dceb59f65e).</description>
    <link>https://dev.to/nico_acosta_bc7dceb59f65e</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%2F3978200%2F8f88872d-ec24-49b7-ab54-afee796bed22.png</url>
      <title>DEV Community: Nico Acosta</title>
      <link>https://dev.to/nico_acosta_bc7dceb59f65e</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/nico_acosta_bc7dceb59f65e"/>
    <language>en</language>
    <item>
      <title>Your background agent needs somewhere to pick up the task and file the result</title>
      <dc:creator>Nico Acosta</dc:creator>
      <pubDate>Mon, 27 Jul 2026 13:17:51 +0000</pubDate>
      <link>https://dev.to/radial/your-background-agent-needs-somewhere-to-pick-up-the-task-and-file-the-result-1c85</link>
      <guid>https://dev.to/radial/your-background-agent-needs-somewhere-to-pick-up-the-task-and-file-the-result-1c85</guid>
      <description>&lt;p&gt;A background agent is an autonomous coding agent that works a task on its own, off your machine, and hands back finished work. You give it a prompt or a ticket, it clones the repo in its own sandbox, writes the code, runs the tests, and surfaces a pull request for you to review. Cursor, Devin, OpenAI's Codex cloud, Ona, and a growing pile of open-source runners all do a version of this. The pitch is the same everywhere: kick it off, walk away, come back to a diff.&lt;/p&gt;

&lt;p&gt;Almost everything written about background agents is about the &lt;em&gt;middle&lt;/em&gt; of that loop, the part where the agent runs. This post is about the two ends, because that is where the actual friction is. A background agent is defined by two touch-points that have nothing to do with the model: it &lt;strong&gt;picks up&lt;/strong&gt; a task, and it &lt;strong&gt;files&lt;/strong&gt; the result. Both of those are reads and writes against a record. If that record is slow, private to one machine, or invisible to the other agents you are running, the fancy autonomous middle does not help you.&lt;/p&gt;

&lt;h2&gt;
  
  
  The loop is: pick up a task, do the work, file the result
&lt;/h2&gt;

&lt;p&gt;Strip a background agent down and the shape is boring, which is the point.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Pick up.&lt;/strong&gt; The agent needs to know what to work on. Somebody, or some other agent, put a task somewhere: an issue in a backlog, a ticket, a line in a queue. The agent reads it.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Do the work.&lt;/strong&gt; This is the part the vendors compete on: the sandbox, the toolchain, the model, the test loop. It is genuinely hard and genuinely theirs.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;File the result.&lt;/strong&gt; The agent finished. Now what? It closes the task, links the branch or PR it opened, and often files the &lt;em&gt;next&lt;/em&gt; task it discovered while working. If it does not write that back somewhere durable, the run evaporates the moment the session ends.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Steps 1 and 3 are tracker operations. The agent is reading its queue and writing its outcome. That is a system-of-record job, and it is the job most "background agent" setups leave undefined, which is why people end up wiring the agent straight into GitHub's API and then discovering it does not scale.&lt;/p&gt;

&lt;h2&gt;
  
  
  Where it breaks: one agent is fine, several is not
&lt;/h2&gt;

&lt;p&gt;Run a single background agent against a single repo and you can get away with almost anything. Point it at GitHub Issues, let it poll, let it comment. It works because there is one reader and one writer.&lt;/p&gt;

&lt;p&gt;The moment you run more than one, the record becomes the bottleneck. A developer on r/github described exactly this after building their own layer:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;"The polling problem bites hard once you have more than one agent checking issue state at the same time."&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Their fix was to stop letting the agents touch GitHub directly and put a single fast API in front of the issue lifecycle, so the agents read and write local state instead of hammering a remote API each. That instinct is right. When several agents are picking up tasks and filing results at once, they need one shared record that is fast to read, safe to write concurrently, and the same list for every agent and every human. A file in one clone is invisible to the other runners. A GitHub issue is a network round-trip and a rate limit away.&lt;/p&gt;

&lt;h2&gt;
  
  
  The record Radial is
&lt;/h2&gt;

&lt;p&gt;Radial is a hosted issue tracker built to be the two ends of that loop. It is not the runner. It does not spin up a sandbox, launch your agent, or fire a webhook to trigger one. It is the fast, shared place the task lives before the agent picks it up and the place the result lands after. You bring the agent; Radial holds the record.&lt;/p&gt;

&lt;p&gt;Here is the file-the-task side, from a human or a script queuing work for a background agent to grab:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;radial create &lt;span class="s2"&gt;"Fix the flaky login test"&lt;/span&gt; &lt;span class="nt"&gt;-t&lt;/span&gt; ENG &lt;span class="nt"&gt;-p&lt;/span&gt; high &lt;span class="nt"&gt;--json&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;That returns the created issue as JSON, including its key (&lt;code&gt;ENG-142&lt;/code&gt;) and URL, so whatever queued it can hand the agent an exact task id. On the other end, when the background agent finishes, it drives the same tracker over the CLI, the REST API, or the MCP server: it reads its queue, closes the issue it just finished, and with Radial's Git integrations connected, the branch and pull request it opened link straight back to that issue. If it found a second bug on the way, it files that as a new issue before it exits, so the next agent, or the next you, picks it up instead of losing it.&lt;/p&gt;

&lt;p&gt;The point is that both ends are the same record, reachable the same way whether a person clicks a board or an agent calls &lt;code&gt;search_issues&lt;/code&gt; and &lt;code&gt;close_issue&lt;/code&gt; over MCP against &lt;code&gt;mcp.radial.build&lt;/code&gt;. One canonical list. Fast reads for the pickup, safe concurrent writes for the filing, legible history for the human who reviews the PR later. That is the &lt;a href="https://radial.build/blog/issue-tracker-for-ai-agents" rel="noopener noreferrer"&gt;tracker your agents can actually drive&lt;/a&gt;, aimed specifically at the delegate-and-return loop.&lt;/p&gt;

&lt;h2&gt;
  
  
  Where the line is
&lt;/h2&gt;

&lt;p&gt;One honest note, because it is the whole product. Radial is the record, not the intelligence and not the orchestration. It does not run your agent, host it, schedule it, or emit an event to kick one off. There is no copilot inside the tracker, no AI summaries, no auto-triage, and no AI credit meter, because your agent already does the thinking and you already pay for it once.&lt;/p&gt;

&lt;p&gt;That is also why more agents never cost more. Every agent credential is a client of the API, CLI, or MCP server, not a billed seat. You pay &lt;strong&gt;$50 per user, per year, flat, billed annually&lt;/strong&gt;, for the humans on the workspace, locked at the rate you join. Run one background agent against the record or ten in parallel and the price does not move. The Plain Software Pledge is the binding version: the day Radial ships a copilot, meters your usage, or charges you for AI you did not ask for, your subscription is free.&lt;/p&gt;

&lt;h2&gt;
  
  
  FAQ
&lt;/h2&gt;

&lt;h3&gt;
  
  
  What are background agents?
&lt;/h3&gt;

&lt;p&gt;Background agents are autonomous coding agents that pick up a task and work it on their own, off your local machine, usually in an isolated cloud sandbox with your repo and toolchain. You hand one a prompt or a ticket, it clones the repo, writes and tests the code, and returns finished work like a pull request for you to review, without tying up your editor. Cursor, Devin, Codex cloud, and Ona are common examples.&lt;/p&gt;

&lt;h3&gt;
  
  
  What are background agents good for?
&lt;/h3&gt;

&lt;p&gt;They are good for well-scoped, verifiable tasks you can describe once and check later: fixing a flagged bug, adding test coverage, a mechanical refactor, or clearing routine backlog items. The pattern shines when the work is defined enough that the agent can run without hand-holding and the result is a diff you review. It works best when each task lives as a clear record the agent reads from and files back to, so you are reviewing outcomes, not babysitting the run.&lt;/p&gt;

&lt;h3&gt;
  
  
  Where should a background agent read its tasks from?
&lt;/h3&gt;

&lt;p&gt;From a shared, hosted record that is fast to read and safe for multiple agents to write at once, not a file in one clone and not GitHub's issue API under polling load. A background agent's whole loop is read a task, do the work, write the result, so the record is load-bearing. Radial is built for that: the agent reads its queue and files results over the CLI, REST API, or MCP, and the same list is visible to every other agent and human on the workspace.&lt;/p&gt;

&lt;h3&gt;
  
  
  Does running more background agents against Radial cost more?
&lt;/h3&gt;

&lt;p&gt;No. Agent credentials are clients of the API, not billed seats. You pay $50 per user, per year, for the humans on the workspace, and that number is locked at the rate you join. Connect one background agent or run ten in parallel and the price is the same. There is no AI credit meter anywhere in the product.&lt;/p&gt;

&lt;h3&gt;
  
  
  Does Radial run or trigger the agent?
&lt;/h3&gt;

&lt;p&gt;No. Radial is the record, not the runner. It does not spin up sandboxes, launch or schedule your agent, or fire webhooks to trigger one. It holds the task the agent picks up and the result the agent files. You bring your own agent and run it wherever you already run it; Radial is the fast, shared place its work lives.&lt;/p&gt;

&lt;h2&gt;
  
  
  The short version
&lt;/h2&gt;

&lt;p&gt;A background agent's job has two ends that are not about the model at all: it picks up a task, and it files a result. Both are reads and writes against a record, and that record has to be hosted, fast, and shared the moment you run more than one agent. Radial is that record, not the runner. Bring your agent, point it at the list, and let it read its work and write its outcome to the same place your team already sees.&lt;/p&gt;

&lt;p&gt;Wire your agent up on the &lt;a href="https://radial.build/developers" rel="noopener noreferrer"&gt;developers&lt;/a&gt; page, or read the &lt;a href="https://radial.build/blog/claude-code-mcp-issue-tracker" rel="noopener noreferrer"&gt;Claude Code MCP walkthrough&lt;/a&gt; to connect one in about five minutes.&lt;/p&gt;




&lt;p&gt;&lt;em&gt;Originally published on the &lt;a href="https://radial.build/blog/background-agents" rel="noopener noreferrer"&gt;Radial blog&lt;/a&gt;.&lt;/em&gt;&lt;/p&gt;

</description>
      <category>ai</category>
      <category>agents</category>
      <category>devtools</category>
      <category>automation</category>
    </item>
    <item>
      <title>How to Capture a Webpage Screenshot Programmatically (URL to Image via API)</title>
      <dc:creator>Nico Acosta</dc:creator>
      <pubDate>Mon, 27 Jul 2026 11:43:30 +0000</pubDate>
      <link>https://dev.to/grabbit/how-to-capture-a-webpage-screenshot-programmatically-url-to-image-via-api-5h8i</link>
      <guid>https://dev.to/grabbit/how-to-capture-a-webpage-screenshot-programmatically-url-to-image-via-api-5h8i</guid>
      <description>&lt;p&gt;To capture a webpage screenshot from your code, send the page URL to a screenshot API and it returns a hosted image. One HTTP request in, one image URL out. The headless browser that renders the page runs in the cloud, so there is no Chromium to install and no browser process to keep alive.&lt;/p&gt;

&lt;p&gt;That is the fast path. Below are the ways to capture a webpage, when each one fits, and how to get the full-page, format, and timing options right.&lt;/p&gt;

&lt;h2&gt;
  
  
  The quick answer
&lt;/h2&gt;

&lt;p&gt;If you just want a URL captured as an image from your code, this is 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;curl https://api.grabbit.live/v1/grabs &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;-H&lt;/span&gt; &lt;span class="s2"&gt;"Authorization: Bearer sk_live_..."&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="s1"&gt;'{
    "url": "https://example.com",
    "width": 1280,
    "height": 720,
    "format": "webp"
  }'&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The response is JSON with a hosted &lt;code&gt;image_url&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"id"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"grb_01jx..."&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"status"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"done"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"target_url"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"https://example.com"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"image_url"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"https://cdn.grabbit.live/grabs/grb_01jx....webp"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"width"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;1280&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"height"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;720&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"format"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"webp"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"bytes"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;62140&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"execution_ms"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;940&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"created_at"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"2026-07-27T09:00:00.000Z"&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;Store that &lt;code&gt;image_url&lt;/code&gt;, embed it, or hand it to another service. The rest of this guide is about picking the right method and getting the capture right.&lt;/p&gt;

&lt;h2&gt;
  
  
  Manual versus programmatic capture
&lt;/h2&gt;

&lt;p&gt;"Capture a webpage screenshot" splits into two different jobs, and the right tool depends on which one you have.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Manual, one page.&lt;/strong&gt; You are looking at a page and want to save it. Your browser already does this. In Chrome or Edge, open the command menu with Ctrl+Shift+P (Cmd+Shift+P on Mac), type "screenshot," and choose "Capture full size screenshot" for the whole page. Edge has a dedicated shortcut: Ctrl+Shift+S, then "Capture full page." An extension like GoFullPage does the same through a toolbar button. For one page you are looking at right now, use one of these and move on.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Programmatic, repeatable.&lt;/strong&gt; You need to capture pages from code, on a schedule, or across many URLs. The manual path falls apart here: you cannot click a browser menu inside a cron job, a CI pipeline, or a webhook handler. You need an endpoint you can call. That is the rest of this guide.&lt;/p&gt;

&lt;h2&gt;
  
  
  Capturing a webpage with an API
&lt;/h2&gt;

&lt;p&gt;For anything repeatable, a screenshot API is the shortest path. You already saw the curl call. Here is the same capture in a few languages, since "how do I do this in my stack" is the real question.&lt;/p&gt;

&lt;p&gt;Python, using &lt;code&gt;requests&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="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;requests&lt;/span&gt;

&lt;span class="n"&gt;resp&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;requests&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;post&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;https://api.grabbit.live/v1/grabs&lt;/span&gt;&lt;span class="sh"&gt;"&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;Authorization&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;Bearer sk_live_...&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;
    &lt;span class="n"&gt;json&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;url&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;https://example.com&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;width&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;1280&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;height&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;720&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;format&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;webp&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="n"&gt;data&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;resp&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;json&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;data&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;image_url&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;Node.js, using the built-in &lt;code&gt;fetch&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;resp&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;fetch&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;https://api.grabbit.live/v1/grabs&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="na"&gt;method&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;POST&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;headers&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="na"&gt;Authorization&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Bearer sk_live_...&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;Content-Type&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;application/json&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="na"&gt;body&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;JSON&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;stringify&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
    &lt;span class="na"&gt;url&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;https://example.com&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;width&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;1280&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;height&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;720&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;format&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;webp&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="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;data&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;resp&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;json&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;data&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;image_url&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Every one of these is a plain HTTP call. No chromedriver, no 300 MB Chromium download, no async browser session to tear down. That is the whole reason to reach for an API over a local capture: the rendering machinery lives somewhere else.&lt;/p&gt;

&lt;h2&gt;
  
  
  Capturing the full page, not just the viewport
&lt;/h2&gt;

&lt;p&gt;By default a capture stops at the viewport height you request. To grab the entire scrollable page as one tall image, set &lt;code&gt;full_page&lt;/code&gt; to &lt;code&gt;true&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;curl https://api.grabbit.live/v1/grabs &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;-H&lt;/span&gt; &lt;span class="s2"&gt;"Authorization: Bearer sk_live_..."&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="s1"&gt;'{
    "url": "https://example.com",
    "width": 1280,
    "full_page": true,
    "format": "webp"
  }'&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;With &lt;code&gt;full_page&lt;/code&gt; on, &lt;code&gt;height&lt;/code&gt; is ignored and the render extends to the bottom of the document. This is the "scrolling screenshot" people ask for: everything below the fold, in one image. &lt;code&gt;width&lt;/code&gt; must be between 320 and 1920 pixels; &lt;code&gt;height&lt;/code&gt;, when you use it, between 240 and 1080.&lt;/p&gt;

&lt;h2&gt;
  
  
  Handling pages that load content late
&lt;/h2&gt;

&lt;p&gt;The reason a real browser beats a raw-HTML fetch is that it runs JavaScript. Single-page apps, lazy-loaded images, and content that appears a beat after load all render the way they do for a visitor, because the capture happens in headless Chromium, not an HTML parser.&lt;/p&gt;

&lt;p&gt;Two options control the timing:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;delay_ms&lt;/code&gt; waits a fixed number of milliseconds (0 to 10000) after load before firing the capture. Use it when content animates or streams in.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;selector&lt;/code&gt; waits for a specific element to appear and captures once it does. Use it when you know the exact node that signals "the page is ready."
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;curl https://api.grabbit.live/v1/grabs &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;-H&lt;/span&gt; &lt;span class="s2"&gt;"Authorization: Bearer sk_live_..."&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="s1"&gt;'{
    "url": "https://example.com/dashboard",
    "width": 1440,
    "height": 900,
    "delay_ms": 1500,
    "format": "png"
  }'&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This is also why an API can capture pages where a browser extension fails. A page can break extension-based capture with its layout, but a server that loads the URL like a normal visitor still gets a clean render of what the browser paints.&lt;/p&gt;

&lt;h2&gt;
  
  
  Choosing PNG, JPEG, or WebP
&lt;/h2&gt;

&lt;p&gt;The &lt;code&gt;format&lt;/code&gt; field takes &lt;code&gt;png&lt;/code&gt;, &lt;code&gt;jpeg&lt;/code&gt;, or &lt;code&gt;webp&lt;/code&gt;, and picking wrong means either bloated files or lost quality.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;WebP&lt;/strong&gt; is the best default. For a typical webpage render it produces the smallest file at the same visual quality, which adds up fast when you store and serve thousands of captures. Use it unless you have a reason not to.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;PNG&lt;/strong&gt; is lossless and supports transparency. Reach for it when exact pixel fidelity matters, for example a baseline image in a &lt;a href="https://www.grabbit.live/blog/visual-regression-testing" rel="noopener noreferrer"&gt;visual regression test&lt;/a&gt;.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;JPEG&lt;/strong&gt; is the safest bet for older tooling that predates WebP. It is lossy, so text edges soften slightly.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Switching format is a one-word change in the request body, so you can experiment without rewriting anything.&lt;/p&gt;

&lt;h2&gt;
  
  
  When a local capture is the better call
&lt;/h2&gt;

&lt;p&gt;An API is not always the answer. Capture locally instead when:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;You are doing a single manual capture.&lt;/strong&gt; Use the browser shortcut from earlier.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;You already have the HTML, not a URL.&lt;/strong&gt; If the markup lives in your app and is never served at an address, a local render avoids a round trip. See &lt;a href="https://www.grabbit.live/blog/html-to-image" rel="noopener noreferrer"&gt;HTML to image&lt;/a&gt; for that pattern, including hosting your template at a URL and capturing it.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;You cannot make outbound requests.&lt;/strong&gt; In an air-gapped environment an API is off the table, so a bundled headless browser is the only option.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;For everything else, especially anything on a schedule or in CI, the API path keeps your deployment small and removes a whole class of "it works locally but not in the container" failures.&lt;/p&gt;

&lt;p&gt;Pricing is worth a word here because it is where these tools differ most. Grabbit charges a flat $0.002 per live capture as prepaid credits that never reset or expire monthly, so a batch that runs once a quarter does not forfeit an unused monthly allowance. That is different from the metered monthly quotas most screenshot tools bill on.&lt;/p&gt;

&lt;h2&gt;
  
  
  Next steps
&lt;/h2&gt;

&lt;p&gt;The &lt;a href="https://www.grabbit.live/screenshot-api" rel="noopener noreferrer"&gt;screenshot API&lt;/a&gt; covers the full parameter set, authentication, and rate limits. If you are wiring this into a job, the &lt;a href="https://www.grabbit.live/blog/screenshot-from-url" rel="noopener noreferrer"&gt;screenshot from a URL&lt;/a&gt; guide walks the request end to end, &lt;a href="https://www.grabbit.live/blog/screenshot-automation" rel="noopener noreferrer"&gt;screenshot automation&lt;/a&gt; covers running captures on a schedule without a browser, and &lt;a href="https://www.grabbit.live/blog/screenshot-list-of-urls" rel="noopener noreferrer"&gt;screenshot a list of URLs&lt;/a&gt; handles capturing many pages at once.&lt;/p&gt;




&lt;p&gt;&lt;em&gt;Originally published on the &lt;a href="https://www.grabbit.live/blog/webpage-screenshot-capture" rel="noopener noreferrer"&gt;Grabbit blog&lt;/a&gt;.&lt;/em&gt;&lt;/p&gt;

</description>
      <category>webdev</category>
      <category>javascript</category>
      <category>programming</category>
      <category>tutorial</category>
    </item>
    <item>
      <title>The AI Coding Workflow That Ships One Feature: Idea to Merged PR</title>
      <dc:creator>Nico Acosta</dc:creator>
      <pubDate>Mon, 27 Jul 2026 11:13:59 +0000</pubDate>
      <link>https://dev.to/braingrid/the-ai-coding-workflow-that-ships-one-feature-idea-to-merged-pr-5hn4</link>
      <guid>https://dev.to/braingrid/the-ai-coding-workflow-that-ships-one-feature-idea-to-merged-pr-5hn4</guid>
      <description>&lt;p&gt;Last week someone on r/codex asked a question that has no business being asked anymore: &lt;a href="https://www.reddit.com/r/codex/comments/1ubq5m6" rel="noopener noreferrer"&gt;"How do you use Codex like a professional AI builder?"&lt;/a&gt; The tools are a year past magic. The models are good enough that "build me a login page" produces a login page. And yet the most upvoted answer was not a clever prompt or a secret setting. It was a workflow, hand-assembled by someone who had clearly been burned: write a spec first, break it into a phased plan, give each phase a definition of done, plan before you code, then review the diffs like a lead engineer.&lt;/p&gt;

&lt;p&gt;Read that again. The top answer to "how do the pros use this AI tool" was not about the AI tool at all. It was a process for working around it. The fastest path to shipping something real with an agent turns out to be the part of software development that has nothing to do with the agent. That is the contradiction this whole piece rests on, and it is the most useful thing you can learn this year: the AI coding workflow that actually ships is mostly the workflow you build around the AI.&lt;/p&gt;

&lt;h2&gt;
  
  
  The workflow everyone keeps reinventing
&lt;/h2&gt;

&lt;p&gt;Spend an hour reading how working builders describe their setup and you notice they have all converged on the same shape without coordinating. Addy Osmani's &lt;a href="https://addyosmani.com/blog/ai-coding-workflow/" rel="noopener noreferrer"&gt;LLM coding workflow going into 2026&lt;/a&gt; describes a virtuous cycle where the AI writes code, automated tools catch issues, and the AI fixes them. The most-shared &lt;a href="https://newsletter.systemdesign.one/p/ai-coding-workflow" rel="noopener noreferrer"&gt;System Design newsletter version&lt;/a&gt; lists five steps and the second one is literally "Plan Before You Code." The &lt;a href="https://dev.to/matthewhou/separate-planning-from-execution-the-ai-coding-workflow-that-actually-works-1n00"&gt;dev.to post that keeps circulating&lt;/a&gt; is titled, with no irony, "Separate Planning From Execution." Different people, different tools, same skeleton.&lt;/p&gt;

&lt;p&gt;Here is that skeleton, the one a thousand builders are rebuilding by hand in a thousand slightly different ways:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;flowchart LR
  A[Idea] --&amp;gt; B[Spec: what done means]
  B --&amp;gt; C[Plan: slice into phases]
  C --&amp;gt; D[Build: agent writes the code]
  D --&amp;gt; E[Verify: check against the spec]
  E --&amp;gt;|passes| F[Merged PR]
  E --&amp;gt;|fails| D
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The shape is not the interesting part. The interesting part is which step does the work. Most people assume the agent, the "Build" box, is where the magic lives, because that is the box that got a thousand times better. They are wrong, and you can prove it to yourself. Take a strong agent and give it a vague idea. You get a confident, plausible, wrong result. Now take a mediocre agent and give it a precise spec with a clear definition of done. You get something you can actually ship. The power is not in the model. It is in the box labeled "what done means," and that box is the one no tool writes for you.&lt;/p&gt;

&lt;h2&gt;
  
  
  One feature, start to finish
&lt;/h2&gt;

&lt;p&gt;Abstractions are easy to nod along to and useless when you sit down to build. So walk one real feature through the loop: a password reset flow, the kind of thing every app needs and nobody wants to write.&lt;/p&gt;

&lt;p&gt;The amateur version is one sentence. "Add password reset." You paste it into your agent, watch it generate four hundred lines across six files, see a form appear in the preview, and merge it because it looked done. Three days later a user emails that the reset link works twice, or never expires, or silently fails for anyone with a plus sign in their email. The demo worked. The feature did not. You shipped the gap.&lt;/p&gt;

&lt;p&gt;Now the version the r/codex commenter was describing. Before any code, you write down what done actually means, in conditions a person can check:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;The user requests a reset by email. A one-time link is sent that expires in 30 minutes and works exactly once. An invalid or expired link shows a clear error, not a crash. After a successful reset, all existing sessions are logged out. An unknown email address returns the same neutral confirmation as a known one, so the form cannot be used to discover who has an account.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;That is five testable conditions, and notice what just happened. You did not write code. You did not need to. But you now have something the one-sentence version never had: a definition of done that exists outside your head, that the agent can build against, and that you can check the result against later even though you cannot read a line of the code that produced it. The spec is the deliverable. The code is downstream of it.&lt;/p&gt;

&lt;p&gt;From there the rest of the loop has somewhere to stand. You slice the work into phases, send the agent off to build, and when the pull request comes back you do not stare at the diff feeling unqualified. You walk your five conditions. Request a reset, click the link twice, watch the second click fail. Wait thirty-one minutes, confirm the link is dead. Enter an email that does not exist, confirm the message gives nothing away. You are reviewing behavior against a checklist you wrote, which is exactly the kind of review a non-engineer is qualified to do. We went deep on that skill in &lt;a href="https://www.braingrid.ai/blog/what-is-a-pull-request-non-engineer-guide" rel="noopener noreferrer"&gt;reviewing a pull request when you cannot read code&lt;/a&gt;; the point here is that the checklist is what makes the review possible at all.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why the spec is the workflow
&lt;/h2&gt;

&lt;p&gt;There is a backlash worth taking seriously. A sharp comment on a recent &lt;a href="https://news.ycombinator.com/item?id=48627815" rel="noopener noreferrer"&gt;Hacker News thread&lt;/a&gt; called the whole agentic-workflow paradigm "a narrative trend pushed by AI companies to get people to 10x their token consumption." That critic is half right, and the half they are right about is the half that matters. A loop with no definition of done is exactly what they describe: an expensive way to keep rolling the dice. You prompt, the agent generates, you squint, you do not like it, you prompt again, and every turn burns credits while you converge on nothing, because there is nothing for the loop to converge toward.&lt;/p&gt;

&lt;p&gt;The thing that turns that slot machine into a workflow is the one ingredient the skeptics never mention. Not a better model. Not more autonomy. A spec the output can be measured against. With it, every loop iteration has a target and a stopping condition: done is when the evidence matches the spec. Without it, "done" is whenever you get tired of pulling the lever. The definition of done is not a nice-to-have step in the workflow. It is the workflow. Everything else is plumbing.&lt;/p&gt;

&lt;p&gt;This is also why the workflow does not get easier as the models get better, which is the part that surprises people. A more capable agent makes more decisions per turn, and every decision you did not write down is one it makes for you, silently, in a direction you will discover later. The more the agent can do on its own, the more the written spec is the only thing standing between its speed and your weekend. The hard part moves toward planning, not away from it.&lt;/p&gt;

&lt;h2&gt;
  
  
  Where this stops being a hand-rolled process
&lt;/h2&gt;

&lt;p&gt;You can run this entire loop yourself with sticky notes and discipline, and many good builders do. But look at what you are actually maintaining by hand: a definition of done for every feature, a way to slice it into buildable phases, a record of which conditions were checked and which were not, and a memory of what this product is supposed to do that survives from one session to the next. That is real work, and it is the same work whether you are one person or a team of fifty. The reason it keeps getting reinvented in Reddit comments is that nobody ships it as a product.&lt;/p&gt;

&lt;p&gt;That is the gap &lt;a href="https://braingrid.ai" rel="noopener noreferrer"&gt;BrainGrid&lt;/a&gt; sits in. You describe the feature in plain language, and the Planning Agent turns it into a requirement with explicit acceptance criteria, asking the clarifying questions you did not think to answer, the kind that catch the expired-link and plus-sign-email cases before any code exists. The Builder Agent then builds against that spec, either in a managed cloud sandbox with a live preview or in your own GitHub repo with &lt;a href="https://www.braingrid.ai/ai-coding-agents" rel="noopener noreferrer"&gt;Claude Code, Cursor, or Codex&lt;/a&gt;. When it is done, verification checks the result against every acceptance criterion, so the pull request arrives with evidence attached instead of a diff you have to decode. That is the full loop, Plan, Build, Verify, Repeat, and the part that does the work, the captured definition of done, is the part the product remembers so you do not have to rebuild it every project. We cover the planning half in depth in &lt;a href="https://www.braingrid.ai/blog/building-the-braingrid-way" rel="noopener noreferrer"&gt;building the BrainGrid way&lt;/a&gt;, and the spec craft itself in &lt;a href="https://www.braingrid.ai/blog/how-to-write-acceptance-criteria-ai-agent-can-verify" rel="noopener noreferrer"&gt;how to write acceptance criteria an AI agent can verify&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;The honest limit: a spec does not write itself, and a bad one ships a bad feature with full confidence. If you write down the wrong conditions, the agent will satisfy them perfectly and you will have built the wrong thing precisely. The workflow does not remove your judgment about what good looks like. It just makes sure that judgment gets recorded once, up front, where it can actually steer the build, instead of living as a fuzzy feeling you compare the result against at the merge button.&lt;/p&gt;

&lt;p&gt;So the next time you catch yourself asking how the pros use the latest agent, remember what the pros actually answered. They did not share a prompt. They shared a workflow, and the center of it was the least glamorous step there is. Write down what done means before you build. The agent got fast. Deciding what it is allowed to call finished is still your job, and it is the only job that ships.&lt;/p&gt;

&lt;h2&gt;
  
  
  FAQ
&lt;/h2&gt;

&lt;h3&gt;
  
  
  What is an AI coding workflow?
&lt;/h3&gt;

&lt;p&gt;An AI coding workflow is the repeatable process you wrap around an AI agent to turn an idea into shipped software, rather than just prompting an agent and hoping. In practice it has converged on a consistent shape: write a spec that defines what "done" means, slice the work into phases, let the agent build against the spec, then verify the result against your original conditions before merging. The agent handles the code generation; the workflow handles everything that makes the code generation aim at the right target. The most common mistake is treating the prompt as the workflow when the spec is the part that actually does the work.&lt;/p&gt;

&lt;h3&gt;
  
  
  How do I set up an AI coding workflow?
&lt;/h3&gt;

&lt;p&gt;Start with the step everyone skips: before any code, write down what done looks like as a short list of conditions a person can check, not as a one-line prompt. Then break the feature into phases small enough to review one at a time, send each to your agent, and when it returns, walk your conditions against the running result instead of reading the diff. Keep a record of what this product is supposed to do so each new session does not start from zero. You can do this manually with notes and discipline, or use a tool that captures the spec, runs the build against it, and verifies the result for you.&lt;/p&gt;

&lt;h3&gt;
  
  
  Is planning really necessary if the AI model is good enough?
&lt;/h3&gt;

&lt;p&gt;Yes, and counterintuitively it matters more as models improve, not less. A more capable agent makes more decisions per turn, and every decision you did not specify is one it makes on its own, in a direction you discover after the fact. A strong model with a vague request produces a confident, plausible, wrong result; a modest model with a precise definition of done produces something you can ship. The work that decides the outcome is in the spec, because that is the only thing the agent's speed has to aim at. Skipping planning is how a loop becomes an expensive way to keep rolling the dice.&lt;/p&gt;

&lt;h3&gt;
  
  
  How do I review an AI pull request if I can't read code?
&lt;/h3&gt;

&lt;p&gt;Review the behavior, not the syntax. Read the summary and confirm the scope matches what you asked for, then open the preview and walk through your acceptance criteria the way a real user would, including the failure cases like expired links, wrong inputs, and empty forms. Notice how many files changed and be more cautious of a sprawling change than a small one. Approve only when the running feature satisfies every condition you wrote up front. This works precisely because you defined done before the build, so the review is checking a list instead of guessing against a fuzzy memory.&lt;/p&gt;

&lt;h3&gt;
  
  
  What's the difference between vibe coding and an AI coding workflow?
&lt;/h3&gt;

&lt;p&gt;Vibe coding is prompt, generate, eyeball, repeat, with no written definition of done and nothing to verify against, which is great for prototypes and a fast way to ship hidden gaps when the app matters. An AI coding workflow adds the missing piece: a spec the output is measured against, so each iteration has a target and a clear stopping condition. The difference is not how good the model is or how fast you go. It is whether there is anything in the loop that can say "not yet," which is what turns generation into shipping.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;&lt;a href="https://braingrid.ai" rel="noopener noreferrer"&gt;BrainGrid&lt;/a&gt; turns your idea into acceptance criteria, builds against them with your choice of agent, and verifies every change with evidence, so the workflow you keep rebuilding by hand is just the product. Try it at &lt;a href="https://braingrid.ai" rel="noopener noreferrer"&gt;braingrid.ai&lt;/a&gt;.&lt;/em&gt;&lt;/p&gt;




&lt;p&gt;&lt;em&gt;Originally published on the &lt;a href="https://www.braingrid.ai/blog/ai-coding-workflow-idea-to-merged-pr" rel="noopener noreferrer"&gt;BrainGrid blog&lt;/a&gt;.&lt;/em&gt;&lt;/p&gt;

</description>
      <category>ai</category>
      <category>programming</category>
      <category>tutorial</category>
      <category>productivity</category>
    </item>
    <item>
      <title>Why the results appear before the spinner would: how a tracker feels instant</title>
      <dc:creator>Nico Acosta</dc:creator>
      <pubDate>Fri, 24 Jul 2026 13:16:51 +0000</pubDate>
      <link>https://dev.to/radial/why-the-results-appear-before-the-spinner-would-how-a-tracker-feels-instant-1774</link>
      <guid>https://dev.to/radial/why-the-results-appear-before-the-spinner-would-how-a-tracker-feels-instant-1774</guid>
      <description>&lt;p&gt;There is a specific feeling you get from a fast tool: you type a filter, and the list is already right. You change a status, and it moves before you have finished letting go of the mouse. There was no spinner, because there was no moment to put one. The work happened where you were looking, and the network sorted itself out afterward.&lt;/p&gt;

&lt;p&gt;That feeling is not a faster server. You cannot buy it with a bigger box, and you cannot A/B your way to it after the fact. It comes from one architectural decision made early: keep the working set close to the user, so the common actions do not wait on a round trip to feel done. This post is about how that works, and, just as importantly, where the honest limits are.&lt;/p&gt;

&lt;h2&gt;
  
  
  The default architecture makes you wait
&lt;/h2&gt;

&lt;p&gt;Most web apps are built the obvious way. The source of truth lives on a server. The client is a thin renderer. Every meaningful action, filtering a list, editing a field, reordering a board, is a request: send it, wait, receive, re-render. Even when each request is fast, you are paying network latency on every interaction, all day.&lt;/p&gt;

&lt;p&gt;We wrote about the extreme version of this in &lt;a href="https://radial.build/blog/why-is-jira-so-slow" rel="noopener noreferrer"&gt;why Jira is so slow&lt;/a&gt;: a single ticket that fans out into a cascade of round trips before the page is usable. But you do not need 200 requests to feel the tax. One request per keystroke of filtering is enough. The problem is not the number, it is the shape: the client asks, the server answers, and you wait in between. As long as the data you are working with lives only on the server, the floor on "how fast can this feel" is set by the network, not by your code.&lt;/p&gt;

&lt;h2&gt;
  
  
  Keep the working set in the client
&lt;/h2&gt;

&lt;p&gt;The fix is to stop treating the client as a thin renderer and start treating it as the place the working set lives.&lt;/p&gt;

&lt;p&gt;When you open a Radial workspace, the issues you are looking at are loaded into a normalized in-memory store on the client (we use &lt;a href="https://github.com/pmndrs/zustand" rel="noopener noreferrer"&gt;zustand&lt;/a&gt;; the pattern matters more than the library). From that point on, the interactions that used to be requests become local computations:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Filtering and sorting&lt;/strong&gt; run as a predicate over the loaded slice. Narrowing by assignee, priority, or label does not call the server; it re-evaluates a function over data already in memory. That is why the list is right the instant you type.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Editing&lt;/strong&gt; is optimistic. When you change a status, the store updates first and the UI reflects it immediately. The write to the database happens in the background, under row-level security. If it fails, the change rolls back and you get a toast. The common case, which is that it succeeds, never made you wait.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Realtime&lt;/strong&gt; keeps the working set honest. When a teammate (or a teammate's agent) changes something, the update arrives over a realtime channel and reconciles into the same store, with the app's own echoes suppressed so your optimistic edit does not fight the confirmation of itself.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The through-line: the network is still there, but it moved off the critical path. You act on local state; the server catches up. The spinner has nowhere to live because the moment it would have filled is already over.&lt;/p&gt;

&lt;h2&gt;
  
  
  Where this is honest, and where it is not
&lt;/h2&gt;

&lt;p&gt;It is easy to oversell this, so here is the line we hold.&lt;/p&gt;

&lt;p&gt;Radial is &lt;strong&gt;not&lt;/strong&gt; local-first software in the &lt;a href="https://www.inkandswitch.com/essay/local-first/" rel="noopener noreferrer"&gt;Ink &amp;amp; Switch sense&lt;/a&gt;: it is not offline-first, it does not use CRDTs, and your data is not primarily on your disk. It is a hosted, multi-tenant app backed by Postgres. What we do is narrower and more mundane: we keep the &lt;em&gt;working set&lt;/em&gt; in the client and make the common actions optimistic, so the tool feels instant even though the source of truth is a server you are talking to.&lt;/p&gt;

&lt;p&gt;Two consequences follow, and we would rather name them than imply they do not exist:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Global search is a server call, not a local scan.&lt;/strong&gt; Typing into the ⌘K palette to find any issue in the workspace hits a database function, because the full corpus is not all in the client. It is fast, but it is a network request, not a local filter. The instant part is filtering and editing the slice you already have loaded, not searching everything you have never opened.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Staying small is what keeps it fast.&lt;/strong&gt; This only works because the working set is small enough to live in the client. Radial is deliberately one thing, an issue tracker, with no 50-field forms and no dashboard suite fanning out behind every page. Speed here is a position you hold by saying no, the same discipline behind &lt;a href="https://radial.build/blog/boring-on-purpose" rel="noopener noreferrer"&gt;boring on purpose&lt;/a&gt;. Bolt on an everything-app and the working set stops fitting, and the trick stops working.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;We also do not publish a latency figure as a measured benchmark, because the only honest claim is the design goal, not a marketing number. The goal is simply that the tool gets out of the way.&lt;/p&gt;

&lt;h2&gt;
  
  
  The same idea, from the terminal
&lt;/h2&gt;

&lt;p&gt;The instant-feel is a property of the architecture, not of the web UI specifically, which is why the command line gets the same benefit. The CLI talks to the same fast surface, and every command takes &lt;code&gt;--json&lt;/code&gt;, so you can pipe the working set straight into whatever you are already scripting:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;radial list &lt;span class="nt"&gt;-a&lt;/span&gt; me &lt;span class="nt"&gt;--status&lt;/span&gt; &lt;span class="s2"&gt;"in progress"&lt;/span&gt; &lt;span class="nt"&gt;--json&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;That returns your in-progress issues as structured JSON in one call, no page to render, no spinner to watch. Your agent gets the same thing over MCP at &lt;code&gt;mcp.radial.build&lt;/code&gt;, and the REST API at &lt;code&gt;api.radial.build&lt;/code&gt; is plain JSON, not a GraphQL SDK you have to learn. The point is consistent across every surface: the tool should not make you wait, whether you are looking at a board, typing in a terminal, or driving it with an agent.&lt;/p&gt;

&lt;h2&gt;
  
  
  FAQ
&lt;/h2&gt;

&lt;h3&gt;
  
  
  What is local-first software?
&lt;/h3&gt;

&lt;p&gt;Local-first software, as defined by Ink &amp;amp; Switch, is a set of principles where the primary copy of your data lives on your own device rather than a remote server, so the app works offline, stays fast, and keeps you in control of your data even without the cloud. It usually relies on conflict-free replicated data types (CRDTs) to merge changes across devices. Radial is not local-first in this strict sense: it is a hosted app that keeps your active working set in the client for speed, which is a different and narrower technique.&lt;/p&gt;

&lt;h3&gt;
  
  
  Does Radial work offline?
&lt;/h3&gt;

&lt;p&gt;No. Radial keeps the issues you are working with in a client-side store so filtering and editing feel instant, but the source of truth is a hosted Postgres database and it needs a connection to sync. It is fast because the working set is local, not because it is offline-capable. If you need a fully offline, on-your-disk tracker, a true local-first or file-based tool is a better fit.&lt;/p&gt;

&lt;h3&gt;
  
  
  Why does filtering feel instant but search does not?
&lt;/h3&gt;

&lt;p&gt;Because they work on different data. Filtering and sorting run over the slice of issues already loaded into the client, so they are local computations with no network in the loop. Global search across the whole workspace has to reach issues that are not loaded, so it calls a database function. One is a local predicate; the other is a fast server request. We would rather be precise about that than pretend everything is local.&lt;/p&gt;

&lt;h3&gt;
  
  
  How does optimistic editing avoid losing my changes?
&lt;/h3&gt;

&lt;p&gt;Every optimistic mutation snapshots the previous state before applying the change to the client store. The database write then happens in the background under row-level security. If the write fails, the change rolls back to the snapshot and you get a toast, so you always know when something did not stick. Realtime updates from other users reconcile into the same store, with the app's own echoes suppressed so your edit is not double-applied.&lt;/p&gt;

&lt;h3&gt;
  
  
  Is a faster tracker actually worth switching for?
&lt;/h3&gt;

&lt;p&gt;If your team touches issues dozens of times a day, the per-interaction wait is a focus tax you pay every day. The value is not a benchmark number; it is that the tool stops interrupting you. A tracker you forget you are using, because it never makes you wait, is the goal.&lt;/p&gt;

&lt;h2&gt;
  
  
  The short version
&lt;/h2&gt;

&lt;p&gt;A tool feels instant when the working set lives in the client: filtering and sorting become local computations, editing is optimistic, and the network catches up in the background instead of blocking each action. That is the architecture behind Radial. It is not local-first software, and global search is still a server call, but the common actions do not wait, and that is where the feeling comes from.&lt;/p&gt;

&lt;p&gt;See the one flat price on &lt;a href="https://radial.build/pricing" rel="noopener noreferrer"&gt;pricing&lt;/a&gt;, or wire the same fast surface into your terminal and your agent at &lt;a href="https://radial.build/developers" rel="noopener noreferrer"&gt;/developers&lt;/a&gt;.&lt;/p&gt;




&lt;p&gt;&lt;em&gt;Originally published on the &lt;a href="https://radial.build/blog/local-first-search" rel="noopener noreferrer"&gt;Radial blog&lt;/a&gt;.&lt;/em&gt;&lt;/p&gt;

</description>
      <category>webdev</category>
      <category>programming</category>
      <category>typescript</category>
      <category>tooling</category>
    </item>
    <item>
      <title>How to Add a Screenshot Tool to Your AI Agent with MCP</title>
      <dc:creator>Nico Acosta</dc:creator>
      <pubDate>Fri, 24 Jul 2026 11:41:38 +0000</pubDate>
      <link>https://dev.to/grabbit/how-to-add-a-screenshot-tool-to-your-ai-agent-with-mcp-fhc</link>
      <guid>https://dev.to/grabbit/how-to-add-a-screenshot-tool-to-your-ai-agent-with-mcp-fhc</guid>
      <description>&lt;p&gt;An AI agent that writes frontend code has no way to see the result. It edits CSS, ships a component, and moves on, blind to whether the page actually renders correctly. The fix is to give the agent a tool that turns a URL into an image it can look at. Model Context Protocol (MCP) is the standard that makes wiring that tool a one-line job.&lt;/p&gt;

&lt;p&gt;A screenshot MCP server exposes exactly one capability the agent was missing: send a URL, get back a rendered image. This walks through what a screenshot MCP server is, how to add a hosted one to Claude Code, Cursor, or any MCP host in a single command, and when a hosted server beats a self-hosted one that runs Chromium on your machine.&lt;/p&gt;

&lt;h2&gt;
  
  
  What a screenshot MCP server actually does
&lt;/h2&gt;

&lt;p&gt;MCP is a protocol for connecting AI hosts to external tools. A host (Claude Code, Cursor, Claude Desktop, an agent framework) speaks MCP to a server, discovers the tools that server offers, and calls them mid-conversation. The agent never needs to know how the tool works internally, only its name, description, and inputs.&lt;/p&gt;

&lt;p&gt;A screenshot MCP server offers a capture tool. The contract is simple:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;The agent calls the tool with a URL (and optional width, format, and a few knobs).&lt;/li&gt;
&lt;li&gt;The server renders the page in a real browser.&lt;/li&gt;
&lt;li&gt;The server returns a hosted image, which the host passes back to the model as an image block.&lt;/li&gt;
&lt;li&gt;The model looks at the pixels and reasons about them: spacing, overflow, a broken hero, a layout that only breaks at a real viewport.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;That is the whole loop. What varies between servers is where the rendering happens. Some servers wrap a local Puppeteer or Playwright install and screenshot on your machine. A hosted server runs the browser on its own infrastructure, so there is nothing to install and nothing to keep alive.&lt;/p&gt;

&lt;h2&gt;
  
  
  Add a hosted screenshot MCP server in one command
&lt;/h2&gt;

&lt;p&gt;Grabbit is a hosted screenshot MCP server at &lt;code&gt;mcp.grabbit.live&lt;/code&gt;, spoken over Streamable HTTP. In Claude Code, adding it is one command plus a browser sign-in:&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;# Add Grabbit's hosted MCP server (Streamable HTTP)&lt;/span&gt;
claude mcp add &lt;span class="nt"&gt;--transport&lt;/span&gt; http grabbit https://mcp.grabbit.live

&lt;span class="c"&gt;# Authenticate in your browser (no API key to paste):&lt;/span&gt;
&lt;span class="c"&gt;#   /mcp   → select "grabbit" → Authenticate&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;That registers five tools with the host: &lt;code&gt;grab&lt;/code&gt; (screenshot a URL), &lt;code&gt;get_grab&lt;/code&gt; and &lt;code&gt;list_grabs&lt;/code&gt; (fetch or browse past captures), &lt;code&gt;get_usage&lt;/code&gt; (credits and plan), and &lt;code&gt;get_pricing&lt;/code&gt; (an honest, dated comparison against ScreenshotOne, Urlbox, Browserless, and others). The agent can now call &lt;code&gt;grab&lt;/code&gt; any time it needs to see a page.&lt;/p&gt;

&lt;p&gt;Prefer a key over the OAuth flow? Pass a Bearer token as a header instead. A free test key (&lt;code&gt;sk_test_...&lt;/code&gt;) returns placeholder renders at no cost, so you can prove the wiring before paying:&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;# Alternative: pass a key as a header instead of the OAuth flow.&lt;/span&gt;
claude mcp add &lt;span class="nt"&gt;--transport&lt;/span&gt; http grabbit https://mcp.grabbit.live &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--header&lt;/span&gt; &lt;span class="s2"&gt;"Authorization: Bearer sk_test_..."&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The same server works for any MCP host that speaks Streamable HTTP, not just Claude Code. Cursor, Claude Desktop, and agent frameworks point at the same &lt;code&gt;https://mcp.grabbit.live&lt;/code&gt; endpoint and discover the same tools.&lt;/p&gt;

&lt;h2&gt;
  
  
  The see, fix, verify loop
&lt;/h2&gt;

&lt;p&gt;The reason this matters shows up the moment an agent is doing visual work. Without a screenshot tool, a coding agent writes CSS and hopes. With one, it can close the loop:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;You, in Claude Code:
  "Screenshot https://my-feature.preview.vercel.app and check the
   hero spacing against the mockup."

Claude calls the grab tool, looks at the returned image, edits the
CSS, then screenshots again to confirm the fix.
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The agent captures the page it just shipped, looks at the rendered pixels, fixes what is off, and screenshots again to confirm. This is the same pattern practitioners keep landing on independently: give the agent a real rendered reference instead of asking it to reason about a page it cannot see. It generalizes past frontend work to visual QA after a deploy, verifying an OG card before a post goes live, or feeding a live competitor page into a vision step.&lt;/p&gt;

&lt;p&gt;Because Grabbit captures public URLs and blocks localhost (SSRF protection is on by default), point the agent at a deployed preview or staging URL. That constraint is also what makes the loop work inside CI and remote runners: there is no local browser to drive, and the hosted render does not care.&lt;/p&gt;

&lt;h2&gt;
  
  
  When to self-host and when to use a hosted server
&lt;/h2&gt;

&lt;p&gt;Search "screenshot MCP server" and most of the results are open-source repos that wrap Puppeteer or Playwright and run the capture locally. Those are a good fit when you want the browser on your own machine and are happy to maintain it. Be honest about what that maintenance is: a headless Chromium to install and patch, memory leaks to bound, fonts and locale to get right on Linux, and the same anti-bot walls a local browser hits on the open web.&lt;/p&gt;

&lt;p&gt;A hosted screenshot MCP server removes that surface entirely. There is no Chromium in your environment, the render runs on infrastructure that already handles cookie and consent banners and JavaScript-heavy pages, and it works in sandboxes and CI where a local browser would have nothing to drive. The tradeoff is that you pay per capture instead of paying in devops time.&lt;/p&gt;

&lt;p&gt;Grabbit's pricing is a flat $0.002 per live capture on the $50/year plan (25,000 captures), with prepaid credits that never reset or expire monthly, and test-environment captures are free. Browserless and Thum.io list a lower per-grab rate; Grabbit's edge is flat annual billing with no monthly-reset waste and the one-line MCP onboarding above. If your only need from a browser is a screenshot of a URL, a hosted server is the shorter path.&lt;/p&gt;

&lt;h2&gt;
  
  
  A screenshot server and a browser-automation server do different jobs
&lt;/h2&gt;

&lt;p&gt;A screenshot MCP server is not a replacement for a browser-driving one. &lt;a href="https://www.grabbit.live/blog/playwright-mcp" rel="noopener noreferrer"&gt;Playwright MCP&lt;/a&gt; gives an agent a real browser to click, type, and read the accessibility tree, which is what you want to walk through a login or a multi-step flow. A screenshot server has a narrower job: turn a URL into an image the agent can look at.&lt;/p&gt;

&lt;p&gt;They pair naturally. The accessibility tree tells the agent what is on the page; a rendered screenshot shows it what the page looks like: visual bugs, CSS regressions, whether the thing actually looks right. Many agents use both. For the deeper pattern of feeding rendered images into a vision model, see &lt;a href="https://www.grabbit.live/blog/screenshots-for-ai-agents" rel="noopener noreferrer"&gt;Screenshots for AI Agents&lt;/a&gt;. To call the same capture from your own code without MCP, &lt;a href="https://www.grabbit.live/blog/screenshot-from-url" rel="noopener noreferrer"&gt;How to Screenshot a Website from a URL&lt;/a&gt; covers the plain HTTP path.&lt;/p&gt;

&lt;h2&gt;
  
  
  Calling the same capture over plain HTTP
&lt;/h2&gt;

&lt;p&gt;MCP is the agent-native front door, but the same capture is one HTTP POST when you want it in a script, a cron job, or a framework tool you define by hand. This is what the &lt;code&gt;grab&lt;/code&gt; MCP tool calls under the hood:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;curl &lt;span class="nt"&gt;-X&lt;/span&gt; POST https://api.grabbit.live/v1/grabs &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;-H&lt;/span&gt; &lt;span class="s2"&gt;"Authorization: Bearer sk_live_..."&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="s1"&gt;'{
    "url": "https://my-feature.preview.vercel.app",
    "width": 1280,
    "height": 720,
    "format": "webp",
    "full_page": true,
    "delay_ms": 500
  }'&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The response includes a hosted &lt;code&gt;image_url&lt;/code&gt; you pass straight to a vision model as an image input. The knobs that matter most for agent work: &lt;code&gt;full_page&lt;/code&gt; captures the whole document, &lt;code&gt;delay_ms&lt;/code&gt; (0 to 10000) waits for late-loading content to settle, &lt;code&gt;selector&lt;/code&gt; targets a single element by CSS selector, and &lt;code&gt;format: "webp"&lt;/code&gt; keeps images small when you are passing them into a model. Width accepts 320 to 1920, height 240 to 1080.&lt;/p&gt;

&lt;p&gt;Whether you wire it through MCP or call it directly, the point is the same: the agent stops working blind. It gets eyes on the web it is building.&lt;/p&gt;

&lt;p&gt;For the full API reference, see the &lt;a href="https://www.grabbit.live/screenshot-api" rel="noopener noreferrer"&gt;Grabbit screenshot API&lt;/a&gt;.&lt;/p&gt;




&lt;p&gt;&lt;em&gt;Originally published on the &lt;a href="https://www.grabbit.live/blog/screenshot-mcp-server" rel="noopener noreferrer"&gt;Grabbit blog&lt;/a&gt;.&lt;/em&gt;&lt;/p&gt;

</description>
      <category>webdev</category>
      <category>programming</category>
      <category>startup</category>
      <category>saas</category>
    </item>
    <item>
      <title>Agentic Coding: How to Start When You Don't Have a CS Degree</title>
      <dc:creator>Nico Acosta</dc:creator>
      <pubDate>Fri, 24 Jul 2026 11:15:15 +0000</pubDate>
      <link>https://dev.to/braingrid/agentic-coding-how-to-start-when-you-dont-have-a-cs-degree-2h9h</link>
      <guid>https://dev.to/braingrid/agentic-coding-how-to-start-when-you-dont-have-a-cs-degree-2h9h</guid>
      <description>&lt;p&gt;The most useful skill in agentic coding is one you already have if you have ever managed a person. Not a framework, not a language, not a CS degree. The ability to say what you want clearly enough that someone else can build it, and to tell whether what came back is actually right. MIT's own course on this, part of its Missing Semester series, lands on the same mental model: you are not the coder anymore, you are the manager of an intern who does the nitty-gritty work but needs direction and occasionally does the wrong thing.&lt;/p&gt;

&lt;p&gt;That reframe is good news and a warning at the same time. Good news, because it means the barrier to entry is judgment, not syntax, and judgment is something a domain expert often has in surplus. The warning is the part nobody puts in the tutorial: agentic coding will get you eighty percent of the way to a working product faster than you can believe, and then quietly strand you in the last twenty. Knowing where that cliff is, before you walk off it, is the difference between shipping and starting over.&lt;/p&gt;

&lt;h2&gt;
  
  
  What agentic coding actually is
&lt;/h2&gt;

&lt;p&gt;Start with a clean definition, because the term gets blurred with everything adjacent. Google Cloud puts it plainly: agentic coding is a software development approach where autonomous AI agents plan, write, test, and modify code with minimal human intervention. The key word is autonomous. A traditional AI assistant waits for you to type and suggests the next line. A coding agent takes a high-level instruction, breaks it into steps, reads and writes files across your project, runs commands, checks its own work, and keeps going until the task is done.&lt;/p&gt;

&lt;p&gt;That is the difference between AI coding and agentic coding, which is the question most beginners actually have. Autocomplete finishes your sentence. An agent runs the errand. MIT's course defines the agent as a conversational AI model with access to tools: reading and writing files, searching the web, invoking shell commands. The model is the brain; the tools are the hands. Give it a goal and it operates your codebase the way a contractor operates on a job site, not the way a dictionary suggests a word.&lt;/p&gt;

&lt;p&gt;The tools you would actually start with are mature now. Claude Code and Codex run in your terminal and drive multi-step work from the command line. Cursor and Windsurf put the same agent inside a code editor. You do not need to know which is best on day one. You need to know that all of them are doing the same thing: taking your intent and turning it into actions, then showing you the result.&lt;/p&gt;

&lt;h2&gt;
  
  
  How to start without a CS degree
&lt;/h2&gt;

&lt;p&gt;The first move is the smallest possible real task, not a whole app. MIT's example is a good shape: take a script you already have and tell the agent, turn this into a proper command-line program with argument parsing. One change, one file, something you can read the result of. You are not testing whether the agent is smart. You are learning the loop: describe, watch, correct, repeat.&lt;/p&gt;

&lt;p&gt;The single skill that compounds from there is writing a good specification, and it is more art than science. The MIT course warns about both failure directions in one breath:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;You want the input to the agent to be descriptive enough so that the agent does what you want it to do, but not overly descriptive to the point where you're doing too much work yourself.&lt;/p&gt;

&lt;p&gt;&lt;cite&gt;&lt;a href="https://missing.csail.mit.edu/2026/agentic-coding/" rel="noopener noreferrer"&gt;MIT, "Agentic Coding," The Missing Semester of Your CS Education&lt;/a&gt;&lt;/cite&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Too vague and the agent guesses, usually wrong. Too detailed and you are writing the code in English, which defeats the point. The sweet spot is stating the what and the how-you-will-know-it-worked, and leaving the how to the agent. Here is the difference in practice.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Vague: "Build me a login page."&lt;/p&gt;

&lt;p&gt;Specific: "Build an email-and-password login page. Show an inline error if the email format is invalid. Lock the account for five minutes after five failed attempts. On success, redirect to /dashboard. Unauthenticated users who reach /dashboard redirect back to /login."&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;The second one is not code. It is a clear description of done, written in plain language, and it is exactly the skill a non-engineer can own. The agent handles the implementation. You hold the standard.&lt;/p&gt;

&lt;p&gt;The last starting habit is keeping a rules file. Every agent looks for a project-level file, usually &lt;code&gt;AGENTS.md&lt;/code&gt;, where you write down your conventions and, more usefully, every mistake the agent has made before so it stops repeating them. Treat it as the onboarding doc for your intern. It is the lowest-effort reliability you will ever buy.&lt;/p&gt;

&lt;p&gt;And yes, you can start for free. Most agents have a free tier or a low-cost entry plan, and open-source options run on your own machine. Cost is not the barrier. The barrier is knowing what to ask for and recognizing when the answer is wrong.&lt;/p&gt;

&lt;h2&gt;
  
  
  The trap: the 80 percent problem
&lt;/h2&gt;

&lt;p&gt;Here is the cliff. Agents are stunningly good at the part of software that looks like a demo, and stubbornly weak at the part that makes it a product. The pattern is common enough that it has a name, the 80 percent problem:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;The agent wrote code that works. The agent did not write code that survives.&lt;/p&gt;

&lt;p&gt;&lt;cite&gt;&lt;a href="https://www.augmentcode.com/guides/the-80-percent-problem-ai-agents-technical-debt" rel="noopener noreferrer"&gt;Augment Code, "The 80% Problem"&lt;/a&gt;&lt;/cite&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;The first eighty percent is the happy path: the feature does the thing when you use it the expected way. The missing twenty is everything that makes it real. Error handling for the inputs you did not anticipate. Security applied across the whole system, not one form. Logging so you can tell why it broke. The edge cases. And that twenty percent does not just sit there waiting politely. Left unaddressed, it compounds into technical debt that is more expensive to retrofit than it would have been to build correctly the first time, because by then the duplication and inconsistency are spread across files you never read.&lt;/p&gt;

&lt;p&gt;This is the moment most beginners hit and misread. The demo worked, so the project feels ninety percent done, and then every new change breaks two old things and nobody can say why. We told one version of that story in &lt;a href="https://www.braingrid.ai/blog/i-built-a-vibe-coding-mess" rel="noopener noreferrer"&gt;I built a vibe-coding mess&lt;/a&gt;, and dug into why that final stretch is so stubborn in &lt;a href="https://www.braingrid.ai/blog/the-second-80-percent" rel="noopener noreferrer"&gt;The Second 80%&lt;/a&gt;. It is not that you did agentic coding wrong. It is that generating code and producing a trustworthy product are different jobs, and the agent only volunteered for the first one. The honest trade-off of this entire approach: speed at the start, a debt at the finish, and the debt is invisible until you are standing in it.&lt;/p&gt;

&lt;h2&gt;
  
  
  The skill that closes the gap
&lt;/h2&gt;

&lt;p&gt;So if the bottleneck is not generating code, what is it? Andrej Karpathy, who coined the term vibe coding and then this year &lt;a href="https://www.youtube.com/watch?v=96jN2OCOfLs" rel="noopener noreferrer"&gt;declared it obsolete for serious work&lt;/a&gt;, drew the line that matters at Sequoia's AI Ascent:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Vibe coding is about raising the floor for everyone in terms of what they can do in software. Agentic engineering is about preserving the quality bar of professional software.&lt;/p&gt;

&lt;p&gt;&lt;cite&gt;Andrej Karpathy, Sequoia AI Ascent 2026&lt;/cite&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Raising the floor is what gets you the eighty percent. Preserving the quality bar is what gets you the twenty, and Karpathy is explicit about how: you work with your agent to design a spec that is very detailed, basically the docs, and then get the agents to write against it. The skill is not coding. It is specifying precisely and verifying honestly. That is the thing the agent cannot do for you, because it is the definition of what right means, and the agent is the one being checked.&lt;/p&gt;

&lt;p&gt;This is the gap BrainGrid was built to close, and it is built for exactly the person without a CS degree. BrainGrid is the system that takes an idea to a live product you can trust. You describe the feature in plain language, the way you described that login page above, and the Planning Agent asks the clarifying questions a senior engineer would, surfaces the edge cases you did not think of, and turns your intent into a requirement with explicit acceptance criteria. That requirement is the spec your agent works against and the standard your result is checked by.&lt;/p&gt;

&lt;p&gt;From there you choose how to build. Hand the scoped tasks to &lt;a href="https://www.braingrid.ai/ai-coding-agents" rel="noopener noreferrer"&gt;Claude Code, Cursor, or Codex&lt;/a&gt; in your own setup, or let the Builder Agent build it in a managed sandbox with a live preview and open a pull request. Either way, verification closes the loop: every acceptance criterion is checked against the result, and the feature is not done until the evidence says it does what you meant. The twenty percent stops being the part you discover in production and becomes the part you specified up front. That is the whole loop, Plan, Build, Verify, Repeat, and it is the same discipline we cover in &lt;a href="https://www.braingrid.ai/agentic-engineering" rel="noopener noreferrer"&gt;agentic engineering&lt;/a&gt; and in &lt;a href="https://www.braingrid.ai/spec-driven-development" rel="noopener noreferrer"&gt;spec-driven development&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Strip away the tool names and the trending terms and the argument holds on its own: agentic coding lowered the cost of producing software to almost nothing, which means the scarce thing is no longer typing. It is knowing what you want and proving you got it. If you are a domain expert who has been told you cannot build because you cannot code, that equation just moved in your favor. The work that is left is the work you were always good at.&lt;/p&gt;

&lt;h2&gt;
  
  
  FAQ
&lt;/h2&gt;

&lt;h3&gt;
  
  
  What is agentic coding?
&lt;/h3&gt;

&lt;p&gt;Agentic coding is a software development approach where autonomous AI agents plan, write, test, and modify code with minimal human intervention. Instead of suggesting the next line like autocomplete, a coding agent takes a high-level instruction, breaks it into steps, reads and writes files across your project, runs commands, checks its own work, and iterates until the task is complete. You direct it and review the result; it does the implementation.&lt;/p&gt;

&lt;h3&gt;
  
  
  What is the difference between AI coding and agentic coding?
&lt;/h3&gt;

&lt;p&gt;Traditional AI coding assistants are reactive: they wait for you to type and offer suggestions or answer questions. Agentic coding tools are autonomous: they take a goal and execute it end to end, navigating files, running terminal commands, and self-correcting when a test fails. The simplest way to put it is that an assistant finishes your sentence, while an agent runs the errand.&lt;/p&gt;

&lt;h3&gt;
  
  
  What is the 80 percent problem in agentic coding?
&lt;/h3&gt;

&lt;p&gt;The 80 percent problem is the gap between the working code an agent reliably produces, roughly eighty percent of a solution, and the production-grade remaining twenty percent: error handling, security, observability, edge cases, and compliance. Agents are excellent at the happy-path demo and weak at the parts that make software survive real use. Left unaddressed, that twenty percent compounds into technical debt that costs more to retrofit than it would have to build correctly from the start. The fix is defining the full standard up front so the agent builds to it, not just to the demo.&lt;/p&gt;

&lt;h3&gt;
  
  
  Can you do agentic coding for free?
&lt;/h3&gt;

&lt;p&gt;Yes. Most coding agents offer a free tier or a low-cost entry plan, and there are open-source agents you can run on your own machine at no cost. Cost is rarely the real barrier to starting. The harder part is learning to write a clear specification and to recognize when the agent's output is wrong, both of which are free to practice on small tasks.&lt;/p&gt;

&lt;h3&gt;
  
  
  Do you need to know how to code to do agentic coding?
&lt;/h3&gt;

&lt;p&gt;You do not need a CS degree, but you do need judgment. The skill that matters most is writing a clear specification, stating what you want and how you will know it worked, and verifying the result honestly. MIT's own framing is that working with a coding agent is like managing an intern: you provide direction and catch mistakes, the agent does the detailed work. Domain experts who know exactly what good looks like in their field often have the most important skill already. Tools like BrainGrid's Planning Agent turn a plain-language idea into a structured requirement with acceptance criteria, so the specifying and checking happen without writing code yourself.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;&lt;a href="https://braingrid.ai" rel="noopener noreferrer"&gt;BrainGrid&lt;/a&gt; is the AI Product Planner that turns your plain-language idea into a spec your coding agent can build against and be checked against, so the last twenty percent stops being a surprise. Try it at &lt;a href="https://braingrid.ai" rel="noopener noreferrer"&gt;braingrid.ai&lt;/a&gt;.&lt;/em&gt;&lt;/p&gt;




&lt;p&gt;&lt;em&gt;Originally published on the &lt;a href="https://www.braingrid.ai/blog/agentic-coding" rel="noopener noreferrer"&gt;BrainGrid blog&lt;/a&gt;.&lt;/em&gt;&lt;/p&gt;

</description>
      <category>ai</category>
      <category>programming</category>
      <category>tutorial</category>
      <category>productivity</category>
    </item>
    <item>
      <title>GitHub stores the code, not the decision. Where the \"why\" should actually live.</title>
      <dc:creator>Nico Acosta</dc:creator>
      <pubDate>Thu, 23 Jul 2026 13:16:05 +0000</pubDate>
      <link>https://dev.to/radial/github-stores-the-code-not-the-decision-where-the-why-should-actually-live-14a0</link>
      <guid>https://dev.to/radial/github-stores-the-code-not-the-decision-where-the-why-should-actually-live-14a0</guid>
      <description>&lt;p&gt;An architecture decision record (ADR) is a short document that captures one architecturally significant decision: the context that forced the choice, the decision itself, and the consequences that follow. Michael Nygard proposed the format in 2011, and the convention that stuck is a numbered Markdown file in &lt;code&gt;doc/adr/&lt;/code&gt; in the repository, one page, written once and never edited. If the decision changes later, you do not rewrite the old record. You write a new one that supersedes it, so the log stays append-only and the history of your thinking stays readable.&lt;/p&gt;

&lt;p&gt;That is the whole idea, and it is a good one. The rest of this post is about the part the format does not solve: most of the decisions that shape a codebase never become an ADR at all, and the reason is not laziness.&lt;/p&gt;

&lt;h2&gt;
  
  
  What goes in an ADR
&lt;/h2&gt;

&lt;p&gt;The Nygard template has four sections, and nearly every variant since (MADR, the AWS and Microsoft prescriptive versions) is a reshuffle of the same bones.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Title.&lt;/strong&gt; A short noun phrase naming the decision. "Use PostgreSQL for the primary datastore," not "Database discussion."&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Status.&lt;/strong&gt; Proposed, accepted, rejected, deprecated, or superseded. The status is what makes the log a log rather than a pile.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Context.&lt;/strong&gt; The forces in play: the constraints, the requirements, the things that were true when you decided. This is the section people skimp on and the section future readers actually need. A decision without its context reads as arbitrary six months later.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Decision.&lt;/strong&gt; What you chose, in active voice. "We will use PostgreSQL," not "PostgreSQL seems like it might be reasonable."&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Consequences.&lt;/strong&gt; What follows, good and bad. What this makes easy, what it makes hard, what you now have to live with, and what you ruled out to get here.&lt;/p&gt;

&lt;p&gt;Keep it to a page. An ADR is a snapshot of a decision, not a design document, and the moment it grows sections it stops getting written.&lt;/p&gt;

&lt;h2&gt;
  
  
  When to write one
&lt;/h2&gt;

&lt;p&gt;The rule of thumb from the AWS guidance and the Red Hat writeup agrees on the shape: write an ADR when the decision is &lt;strong&gt;architecturally significant&lt;/strong&gt;, meaning it is expensive to reverse, it affects the structure of the system, or it constrains what the team can do later. Choosing Postgres over DynamoDB is an ADR. Choosing a variable name is not. Adopting an event-driven boundary between two services is an ADR. Bumping a patch version is not.&lt;/p&gt;

&lt;p&gt;The useful test is the one Red Hat gives: an ADR is not a glorified changelog, and it does not replace good commit messages. If the answer to "why is this like this" is already visible in the diff, you do not need a record. You need one when the answer lives in a conversation that the diff cannot show, and when getting it wrong later will cost a rewrite.&lt;/p&gt;

&lt;h2&gt;
  
  
  The decisions that never make it to &lt;code&gt;doc/adr/&lt;/code&gt;
&lt;/h2&gt;

&lt;p&gt;Here is the honest gap, and it is worth naming precisely because ADRs are good, not because they are not.&lt;/p&gt;

&lt;p&gt;The ADR format works well for the decisions you know are significant at the moment you make them. You recognize the fork, you convene the discussion, someone writes the file. But most architectural drift is not made of those. It is made of the smaller decisions that only look significant in retrospect: the retry semantics someone picked while fixing a flaky test, the approach that got ruled out in a pull request thread and never wrote itself down, the reason the third service does its own auth instead of using the shared middleware. Nobody stops mid-fix to open a numbered Markdown file for those. They are load-bearing anyway.&lt;/p&gt;

&lt;p&gt;Those decisions do get made somewhere, and the somewhere is almost always the work: an issue, a comment thread, a pull request review, a session with a coding agent. Git stores the code that came out the other side. It does not store the four alternatives you rejected, the constraint that ruled out the obvious approach, or the argument that changed someone's mind. The commit is the artifact, not the reasoning.&lt;/p&gt;

&lt;p&gt;This has gotten sharper with agents in the loop. When an agent plans an approach, rules out two others, and implements the third, all of that reasoning exists in a context window, and the context window compacts or ends. The code survives. The reasoning does not, unless something outside the model wrote it down.&lt;/p&gt;

&lt;h2&gt;
  
  
  Two records, one repository
&lt;/h2&gt;

&lt;p&gt;The version that works in practice is not "ADRs or the tracker." It is both, doing different jobs.&lt;/p&gt;

&lt;p&gt;Keep &lt;code&gt;doc/adr/&lt;/code&gt; for the big irreversible forks: the datastore, the service boundaries, the auth model. Those deserve a numbered, append-only, superseded-not-edited file that ships with the code and gets read on onboarding.&lt;/p&gt;

&lt;p&gt;Then let the ordinary decisions land where the work already is, in the issue that produced them, at the moment they are made. The record is worth having only if writing it costs nothing, which means it has to happen in the same motion as the work, not as a separate ceremony afterward.&lt;/p&gt;

&lt;p&gt;Radial is an issue tracker built so that motion is one command. It has a real CLI, a plain REST API, and an MCP server against the same issue lifecycle, so the same decision gets recorded whether you are in the terminal, in CI, or driving it with an agent. Every command takes &lt;code&gt;--json&lt;/code&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# Record the decision on the issue that produced it, as it happens&lt;/span&gt;
radial comment RAD-482 &lt;span class="nt"&gt;-m&lt;/span&gt; &lt;span class="s2"&gt;"Chose optimistic locking over a row-level mutex. Ruled out the queue: adds a broker to a path that must stay synchronous. Revisit if write contention exceeds ~200/s."&lt;/span&gt;

&lt;span class="c"&gt;# Link it to the issue that will actually have to live with it&lt;/span&gt;
radial &lt;span class="nb"&gt;link &lt;/span&gt;RAD-482 related RAD-611

&lt;span class="c"&gt;# The constraint that ruled out the obvious approach is itself work: file it&lt;/span&gt;
radial create &lt;span class="s2"&gt;"Write ADR-014: locking strategy for the ledger writer"&lt;/span&gt; &lt;span class="nt"&gt;-t&lt;/span&gt; ENG &lt;span class="nt"&gt;-p&lt;/span&gt; high &lt;span class="nt"&gt;--json&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;That last command is the seam between the two records. The tracker catches the decision at the moment it is made, when the context is still in someone's head, and the ones that turn out to be architecturally significant graduate into a proper ADR in the repository. The ones that do not are still findable, still attached to the work, and still readable by the next person or the next agent.&lt;/p&gt;

&lt;p&gt;Nothing about this is an AI feature. Radial does not read your code, summarize your diffs, or generate your ADRs. It is a fast issue tracker that people and agents can drive over CLI, REST, and MCP, and the durable record is what happens when decisions get filed the ordinary way. The intelligence stays in your agent, your model, your keys.&lt;/p&gt;

&lt;h2&gt;
  
  
  FAQ
&lt;/h2&gt;

&lt;h3&gt;
  
  
  What is an architecture decision record?
&lt;/h3&gt;

&lt;p&gt;An architecture decision record (ADR) is a short document capturing one architecturally significant decision, along with its context, the decision itself, and its consequences. It is typically a numbered Markdown file stored in the code repository, often under &lt;code&gt;doc/adr/&lt;/code&gt;, and it is written once rather than edited: if the decision changes, a new record supersedes the old one, keeping the log append-only.&lt;/p&gt;

&lt;h3&gt;
  
  
  What should an ADR contain?
&lt;/h3&gt;

&lt;p&gt;The Nygard template, which most variants follow, has four sections: title (a short noun phrase naming the decision), status (proposed, accepted, rejected, deprecated, or superseded), context (the forces and constraints that made the decision necessary), decision (what you chose, in active voice), and consequences (what follows, including what you ruled out and what you now have to live with). Keep it to roughly one page.&lt;/p&gt;

&lt;h3&gt;
  
  
  When should you write an architecture decision record?
&lt;/h3&gt;

&lt;p&gt;Write one when the decision is architecturally significant: expensive to reverse, structural, or constraining on what the team can do later. Choosing a datastore or a service boundary qualifies. Naming a variable does not. If the reason is already visible in the diff, a good commit message is enough. Write the ADR when the reasoning lives in a conversation the diff cannot show.&lt;/p&gt;

&lt;h3&gt;
  
  
  What tools are there for architecture decision records?
&lt;/h3&gt;

&lt;p&gt;The common ones are Markdown-based and deliberately minimal: MADR provides a lean template, and &lt;code&gt;adr-tools&lt;/code&gt; is a set of command-line tools that create, number, and supersede records in your repository. Both keep the records as plain files next to the code, which is the point of the format. For the smaller decisions that never reach a numbered file, an issue tracker with a comment thread does the job at the moment the decision is made.&lt;/p&gt;

&lt;h3&gt;
  
  
  Where should decisions live if not in an ADR?
&lt;/h3&gt;

&lt;p&gt;In the work that produced them. Most decisions are not architecturally significant enough to justify a numbered file, but they still shape the system: the retry semantics, the ruled-out approach, the constraint nobody documented. Recording them on the issue keeps them attached to the work, findable later, and readable by the next person or agent. See &lt;a href="https://radial.build/blog/agentic-coding" rel="noopener noreferrer"&gt;agentic coding ships the 80%&lt;/a&gt; for the version of this argument that involves coding agents.&lt;/p&gt;

&lt;h3&gt;
  
  
  Do ADRs replace commit messages or a changelog?
&lt;/h3&gt;

&lt;p&gt;No. An ADR is not a glorified changelog, and it does not replace good commit messages. A changelog records what changed; a commit message records what this change does; an ADR records why a significant structural choice was made and what it rules out. They answer different questions and the ADR is the most expensive of the three, which is why it should be reserved for decisions that are costly to reverse.&lt;/p&gt;

&lt;h2&gt;
  
  
  The short version
&lt;/h2&gt;

&lt;p&gt;An ADR is a one-page, append-only record of a significant technical decision: context, decision, consequences, stored with the code. Use it for the forks that are expensive to reverse. But most of what shapes a codebase is decided in smaller moments, in an issue, a review, or an agent session, and Git stores what those produced, not why. Record those where the work already lives, and let the ones that turn out to matter graduate into a real ADR.&lt;/p&gt;

&lt;p&gt;See the full CLI verbs and the API on the &lt;a href="https://radial.build/developers" rel="noopener noreferrer"&gt;developers&lt;/a&gt; page, or read the &lt;a href="https://radial.build/manifesto" rel="noopener noreferrer"&gt;manifesto&lt;/a&gt; for why the tracker stays a tracker.&lt;/p&gt;




&lt;p&gt;&lt;em&gt;Originally published on the &lt;a href="https://radial.build/blog/architecture-decision-record" rel="noopener noreferrer"&gt;Radial blog&lt;/a&gt;.&lt;/em&gt;&lt;/p&gt;

</description>
      <category>webdev</category>
      <category>programming</category>
      <category>typescript</category>
      <category>tooling</category>
    </item>
    <item>
      <title>How to Run Playwright in Docker (and the Browser-Deps Tax It Adds)</title>
      <dc:creator>Nico Acosta</dc:creator>
      <pubDate>Thu, 23 Jul 2026 11:39:40 +0000</pubDate>
      <link>https://dev.to/grabbit/how-to-run-playwright-in-docker-and-the-browser-deps-tax-it-adds-275g</link>
      <guid>https://dev.to/grabbit/how-to-run-playwright-in-docker-and-the-browser-deps-tax-it-adds-275g</guid>
      <description>&lt;p&gt;Playwright in Docker is mostly a solved problem, and the solution is one line: use the official image. The reason people end up here anyway is the failure mode underneath it. Playwright drives a real browser, a real browser links against dozens of native shared libraries, and slim container base images do not ship them. Miss one and you get a launch error that names a &lt;code&gt;.so&lt;/code&gt; file instead of anything to do with your test.&lt;/p&gt;

&lt;p&gt;This guide covers the working setup, the errors that actually show up in CI, and the honest boundary where containerizing a browser stops being worth it.&lt;/p&gt;

&lt;h2&gt;
  
  
  Use the official image
&lt;/h2&gt;

&lt;p&gt;Microsoft publishes an image with the browsers and their system dependencies preinstalled:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;mcr.microsoft.com/playwright:v1.61.1-noble
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Pin the tag to the same Playwright version your project depends on. This is not a style preference. The npm package and the browser binaries are versioned together, and a mismatch produces a launch failure that reads like a corrupted install. If &lt;code&gt;package.json&lt;/code&gt; says &lt;code&gt;1.61.1&lt;/code&gt;, the image tag says &lt;code&gt;v1.61.1&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;A working Dockerfile for a Node project:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight docker"&gt;&lt;code&gt;&lt;span class="k"&gt;FROM&lt;/span&gt;&lt;span class="s"&gt; mcr.microsoft.com/playwright:v1.61.1-noble&lt;/span&gt;

&lt;span class="k"&gt;WORKDIR&lt;/span&gt;&lt;span class="s"&gt; /app&lt;/span&gt;
&lt;span class="k"&gt;COPY&lt;/span&gt;&lt;span class="s"&gt; package*.json ./&lt;/span&gt;
&lt;span class="k"&gt;RUN &lt;/span&gt;npm ci
&lt;span class="k"&gt;COPY&lt;/span&gt;&lt;span class="s"&gt; . .&lt;/span&gt;

&lt;span class="k"&gt;CMD&lt;/span&gt;&lt;span class="s"&gt; ["npx", "playwright", "test"]&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Note what is missing: no &lt;code&gt;playwright install&lt;/code&gt;, no &lt;code&gt;install-deps&lt;/code&gt;, no &lt;code&gt;apt-get&lt;/code&gt; block. The image already has the browsers and the libraries. What it does not have is your &lt;code&gt;node_modules&lt;/code&gt;, which is why &lt;code&gt;npm ci&lt;/code&gt; still runs. That distinction, the browsers are in the image but the package is in your project, is the thing the Stack Overflow threads keep circling.&lt;/p&gt;

&lt;p&gt;Run it:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;docker build &lt;span class="nt"&gt;-t&lt;/span&gt; my-tests &lt;span class="nb"&gt;.&lt;/span&gt;
docker run &lt;span class="nt"&gt;--rm&lt;/span&gt; &lt;span class="nt"&gt;--ipc&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;host my-tests
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The &lt;code&gt;--ipc=host&lt;/code&gt; flag is not optional in practice. Chromium uses shared memory heavily, Docker's default &lt;code&gt;/dev/shm&lt;/code&gt; is 64MB, and the result is a browser that starts fine and then dies partway through a run. If you cannot use &lt;code&gt;--ipc=host&lt;/code&gt;, use &lt;code&gt;--shm-size=1gb&lt;/code&gt; instead.&lt;/p&gt;

&lt;h2&gt;
  
  
  Building your own image
&lt;/h2&gt;

&lt;p&gt;Sometimes the official image does not fit: you need a specific base, a different runtime version, or a smaller final layer. Then you own the dependency problem, and the command that solves it is:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight docker"&gt;&lt;code&gt;&lt;span class="k"&gt;RUN &lt;/span&gt;npx playwright &lt;span class="nb"&gt;install&lt;/span&gt; &lt;span class="nt"&gt;--with-deps&lt;/span&gt; chromium
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;code&gt;--with-deps&lt;/code&gt; is the important half. Without it you get the browser binary and none of the system libraries it dynamically links against, which is exactly the error people paste into forums: &lt;code&gt;libnss3.so: cannot open shared object file&lt;/code&gt;. Naming a single browser (&lt;code&gt;chromium&lt;/code&gt;) rather than installing all three cuts image size substantially when your suite only targets one.&lt;/p&gt;

&lt;p&gt;Two things to know before you go this route. Alpine is not supported, because Playwright's browser builds link against glibc and Alpine ships musl, so start from a Debian or Ubuntu base. And installing browsers yourself adds a slow, network-dependent layer to every rebuild that the official image has already paid for.&lt;/p&gt;

&lt;h2&gt;
  
  
  The errors you will actually hit
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;The container exits immediately.&lt;/strong&gt; Usually correct behavior rather than a bug: a container lives as long as PID 1, and if PID 1 is &lt;code&gt;playwright test&lt;/code&gt;, the container stops when the run ends. Check the exit code before assuming it crashed. A non-zero code means the tests failed; zero means it worked and finished.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Browser closed unexpectedly, mid-run.&lt;/strong&gt; Shared memory. Use &lt;code&gt;--ipc=host&lt;/code&gt; or &lt;code&gt;--shm-size=1gb&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;A missing &lt;code&gt;.so&lt;/code&gt; file at launch.&lt;/strong&gt; Either a custom image without &lt;code&gt;--with-deps&lt;/code&gt;, or an Alpine base.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Works locally, fails in CI.&lt;/strong&gt; Nearly always a version mismatch between the image tag and the installed Playwright package after a dependency bump. Pin both, bump both together.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Screenshots differ between your machine and the container.&lt;/strong&gt; This is expected, not broken. Font rendering and available font families differ across environments, so pixel comparisons made on a laptop will not match baselines made in a container. The fix is to generate baselines in the same container that verifies them, which is the main reason visual regression suites get containerized at all. There is more on that in &lt;a href="https://www.grabbit.live/blog/playwright-visual-regression-testing" rel="noopener noreferrer"&gt;visual regression testing in Playwright CI&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  When Docker is the right call, and when it is not
&lt;/h2&gt;

&lt;p&gt;Containerizing Playwright buys you a reproducible browser environment. That is worth real setup cost when your suite is doing browser work: navigating multi-step flows, logging in, filling forms, asserting on state, comparing screenshots against baselines that must be byte-stable. In those cases you need the whole browser under your control, and a container is how you make "the whole browser" identical everywhere.&lt;/p&gt;

&lt;p&gt;The calculation changes when the browser is incidental. A large share of Playwright-in-Docker setups exist to produce an image of a page: an OG card, a report thumbnail, a nightly capture of a dashboard, a preview for a marketing site. There the container is not giving you determinism you need, it is giving you a browser you are obligated to maintain, so that an HTTP request can come back with a PNG.&lt;/p&gt;

&lt;p&gt;If that is the job, the render can move off your infrastructure entirely:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;curl &lt;span class="nt"&gt;-sS&lt;/span&gt; https://api.grabbit.live/v1/grabs &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;-H&lt;/span&gt; &lt;span class="s2"&gt;"Authorization: Bearer sk_live_..."&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="s1"&gt;'{
    "url": "https://example.com/pricing",
    "width": 1280,
    "full_page": true,
    "delay_ms": 1000,
    "format": "webp"
  }'&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The response carries an &lt;code&gt;image_url&lt;/code&gt; pointing at the stored capture. No image to build, no browser version to pin, no &lt;code&gt;--shm-size&lt;/code&gt; to remember, nothing to rebuild when a base image ships a new glibc.&lt;/p&gt;

&lt;p&gt;The parameters map closely to what you would have written in Playwright: &lt;code&gt;width&lt;/code&gt; (320 to 1920) pins the viewport the way &lt;code&gt;setViewportSize&lt;/code&gt; does, &lt;code&gt;full_page&lt;/code&gt; is the equivalent of &lt;code&gt;fullPage: true&lt;/code&gt;, &lt;code&gt;delay_ms&lt;/code&gt; (0 to 10000) replaces a fixed &lt;code&gt;waitForTimeout&lt;/code&gt; after load, &lt;code&gt;selector&lt;/code&gt; captures a single element the way a locator screenshot does, and &lt;code&gt;format&lt;/code&gt; accepts &lt;code&gt;png&lt;/code&gt;, &lt;code&gt;jpeg&lt;/code&gt;, or &lt;code&gt;webp&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;The honest boundary: if the capture has to happen after a login, a click, or a form submission, none of this helps you and Playwright in a container is the correct tool. An API call renders a URL. It does not drive a session.&lt;/p&gt;

&lt;h2&gt;
  
  
  Cost, honestly
&lt;/h2&gt;

&lt;p&gt;The relevant comparison is not per-request price against zero, because self-hosting is not free either. A containerized browser costs CI minutes on every build, image storage, and the recurring attention of whoever fixes it when a base image or Playwright version breaks the setup.&lt;/p&gt;

&lt;p&gt;Grabbit is $0.002 per live grab, prepaid, with credits that do not reset monthly, which fits bursty work like a capture pipeline that runs hard some weeks and not at all in others. Test-environment keys return placeholder images at no cost, so you can wire the whole flow before any real capture runs. Other providers list lower per-grab rates, so if unit price is the only variable you care about, compare directly.&lt;/p&gt;

&lt;h2&gt;
  
  
  Where to go next
&lt;/h2&gt;

&lt;p&gt;If you are keeping the container and just want the capture code to be right, &lt;a href="https://www.grabbit.live/blog/playwright-screenshot" rel="noopener noreferrer"&gt;how to take screenshots in Playwright&lt;/a&gt; covers the full-page, element, and CI cases. If you are weighing whether to keep running a browser at all, &lt;a href="https://www.grabbit.live/blog/puppeteer-alternative" rel="noopener noreferrer"&gt;Puppeteer alternatives in 2026&lt;/a&gt; walks the same decision from the other library, and the &lt;a href="https://www.grabbit.live/screenshot-api" rel="noopener noreferrer"&gt;screenshot API&lt;/a&gt; page covers the hosted side in depth.&lt;/p&gt;




&lt;p&gt;&lt;em&gt;Originally published on the &lt;a href="https://www.grabbit.live/blog/playwright-docker" rel="noopener noreferrer"&gt;Grabbit blog&lt;/a&gt;.&lt;/em&gt;&lt;/p&gt;

</description>
      <category>webdev</category>
      <category>javascript</category>
      <category>node</category>
      <category>programming</category>
    </item>
    <item>
      <title>Harness Engineering, Explained: What Separates Top Agentic Engineers</title>
      <dc:creator>Nico Acosta</dc:creator>
      <pubDate>Thu, 23 Jul 2026 11:11:33 +0000</pubDate>
      <link>https://dev.to/braingrid/harness-engineering-explained-what-separates-top-agentic-engineers-4e15</link>
      <guid>https://dev.to/braingrid/harness-engineering-explained-what-separates-top-agentic-engineers-4e15</guid>
      <description>&lt;p&gt;OpenAI spent five months building an internal product where humans wrote zero lines of code, and the engineers on that team were busier than ever. Roughly a million lines shipped across more than 1,500 pull requests, every one of them authored, reviewed, and merged by agents. The work did not disappear. It moved into something the team calls the harness, and in the four months since, the idea has completed its arc from insider habit to formal discipline. There is now an open-source framework, &lt;a href="https://github.com/aiming-lab/AutoHarness" rel="noopener noreferrer"&gt;AutoHarness&lt;/a&gt;, whose entire premise is that an agent is just a model plus a harness, and the harness is the part you engineer.&lt;/p&gt;

&lt;p&gt;Here is the claim worth testing: what separates the builders getting production-grade output from agents is not the model they picked or the prompts they write. It is whether they built the environment around the agent. And inside that claim hides a second one that matters even more, because the harness, for all its power, cannot generate its own most important input.&lt;/p&gt;

&lt;h2&gt;
  
  
  What harness engineering actually is
&lt;/h2&gt;

&lt;p&gt;The term has a precise origin story. Mitchell Hashimoto, the creator of Terraform and Vagrant, described the practice in &lt;a href="https://mitchellh.com/writing/my-ai-adoption-journey" rel="noopener noreferrer"&gt;his AI adoption journey&lt;/a&gt; in early February 2026, as the step where his agent use stopped being frustrating and started compounding:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Anytime you find an agent makes a mistake, you take the time to engineer a solution such that the agent never makes that mistake again.&lt;/p&gt;

&lt;p&gt;&lt;cite&gt;&lt;a href="https://mitchellh.com/writing/my-ai-adoption-journey" rel="noopener noreferrer"&gt;Mitchell Hashimoto, "My AI Adoption Journey"&lt;/a&gt;&lt;/cite&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Six days later, OpenAI &lt;a href="https://openai.com/index/harness-engineering/" rel="noopener noreferrer"&gt;gave the discipline its formal name&lt;/a&gt;. Ryan Lopopolo's post described how their agent-first team operates: engineers do not write the code, they build the structure, documentation, and automated checks that keep agents on track. The post's core principle fits in four words: humans steer, agents execute. The metaphor is deliberate. A harness, in the original sense, is the equipment that channels a powerful animal's strength in a useful direction. Nothing about the harness makes the horse stronger. It makes the strength go somewhere.&lt;/p&gt;

&lt;p&gt;So a working definition: harness engineering is the practice of designing the environment around an AI coding agent, the context it reads, the tools it can call, and the checks that catch its output, so the agent produces the right result instead of a plausible one. Your agent's raw capability is rented from a model provider, and it is the same capability everyone else rents. The harness is the part you own.&lt;/p&gt;

&lt;h2&gt;
  
  
  What a harness is made of
&lt;/h2&gt;

&lt;p&gt;Birgitta Böckeler, writing the most rigorous treatment of the subject &lt;a href="https://martinfowler.com/articles/harness-engineering.html" rel="noopener noreferrer"&gt;on martinfowler.com&lt;/a&gt;, defines the harness as everything in an agent except the model itself, and sorts its parts into two families. Guides act before the agent does: the AGENTS.md file that carries your conventions, the architecture notes, the bootstrap scripts that set up a working environment. Sensors act after: linters, type checkers, test suites, and review agents that inspect what came out and feed problems back in.&lt;/p&gt;

&lt;p&gt;The whole practice reduces to one move, applied relentlessly. When the agent does something wrong, you do not correct it in chat. You encode the correction where every future run will hit it.&lt;/p&gt;

&lt;p&gt;The difference is easy to see side by side. Chat correction: "No, use our date helper, not raw &lt;code&gt;Date()&lt;/code&gt;," and the agent complies, and that knowledge evaporates the moment the session ends. Harness correction: a lint rule that flags raw &lt;code&gt;Date()&lt;/code&gt; plus one line in AGENTS.md explaining the helper, and no agent in any future session ships that mistake again. The first is a conversation. The second is an asset. Top agentic engineers are the ones with eight months of accumulated corrections sitting in their repo, which is why their agents seem eerily reliable and yours seems forgetful. Same model. Different harness.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;flowchart LR
    S[Spec with acceptance criteria] --&amp;gt; A[Agent run]
    G[Guides: AGENTS.md, conventions, scripts] --&amp;gt; A
    A --&amp;gt; C{Sensors: tests, linters, review}
    C --&amp;gt;|fail| F[Feedback to agent]
    F --&amp;gt; A
    C --&amp;gt;|pass| P[PR you can trust]
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  The harness governs how. It cannot decide what.
&lt;/h2&gt;

&lt;p&gt;Now for the part the harness explainers skip, and the reason the diagram above starts where it starts. Every component of a harness shares one property: it evaluates the agent's output against a standard somebody already wrote down. The linter knows your formatting rules. The type checker knows your interfaces. The test suite knows the behavior somebody specified. Feed the harness a vague goal and it will happily help the agent build the wrong thing correctly, with clean types, passing tests it wrote for itself, and conventions intact.&lt;/p&gt;

&lt;p&gt;Böckeler lands on exactly this limit, and her conclusion is the reframe that should change how you spend your effort:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;A good harness should not necessarily aim to fully eliminate human input, but to direct it to where our input is most important.&lt;/p&gt;

&lt;p&gt;&lt;cite&gt;&lt;a href="https://martinfowler.com/articles/harness-engineering.html" rel="noopener noreferrer"&gt;Birgitta Böckeler, "Harness engineering for coding agent users"&lt;/a&gt;&lt;/cite&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Where is your input most important? Not in the lint rules. The agent can draft those. Not in the test scaffolding. The agent writes that too, and the harness checks it. The one input the harness cannot generate, verify, or even detect the absence of is the definition of what you are building and how you will know it is right. A harness with no spec is scaffolding around an empty lot. This is also what distinguishes the harness from its sibling buzzword: the loop schedules and re-runs your agent, the harness shapes and checks each run, and the spec tells both of them what done means. We covered the loop layer in &lt;a href="https://www.braingrid.ai/blog/loop-engineering" rel="noopener noreferrer"&gt;What Is Loop Engineering?&lt;/a&gt;; the punchline there was that the loop's verifier needs a standard. The harness is that verifier, and it needs the standard just as badly.&lt;/p&gt;

&lt;p&gt;This is the gap BrainGrid fills. BrainGrid is the system that takes an idea to a live product you can trust, and it sits exactly at the harness's missing input. You describe the feature in plain language, and the Planning Agent interrogates it the way a senior engineer would, surfacing edge cases and turning intent into a requirement with explicit acceptance criteria. That requirement becomes the harness's reference point. Run the build either way: hand the scoped tasks to &lt;a href="https://www.braingrid.ai/ai-coding-agents" rel="noopener noreferrer"&gt;Claude Code, Cursor, or Codex&lt;/a&gt; over MCP inside your own harness, or let the Builder Agent build in a managed sandbox with a live preview and open the PR. Then verification closes the loop the way a linter never can, checking the result against each acceptance criterion and holding the feature short of done until the evidence says it does what you intended. Code review tells you the code is well-written. Verification tells you it does what you meant.&lt;/p&gt;

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

&lt;p&gt;Two trade-offs, because the discipline has them. First, a harness is a second codebase. Hashimoto's rule sounds light, but engineering a permanent fix for every mistake is real, ongoing work, and on a legacy codebase with years of unwritten conventions, the backfill is steep. Budget for it like infrastructure, because that is what it is.&lt;/p&gt;

&lt;p&gt;Second, the harness has a ceiling, and you should know where it is. Böckeler's warning is that no current check, mechanical or model-based, reliably catches the agent that misunderstood the assignment. The failure mode that actually burns builders is not malformed code. It is well-formed code that solves the wrong problem. That failure is invisible to every sensor in the harness except one: a written statement of what the right problem was. The better your agents get, the more this holds, because more capable agents produce more output per unit of your attention, and every unit of output that nobody specified is a decision nobody made.&lt;/p&gt;

&lt;p&gt;If you are building with Claude Code or Cursor today, here is what this means concretely: your edge is no longer in the prompt box. An hour spent writing one more clever prompt is worth less than an hour spent adding the lint rule, the AGENTS.md entry, and the acceptance criteria that make the next hundred prompts land. The builders pulling ahead right now are not prompting better. They are accumulating a harness, and they are feeding it specs.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Start your harness this week:&lt;/strong&gt;&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Create an AGENTS.md file and add one entry for every mistake your agent has made twice. (&lt;a href="https://www.braingrid.ai/blog/claude-skills" rel="noopener noreferrer"&gt;Claude Code skills&lt;/a&gt; are the structured version of the same move.)&lt;/li&gt;
&lt;li&gt;Wire your existing checks into the agent's path: tests, linter, type checker, so failures feed back automatically.&lt;/li&gt;
&lt;li&gt;Before the next feature, write the acceptance criteria first, in BrainGrid or anywhere, so the harness has something real to check against.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Strip away the OpenAI post, the GitHub stars, and the new vocabulary, and the argument stands on its own: an agent's output is only as trustworthy as the environment that checks it, and the environment is only as good as the intent you wrote down. The harness is how the agent stops repeating mistakes. The spec is how it stops making the expensive one.&lt;/p&gt;

&lt;h2&gt;
  
  
  FAQ
&lt;/h2&gt;

&lt;h3&gt;
  
  
  What is harness engineering?
&lt;/h3&gt;

&lt;p&gt;Harness engineering is the practice of designing the environment around an AI coding agent so it produces reliable results: the context files it reads (like AGENTS.md), the tools and scripts it can call, and the automated checks (tests, linters, review agents) that catch its output and feed problems back. The core move is encoding every correction permanently in the environment instead of repeating it in chat.&lt;/p&gt;

&lt;h3&gt;
  
  
  Who coined the term harness engineering?
&lt;/h3&gt;

&lt;p&gt;Mitchell Hashimoto, creator of Terraform, described "engineering the harness" in his "My AI Adoption Journey" post on February 5, 2026. OpenAI formalized the term six days later in Ryan Lopopolo's February 11 post about building an internal product with zero human-written code. The underlying word is older: a harness has long meant the scaffolding around a model, and the equestrian metaphor, equipment that directs strength, is intentional.&lt;/p&gt;

&lt;h3&gt;
  
  
  What is the difference between harness engineering and spec-driven development?
&lt;/h3&gt;

&lt;p&gt;They cover opposite sides of the same problem. &lt;a href="https://www.braingrid.ai/spec-driven-development" rel="noopener noreferrer"&gt;Spec-driven development&lt;/a&gt; defines what to build before the agent starts: requirements, behavior, acceptance criteria. Harness engineering shapes how the agent works and checks what it produced: conventions, tools, tests, and feedback. A harness verifies output against a standard, and the spec is that standard, so each practice is incomplete without the other.&lt;/p&gt;

&lt;h3&gt;
  
  
  What is the difference between a harness and a loop?
&lt;/h3&gt;

&lt;p&gt;The harness is the environment around a single agent run: what the agent knows, what it can touch, and what checks its work. A loop is the orchestration above it: the scheduler that finds work, runs the agent again and again, and decides when to stop. The loop re-runs the agent; the harness makes each run trustworthy. Both depend on a spec to define what done means.&lt;/p&gt;

&lt;h3&gt;
  
  
  Do you need to be an engineer to benefit from harness engineering?
&lt;/h3&gt;

&lt;p&gt;The principle transfers even if you never write a lint rule. Any builder can keep a running file of corrections their agent must follow, and any builder can define acceptance criteria before the agent starts. Tools handle the rest: BrainGrid's Planning Agent turns a plain-language idea into a requirement with testable criteria, and its verification checks the build against each one, which is the harness's feedback layer without the infrastructure work.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;&lt;a href="https://braingrid.ai" rel="noopener noreferrer"&gt;BrainGrid&lt;/a&gt; is the AI Product Planner that gives your harness the one input it cannot generate: a spec with acceptance criteria to check every build against. Try it at &lt;a href="https://braingrid.ai" rel="noopener noreferrer"&gt;braingrid.ai&lt;/a&gt;.&lt;/em&gt;&lt;/p&gt;




&lt;p&gt;&lt;em&gt;Originally published on the &lt;a href="https://www.braingrid.ai/blog/harness-engineering" rel="noopener noreferrer"&gt;BrainGrid blog&lt;/a&gt;.&lt;/em&gt;&lt;/p&gt;

</description>
      <category>ai</category>
      <category>programming</category>
      <category>webdev</category>
      <category>softwaredevelopment</category>
    </item>
    <item>
      <title>Agentic coding ships the 80%. Your tracker holds the other 20%.</title>
      <dc:creator>Nico Acosta</dc:creator>
      <pubDate>Wed, 22 Jul 2026 13:17:18 +0000</pubDate>
      <link>https://dev.to/radial/agentic-coding-ships-the-80-your-tracker-holds-the-other-20-2lio</link>
      <guid>https://dev.to/radial/agentic-coding-ships-the-80-your-tracker-holds-the-other-20-2lio</guid>
      <description>&lt;p&gt;Agentic coding is a way of building software where an autonomous AI agent takes a high-level instruction and plans, writes, tests, and revises the code with little step-by-step human input. Unlike an inline assistant that waits for you to type and then completes the line, an agent runs a loop: it decides what to do next, does it, checks the result, and corrects itself, until the task is done or it gets stuck. Claude Code, Codex, Cursor's agent mode, and Gemini's coding agent are all versions of the same idea.&lt;/p&gt;

&lt;p&gt;That loop is genuinely fast, and it is why the term went from niche to everywhere in a year. It is also why a specific gap keeps showing up in the discussion: the agent reliably produces roughly the functional 80% of a working change, and the remaining 20%, the part that has to be right for the work to actually be done and remembered, does not live in the code the agent just wrote. This post is about where that 20% should go.&lt;/p&gt;

&lt;h2&gt;
  
  
  The 80% problem, stated plainly
&lt;/h2&gt;

&lt;p&gt;If you search "agentic coding," one of the first questions Google surfaces is "what is the 80% problem in agentic coding?" The short version people have converged on: agents ship the working majority of a change quickly, and the hard, compounding remainder, the error handling, the security edges, the "why did we do it this way," the coordination between the change and everything else in flight, is where the debt accumulates when nobody captures it.&lt;/p&gt;

&lt;p&gt;Notice that most of that remainder is not more code. It is context and decisions. Which approach was chosen and why. What was ruled out. What the agent noticed on its way through that nobody asked it to fix. Which of three parallel agents already touched this module. That information is real work product, and by default it lives in exactly one place: the context window of the session that produced it. When the window fills and compacts, or the session ends, it is gone. The code survives; the reasoning behind it does not.&lt;/p&gt;

&lt;p&gt;So the 80% problem is not really "agents write sloppy code." It is that the fast part and the durable part have different homes, and agentic workflows are very good at producing the fast part and very bad at persisting the durable part.&lt;/p&gt;

&lt;h2&gt;
  
  
  Where the durable 20% should live
&lt;/h2&gt;

&lt;p&gt;The cheapest durable unit for the 20% is an issue. Not a document, not a chat log, an issue: it has an owner, a status, a priority, and a comment thread, and it sits outside the model entirely, so it survives every context reset by construction. When an agent opens an issue for the work, comments on it as it makes decisions, and closes it with a note when the change lands, the reasoning stops being a casualty of the next auto-compact. The next session, or the next agent, reads the current state instead of reconstructing it from a summary that got squeezed out.&lt;/p&gt;

&lt;p&gt;This is also the piece that lets more than one agent work at once without stepping on each other. A developer in a recent r/github thread described building the missing layer by hand: a single local API for the full issue lifecycle so that, in their words, "the agent never polls GitHub, it just checks local state and the sync layer handles the rest." That instinct is exactly right. The agents need one fast place to read and write the state of the work, separate from any one session's memory. The only question is whether you hand-roll it or point at one that already exists.&lt;/p&gt;

&lt;p&gt;Radial is that record, built so agents can drive it directly. It is a fast issue tracker with a real CLI, a plain REST API, and an MCP server, all against the same issue lifecycle, so however your agent reaches it, it reads and writes the same state. Here is the loop an agentic coding session runs against it, using the CLI (every command takes &lt;code&gt;--json&lt;/code&gt;, so the output pipes straight into the next step):&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;# The agent reads what's assigned to it, in progress, as structured JSON&lt;/span&gt;
radial list &lt;span class="nt"&gt;--assignee&lt;/span&gt; me &lt;span class="nt"&gt;--status&lt;/span&gt; &lt;span class="s2"&gt;"in progress"&lt;/span&gt; &lt;span class="nt"&gt;--json&lt;/span&gt;

&lt;span class="c"&gt;# It makes the change, then closes the issue with a note the next session can read&lt;/span&gt;
radial close RAD-482 &lt;span class="nt"&gt;-m&lt;/span&gt; &lt;span class="s2"&gt;"Chose optimistic locking over a mutex; ruled out the queue approach, added in #611."&lt;/span&gt;

&lt;span class="c"&gt;# It spotted a second problem on the way through, so it files that too&lt;/span&gt;
radial create &lt;span class="s2"&gt;"Token refresh retries on 401 instead of re-auth"&lt;/span&gt; &lt;span class="nt"&gt;-t&lt;/span&gt; ENG &lt;span class="nt"&gt;-p&lt;/span&gt; high &lt;span class="nt"&gt;-l&lt;/span&gt; bug &lt;span class="nt"&gt;-a&lt;/span&gt; me &lt;span class="nt"&gt;--json&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Nothing in that loop is specific to one agent. The same three verbs run from a REST call in CI, from the MCP server a different agent is connected to, and from your own terminal, all against one shared record. The 80% (the code) ships however it ships; the 20% (the decisions, the follow-ups, the state) is now written down where it outlives the session.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why this is a tracker, not an AI feature
&lt;/h2&gt;

&lt;p&gt;There is a line here worth being precise about. Radial does not read your code, summarize your diffs, or try to be a smarter memory layer for your agent. It is a fast issue tracker that agents happen to be able to drive over CLI, REST, and MCP, and the durable record is a side effect of agents filing and updating issues the normal way, not an AI feature bolted on top. The intelligence stays in your agent, your model, your keys. Radial holds the record.&lt;/p&gt;

&lt;p&gt;That is deliberate, and it is binding. There is no copilot in the product, no auto-summary, no AI triage, and no AI credit meter, because your agent already does the thinking and you already pay for it once. The Plain Software Pledge is the enforceable version: the day Radial ships a copilot, meters your usage, or charges you for AI you didn't ask for, your subscription is free.&lt;/p&gt;

&lt;p&gt;And because every agent credential is an API client, not a billed seat, running a fleet of coding agents against Radial costs nothing extra. The price is &lt;strong&gt;$50 per user, per year, flat, billed annually&lt;/strong&gt;, for the humans on the workspace, locked at the rate you join. Ten agents writing to the same tracker is still just the humans. Agents ride free; that is the structure, not a promotion.&lt;/p&gt;

&lt;h2&gt;
  
  
  FAQ
&lt;/h2&gt;

&lt;h3&gt;
  
  
  What is agentic coding?
&lt;/h3&gt;

&lt;p&gt;Agentic coding is a software development approach where an autonomous AI agent plans, writes, tests, and modifies code from a high-level instruction, with minimal human intervention. Instead of completing a line you are typing, the agent runs its own loop: it decides the next step, executes it, checks the outcome, and self-corrects until the task is done. Claude Code, Codex, and Cursor's agent mode are common examples.&lt;/p&gt;

&lt;h3&gt;
  
  
  What is the 80% problem in agentic coding?
&lt;/h3&gt;

&lt;p&gt;It is the gap between the functional majority of a change that agents produce quickly (roughly 80%) and the durable remainder (the error handling, the security edges, the decisions, and the coordination) that compounds into debt when it is never captured. Much of that remainder is context and reasoning, not code, and it disappears when a session's context window compacts or ends unless it was written down somewhere outside the model.&lt;/p&gt;

&lt;h3&gt;
  
  
  How is agentic coding different from vibe coding?
&lt;/h3&gt;

&lt;p&gt;Vibe coding is prompt-driven and exploratory: you describe what you want and iterate on the output by feel. Agentic coding is outcome-oriented and self-directed: the agent runs a plan-act-check loop toward a defined result and corrects itself along the way. In practice the two blur, but the agentic version produces more work product (decisions, follow-ups, state changes) that needs a durable home.&lt;/p&gt;

&lt;h3&gt;
  
  
  Where should an AI coding agent record its work?
&lt;/h3&gt;

&lt;p&gt;In an issue tracker it can drive directly, so the record survives context resets and hands off cleanly to the next session or agent. An issue is a cheap durable unit: owner, status, priority, and a comment thread, all outside the model. Point your agent at Radial's CLI, REST API, or MCP server and it can read its queue, file issues, comment as it decides, and close them when done. See &lt;a href="https://radial.build/blog/claude-code-mcp-issue-tracker" rel="noopener noreferrer"&gt;give Claude Code access to your issue tracker in 5 minutes&lt;/a&gt; for the MCP setup.&lt;/p&gt;

&lt;h3&gt;
  
  
  Does running multiple coding agents against Radial cost more?
&lt;/h3&gt;

&lt;p&gt;No. Agent credentials are API clients, not billed seats. You pay $50 per user, per year, for the humans on the workspace, locked at the rate you join, and a fleet of agents writing to the same tracker adds nothing to that.&lt;/p&gt;

&lt;h2&gt;
  
  
  The short version
&lt;/h2&gt;

&lt;p&gt;Agentic coding ships the functional 80% fast, and the durable 20%, the decisions, the follow-ups, and the coordination, is context, not code. By default that context dies with the session's memory. Give your agents one durable record they drive over CLI, REST, and MCP, and the reasoning behind the work outlives the window that produced it.&lt;/p&gt;

&lt;p&gt;See the full tool list and the CLI verbs on the &lt;a href="https://radial.build/developers" rel="noopener noreferrer"&gt;developers&lt;/a&gt; page, or read &lt;a href="https://radial.build/blog/claude-code-agents" rel="noopener noreferrer"&gt;your Claude Code sub-agents keep losing the thread&lt;/a&gt; for the multi-agent version of the same argument.&lt;/p&gt;




&lt;p&gt;&lt;em&gt;Originally published on the &lt;a href="https://radial.build/blog/agentic-coding" rel="noopener noreferrer"&gt;Radial blog&lt;/a&gt;.&lt;/em&gt;&lt;/p&gt;

</description>
      <category>ai</category>
      <category>agents</category>
      <category>devtools</category>
      <category>automation</category>
    </item>
    <item>
      <title>Screenshot Automation: How to Capture Web Pages on a Schedule</title>
      <dc:creator>Nico Acosta</dc:creator>
      <pubDate>Wed, 22 Jul 2026 11:40:28 +0000</pubDate>
      <link>https://dev.to/grabbit/screenshot-automation-how-to-capture-web-pages-on-a-schedule-1h71</link>
      <guid>https://dev.to/grabbit/screenshot-automation-how-to-capture-web-pages-on-a-schedule-1h71</guid>
      <description>&lt;p&gt;Screenshot automation for web pages comes down to two pieces: something that fires on a schedule, and something that renders the page. You almost certainly already have the first piece. The second is where people get stuck, because the obvious answer, running headless Chromium yourself, means owning a browser fleet to produce a single image on a timer.&lt;/p&gt;

&lt;p&gt;This guide covers the scheduled-capture pattern: how to wire a cron job to a screenshot API, how to pick an interval, how to keep captures consistent enough to compare, and where to store the results.&lt;/p&gt;

&lt;p&gt;One clarification first, because search results for this topic mix two unrelated jobs. Capturing your own desktop or phone screen on a timer needs a local screen-capture utility. Capturing a &lt;strong&gt;web page&lt;/strong&gt; on a timer, from a server, with no one at the keyboard, is what this post is about.&lt;/p&gt;

&lt;h2&gt;
  
  
  The pattern: scheduler plus render
&lt;/h2&gt;

&lt;p&gt;Every scheduled screenshot setup has the same three stages:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Trigger.&lt;/strong&gt; A cron entry, a GitHub Actions schedule, a Vercel or Cloudflare cron, or a workflow tool fires on an interval.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Render.&lt;/strong&gt; One HTTP request per URL returns a hosted image.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Store.&lt;/strong&gt; Write the image URL plus a timestamp somewhere you can query later.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;The trigger is a solved problem in whatever stack you already run. The render stage is the only real decision, and it comes down to whether you run the browser or someone else does.&lt;/p&gt;

&lt;h2&gt;
  
  
  Running the browser yourself
&lt;/h2&gt;

&lt;p&gt;The DIY version is a scheduled script that launches Chromium through Puppeteer or Playwright, navigates to the URL, calls &lt;code&gt;page.screenshot()&lt;/code&gt;, and uploads the result. It works, and for a single URL on a laptop it is genuinely fine.&lt;/p&gt;

&lt;p&gt;The cost shows up when it has to run unattended. A scheduled job means a long-lived environment, which means keeping the browser alive across runs or paying the launch cost every time. Serverless runtimes do not ship the native libraries Chromium expects, so you end up on &lt;code&gt;@sparticuz/chromium&lt;/code&gt; or a custom container. Chromium leaks memory over long runs, so scheduled jobs that never restart eventually get killed. None of these are unsolvable, but they are all maintenance on infrastructure whose entire output is a PNG.&lt;/p&gt;

&lt;p&gt;The honest rule: if your scheduled job needs to log in, click through a flow, or fill a form before the capture, you need your own browser automation and this maintenance is the price of the job. If it just needs to render a URL, you do not.&lt;/p&gt;

&lt;h2&gt;
  
  
  Running it as an API call
&lt;/h2&gt;

&lt;p&gt;If the deliverable is a rendered image of a URL, the render stage collapses into one request. A cron entry, once a day at 6am:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;0 6 &lt;span class="k"&gt;*&lt;/span&gt; &lt;span class="k"&gt;*&lt;/span&gt; &lt;span class="k"&gt;*&lt;/span&gt; curl &lt;span class="nt"&gt;-sS&lt;/span&gt; https://api.grabbit.live/v1/grabs &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;-H&lt;/span&gt; &lt;span class="s2"&gt;"Authorization: Bearer sk_live_..."&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="s1"&gt;'{
    "url": "https://example.com/pricing",
    "width": 1280,
    "full_page": true,
    "delay_ms": 1000,
    "format": "webp"
  }'&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&amp;gt;&lt;/span&gt; /var/log/pricing-snapshots.log
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The response includes an &lt;code&gt;image_url&lt;/code&gt; pointing at the stored capture. That URL is the artifact: append it to a log, insert it into a table, post it to Slack. There is no browser on your side, nothing to keep alive between runs, and nothing to patch.&lt;/p&gt;

&lt;p&gt;The parameters are the consistency controls, and they matter more in a scheduled pipeline than in a one-off capture:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;width&lt;/code&gt;&lt;/strong&gt; (320 to 1920) pins the viewport. Fix it and the layout is identical every run, so a diff shows real changes rather than reflow.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;full_page&lt;/code&gt;&lt;/strong&gt; captures the entire document instead of just the visible viewport, so a change below the fold is not invisible to your pipeline.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;delay_ms&lt;/code&gt;&lt;/strong&gt; (0 to 10000) waits after load for lazy-loaded images and animations to settle. This is the single most common fix for scheduled captures that look different every run for no reason.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;format&lt;/code&gt;&lt;/strong&gt; accepts &lt;code&gt;png&lt;/code&gt;, &lt;code&gt;jpeg&lt;/code&gt;, or &lt;code&gt;webp&lt;/code&gt;. For an archive that accumulates one image per run, &lt;code&gt;webp&lt;/code&gt; keeps storage down.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;selector&lt;/code&gt;&lt;/strong&gt; captures a single element instead of the page. Scoping a monitor to &lt;code&gt;#pricing-table&lt;/code&gt; removes noise from unrelated sections of the page.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Scheduling more than one URL
&lt;/h2&gt;

&lt;p&gt;Most real automations track a list, not a single page. Loop the list in the scheduled job and make one request per URL. Keep two things in mind:&lt;/p&gt;

&lt;p&gt;Grabbit rate-limits to 60 requests per minute per team, so a list longer than about 60 URLs needs to be paced rather than fired in one burst. For a nightly job this is a non-issue; add a short sleep between calls and it stays well under.&lt;/p&gt;

&lt;p&gt;Second, be deliberate about the identifier you store. A screenshot is only useful later if you can find it, so record the URL, the capture timestamp, and the returned &lt;code&gt;image_url&lt;/code&gt; together. Overwriting a single "latest.png" throws away the history that made the automation worth building.&lt;/p&gt;

&lt;p&gt;There is a fuller walkthrough of the batch case in &lt;a href="https://www.grabbit.live/blog/screenshot-list-of-urls" rel="noopener noreferrer"&gt;how to screenshot a list of URLs&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Picking an interval
&lt;/h2&gt;

&lt;p&gt;The interval should track how fast the content actually changes, not how often you would like to look at it:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;What you are capturing&lt;/th&gt;
&lt;th&gt;Reasonable interval&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Your own app, for visual regressions&lt;/td&gt;
&lt;td&gt;On every deploy, not a clock&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Competitor pricing or marketing pages&lt;/td&gt;
&lt;td&gt;Daily&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;News, dashboards, status pages&lt;/td&gt;
&lt;td&gt;Hourly&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Legal or compliance archiving&lt;/td&gt;
&lt;td&gt;Daily, retained long term&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;A page that changes weekly at most&lt;/td&gt;
&lt;td&gt;Weekly&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;Running more often than the page changes produces a pile of identical images and a bill that scales with nothing. The exception is your own application, where the right trigger is not a clock at all: capture on deploy, because that is the moment the page can change.&lt;/p&gt;

&lt;h2&gt;
  
  
  From automation to monitoring
&lt;/h2&gt;

&lt;p&gt;A scheduled capture on its own is an archive. Add an image diff between consecutive captures and it becomes a monitor that tells you when something changed, which is usually the actual goal. &lt;code&gt;pixelmatch&lt;/code&gt; handles strict pixel comparison and &lt;code&gt;resemblejs&lt;/code&gt; does a more forgiving perceptual comparison for pages with anti-aliased text.&lt;/p&gt;

&lt;p&gt;The consistency parameters above are what make that diff trustworthy. A pipeline with a floating viewport width or no settle delay will produce diffs on every run and you will stop reading the alerts within a week. &lt;a href="https://www.grabbit.live/blog/website-change-monitoring" rel="noopener noreferrer"&gt;Monitoring a website for visual changes&lt;/a&gt; covers the diff and alerting side in full.&lt;/p&gt;

&lt;h2&gt;
  
  
  Cost, honestly
&lt;/h2&gt;

&lt;p&gt;Scheduled automation is the case where per-request pricing compounds, so it is worth doing the arithmetic before you set the interval. Grabbit is $0.002 per live grab. Fifty URLs captured daily is 1,500 grabs a month, or about $3. The same fifty URLs hourly is roughly 36,000 grabs a month, closer to $72. The interval, not the tool, is what drives the number.&lt;/p&gt;

&lt;p&gt;Credits are prepaid and do not reset monthly, which suits scheduled work specifically: a job that runs unevenly, or that you pause for a quarter, does not forfeit anything at a billing boundary. Test-environment keys return placeholder images at no cost, so you can wire and debug the full schedule before any real capture runs. Other providers list lower per-grab rates, so if raw unit price is the only variable that matters to you, compare directly.&lt;/p&gt;

&lt;h2&gt;
  
  
  Where to go next
&lt;/h2&gt;

&lt;p&gt;If you are building scheduled capture into a product, &lt;a href="https://www.grabbit.live/automated-screenshots" rel="noopener noreferrer"&gt;automated screenshots&lt;/a&gt; covers the API side in more depth. If you are starting from a single URL and have not automated anything yet, &lt;a href="https://www.grabbit.live/blog/screenshot-from-url" rel="noopener noreferrer"&gt;how to screenshot a website from a URL&lt;/a&gt; is the shorter starting point.&lt;/p&gt;




&lt;p&gt;&lt;em&gt;Originally published on the &lt;a href="https://www.grabbit.live/blog/screenshot-automation" rel="noopener noreferrer"&gt;Grabbit blog&lt;/a&gt;.&lt;/em&gt;&lt;/p&gt;

</description>
      <category>webdev</category>
      <category>javascript</category>
      <category>programming</category>
      <category>tutorial</category>
    </item>
    <item>
      <title>What Is Loop Engineering? Stop Prompting, Start Designing Loops</title>
      <dc:creator>Nico Acosta</dc:creator>
      <pubDate>Wed, 22 Jul 2026 11:13:41 +0000</pubDate>
      <link>https://dev.to/braingrid/what-is-loop-engineering-stop-prompting-start-designing-loops-4oo8</link>
      <guid>https://dev.to/braingrid/what-is-loop-engineering-stop-prompting-start-designing-loops-4oo8</guid>
      <description>&lt;p&gt;Every few months the AI building world agrees on a new thing you are supposed to be doing, and this month it is loop engineering. The pitch is clean: you do not prompt your coding agent one step at a time anymore. You design the loop that prompts it for you, point it at a goal, and walk away.&lt;/p&gt;

&lt;p&gt;It is a real shift, and it is worth understanding. But the part the loop tweets leave out is the interesting part. The loop takes prompting off your plate and hands you back a harder job, one you cannot automate your way out of. Knowing what that job is tells you exactly where to put your effort.&lt;/p&gt;

&lt;h2&gt;
  
  
  What loop engineering actually means
&lt;/h2&gt;

&lt;p&gt;Loop engineering is the practice of designing a system that prompts and orchestrates an AI coding agent on a schedule, instead of you typing each prompt by hand. Addy Osmani, whose explainer became the reference for the term, defines it plainly:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Loop engineering is replacing yourself as the person who prompts the agent. You design the system that does it instead.&lt;/p&gt;

&lt;p&gt;&lt;cite&gt;&lt;a href="https://addyosmani.com/blog/loop-engineering/" rel="noopener noreferrer"&gt;Addy Osmani, "Loop Engineering"&lt;/a&gt;&lt;/cite&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;A loop is roughly five pieces plus one. Automations that fire on a schedule and find work. Worktrees so two agents do not collide. Skills that write down project knowledge the agent would otherwise guess. Connectors that plug the agent into your real tools. Sub-agents, so the one that writes the code is not the one that grades it. And the sixth piece, the one that makes the whole thing hold together: a memory that lives outside any single run and remembers what is done and what is next.&lt;/p&gt;

&lt;p&gt;The wave started with two sentences from people who would know. Peter Steinberger, who built the open-source agent gateway OpenClaw and now works at OpenAI, put it as a standing reminder:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Here's your monthly reminder that you shouldn't be prompting coding agents anymore. You should be designing loops that prompt your agents.&lt;/p&gt;

&lt;p&gt;&lt;cite&gt;&lt;a href="https://x.com/steipete/status/2063697162748260627" rel="noopener noreferrer"&gt;Peter Steinberger on X&lt;/a&gt;&lt;/cite&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Days earlier, Boris Cherny, who leads Claude Code at Anthropic, described on stage where his own job had gone:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;I don't prompt Claude anymore. I have loops running that prompt Claude and figuring out what to do. My job is to write loops.&lt;/p&gt;

&lt;p&gt;&lt;cite&gt;Boris Cherny, head of Claude Code at Anthropic&lt;/cite&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Steinberger's post hit 6.5 million views in a week. It landed because the pieces ship inside the products now: &lt;code&gt;/goal&lt;/code&gt; in Claude Code defines a testable end state and keeps working until it is true, with a separate model grading whether it is done. The leverage point moved, and everyone felt it at once.&lt;/p&gt;

&lt;h2&gt;
  
  
  The job the loop hands back
&lt;/h2&gt;

&lt;p&gt;Here is the contradiction at the center of the hype. A loop running unattended is also a loop failing unattended. Take prompting off your plate and you have not removed the work, you have moved it somewhere worse: into runs you were never watching, with far too much output to read by hand. Akshay Pachaar said it sharply this week:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Loop engineering takes you off prompting. It takes you off curating context. It takes you off babysitting a single run. It does not take you off debugging. It just moves the debugging somewhere worse, into runs you were never watching.&lt;/p&gt;

&lt;p&gt;&lt;cite&gt;&lt;a href="https://x.com/akshay_pachaar/status/2064265203488076020" rel="noopener noreferrer"&gt;Akshay Pachaar on X&lt;/a&gt;&lt;/cite&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Stack the layers up, prompt to context to harness to loop, and one job survives all of them. Closing the loop on failure. You can only walk away from a loop if you trust the thing checking it, and a checker you do not trust drops you right back into reading every output by hand, which is the exact work the loop was supposed to take off you.&lt;/p&gt;

&lt;p&gt;So the real question loop engineering raises is not "how do I build the loop." Both Claude Code and Codex ship the loop. The real question is "what does my checker check against." A loop with no definition of done is just a faster way to generate work you still have to inspect line by line.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why this moves the work upstream, not away
&lt;/h2&gt;

&lt;p&gt;Watch what happens to the human as each layer of automation arrives. Prompt engineering asked you to write a good message. Context engineering asked you to curate what the model sees. Loop engineering asks you to design the system that does both on a schedule. At every step the manual labor shrinks and the judgment concentrates. The thing you cannot hand off is deciding what you actually want and stating it precisely enough that a machine can check the result.&lt;/p&gt;

&lt;p&gt;That is a spec. Not a vague ticket, not a paragraph in Slack, but a requirement with testable acceptance criteria: the agent built this, here is how we know it is right. The sub-agent that "verifies against the spec," in Osmani's words, needs a spec to verify against. The loop's memory, the file that survives between runs, has to hold something worth remembering. Both of those are the same artifact, and writing it is the work that does not disappear.&lt;/p&gt;

&lt;p&gt;Consider the before and after. Before: you babysit a single run, catch the drift in real time, correct it prompt by prompt. After: the loop runs ten times overnight and you wake up to ten pull requests, half of them subtly wrong in ways no human would have written, and no clear standard to grade them against. The loop made you faster at producing work and slower at trusting it. The fix is not a better loop. It is a definition of done the checker can actually use.&lt;/p&gt;

&lt;h2&gt;
  
  
  Where BrainGrid fits in a loop
&lt;/h2&gt;

&lt;p&gt;This is the gap BrainGrid was built for. BrainGrid is a product factory that runs the loop Plan, Build, Verify, Repeat, and it owns the two pieces a loop cannot generate for itself: the plan and the standard.&lt;/p&gt;

&lt;p&gt;You describe a feature in plain language. The Planning Agent asks the clarifying questions a senior engineer would, surfaces the edge cases, and writes an engineering-grade requirement with acceptance criteria. That requirement is the spec your loop's verifier checks against. It is also the memory: it persists outside any single run, so when tomorrow's automation fires, the context is already there. The agent forgets between sessions. BrainGrid does not.&lt;/p&gt;

&lt;p&gt;From there the loop runs either way. Load the scoped tasks into Claude Code, Cursor, or Codex over MCP and let your own harness drive them, or let BrainGrid's Builder Agent build the requirement in a managed sandbox and open a pull request, with AI code review checking the result against the acceptance criteria item by item. Verification stops being a thing you hope the loop got right and becomes a thing the loop can prove. That is what makes walking away safe. This is the same discipline we cover in &lt;a href="https://www.braingrid.ai/agentic-engineering" rel="noopener noreferrer"&gt;agentic engineering&lt;/a&gt;, and the full five-stage version lives in &lt;a href="https://www.braingrid.ai/loop" rel="noopener noreferrer"&gt;The BrainGrid Loop&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  So is it real, or just another buzzword
&lt;/h2&gt;

&lt;p&gt;Both, the way most of these are. The mechanics are real and genuinely useful: scheduled, self-checking agent runs are a meaningful jump in capability, and if you have not tried &lt;code&gt;/goal&lt;/code&gt; in Claude Code you should. The buzzword part is the implication that designing the loop is the whole job. It is not. It is the part that got automated. The part that did not, defining what done means and giving the checker something real to check, is where the work went. Put your effort there, and the loop pays off. Skip it, and the loop is just a faster way to dig the hole we wrote about in &lt;a href="https://www.braingrid.ai/blog/i-built-a-vibe-coding-mess" rel="noopener noreferrer"&gt;I built a vibe-coding mess&lt;/a&gt;. The loop-as-a-running-joke version is our take on &lt;a href="https://www.braingrid.ai/blog/ralph-wiggum-plugin" rel="noopener noreferrer"&gt;the Ralph Wiggum plugin&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  FAQ
&lt;/h2&gt;

&lt;h3&gt;
  
  
  What is loop engineering?
&lt;/h3&gt;

&lt;p&gt;Loop engineering is the practice of designing a system that prompts and orchestrates an AI coding agent automatically, on a schedule, instead of typing each prompt yourself. A loop typically combines scheduled automations, isolated worktrees, project skills, tool connectors, and sub-agents that check each other's work, plus a memory that persists between runs. The goal is to move from holding the agent's hand through every step to designing the system that runs it for you.&lt;/p&gt;

&lt;h3&gt;
  
  
  Is loop engineering real or just another AI buzzword?
&lt;/h3&gt;

&lt;p&gt;It is both. The underlying mechanics are real and shipping today in Claude Code and Codex, and scheduled self-checking agent runs are a genuine step up. The buzzword part is the suggestion that building the loop is the whole job. The loop automates the prompting, but it hands back the harder work: defining what "done" means so the loop's checker has a standard to verify against. Designing the loop is the easy half. Closing the loop on failure is the half that survives.&lt;/p&gt;

&lt;h3&gt;
  
  
  What is the difference between loop engineering and prompt engineering?
&lt;/h3&gt;

&lt;p&gt;Prompt engineering optimizes a single message you send by hand. Loop engineering designs the system that sends those messages for you, on a cadence, and checks the results. Prompt engineering is one turn; loop engineering is the machine that runs many turns unattended. The skill moves from wording a request to specifying a goal precisely enough that an automated checker can confirm it was met.&lt;/p&gt;

&lt;h3&gt;
  
  
  Does Claude Code support loop engineering?
&lt;/h3&gt;

&lt;p&gt;Yes. Claude Code ships the core pieces: &lt;code&gt;/loop&lt;/code&gt; re-runs a prompt on a cadence, &lt;code&gt;/goal&lt;/code&gt; keeps working until a condition you defined is true with a separate model grading whether it is done, and hooks plus scheduled tasks let you run autonomous work on an interval. Codex offers the same primitives through its Automations tab. The pieces are in the products now, which is why the term took off.&lt;/p&gt;

&lt;h3&gt;
  
  
  What does loop engineering not solve?
&lt;/h3&gt;

&lt;p&gt;Verification and your own understanding. A loop running unattended is also a loop making mistakes unattended, and the more code it ships that you did not write, the larger the gap between what exists and what you actually understand. The loop can take you off prompting, context curation, and babysitting a single run. It cannot take you off deciding what you want and confirming the result matches. That is why a clear spec with acceptance criteria, which is what BrainGrid produces, is the piece that makes a loop trustworthy enough to walk away from.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;&lt;a href="https://braingrid.ai" rel="noopener noreferrer"&gt;BrainGrid&lt;/a&gt; is the AI Product Planner that gives your agent loops the one thing they cannot generate for themselves: a spec with acceptance criteria the verifier can check against. Try it at &lt;a href="https://braingrid.ai" rel="noopener noreferrer"&gt;braingrid.ai&lt;/a&gt;.&lt;/em&gt;&lt;/p&gt;




&lt;p&gt;&lt;em&gt;Originally published on the &lt;a href="https://www.braingrid.ai/blog/loop-engineering" rel="noopener noreferrer"&gt;BrainGrid blog&lt;/a&gt;.&lt;/em&gt;&lt;/p&gt;

</description>
      <category>ai</category>
      <category>programming</category>
      <category>productivity</category>
      <category>career</category>
    </item>
  </channel>
</rss>
