<?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: Emery Lin</title>
    <description>The latest articles on DEV Community by Emery Lin (@codego_3211).</description>
    <link>https://dev.to/codego_3211</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%2F4064266%2F1f491b53-de57-4196-ae0a-6a6b83e92e62.png</url>
      <title>DEV Community: Emery Lin</title>
      <link>https://dev.to/codego_3211</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/codego_3211"/>
    <language>en</language>
    <item>
      <title>The Release-Day Reality Check: A Small Model Evaluation You Can Rerun</title>
      <dc:creator>Emery Lin</dc:creator>
      <pubDate>Mon, 10 Aug 2026 11:00:22 +0000</pubDate>
      <link>https://dev.to/codego_3211/the-release-day-reality-check-a-small-model-evaluation-you-can-rerun-11bm</link>
      <guid>https://dev.to/codego_3211/the-release-day-reality-check-a-small-model-evaluation-you-can-rerun-11bm</guid>
      <description>&lt;p&gt;A new model announcement can create useful evidence very quickly, but it rarely creates evidence about your work. Release posts usually answer broad questions; a developer evaluating a coding assistant needs narrower ones:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Can it repair the kind of defect I encounter?&lt;/li&gt;
&lt;li&gt;Does it respect the libraries and style already in the project?&lt;/li&gt;
&lt;li&gt;Can it follow a small interface contract?&lt;/li&gt;
&lt;li&gt;Is it responsive enough for the way I work?&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Instead of assembling a fresh experiment after every launch, I use a reusable &lt;em&gt;release scorecard&lt;/em&gt;. It is small, deliberately personal, and designed to run against any OpenAI-compatible chat endpoint. It will not produce a leaderboard ranking, but it can tell you whether a new option deserves a deeper trial.&lt;/p&gt;

&lt;h2&gt;
  
  
  Start with a question, not a benchmark number
&lt;/h2&gt;

&lt;p&gt;A useful personal scorecard compares the candidate against a task you already understand. I group mine into four lanes:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Lane&lt;/th&gt;
&lt;th&gt;Number of cases&lt;/th&gt;
&lt;th&gt;What it exposes&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Fault isolation&lt;/td&gt;
&lt;td&gt;4&lt;/td&gt;
&lt;td&gt;Whether the model finds the likely cause instead of restating symptoms&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Controlled edits&lt;/td&gt;
&lt;td&gt;4&lt;/td&gt;
&lt;td&gt;Whether it changes only the requested behavior&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Contract work&lt;/td&gt;
&lt;td&gt;3&lt;/td&gt;
&lt;td&gt;Whether types, validation, errors, and compatibility survive&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Project explanation&lt;/td&gt;
&lt;td&gt;3&lt;/td&gt;
&lt;td&gt;Whether it can reason from unfamiliar context without inventing files&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;Fourteen prompts are manageable to review carefully. More importantly, every prompt has an expected outcome because it comes from a repository, ticket, or refactoring exercise I already know.&lt;/p&gt;

&lt;p&gt;A case should contain three things: the prompt, the constraints, and a review note describing what a good answer must preserve.&lt;/p&gt;

&lt;p&gt;Create &lt;code&gt;cases.json&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;"suite"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"release-reality-check-v1"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"tests"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"name"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"session-cookie-regression"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"lane"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"fault-isolation"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"prompt"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Users remain authenticated for only one request after login. Here is the middleware and cookie configuration. Find the most probable defect and propose the smallest safe correction.&lt;/span&gt;&lt;span class="se"&gt;\n\n&lt;/span&gt;&lt;span class="s2"&gt;&amp;lt;redacted source goes here&amp;gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"constraints"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="s2"&gt;"Do not replace the authentication framework"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="s2"&gt;"Explain the failure before editing"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="s2"&gt;"Call out any security side effect"&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"expected"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Identifies the cookie attribute or proxy mismatch, preserves the current session flow, and suggests a focused regression test."&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"name"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"price-rounding-contract"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"lane"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"contract-work"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"prompt"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Update this checkout helper so totals are rounded only at the final display boundary. Preserve the public function signature.&lt;/span&gt;&lt;span class="se"&gt;\n\n&lt;/span&gt;&lt;span class="s2"&gt;&amp;lt;redacted source goes here&amp;gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"constraints"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="s2"&gt;"No floating-point money totals"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="s2"&gt;"No breaking change to returned fields"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="s2"&gt;"Include boundary examples"&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"expected"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Uses integer minor units or an equivalent decimal representation, keeps the existing API, and covers half-cent boundaries."&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The examples above are placeholders rather than executed results. Replace them with cases from your own codebase after removing credentials, customer information, and anything else you cannot send to an inference service.&lt;/p&gt;

