<?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: Nic Flemmer</title>
    <description>The latest articles on DEV Community by Nic Flemmer (@nicholasflemmer).</description>
    <link>https://dev.to/nicholasflemmer</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%2F4168431%2F58974da3-69f5-4031-bca1-727e77ff3904.jpg</url>
      <title>DEV Community: Nic Flemmer</title>
      <link>https://dev.to/nicholasflemmer</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/nicholasflemmer"/>
    <language>en</language>
    <item>
      <title>Do you run the checks before or after merging main into an agent's branch?</title>
      <dc:creator>Nic Flemmer</dc:creator>
      <pubDate>Wed, 28 Oct 2026 15:03:17 +0000</pubDate>
      <link>https://dev.to/nicholasflemmer/do-you-run-the-checks-before-or-after-merging-main-into-an-agents-branch-29jl</link>
      <guid>https://dev.to/nicholasflemmer/do-you-run-the-checks-before-or-after-merging-main-into-an-agents-branch-29jl</guid>
      <description>&lt;p&gt;A coding agent's branch is cut from main, and main keeps moving while it works. By the time the agent says it's done, the branch is a few commits behind.&lt;/p&gt;

&lt;p&gt;There are three places to run the tests before the work goes in.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;On the branch as the agent left it.&lt;/strong&gt; Fast, and it tells you the agent's own work passes. It tells you nothing about how that work sits with whatever landed on main in the meantime.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;After merging main into the branch.&lt;/strong&gt; This is what I settled on. A clash between two agents' changes shows up on the branch, where the agent can still fix it, rather than on main. The cost: the checks run again every time main has moved.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Only on main, after the merge.&lt;/strong&gt; Simple, but a failure now sits on main and you're reverting or fixing forward.&lt;/p&gt;

&lt;p&gt;In Styx, the app I'm building, Land does the middle one: it brings main into the task's branch, runs the project's checks there, and only merges if they pass. If a step fails, nothing reaches main. It merges rather than rebases, so the agent's undo points stay valid.&lt;/p&gt;

&lt;p&gt;Where do you run them: on the branch as it was, after bringing main in, or only on main? And does that change when two agents are working at once?&lt;/p&gt;

</description>
      <category>ci</category>
      <category>devops</category>
      <category>git</category>
      <category>softwareengineering</category>
    </item>
    <item>
      <title>Your coding agent CLI may approve its own tool calls by default</title>
      <dc:creator>Nic Flemmer</dc:creator>
      <pubDate>Tue, 27 Oct 2026 15:06:03 +0000</pubDate>
      <link>https://dev.to/nicholasflemmer/your-coding-agent-cli-may-approve-its-own-tool-calls-by-default-5dfd</link>
      <guid>https://dev.to/nicholasflemmer/your-coding-agent-cli-may-approve-its-own-tool-calls-by-default-5dfd</guid>
      <description>&lt;p&gt;I added Goose and Amp to a tool that runs several coding agents. The most useful thing I learned wasn't about the protocol. It was the defaults.&lt;/p&gt;

&lt;p&gt;Goose's default mode approves tool calls automatically. If you start it through a wrapper that doesn't set a mode, it runs commands without asking anyone. So I set the mode per process instead of relying on whatever is in the user's Goose config. Default, accept-edits and auto all use Goose's approval mode, so tool calls ask. Only the permission mode literally called bypass picks automatic approval, and none of it touches the user's own Goose configuration.&lt;/p&gt;

&lt;p&gt;The second surprise was Goose's chat-only mode. It disables all tools, reads included. A plan mode you'd expect to still read your files doesn't, so mapping 'plan' to it means the agent can only talk.&lt;/p&gt;

&lt;p&gt;Amp went the other way. Its streaming output has no documented way to answer an approval from outside, so approvals stay in its own terminal. Its plan and don't-ask modes reject every tool, reads included, and only bypass asks for automatic approval.&lt;/p&gt;

&lt;p&gt;What I do with any new agent CLI now: before wiring up anything else, run it once in an empty directory and see what it does with a tool call by default. Two agents gave me two very different answers, and neither was in the first page of the docs.&lt;/p&gt;

&lt;p&gt;Which of your agent CLIs would run a command without asking, if you started it the way you usually do? I'm building Styx, where this came from.&lt;/p&gt;

