<?xml version="1.0" encoding="UTF-8"?><rss version="2.0" xmlns:content="http://purl.org/rss/1.0/modules/content/" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:fh="http://purl.org/syndication/history/1.0"><channel><title>HarnessGrad | Blog</title><description>The gradient for harness self-improvement: measurable, attributable, comparable.</description><link>https://zjunlp.github.io/</link><language>en</language><fh:complete/><atom:link rel="self" href="https://zjunlp.github.io/HarnessGrad/blog/rss.xml"/><item><title>Why the method must live outside the harness</title><link>https://zjunlp.github.io/HarnessGrad/blog/2026-10-16-why-the-method-must-live-outside-the-harness/</link><guid isPermaLink="true">https://zjunlp.github.io/HarnessGrad/blog/2026-10-16-why-the-method-must-live-outside-the-harness/</guid><pubDate>Fri, 16 Oct 2026 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;The first design of this platform put the method &lt;em&gt;inside&lt;/em&gt; the harness. The method
implemented two entrypoints that the harness declared in &lt;code dir=&quot;auto&quot;&gt;harness.json&lt;/code&gt;, and the platform
invoked them as subprocesses:&lt;/p&gt;
&lt;div&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;improve_entrypoint&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;: [&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;python&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;-m&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;method.improve&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;],&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;accept_entrypoint&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;:  [&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;python&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;-m&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;method.accept&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;]&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;p&gt;It seemed natural: the method improves &lt;em&gt;this&lt;/em&gt; harness, so it lives with &lt;em&gt;this&lt;/em&gt; harness. It
turned out to make the central measurement impossible, and the reason is worth writing
down, because the fix looks like a refactor and is actually the whole point.&lt;/p&gt;
&lt;div&gt;&lt;h2 id=&quot;the-problem-with-a-method-that-ships-inside-the-artifact&quot;&gt;The problem with a method that ships inside the artifact&lt;/h2&gt;&lt;/div&gt;
&lt;p&gt;If the method lives inside the harness repository, it is inseparable from one artifact. It
can only ever improve the harness it shipped with. Two methods cannot be compared, because
each faces a different starting point — and “the same method applied to a different base
harness” becomes unrepresentable rather than merely untested.&lt;/p&gt;
&lt;p&gt;So the method moved out. A method is now handed a pristine copy of a base harness and
returns candidate harnesses; the platform measures those candidates itself, on its own
fixed task set, with its own scorer. That is what makes a shared axis possible at all.&lt;/p&gt;
&lt;div&gt;&lt;h2 id=&quot;why-subprocesses-and-not-imports&quot;&gt;Why subprocesses and not imports&lt;/h2&gt;&lt;/div&gt;
&lt;p&gt;Even in the old design the entrypoints were invoked as subprocesses, never imported, and
that was not a style preference. An imported module is frozen at load time. The case this
platform exists to measure is a harness that &lt;strong&gt;rewrites its own improvement mechanism&lt;/strong&gt;.
Importing does not complicate that case — it makes it unrepresentable.&lt;/p&gt;
&lt;div&gt;&lt;h2 id=&quot;keeping-the-boundary-measurable&quot;&gt;Keeping the boundary measurable&lt;/h2&gt;&lt;/div&gt;
&lt;p&gt;There is a real cost to moving the method out: the line between “the method” and “the
harness” stops being obvious from the file layout. The platform’s answer is to measure it
rather than assert it. Because the method and the harness both live in the candidate tree,
every curve point records &lt;code dir=&quot;auto&quot;&gt;editable_surface_touched&lt;/code&gt;, read from &lt;code dir=&quot;auto&quot;&gt;git diff&lt;/code&gt;:&lt;/p&gt;
&lt;div&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;control method (returns the base)      touched: []                score 0.00&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;a real method  (edits the harness)     touched: [&apos;agent.py&apos;]      score 1.00&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;p&gt;&lt;code dir=&quot;auto&quot;&gt;&quot;method/accept.py&quot;&lt;/code&gt; appearing in that list would mean the method rewrote its &lt;strong&gt;own
acceptance rule&lt;/strong&gt; — the line most published systems state they do not cross. This is also
why the platform has no “real RSI” mode. A declared mode would be a self-report; the
genuine/pseudo distinction is a reading of the diff, not a setting.&lt;/p&gt;
&lt;div&gt;&lt;h2 id=&quot;the-same-axis-for-both-execution-styles&quot;&gt;The same axis for both execution styles&lt;/h2&gt;&lt;/div&gt;
&lt;p&gt;Some methods already exist and cannot be re-wired just to be measured — one owns an
annealed fixed-round budget, another an archive. So there are two modes, differing only in
who owns the schedule. In &lt;strong&gt;Mode A&lt;/strong&gt; the platform owns the loop and calls the method once
per round for one step; in &lt;strong&gt;Mode B&lt;/strong&gt; the method runs its own search and returns a whole
trajectory, and the platform resolves and scores the states it names.&lt;/p&gt;
&lt;p&gt;Both write the same &lt;code dir=&quot;auto&quot;&gt;curve.jsonl&lt;/code&gt;. That is the point: a method’s internal schedule is its
own business, and the curve is the platform’s.&lt;/p&gt;
&lt;div&gt;&lt;h2 id=&quot;the-general-lesson&quot;&gt;The general lesson&lt;/h2&gt;&lt;/div&gt;
&lt;p&gt;The instrument and the thing it measures have to be separable &lt;em&gt;by construction&lt;/em&gt;, not by
convention. Every design choice above — external methods, subprocess invocation, a measured
editable surface, one curve format for both modes — follows from taking that seriously.&lt;/p&gt;
</content:encoded></item></channel></rss>