<?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: Diogo Winck</title>
    <description>The latest articles on DEV Community by Diogo Winck (@dvwinck).</description>
    <link>https://dev.to/dvwinck</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%2F437340%2Fabac1e34-d297-4af9-88c2-9a6f7c9427f5.jpeg</url>
      <title>DEV Community: Diogo Winck</title>
      <link>https://dev.to/dvwinck</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/dvwinck"/>
    <language>en</language>
    <item>
      <title>I got tired of typing "do 1 and 3, skip 2" to Claude Code, so I built an approve/reject board</title>
      <dc:creator>Diogo Winck</dc:creator>
      <pubDate>Sun, 25 Oct 2026 00:47:44 +0000</pubDate>
      <link>https://dev.to/dvwinck/i-got-tired-of-typing-do-1-and-3-skip-2-to-claude-code-so-i-built-an-approvereject-board-35hf</link>
      <guid>https://dev.to/dvwinck/i-got-tired-of-typing-do-1-and-3-skip-2-to-claude-code-so-i-built-an-approvereject-board-35hf</guid>
      <description>&lt;p&gt;You ask Claude Code for ten improvements to your project. It answers with a tidy numbered list. Then the real work starts: you scroll, you type "do 1 and 3, skip 2 because we tried that, maybe 5 later," and you hope the agent maps your sentence back to the right items. Next week, in a new session, it proposes number 2 again.&lt;/p&gt;

&lt;p&gt;So I built &lt;strong&gt;ar-board&lt;/strong&gt;, a Claude Code plugin that turns the list into cards on a local page. You judge each card with one key (approve, reject, or maybe) and a short reason. The agent gets back an exact list of what you approved, and your reasons go into a preferences file it reads before it proposes anything next time. One Python file, standard library only, MIT licensed.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Ftl1b8npbvy5ytcbwy0a4.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Ftl1b8npbvy5ytcbwy0a4.gif" alt="The ar-board panel: a comment is typed, four cards are judged with M, A, R and A, then Finish is pressed" width="799" height="562"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;The panel opens in English, and an EN/PT toggle in its header switches it to Portuguese. The cards are in whatever language you and the agent are using. You can &lt;a href="https://dvwinck.github.io/capybara-toolbox/" rel="noopener noreferrer"&gt;try a sketch of the board in your browser&lt;/a&gt; before installing.&lt;/em&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  What it does
&lt;/h2&gt;

&lt;p&gt;You ask in plain language, for example: "Give me 10 architecture improvements as an AR board." Claude then:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;reads your past preferences from &lt;code&gt;.ar-board/preferences.md&lt;/code&gt;, so it knows which patterns you have already rejected;&lt;/li&gt;
&lt;li&gt;writes the proposals to &lt;code&gt;batch.json&lt;/code&gt; and starts the panel on &lt;code&gt;127.0.0.1&lt;/code&gt;;&lt;/li&gt;
&lt;li&gt;waits while you judge each card from the keyboard (&lt;code&gt;A&lt;/code&gt; approve, &lt;code&gt;R&lt;/code&gt; reject, &lt;code&gt;M&lt;/code&gt; maybe) and add a comment saying &lt;em&gt;why&lt;/em&gt;;&lt;/li&gt;
&lt;li&gt;reads your decisions with the &lt;code&gt;summary&lt;/code&gt; command, acts only on the approved items (anything rejected, marked maybe, or left undecided counts as "no"), and asks you to confirm the preference rules it inferred from your comments.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Each card carries more than a title: what changes, why, a risk level, and any extra fields the agent wants to show. Pressing the same key again undoes the decision, and holding a key down never judges several cards in a row.&lt;/p&gt;

&lt;h2&gt;
  
  
  Install
&lt;/h2&gt;

&lt;p&gt;Inside Claude Code:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;/plugin marketplace add dvwinck/capybara-toolbox
/plugin install ar-board@capybara-toolbox
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Restart Claude Code, then ask for an AR board or run &lt;code&gt;/ar-board:ar-board&lt;/code&gt;. Add &lt;code&gt;.ar-board/&lt;/code&gt; to your &lt;code&gt;.gitignore&lt;/code&gt;, because your comments may contain internal details.&lt;/p&gt;

&lt;h2&gt;
  
  
  Bonus: question-board
&lt;/h2&gt;

&lt;p&gt;The same plugin also ships &lt;strong&gt;question-board&lt;/strong&gt;, a one-screen form for when the agent needs several independent answers before it can continue. Keys 1 to 9 pick an option; a recommended option is marked but never preselected.&lt;/p&gt;