</description>
      <category>agents</category>
      <category>ai</category>
      <category>cli</category>
      <category>tooling</category>
    </item>
    <item>
      <title>Message an agent mid-turn: interrupt it or queue the message?</title>
      <dc:creator>Nic Flemmer</dc:creator>
      <pubDate>Mon, 26 Oct 2026 15:02:21 +0000</pubDate>
      <link>https://dev.to/nicholasflemmer/message-an-agent-mid-turn-interrupt-it-or-queue-the-message-3loh</link>
      <guid>https://dev.to/nicholasflemmer/message-an-agent-mid-turn-interrupt-it-or-queue-the-message-3loh</guid>
      <description>&lt;p&gt;You give a coding agent a task, watch it start going the wrong way, and type a correction. What should happen to that message?&lt;/p&gt;

&lt;p&gt;There are two designs. Interrupt: the message goes in straight away and the agent changes course. Queue: it waits, and the agent reads it when the current turn ends.&lt;/p&gt;

&lt;p&gt;Interrupting is quicker when you've caught a mistake early. But the agent may be halfway through an edit, and a correction can land at a bad moment. Queueing is safer for the work in progress, but a wrong turn keeps running until it's done.&lt;/p&gt;

&lt;p&gt;Which one you get depends on how the agent exposes itself. In Styx, the app I'm building, Codex runs over &lt;code&gt;codex app-server&lt;/code&gt;, so it takes a message at once and adjusts. Claude Code, Gemini CLI, Cursor's agent and OpenCode hold the message and send it when the turn ends, and a Send now button skips the wait.&lt;/p&gt;

&lt;p&gt;If you could pick, would you want messages to interrupt by default, or queue by default? Does the answer change with how long the turn is?&lt;/p&gt;

</description>
      <category>agents</category>
      <category>ai</category>
      <category>softwaredevelopment</category>
      <category>tools</category>
    </item>
    <item>
      <title>Your coding agent hits its usage limit halfway through a task. Wait or switch?</title>
      <dc:creator>Nic Flemmer</dc:creator>
      <pubDate>Sun, 25 Oct 2026 14:02:23 +0000</pubDate>
      <link>https://dev.to/nicholasflemmer/your-coding-agent-hits-its-usage-limit-halfway-through-a-task-wait-or-switch-3lbg</link>
      <guid>https://dev.to/nicholasflemmer/your-coding-agent-hits-its-usage-limit-halfway-through-a-task-wait-or-switch-3lbg</guid>
      <description>&lt;p&gt;Most coding agent plans have a usage limit, and it has a way of running out in the middle of something rather than between tasks.&lt;/p&gt;

&lt;p&gt;You have roughly three options. Wait until it resets. Pay for more. Or hand the rest to a different agent.&lt;/p&gt;

&lt;p&gt;Waiting keeps everything the agent had worked out, but it might cost you hours. Switching gets you moving right away, but the second agent starts cold: it sees the repo and your prompt, and the first agent's partial work only if you pass it along.&lt;/p&gt;

&lt;p&gt;In Styx, the app I'm building, I made the choice visible instead of burying it: when a plan is out, it shows when the limit resets, or sends the same ask to another agent.&lt;/p&gt;

&lt;p&gt;So, when your plan runs out mid-task, what do you do? And if you switch agents, does the second one start from your original prompt, or from whatever the first one had done by then?&lt;/p&gt;

</description>
      <category>agents</category>
      <category>ai</category>
      <category>discuss</category>
      <category>llm</category>
    </item>
    <item>
      <title>What I set up so a stranger could send a first pull request</title>
      <dc:creator>Nic Flemmer</dc:creator>
      <pubDate>Sat, 24 Oct 2026 16:02:31 +0000</pubDate>
      <link>https://dev.to/nicholasflemmer/what-i-set-up-so-a-stranger-could-send-a-first-pull-request-8g</link>
      <guid>https://dev.to/nicholasflemmer/what-i-set-up-so-a-stranger-could-send-a-first-pull-request-8g</guid>
      <description>&lt;p&gt;Styx went open source on 9 October (Apache-2.0). 'Contributions welcome' in a README tends to get nothing, so I tried to make the first step small and specific instead.&lt;/p&gt;

