<?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: Phạm Cảnh</title>
    <description>The latest articles on DEV Community by Phạm Cảnh (@phamcanh).</description>
    <link>https://dev.to/phamcanh</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%2F4233083%2F7831d7f8-e7ad-4a38-87e9-2d71dca641fe.png</url>
      <title>DEV Community: Phạm Cảnh</title>
      <link>https://dev.to/phamcanh</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/phamcanh"/>
    <language>en</language>
    <item>
      <title>A harness for OpenCode that admits when it costs more</title>
      <dc:creator>Phạm Cảnh</dc:creator>
      <pubDate>Mon, 09 Nov 2026 07:16:14 +0000</pubDate>
      <link>https://dev.to/phamcanh/a-harness-for-opencode-that-admits-when-it-costs-more-4dg7</link>
      <guid>https://dev.to/phamcanh/a-harness-for-opencode-that-admits-when-it-costs-more-4dg7</guid>
      <description>&lt;p&gt;A coding harness is model-specific even when the tool is not. Context reserve, effort levels, temperature and privacy terms change with the provider. Re-answer those in a prompt and the model reads them once. Hard-code them and you fork the project.&lt;/p&gt;

&lt;p&gt;opencode-harness-kit takes a different route: the model is data.&lt;/p&gt;

&lt;h2&gt;
  
  
  The problem
&lt;/h2&gt;

&lt;p&gt;OpenCode lets you point the CLI at almost any provider. The hard part starts after that. A free route, a paid route and a local route each have different context limits, different reasoning controls and different terms about what happens to prompts. If you switch routes often, you end up maintaining the same model-specific answers in several places.&lt;/p&gt;

&lt;h2&gt;
  
  
  The design
&lt;/h2&gt;

&lt;p&gt;&lt;code&gt;oc-harness use &amp;lt;provider&amp;gt;/&amp;lt;model&amp;gt;&lt;/code&gt; reads the live models.dev record and writes a profile to &lt;code&gt;~/.config/opencode/profiles/&amp;lt;id&amp;gt;.json&lt;/code&gt;. The profile is compiled into &lt;code&gt;opencode.jsonc&lt;/code&gt; as ten lanes:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;plan&lt;/li&gt;
&lt;li&gt;build&lt;/li&gt;
&lt;li&gt;reviewer&lt;/li&gt;
&lt;li&gt;locate&lt;/li&gt;
&lt;li&gt;verify&lt;/li&gt;
&lt;li&gt;vision&lt;/li&gt;
&lt;li&gt;doc&lt;/li&gt;
&lt;li&gt;schema&lt;/li&gt;
&lt;li&gt;sweep&lt;/li&gt;
&lt;li&gt;longform&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Each lane gets its own model, output cap and fallback chain. The plugin adds a pruner for old tool output, a guard and per-lane telemetry. &lt;code&gt;oc-harness verify&lt;/code&gt; checks that the config, prompts, plugin and skills still match the profile.&lt;/p&gt;

&lt;p&gt;There is no model name in &lt;code&gt;src/&lt;/code&gt;. The kit ships no model list and no default model, because which model you can reach is a fact about your account, not about the package.&lt;/p&gt;

&lt;h2&gt;
  
  
  The result that matters
&lt;/h2&gt;

&lt;p&gt;The README publishes the A/B where the kit loses. Same task, same model, ten reps per arm:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Task&lt;/th&gt;
&lt;th&gt;Arm&lt;/th&gt;
&lt;th&gt;Uncached input&lt;/th&gt;
&lt;th&gt;Wall time&lt;/th&gt;
&lt;th&gt;Correct&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;One read&lt;/td&gt;
&lt;td&gt;Without kit&lt;/td&gt;
&lt;td&gt;10,540&lt;/td&gt;
&lt;td&gt;21s&lt;/td&gt;
&lt;td&gt;55/60&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;One read&lt;/td&gt;
&lt;td&gt;With kit&lt;/td&gt;
&lt;td&gt;56,205&lt;/td&gt;
&lt;td&gt;43s&lt;/td&gt;
&lt;td&gt;54/60&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Twelve files&lt;/td&gt;
&lt;td&gt;With kit vs without&lt;/td&gt;
&lt;td&gt;-69%&lt;/td&gt;
&lt;td&gt;-60%&lt;/td&gt;
&lt;td&gt;no regression&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;On a task that fits in one read, the harness is a cost: more input tokens and more wall time for the same correctness. On a long task, the pruner earns its place. Both numbers are in the repo, not just the good one.&lt;/p&gt;

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



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;npx &lt;span class="nt"&gt;-y&lt;/span&gt; opencode-harness-kit@latest init
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The initializer asks once for a provider/model, writes the profile, and prints the route it selected. You can re-run it when a free model disappears or a provider changes its limits.&lt;/p&gt;

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

&lt;ul&gt;
&lt;li&gt;No mid-session failover. A plugin hook cannot change the model of a running session.&lt;/li&gt;
&lt;li&gt;No hosted service.&lt;/li&gt;
&lt;li&gt;No model list and no default model.&lt;/li&gt;
&lt;li&gt;No telemetry upload.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Node 22. OpenCode V1 and V2. MIT.&lt;/p&gt;

&lt;p&gt;Repo: &lt;a href="https://github.com/pham-canh/opencode-harness-kit" rel="noopener noreferrer"&gt;https://github.com/pham-canh/opencode-harness-kit&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;I'm the author of opencode-harness-kit. The benchmark table and the losing arm are in the repo, so you can check the numbers rather than trust the summary.&lt;/p&gt;

&lt;p&gt;If you try it on a route I have not tested, the useful feedback is where the lane split breaks, not whether the install command works.&lt;/p&gt;

</description>
      <category>opencode</category>
      <category>ai</category>
      <category>opensource</category>
      <category>node</category>
    </item>
  </channel>
</rss>