&lt;h2&gt;
  
  
  How it's built
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;One file, no dependencies.&lt;/strong&gt; Validation, the HTTP server, the CLI, and the HTML/CSS/JS page all live in &lt;code&gt;ar_board.py&lt;/code&gt; (1,512 lines, much of it the embedded page). There is no &lt;code&gt;pip&lt;/code&gt;, no Node, and no outbound network traffic: the only socket is the panel's listener on loopback. It needs Python 3.9 or later.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The channel between agent and panel is a file.&lt;/strong&gt; The panel writes &lt;code&gt;decisions.json&lt;/code&gt; atomically: a private temp file with mode &lt;code&gt;0600&lt;/code&gt;, moved into place with &lt;code&gt;os.replace&lt;/code&gt;. The agent reads the file with the &lt;code&gt;wait&lt;/code&gt; and &lt;code&gt;summary&lt;/code&gt; commands, which ignore any file from another batch or round, so a previous round's approvals never reach it.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Rounds, not overwrites.&lt;/strong&gt; A refinement round keeps the same &lt;code&gt;batch_id&lt;/code&gt; and sets &lt;code&gt;round: N+1&lt;/code&gt;. When the server starts the new round, it archives the old decisions as &lt;code&gt;decisions.rN.json&lt;/code&gt; and never overwrites an earlier archive.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;It learns from your reasons, but only with your sign-off.&lt;/strong&gt; The agent appends your comments to &lt;code&gt;preferences.md&lt;/code&gt;, quoting what you said and marking its own interpretations as "to be confirmed." Only the rules you confirm become firm constraints for future batches.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Hardened, even on localhost.&lt;/strong&gt; The panel binds &lt;code&gt;127.0.0.1&lt;/code&gt; on a free port the OS assigns, with a fresh 128-bit token per run, required on every request. Writes must carry the token in an &lt;code&gt;X-AR-Token&lt;/code&gt; header, which a cross-origin page cannot send without a CORS preflight the server never answers. Any &lt;code&gt;Host&lt;/code&gt; other than &lt;code&gt;127.0.0.1&lt;/code&gt; or &lt;code&gt;localhost&lt;/code&gt; is rejected, to block DNS rebinding. Card text and comments are HTML-escaped, and the page's CSP forbids framing. Images get a sandbox CSP, so an SVG cannot run scripts.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Custom labels and a second press for risky cards.&lt;/strong&gt; A board can rename the three decisions and give each a tone (green, amber, or red), for example &lt;em&gt;Delete / Keep / Later&lt;/em&gt;, with Delete in red. A card can also require a second press before an approval counts. This guards against a reflexive keypress; it is not a security boundary. A sibling plugin, tidy-board, uses this to delete stale git branches, worktrees, and stashes: anything not provably merged needs the second press and gets a backup first, and every approved item is rechecked right before deletion.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;No localhost? Use offline mode.&lt;/strong&gt; In a cloud or remote session, the &lt;code&gt;build&lt;/code&gt; command writes a self-contained &lt;code&gt;board.html&lt;/code&gt; instead. You make your decisions in that file and paste the result back into the chat.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Tested.&lt;/strong&gt; The ar-board suite has 249 tests, including security regression tests for the token, the &lt;code&gt;Host&lt;/code&gt; check, path traversal, a symlink that points outside the batch folder, and a symlink planted at the temporary file used to save &lt;code&gt;decisions.json&lt;/code&gt;. CI runs them on Ubuntu and macOS with Python 3.9, 3.11, 3.13, and 3.14.&lt;/p&gt;

&lt;h2&gt;
  
  
  Limitations
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;It is a single-user tool for your own machine. The token sits in the URL, so it ends up in your browser history. There is no HTTPS; traffic stays on loopback.&lt;/li&gt;
&lt;li&gt;"Act only on what I approved" and "learn from my comments" are instructions to the model, not code. The board makes your decisions unambiguous; it cannot force the agent to follow them.&lt;/li&gt;
&lt;li&gt;Windows is not tested yet.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Credits
&lt;/h2&gt;

&lt;p&gt;The "AR board" idea comes from the video &lt;a href="https://youtu.be/Ckuo9Bvy3zk" rel="noopener noreferrer"&gt;&lt;em&gt;Minha NOVA SKILL Favorita do Claude Code&lt;/em&gt;&lt;/a&gt; by the Maestros da IA channel. This is an independent implementation: it uses no code or material from the original skill and is not affiliated with the channel.&lt;/p&gt;

&lt;p&gt;ar-board is part of &lt;a href="https://github.com/dvwinck/capybara-toolbox" rel="noopener noreferrer"&gt;Capybara Toolbox&lt;/a&gt;, a small set of MIT-licensed Claude Code plugins. If it breaks for you, please open an issue. And a question for the comments: what do you do today when an agent hands you ten proposals at once?&lt;/p&gt;

</description>
      <category>claude</category>
      <category>ai</category>
      <category>opensource</category>
      <category>python</category>
    </item>
  </channel>
</rss>