&lt;p&gt;There are two self-contained ways in: add support for another coding agent, or add another deploy target. Each has a guide with a worked example and a checklist. Matching issues are labelled 'good first issue': Aider, Goose and Amp for agents, and targets such as Netlify. The repo's &lt;code&gt;CLAUDE.md&lt;/code&gt; is written for coding agents and people alike, so the conventions live in one file.&lt;/p&gt;

&lt;p&gt;I also promised that new issues and pull requests get a first reply within two working days. A pull request nobody answers is a worse experience than none, so that one is for the sender's sake.&lt;/p&gt;

&lt;p&gt;A contributor has now sent a draft Cloudflare adapter. They'd found that the way the app hands out credentials today can't limit a broad Wrangler token to one account, one Worker and one command. So the draft adds the groundwork (account and Worker selection, read-only Worker discovery, a command classifier) and keeps Cloudflare switched off until that's solved.&lt;/p&gt;

&lt;p&gt;What I'd tell anyone else: pick two entry points, write them as if for a stranger, and answer fast.&lt;/p&gt;

&lt;p&gt;What made you send your first pull request to a project you didn't know? And what put you off the ones you skipped?&lt;/p&gt;

</description>
      <category>beginners</category>
      <category>git</category>
      <category>opensource</category>
      <category>softwaredevelopment</category>
    </item>
    <item>
      <title>Keep undo points in hidden git refs, not commits</title>
      <dc:creator>Nic Flemmer</dc:creator>
      <pubDate>Fri, 23 Oct 2026 14:02:30 +0000</pubDate>
      <link>https://dev.to/nicholasflemmer/keep-undo-points-in-hidden-git-refs-not-commits-13cm</link>
      <guid>https://dev.to/nicholasflemmer/keep-undo-points-in-hidden-git-refs-not-commits-13cm</guid>
      <description>&lt;p&gt;I wanted an agent's turns to be undoable one at a time, without touching the branch history. Committing after every turn would do it, but then your branch is full of commits you didn't write, and they'd go out with your next push.&lt;/p&gt;

&lt;p&gt;What I do instead: at the start and end of every turn, the app records the state of the task's files as a checkpoint and keeps each one as a hidden git ref under &lt;code&gt;refs/styx/checkpoints/&lt;/code&gt;. A ref outside &lt;code&gt;refs/heads/&lt;/code&gt; isn't a branch, so it doesn't show in &lt;code&gt;git branch&lt;/code&gt;, plain &lt;code&gt;git log&lt;/code&gt; doesn't list it, and a normal &lt;code&gt;git push&lt;/code&gt; doesn't send it. It doesn't touch your index or your commits either.&lt;/p&gt;

&lt;p&gt;You can see them with plain git:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;git &lt;span class="k"&gt;for&lt;/span&gt;&lt;span class="nt"&gt;-each-ref&lt;/span&gt; refs/styx/checkpoints/
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Because they're real refs, git's garbage collection keeps the objects they point to, so nothing needs a separate store.&lt;/p&gt;

&lt;p&gt;Undo then works from those two snapshots: take what a turn changed and reverse just that, leaving later turns and your own edits alone. If a later change touched the same lines, it refuses rather than guess.&lt;/p&gt;

&lt;p&gt;The trade-offs: the refs live in your clone only, so teammates don't get them, and they're not a backup. They're cleaned up when a task is archived. And anything that rewrites history under them (a rebase of the branch, say) would orphan them, which is why the app merges &lt;code&gt;main&lt;/code&gt; into a task instead of rebasing.&lt;/p&gt;

&lt;p&gt;Have you tried hidden refs, git notes or stash for this kind of thing? What bit you?&lt;/p&gt;

</description>
      <category>git</category>
      <category>programming</category>
      <category>softwaredevelopment</category>
    </item>
    <item>
      <title>An append-only, hash-chained audit log in SQLite, and a script to check it</title>
      <dc:creator>Nic Flemmer</dc:creator>
      <pubDate>Thu, 22 Oct 2026 15:02:26 +0000</pubDate>
      <link>https://dev.to/nicholasflemmer/an-append-only-hash-chained-audit-log-in-sqlite-and-a-script-to-check-it-he9</link>
      <guid>https://dev.to/nicholasflemmer/an-append-only-hash-chained-audit-log-in-sqlite-and-a-script-to-check-it-he9</guid>
      <description>&lt;p&gt;When a coding agent can ask for production access, the question afterwards is simple: who touched production, with what, and why? &lt;a href="https://github.com/NicholasFlemmer/styx-app" rel="noopener noreferrer"&gt;Styx&lt;/a&gt; answers it with an audit log on your machine. This post is about how that log is built, what it does and doesn't protect against, and a short script you can run to verify it.&lt;/p&gt;

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