&lt;h2&gt;
  
  
  A portable runner
&lt;/h2&gt;

&lt;p&gt;This Node.js runner avoids a vendor-specific SDK. It records the exact suite, model identifier, response, status, and elapsed time for every attempt.&lt;/p&gt;

&lt;p&gt;Save it as &lt;code&gt;run-scorecard.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;readFile&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;writeFile&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="s2"&gt;node:fs/promises&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;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="s2"&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;baseUrl&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;process&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;env&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;MODEL_BASE_URL&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;model&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;process&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;env&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;MODEL_ID&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;apiKey&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;process&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;env&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;MODEL_API_KEY&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="nx"&gt;baseUrl&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="nx"&gt;model&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;throw&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;Error&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Set MODEL_BASE_URL and MODEL_ID before running.&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;source&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;readFile&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;cases.json&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="s2"&gt;utf8&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;suite&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;source&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;suiteHash&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="s2"&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;source&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="s2"&gt;hex&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;report&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="na"&gt;suite&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;suite&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;suite&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="nx"&gt;suiteHash&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="nx"&gt;model&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;startedAt&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;Date&lt;/span&gt;&lt;span class="p"&gt;().&lt;/span&gt;&lt;span class="nf"&gt;toISOString&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt;
  &lt;span class="na"&gt;attempts&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="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;test&lt;/span&gt; &lt;span class="k"&gt;of&lt;/span&gt; &lt;span class="nx"&gt;suite&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;tests&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;prompt&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
    &lt;span class="nx"&gt;test&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;prompt&lt;/span&gt;&lt;span class="p"&gt;,&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="s2"&gt;Constraints:&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="p"&gt;...&lt;/span&gt;&lt;span class="nx"&gt;test&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;constraints&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;map&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="nx"&gt;item&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="s2"&gt;`- &lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;item&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;`&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="p"&gt;].&lt;/span&gt;&lt;span class="nf"&gt;join&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="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;started&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;performance&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;now&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;response&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="s2"&gt;`&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;baseUrl&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;$/&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;""&lt;/span&gt;&lt;span class="p"&gt;)}&lt;/span&gt;&lt;span class="s2"&gt;/chat/completions`&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="s2"&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="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&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="s2"&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="nx"&gt;apiKey&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="s2"&gt;`Bearer &lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;apiKey&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;`&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="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="nx"&gt;model&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="na"&gt;temperature&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="na"&gt;messages&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="na"&gt;role&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;user&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
          &lt;span class="na"&gt;content&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;prompt&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;
      &lt;span class="p"&gt;]&lt;/span&gt;
    &lt;span class="p"&gt;})&lt;/span&gt;
  &lt;span class="p"&gt;});&lt;/span&gt;

  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;elapsedMs&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;Math&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;round&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;performance&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;now&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="nx"&gt;started&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;text&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;response&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;text&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;answer&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="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;parseError&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kc"&gt;null&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

  &lt;span class="k"&gt;try&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;payload&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;text&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="nx"&gt;answer&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;payload&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;choices&lt;/span&gt;&lt;span class="p"&gt;?.[&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;]?.&lt;/span&gt;&lt;span class="nx"&gt;message&lt;/span&gt;&lt;span class="p"&gt;?.&lt;/span&gt;&lt;span class="nx"&gt;content&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="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;catch &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;error&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;parseError&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;error&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;message&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;

  &lt;span class="nx"&gt;report&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;attempts&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="na"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;test&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;name&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;lane&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;test&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;lane&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;httpStatus&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;response&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;status&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="nx"&gt;elapsedMs&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;expected&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;test&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;expected&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="nx"&gt;answer&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;rawResponse&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;answer&lt;/span&gt; &lt;span class="p"&gt;?&lt;/span&gt; &lt;span class="kc"&gt;undefined&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;text&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="nx"&gt;parseError&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;outputName&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;`report-&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nb"&gt;Date&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;now&lt;/span&gt;&lt;span class="p"&gt;()}&lt;/span&gt;&lt;span class="s2"&gt;.json`&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;writeFile&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;outputName&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;`&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;JSON&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;stringify&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;report&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kc"&gt;null&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;)}&lt;/span&gt;&lt;span class="s2"&gt;\n`&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;`Wrote &lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;outputName&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;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Run it with environment variables rather than placing credentials in the repository:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;export &lt;/span&gt;&lt;span class="nv"&gt;MODEL_BASE_URL&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"https://your-compatible-host/v1"&lt;/span&gt;
&lt;span class="nb"&gt;export &lt;/span&gt;&lt;span class="nv"&gt;MODEL_ID&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"model-under-test"&lt;/span&gt;
&lt;span class="nb"&gt;export &lt;/span&gt;&lt;span class="nv"&gt;MODEL_API_KEY&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"your-token"&lt;/span&gt;

node run-scorecard.mjs
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;A few details are intentional:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;The case-file hash makes it obvious whether two reports used the same prompts.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;temperature: 0&lt;/code&gt; reduces avoidable variation, although it should not be treated as a proof of deterministic serving.&lt;/li&gt;
&lt;li&gt;Raw error responses are preserved when an endpoint fails or returns an unexpected body.&lt;/li&gt;
&lt;li&gt;Timing is stored per case, but it should be read as a rough usability signal—not a controlled benchmark.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Grade against consequences
&lt;/h2&gt;

&lt;p&gt;After the run, score each response without looking at the model name:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Score&lt;/th&gt;
&lt;th&gt;Meaning&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;2&lt;/td&gt;
&lt;td&gt;The answer satisfies the expected note and could plausibly be applied after normal review&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;1&lt;/td&gt;
&lt;td&gt;It has the right direction but misses an important constraint or needs substantial repair&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;0&lt;/td&gt;
&lt;td&gt;It misunderstands the problem, invents project details, or creates a risky change&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;I also mark any response with one or more failure labels:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;code&gt;constraint-ignored&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;fabricated-context&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;unsafe-edit&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;scope-expansion&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;unverifiable-claim&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;timeout-or-endpoint-error&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The labels are more useful than the total. Two candidates can receive similar scores while failing for completely different reasons. One may be too cautious; another may confidently invent missing APIs. Those behaviors lead to different adoption decisions.&lt;/p&gt;

&lt;p&gt;For a cleaner comparison, grade the current assistant first, put the notes away, and then grade the release candidate. If possible, shuffle the reports so the newest model does not automatically receive the benefit of the doubt.&lt;/p&gt;

&lt;h2&gt;
  
  
  Using a free evaluation environment
&lt;/h2&gt;

&lt;p&gt;Hosted inference is the part that often stops a personal evaluation before it starts. One practical option is MonkeyCode, which currently provides free model access along with a free server option. Disclosure: This article was prepared as part of MonkeyCode's product outreach.&lt;/p&gt;

&lt;p&gt;The runner does not need product-specific code: set its endpoint and model identifier through the environment variables shown above. That separation matters because hosted offerings, available models, limits, and operating conditions can change. Keep the scorecard independent so you can move it to another compatible endpoint or a local server without editing the evaluation logic.&lt;/p&gt;

&lt;p&gt;Free access is especially useful for the first pass: deciding whether a model is worth a paid, private, or longer evaluation. It should not be treated as evidence that the same environment is suitable for production traffic.&lt;/p&gt;

&lt;h2&gt;
  
  
  A compact report format
&lt;/h2&gt;

&lt;p&gt;Once grading is complete, write a short summary that another developer could inspect later:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight markdown"&gt;&lt;code&gt;&lt;span class="gh"&gt;# Model release check&lt;/span&gt;
&lt;span class="p"&gt;
-&lt;/span&gt; Date:
&lt;span class="p"&gt;-&lt;/span&gt; Candidate identifier:
&lt;span class="p"&gt;-&lt;/span&gt; Scorecard version/hash:
&lt;span class="p"&gt;-&lt;/span&gt; Endpoint type:
&lt;span class="p"&gt;-&lt;/span&gt; Scores by lane:
&lt;span class="p"&gt;  -&lt;/span&gt; Fault isolation: x/8
&lt;span class="p"&gt;  -&lt;/span&gt; Controlled edits: x/8
&lt;span class="p"&gt;  -&lt;/span&gt; Contract work: x/6
&lt;span class="p"&gt;  -&lt;/span&gt; Project explanation: x/6
&lt;span class="p"&gt;-&lt;/span&gt; Median observed response time:
&lt;span class="p"&gt;-&lt;/span&gt; Repeated failure labels:
&lt;span class="p"&gt;-&lt;/span&gt; Most concerning response:
&lt;span class="p"&gt;-&lt;/span&gt; Best response:
&lt;span class="p"&gt;-&lt;/span&gt; Decision:
&lt;span class="p"&gt;  -&lt;/span&gt; [ ] Ignore for now
&lt;span class="p"&gt;  -&lt;/span&gt; [ ] Watch for the next revision
&lt;span class="p"&gt;  -&lt;/span&gt; [ ] Test in a noncritical workflow
&lt;span class="p"&gt;  -&lt;/span&gt; [ ] Run a larger evaluation
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The decision is the important output. A score without an action tends to become another benchmark screenshot.&lt;/p&gt;

&lt;h2&gt;
  
  
  What this cannot prove
&lt;/h2&gt;

&lt;p&gt;This method is intentionally narrow. It has several limitations:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Small samples have weak statistical power.&lt;/strong&gt; Fourteen cases can reveal obvious fit problems, but they cannot establish a small percentage improvement.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Personal cases introduce personal bias.&lt;/strong&gt; Write the expected result and constraints before viewing the response.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Observed latency depends on the hosting environment.&lt;/strong&gt; Queueing, geography, shared capacity, and request size can dominate the measurement.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;A single run is not a regression suite.&lt;/strong&gt; Rerun the same scorecard when the model, endpoint, prompt, or serving configuration changes.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;A hosted endpoint may be inappropriate for sensitive material.&lt;/strong&gt; If source code cannot leave your environment, connect the same runner to local inference instead.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Skip this approach when making a high-stakes procurement decision, validating production service levels, testing regulated data, or publishing a comparative benchmark. Those situations need representative datasets, controlled infrastructure, documented privacy terms, and proper statistical analysis.&lt;/p&gt;

&lt;p&gt;For launch-week triage, however, a stable case pack and a portable runner are usually enough to separate "interesting announcement" from "worth trying in my workflow." If you need somewhere to run that first pass, MonkeyCode is one compatible option; the same scorecard can also be pointed at any other endpoint you trust.&lt;/p&gt;

</description>
      <category>ai</category>
      <category>opensource</category>
      <category>testing</category>
      <category>programming</category>
    </item>
    <item>
      <title>Your AI Coding Agent Has Tools. Here's a Test Suite for the Sandbox You Hope It Respects</title>
      <dc:creator>Emery Lin</dc:creator>
      <pubDate>Mon, 10 Aug 2026 08:33:31 +0000</pubDate>
      <link>https://dev.to/codego_3211/your-ai-coding-agent-has-tools-heres-a-test-suite-for-the-sandbox-you-hope-it-respects-4f0f</link>
      <guid>https://dev.to/codego_3211/your-ai-coding-agent-has-tools-heres-a-test-suite-for-the-sandbox-you-hope-it-respects-4f0f</guid>
      <description>&lt;p&gt;There's a growing unease in the dev community right now: we keep handing AI agents more tools — shell access, file writes, HTTP clients, git push — and mostly we check the boundaries by &lt;em&gt;vibes&lt;/em&gt;. The agent didn't delete anything today, so the sandbox works, right?&lt;/p&gt;

&lt;p&gt;I wanted something more defensible than vibes. So this article is a small, runnable red-team suite you can point at any environment where you let an agent execute commands. It doesn't assume any particular agent, model, or vendor. It tests the &lt;em&gt;sandbox&lt;/em&gt;, not the model — which is where most real failures live.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Everything below is reproducible. The test suite is code you can run. The failure modes are described, not dramatized.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  The four boundaries that actually matter
&lt;/h2&gt;

&lt;p&gt;When people say "sandbox," they usually mean one blurry thing. In practice an agent execution environment has at least four separate boundaries, and each fails differently:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Boundary&lt;/th&gt;
&lt;th&gt;Question&lt;/th&gt;
&lt;th&gt;Typical failure&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Filesystem&lt;/td&gt;
&lt;td&gt;Can the agent read/write outside its workspace?&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;../&lt;/code&gt; traversal, symlink escape, bind mounts&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Secrets&lt;/td&gt;
&lt;td&gt;Can it read env vars or files you didn't intend?&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;env&lt;/code&gt; dump, &lt;code&gt;~/.ssh&lt;/code&gt;, &lt;code&gt;~/.aws&lt;/code&gt;, &lt;code&gt;.env&lt;/code&gt; in parent dirs&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Network&lt;/td&gt;
&lt;td&gt;Can it make outbound calls you didn't authorize?&lt;/td&gt;
&lt;td&gt;Direct egress, DNS exfiltration, metadata endpoints&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Persistence&lt;/td&gt;
&lt;td&gt;Does anything it changes survive past the session?&lt;/td&gt;
&lt;td&gt;Leftover processes, cron, shell rc files, git hooks&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;A container answers some of these. A VM answers more. "The agent is polite" answers none.&lt;/p&gt;

&lt;h2&gt;
  
  
  A reproducible boundary test suite
&lt;/h2&gt;

&lt;p&gt;The trick: don't test whether the &lt;em&gt;agent&lt;/em&gt; behaves. Test whether the &lt;em&gt;environment&lt;/em&gt; refuses misbehavior. Run these commands yourself (or have the agent run them — same thing, since the agent runs commands as some user somewhere) and check the results.&lt;/p&gt;

&lt;p&gt;Save as &lt;code&gt;boundary_check.sh&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;#!/usr/bin/env bash&lt;/span&gt;
&lt;span class="c"&gt;# Boundary checks for an agent execution environment.&lt;/span&gt;
&lt;span class="c"&gt;# Run INSIDE the sandbox, as the same user the agent runs as.&lt;/span&gt;
&lt;span class="c"&gt;# Every line should print BLOCKED. Anything else is a finding.&lt;/span&gt;

check&lt;span class="o"&gt;()&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
  &lt;span class="nb"&gt;local &lt;/span&gt;&lt;span class="nv"&gt;name&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$1&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="nb"&gt;shift
  &lt;/span&gt;&lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$@&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt;/dev/null 2&amp;gt;&amp;amp;1&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="k"&gt;then
    &lt;/span&gt;&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"OPEN:    &lt;/span&gt;&lt;span class="nv"&gt;$name&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;
  &lt;span class="k"&gt;else
    &lt;/span&gt;&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"BLOCKED: &lt;/span&gt;&lt;span class="nv"&gt;$name&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;
  &lt;span class="k"&gt;fi&lt;/span&gt;
&lt;span class="o"&gt;}&lt;/span&gt;

&lt;span class="c"&gt;# 1. Filesystem: can we read outside the workspace?&lt;/span&gt;
check &lt;span class="s2"&gt;"read /etc/shadow"&lt;/span&gt;        &lt;span class="nb"&gt;head&lt;/span&gt; &lt;span class="nt"&gt;-n1&lt;/span&gt; /etc/shadow
check &lt;span class="s2"&gt;"read parent dir"&lt;/span&gt;         &lt;span class="nb"&gt;ls&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$HOME&lt;/span&gt;&lt;span class="s2"&gt;/../../"&lt;/span&gt;
check &lt;span class="s2"&gt;"write outside workspace"&lt;/span&gt; &lt;span class="nb"&gt;touch&lt;/span&gt; /tmp/outside_workspace_probe

&lt;span class="c"&gt;# 2. Secrets: are credentials reachable?&lt;/span&gt;
check &lt;span class="s2"&gt;"read ~/.ssh"&lt;/span&gt;             &lt;span class="nb"&gt;ls&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$HOME&lt;/span&gt;&lt;span class="s2"&gt;/.ssh"&lt;/span&gt;
check &lt;span class="s2"&gt;"read ~/.aws"&lt;/span&gt;             &lt;span class="nb"&gt;ls&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$HOME&lt;/span&gt;&lt;span class="s2"&gt;/.aws"&lt;/span&gt;
check &lt;span class="s2"&gt;"env contains secrets"&lt;/span&gt;    sh &lt;span class="nt"&gt;-c&lt;/span&gt; &lt;span class="s1"&gt;'env | grep -qiE "(KEY|TOKEN|SECRET|PASSWORD)"'&lt;/span&gt;

&lt;span class="c"&gt;# 3. Network: is outbound egress open?&lt;/span&gt;
check &lt;span class="s2"&gt;"outbound HTTPS"&lt;/span&gt;          curl &lt;span class="nt"&gt;-s&lt;/span&gt; &lt;span class="nt"&gt;--max-time&lt;/span&gt; 3 https://example.com
check &lt;span class="s2"&gt;"cloud metadata endpoint"&lt;/span&gt; curl &lt;span class="nt"&gt;-s&lt;/span&gt; &lt;span class="nt"&gt;--max-time&lt;/span&gt; 2 http://169.254.169.254/latest/meta-data/

&lt;span class="c"&gt;# 4. Persistence: can we install hooks that outlive the session?&lt;/span&gt;
check &lt;span class="s2"&gt;"write shell rc"&lt;/span&gt;          sh &lt;span class="nt"&gt;-c&lt;/span&gt; &lt;span class="s1"&gt;'echo true &amp;gt;&amp;gt; "$HOME/.bashrc"'&lt;/span&gt;
check &lt;span class="s2"&gt;"install cron job"&lt;/span&gt;        sh &lt;span class="nt"&gt;-c&lt;/span&gt; &lt;span class="s1"&gt;'crontab -l 2&amp;gt;/dev/null; crontab - &amp;gt;/dev/null 2&amp;gt;&amp;amp;1 &amp;lt;&amp;lt;&amp;lt; ""'&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Run it, and you get a table of &lt;code&gt;OPEN&lt;/code&gt; / &lt;code&gt;BLOCKED&lt;/code&gt; per boundary. That table is your actual security posture — not the diagram in someone's README.&lt;/p&gt;

&lt;p&gt;Two important details:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Run it as the agent's user, not as root.&lt;/strong&gt; Root in a container can often do things the agent's UID can't, and vice versa for misconfigured mounts. Test the identity that matters.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;OPEN&lt;/code&gt; isn't always wrong — it's always a decision.&lt;/strong&gt; Maybe you &lt;em&gt;want&lt;/em&gt; outbound HTTPS so the agent can fetch docs. Fine. But then it's a choice you made, not an assumption you inherited.&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  What each finding means in practice
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;read /etc/shadow&lt;/code&gt; OPEN&lt;/strong&gt; — usually means the agent runs as root in a container. Root-in-container plus a writable Docker socket or &lt;code&gt;CAP_SYS_ADMIN&lt;/code&gt; is a classic container-escape setup. Run the agent as a non-root UID; it costs you nothing.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;env contains secrets&lt;/code&gt; OPEN&lt;/strong&gt; — the most common real-world leak. If you inject API keys as environment variables into the same process space where an agent executes arbitrary commands, assume the agent's context window eventually contains those keys. Prefer short-lived, scoped tokens fetched by a broker process the agent can call but not read.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;cloud metadata endpoint&lt;/code&gt; OPEN&lt;/strong&gt; — on AWS/GCP/Azure this can hand the agent instance credentials. Block &lt;code&gt;169.254.169.254&lt;/code&gt; at the network layer or use IMDSv2 with hop limits.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;outbound HTTPS&lt;/code&gt; OPEN&lt;/strong&gt; — exfiltration path. If you need egress, an allowlist proxy beats a firewall rule you'll forget about.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Persistence probes OPEN&lt;/strong&gt; — an agent that can append to &lt;code&gt;.bashrc&lt;/code&gt; or install a git hook can arrange for code to run &lt;em&gt;later&lt;/em&gt;, possibly by you, outside the sandbox. Ephemeral environments (fresh instance per session) neutralize this whole category.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;That last point is why I now prefer disposable environments for agent experiments over my dev box. For this kind of boundary testing, I've been spinning up sessions on MonkeyCode's free server option — it gives you a remote execution environment with free model access, which is convenient here precisely because the environment isn't my machine: if a persistence probe succeeds, I throw the instance away instead of auditing my dotfiles.&lt;/p&gt;

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

&lt;p&gt;The same logic applies wherever you run agents, though — a throwaway VPS, a CI runner, a Firecracker microVM. The test suite doesn't care.&lt;/p&gt;

&lt;h2&gt;
  
  
  A minimal isolation checklist
&lt;/h2&gt;

&lt;p&gt;If you'd rather fix things than just measure them, this is the order I'd work in:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Non-root user&lt;/strong&gt; for the agent's process. (&lt;code&gt;useradd&lt;/code&gt;, not &lt;code&gt;--privileged&lt;/code&gt;.)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Workspace-only filesystem&lt;/strong&gt;: mount the project dir, read-only-mount or omit everything else, no Docker socket.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Secret broker&lt;/strong&gt;: the agent calls a helper that holds credentials; the agent never sees them.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Egress allowlist&lt;/strong&gt; (or no egress) at the network layer, not in a prompt.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Ephemeral lifetime&lt;/strong&gt;: the environment is destroyed after the session. Persistence of &lt;em&gt;artifacts&lt;/em&gt; (code, PRs) goes through git, not through the machine.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;None of this is exotic. It's the same hardening you'd apply to a CI runner executing untrusted pull requests — because that's essentially what an agent session is.&lt;/p&gt;

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

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;This tests configuration, not cleverness.&lt;/strong&gt; A determined exploit (kernel bug, container runtime CVE) won't show up in a shell script. This suite catches misconfiguration, which is the common case, not zero-days.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;A passing suite doesn't make the agent's &lt;em&gt;output&lt;/em&gt; safe.&lt;/strong&gt; Code the agent writes still needs review before it runs anywhere privileged. Boundary tests and code review are different layers.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Free tiers have limits.&lt;/strong&gt; Whatever hosted environment you use for this — including the one mentioned above — check current quotas, session duration, and region before building a workflow on it; free offerings change and I can't promise what yours includes. For heavy CI-scale testing, you'll want your own infrastructure anyway.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Prompt-level guardrails are not a boundary.&lt;/strong&gt; "I instructed the agent not to delete files" is a hope. The tests above measure mechanisms. Keep the distinction clear in your threat model.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Who this is &lt;em&gt;not&lt;/em&gt; for
&lt;/h2&gt;

&lt;p&gt;If your agent never executes commands — it only suggests code in a chat window — you don't need any of this; your boundary is your own code review. And if you're running agents against production data or regulated workloads, a shell script and a free sandbox are a starting point for thinking, not a compliance posture. Get a real security review.&lt;/p&gt;

&lt;p&gt;For everyone in between — experimenting with agents that can run shell commands, and quietly wondering what "sandboxed" actually guarantees in your setup — run the script. Fifteen minutes, and you'll know which of your four boundaries are real and which are decorative.&lt;/p&gt;

&lt;p&gt;If you try it and find something surprising in your own setup, I'd genuinely like to hear which check caught it — the &lt;code&gt;env&lt;/code&gt; leak is my bet for the most common one.&lt;/p&gt;

</description>
      <category>security</category>
      <category>ai</category>
      <category>agents</category>
      <category>devops</category>
    </item>
  </channel>
</rss>