&lt;p&gt;Every time an agent asks for access, and every time access is granted, used, denied, revoked or expires, one row is written. Connecting or removing a target, changing a policy and opening a pull request are rows too. Each row carries:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;the actor: you, the system (a policy or a timer), or the agent by name&lt;/li&gt;
&lt;li&gt;the target, the scope and the duration&lt;/li&gt;
&lt;li&gt;the agent session and the worktree it came from&lt;/li&gt;
&lt;li&gt;what triggered it: for a use, the command itself, like &lt;code&gt;$ vercel deploy --prod&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;the policy that decided it, if one did&lt;/li&gt;
&lt;li&gt;detail such as the agent's reason, and whether you verified with Touch ID, Windows Hello or your password&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Before a row is written, anything shaped like a secret is scrubbed out of commands, reasons and labels, so a token an agent pasted into a command line doesn't end up in the log.&lt;/p&gt;

&lt;h2&gt;
  
  
  Append-only means the database says no
&lt;/h2&gt;

&lt;p&gt;The log is a table in the local SQLite database. Triggers on the table abort any update or delete. Nothing in the app edits a row, and rows are only written through one function, &lt;code&gt;AuditService.append&lt;/code&gt;. When a grant is revoked, Styx doesn't change the 'granted' row; it adds a 'revoked' row.&lt;/p&gt;

&lt;h2&gt;
  
  
  The chain
&lt;/h2&gt;

&lt;p&gt;Each row has a sequence number, the hash of the row before it (&lt;code&gt;prev_hash&lt;/code&gt;) and its own &lt;code&gt;hash&lt;/code&gt;: a SHA-256 of the previous hash and the row's contents. Change any field of any row, or remove one from the middle, and the chain breaks from that point on.&lt;/p&gt;

&lt;h2&gt;
  
  
  Verifying it yourself
&lt;/h2&gt;

&lt;p&gt;The app has no verify button yet, but the data is just a table. Export it read-only with &lt;code&gt;sqlite3&lt;/code&gt; (3.33 or later, for &lt;code&gt;-json&lt;/code&gt;) and pipe it into Node:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;sqlite3 &lt;span class="nt"&gt;-readonly&lt;/span&gt; &lt;span class="nt"&gt;-json&lt;/span&gt; ~/Library/Application&lt;span class="se"&gt;\ &lt;/span&gt;Support/Styx/styx.db &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="s2"&gt;"SELECT * FROM audit_entries ORDER BY seq"&lt;/span&gt; | node verify-audit.mjs
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;With this saved as &lt;code&gt;verify-audit.mjs&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="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;createHash&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;node:crypto&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;chunks&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[];&lt;/span&gt;
&lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="k"&gt;await &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;chunk&lt;/span&gt; &lt;span class="k"&gt;of&lt;/span&gt; &lt;span class="nx"&gt;process&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;stdin&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="nx"&gt;chunks&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;push&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;chunk&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;rows&lt;/span&gt; &lt;span class="o"&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;parse&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;Buffer&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;concat&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;chunks&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;toString&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;utf8&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;[]&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;camel&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;k&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;k&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;replace&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sr"&gt;/_&lt;/span&gt;&lt;span class="se"&gt;([&lt;/span&gt;&lt;span class="sr"&gt;a-z&lt;/span&gt;&lt;span class="se"&gt;])&lt;/span&gt;&lt;span class="sr"&gt;/g&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;_&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;c&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;c&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;toUpperCase&lt;/span&gt;&lt;span class="p"&gt;());&lt;/span&gt;

&lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;prev&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;''&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;for &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;r&lt;/span&gt; &lt;span class="k"&gt;of&lt;/span&gt; &lt;span class="nx"&gt;rows&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;row&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{};&lt;/span&gt;
  &lt;span class="k"&gt;for &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;k&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;v&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="k"&gt;of&lt;/span&gt; &lt;span class="nb"&gt;Object&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;entries&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;r&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt; &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;k&lt;/span&gt; &lt;span class="o"&gt;!==&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;hash&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="nx"&gt;row&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nf"&gt;camel&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;k&lt;/span&gt;&lt;span class="p"&gt;)]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;v&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;canonical&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;JSON&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;stringify&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;row&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nb"&gt;Object&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;keys&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;row&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;sort&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;hash&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;createHash&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;sha256&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;update&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;prev&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;update&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;update&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;canonical&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;digest&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;hex&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;row&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;prevHash&lt;/span&gt; &lt;span class="o"&gt;!==&lt;/span&gt; &lt;span class="nx"&gt;prev&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="nx"&gt;hash&lt;/span&gt; &lt;span class="o"&gt;!==&lt;/span&gt; &lt;span class="nx"&gt;r&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;hash&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;`Chain broken at entry &lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;row&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;seq&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;`&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="nx"&gt;process&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;exit&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="nx"&gt;prev&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;r&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;hash&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;`Chain intact: &lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;rows&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;length&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt; entries`&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;It prints &lt;code&gt;Chain intact&lt;/code&gt; with the number of entries, or the first row where the chain breaks. The database is at &lt;code&gt;~/Library/Application Support/Styx/styx.db&lt;/code&gt; on a Mac, &lt;code&gt;%APPDATA%\Styx\styx.db&lt;/code&gt; on Windows and &lt;code&gt;~/.config/Styx/styx.db&lt;/code&gt; on Linux.&lt;/p&gt;

&lt;h2&gt;
  
  
  What the chain can't do
&lt;/h2&gt;

&lt;p&gt;The chain shows that rows weren't edited one at a time after the fact. It doesn't stop someone who can write to your files from rewriting the whole log and recomputing every hash. If that matters to you, copy the latest &lt;code&gt;hash&lt;/code&gt; somewhere else now and then; a later rewrite of the history before it won't match.&lt;/p&gt;

&lt;p&gt;It's also a record, not a barrier. Styx is a guardrail, not a sandbox: agents run as you, and one that deliberately goes around Styx's tools isn't stopped by the log or by anything else in it. What the log gives you is a trustworthy account of everything that did go through Styx.&lt;/p&gt;

&lt;p&gt;The code is Apache-2.0 at &lt;a href="https://github.com/NicholasFlemmer/styx-app" rel="noopener noreferrer"&gt;github.com/NicholasFlemmer/styx-app&lt;/a&gt;.&lt;/p&gt;

</description>
      <category>database</category>
      <category>opensource</category>
      <category>security</category>
    </item>
    <item>
      <title>What's new in Styx 0.4.17</title>
      <dc:creator>Nic Flemmer</dc:creator>
      <pubDate>Wed, 21 Oct 2026 19:03:30 +0000</pubDate>
      <link>https://dev.to/nicholasflemmer/whats-new-in-styx-0417-1n18</link>
      <guid>https://dev.to/nicholasflemmer/whats-new-in-styx-0417-1n18</guid>
      <description>&lt;p&gt;Styx 0.4.17 is out. Styx runs Claude Code, Codex, Gemini CLI, Cursor and OpenCode on all your projects in one window, each task on its own branch, and agents ask before they touch production. Free and open source (Apache-2.0); Mac, with Windows and Linux in beta.&lt;/p&gt;

&lt;p&gt;What changed:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Stop offering unapplied editor preference imports (#3) and thank @delusional-whispers in the README.&lt;/li&gt;
&lt;li&gt;Bake Ponytail coding guidance into all five agents with MIT attribution and prepare release 0.4.17.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Full release notes and downloads: &lt;a href="https://github.com/NicholasFlemmer/styx-app/releases/tag/v0.4.17" rel="noopener noreferrer"&gt;https://github.com/NicholasFlemmer/styx-app/releases/tag/v0.4.17&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Code: &lt;a href="https://github.com/NicholasFlemmer/styx-app" rel="noopener noreferrer"&gt;https://github.com/NicholasFlemmer/styx-app&lt;/a&gt; · Site: &lt;a href="https://heystyx.com" rel="noopener noreferrer"&gt;https://heystyx.com&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;If you run into anything, issues and discussions on GitHub are the best place; I read all of them.&lt;/p&gt;

</description>
      <category>agents</category>
      <category>ai</category>
      <category>github</category>
      <category>opensource</category>
    </item>
    <item>
      <title>Let the agent work out your test command once, then write it down</title>
      <dc:creator>Nic Flemmer</dc:creator>
      <pubDate>Wed, 21 Oct 2026 14:02:36 +0000</pubDate>
      <link>https://dev.to/nicholasflemmer/let-the-agent-work-out-your-test-command-once-then-write-it-down-5ch7</link>
      <guid>https://dev.to/nicholasflemmer/let-the-agent-work-out-your-test-command-once-then-write-it-down-5ch7</guid>
      <description>&lt;p&gt;An agent can read a repo and work out how it checks itself: usually a typecheck, the tests and a linter, taken from the package scripts. It's good at that. It's a poor thing to depend on every time you want to merge.&lt;/p&gt;

&lt;p&gt;So in the app I'm building, Styx, the agent does it once. The first time you land a task in a project whose checks aren't known, the task's agent gets a turn to work out the command, run it until it passes, and report it back through a &lt;code&gt;remember_command&lt;/code&gt; tool. After that the same command runs before every landing, with no agent involved.&lt;/p&gt;

&lt;p&gt;The answer is saved in the project's &lt;code&gt;.styx/project.json&lt;/code&gt; as &lt;code&gt;checks.command&lt;/code&gt;, not in some hidden app state. That matters for three reasons. A person can read it and see what 'passing' means here. Everyone on the project shares it. And if it's wrong, you fix it by editing one line, or clear it and the next landing asks the agent again.&lt;/p&gt;

&lt;p&gt;One guard: a command that looks like it carries a secret is refused, because that file is committed.&lt;/p&gt;

&lt;p&gt;When it doesn't apply: if the agent can't work the command out, the landing goes ahead without checks and says so ('no checks ran: none are known for this project'), rather than blocking you. And the stored command goes stale the day someone renames a script, so treat it like any other config.&lt;/p&gt;

&lt;p&gt;The general version: let the agent discover, then write the result somewhere a human can read and edit. Do you cache what your agents work out, or let them rediscover it each time?&lt;/p&gt;

</description>
      <category>agents</category>
      <category>ai</category>
      <category>automation</category>
      <category>testing</category>
    </item>
    <item>
      <title>Should two coding agents ever edit the same file?</title>
      <dc:creator>Nic Flemmer</dc:creator>
      <pubDate>Tue, 20 Oct 2026 15:02:33 +0000</pubDate>
      <link>https://dev.to/nicholasflemmer/should-two-coding-agents-ever-edit-the-same-file-4loi</link>
      <guid>https://dev.to/nicholasflemmer/should-two-coding-agents-ever-edit-the-same-file-4loi</guid>
      <description>&lt;p&gt;Say you split a feature between two coding agents: one builds the &lt;code&gt;/orders&lt;/code&gt; endpoint, the other builds the page that calls it. Each works on its own branch in its own copy of the repo, so neither can overwrite the other's files while they work.&lt;/p&gt;

&lt;p&gt;The collisions come anyway, usually in the same few places: &lt;code&gt;package.json&lt;/code&gt;, a lockfile, a migration, a route table.&lt;/p&gt;

&lt;p&gt;In the tool I'm building I went for warnings rather than locks. When one agent changes a file the other has already changed, both chats get one line naming the other task. For those shared files the line is firmer and asks the two to agree on one owner first. The agents can also message each other, so they can settle it themselves. And if two branches still conflict when &lt;code&gt;main&lt;/code&gt; comes in, the conflict goes back to the agent that made it, with both sides' intent.&lt;/p&gt;

&lt;p&gt;The case for a hard lock is that it's simple: the first agent to touch a file owns it until it lands. The cost is that the second agent has to wait or work around a file it needs, and a lock held by an agent that's stuck blocks everyone.&lt;/p&gt;

&lt;p&gt;So I'm curious how others handle it. If you run more than one agent on a codebase, do you split the work so files never overlap, let them collide and sort it out at merge time, or lock files? And what do you do about the shared ones, like lockfiles and migrations?&lt;/p&gt;

&lt;p&gt;(I'm building &lt;a href="https://github.com/NicholasFlemmer/styx-app" rel="noopener noreferrer"&gt;Styx&lt;/a&gt;, an open-source app that does the warnings part.)&lt;/p&gt;

</description>
      <category>agents</category>
      <category>ai</category>
      <category>programming</category>
      <category>softwaredevelopment</category>
    </item>
    <item>
      <title>What's new in Styx 0.4.16</title>
      <dc:creator>Nic Flemmer</dc:creator>
      <pubDate>Tue, 20 Oct 2026 08:03:33 +0000</pubDate>
      <link>https://dev.to/nicholasflemmer/whats-new-in-styx-0416-2i7l</link>
      <guid>https://dev.to/nicholasflemmer/whats-new-in-styx-0416-2i7l</guid>
      <description>&lt;p&gt;Styx 0.4.16 is out. Styx runs Claude Code, Codex, Gemini CLI, Cursor and OpenCode on all your projects in one window, each task on its own branch, and agents ask before they touch production. Free and open source (Apache-2.0); Mac, with Windows and Linux in beta.&lt;/p&gt;

&lt;p&gt;What changed:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;fix: use shared settings footer copy in simulation&lt;/li&gt;
&lt;li&gt;Fix the design canvas startup white screen when an iframe has no HTML root; prepare 0.4.16.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Full release notes and downloads: &lt;a href="https://github.com/NicholasFlemmer/styx-app/releases/tag/v0.4.16" rel="noopener noreferrer"&gt;https://github.com/NicholasFlemmer/styx-app/releases/tag/v0.4.16&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Code: &lt;a href="https://github.com/NicholasFlemmer/styx-app" rel="noopener noreferrer"&gt;https://github.com/NicholasFlemmer/styx-app&lt;/a&gt; · Site: &lt;a href="https://heystyx.com" rel="noopener noreferrer"&gt;https://heystyx.com&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;If you run into anything, issues and discussions on GitHub are the best place; I read all of them.&lt;/p&gt;

</description>
      <category>agents</category>
      <category>ai</category>
      <category>opensource</category>
      <category>showdev</category>
    </item>
    <item>
      <title>What's new in Styx 0.4.15</title>
      <dc:creator>Nic Flemmer</dc:creator>
      <pubDate>Mon, 19 Oct 2026 18:03:58 +0000</pubDate>
      <link>https://dev.to/nicholasflemmer/whats-new-in-styx-0415-4knj</link>
      <guid>https://dev.to/nicholasflemmer/whats-new-in-styx-0415-4knj</guid>
      <description>&lt;p&gt;Styx 0.4.15 is out. Styx runs Claude Code, Codex, Gemini CLI, Cursor and OpenCode on all your projects in one window, each task on its own branch, and agents ask before they touch production. Free and open source (Apache-2.0); Mac, with Windows and Linux in beta.&lt;/p&gt;

&lt;p&gt;What changed:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Add disabled Cloudflare adapter and strict operation plans&lt;/li&gt;
&lt;li&gt;List every agent and deploy target where Styx names them&lt;/li&gt;
&lt;li&gt;Thank contributors in the README&lt;/li&gt;
&lt;li&gt;Retry a design screen that fails to load, and keep e2e failure snapshots in CI&lt;/li&gt;
&lt;li&gt;Add a Do not disturb switch to Settings and the palette&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Full release notes and downloads: &lt;a href="https://github.com/NicholasFlemmer/styx-app/releases/tag/v0.4.15" rel="noopener noreferrer"&gt;https://github.com/NicholasFlemmer/styx-app/releases/tag/v0.4.15&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Code: &lt;a href="https://github.com/NicholasFlemmer/styx-app" rel="noopener noreferrer"&gt;https://github.com/NicholasFlemmer/styx-app&lt;/a&gt; · Site: &lt;a href="https://heystyx.com" rel="noopener noreferrer"&gt;https://heystyx.com&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;If you run into anything, issues and discussions on GitHub are the best place; I read all of them.&lt;/p&gt;

</description>
      <category>agents</category>
      <category>ai</category>
      <category>opensource</category>
      <category>tools</category>
    </item>
  </channel>
</rss>
