<?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: Abhishek Nandy</title>
    <description>The latest articles on DEV Community by Abhishek Nandy (@abhilegend).</description>
    <link>https://dev.to/abhilegend</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%2F1460890%2F7633225a-d1e0-45e5-a5af-b965045c1cae.jpeg</url>
      <title>DEV Community: Abhishek Nandy</title>
      <link>https://dev.to/abhilegend</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/abhilegend"/>
    <language>en</language>
    <item>
      <title>I Wanted to Be Lazy About Calculus. So I Built a Gemma Lab That Does the Experiments for Me</title>
      <dc:creator>Abhishek Nandy</dc:creator>
      <pubDate>Sun, 25 Oct 2026 22:16:11 +0000</pubDate>
      <link>https://dev.to/abhilegend/i-wanted-to-be-lazy-about-calculus-so-i-built-a-gemma-lab-that-does-the-experiments-for-me-5989</link>
      <guid>https://dev.to/abhilegend/i-wanted-to-be-lazy-about-calculus-so-i-built-a-gemma-lab-that-does-the-experiments-for-me-5989</guid>
      <description>&lt;p&gt;&lt;em&gt;This is a submission for the &lt;a href="https://dev.to/challenges/hacktoberfest-week3-2026-10-19"&gt;Hacktoberfest Open-Source AI Challenge Week 3: Slow and Steady&lt;/a&gt;&lt;/em&gt;&lt;/p&gt;

&lt;h1&gt;
  
  
  I asked Gemma to explain a vector field. It was confidently wrong. So I built FieldTrip.
&lt;/h1&gt;

&lt;p&gt;&lt;em&gt;Less setup. More aha. A local Gemma-powered vector calculus lab for the Slow and Steady challenge.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;Gemma told me a smooth vector field was singular.&lt;/p&gt;

&lt;p&gt;The animation looked convincing. The explanation was wrong.&lt;/p&gt;

&lt;p&gt;I had asked it about a rotating field with components &lt;code&gt;(-2y, 2x, 0)&lt;/code&gt;. Its explanation mixed up the curl, the integrals, and an excluded axis that this field does not have.&lt;/p&gt;

&lt;p&gt;That became the most useful failure in this project. I needed a clearer division of work: let the model interpret the question, let math tools calculate, and let me inspect both.&lt;/p&gt;

&lt;p&gt;I also wanted to stop doing the repetitive parts of exploring calculus: writing plotting code, rebuilding a scene, changing one parameter, recording the result, and doing it again.&lt;/p&gt;

&lt;p&gt;So I built &lt;strong&gt;FieldTrip&lt;/strong&gt;, a small vector calculus lab where one question can become a reviewable experiment, an interactive 3D scene, and a table of comparisons you can replay.&lt;/p&gt;

&lt;p&gt;The sloth gets to watch the arrows. The lab does the setup.&lt;/p&gt;

&lt;h2&gt;
  
  
  What I Built
&lt;/h2&gt;

&lt;p&gt;FieldTrip turns supported natural-language questions into visual investigations of vector fields.&lt;/p&gt;

&lt;p&gt;Ask:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Show a rotating field and compare loop sizes.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Local Gemma selects a field and a comparison. FieldTrip shows that selection before running it. After I accept, code supplies the formula, calculates derivatives and integrals, and builds the scene.&lt;/p&gt;

&lt;p&gt;The lab includes five starting points:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Field&lt;/th&gt;
&lt;th&gt;Question to explore&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Vortex&lt;/td&gt;
&lt;td&gt;Can something rotate without expanding?&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Source&lt;/td&gt;
&lt;td&gt;Can something expand without rotating?&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Saddle&lt;/td&gt;
&lt;td&gt;Can a shape stretch while preserving local volume?&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Gradient&lt;/td&gt;
&lt;td&gt;What does following an uphill direction look like?&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Excluded-axis vortex&lt;/td&gt;
&lt;td&gt;Why can zero curl coexist with nonzero circulation?&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;There are particles to follow, arrows to inspect, a boundary curve, a spanning surface, and controls for radius, strength, surface lift, and orientation. The math panel exposes symbolic divergence and curl. A point probe lets you inspect a location.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Explore for me&lt;/strong&gt; runs parameter comparisons. Each table row has a replay button that restores its configuration. Investigations can be exported as JSON and imported again; calculations are recomputed rather than accepted from the imported numbers.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fyfh1j1hdx4itvcwkwcfy.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fyfh1j1hdx4itvcwkwcfy.png" alt="FieldTrip running on Render, with the saddle field and its explanation." width="799" height="389"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;The public lab runs curated experiments immediately. Local Gemma inference uses the downloaded app and Ollama.&lt;/em&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Demo
&lt;/h2&gt;

&lt;p&gt;  &lt;iframe src="https://www.youtube.com/embed/aLy33ZnP9yo" width="710" height="399"&gt;
  &lt;/iframe&gt;
&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://fieldtrip-lab.onrender.com" rel="noopener noreferrer"&gt;Try the public lab on Render&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://youtu.be/aLy33ZnP9yo" rel="noopener noreferrer"&gt;Watch the video&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/abhitorch81/r9" rel="noopener noreferrer"&gt;Read the source and run it locally&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The public deployment and the local AI version have different responsibilities. &lt;strong&gt;Render hosts the interactive front end. Gemma runs on my laptop through Ollama.&lt;/strong&gt; Opening the public URL does not provision a model for the visitor.&lt;/p&gt;

&lt;h2&gt;
  
  
  How I Built It
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Give Gemma a small, useful job
&lt;/h3&gt;

&lt;p&gt;The first version gave the model too much freedom. It could produce expressions and explanations that sounded plausible while disagreeing with the calculations.&lt;/p&gt;

&lt;p&gt;The current planner returns a small structured object:&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;"field"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"vortex"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"comparison"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"radius"&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 allowed field IDs correspond to the five examples, plus an outside-scope response. The comparison is radius, surface, orientation, or none. Unknown IDs and extra properties are rejected.&lt;/p&gt;

&lt;p&gt;Gemma does not generate the preset formulas. It chooses which supported experiment fits the request.&lt;/p&gt;

&lt;p&gt;Before anything changes, a review dialog shows the selected field, comparison, formula, and defaults. I can run it or keep the current experiment.&lt;/p&gt;

&lt;p&gt;![&lt;a href="https://dev-to-uploads.s3.us-east-2.amazonaws.com/uploads/articles/hzj28ogq0ijxsl2m9i6n.png" rel="noopener noreferrer"&gt;Reviewing Gemma's selected vortex and radius comparison before execution.&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Schema validation checks the shape of the answer. The review step checks whether that answer matches what I meant.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;Explicit expressions such as &lt;code&gt;F=(x,y,z)&lt;/code&gt; take a separate path through a restricted parser. That route does not need an AI call. The interface distinguishes it from a Gemma-selected experiment.&lt;/p&gt;

&lt;h3&gt;
  
  
  Make the calculations inspectable
&lt;/h3&gt;

&lt;p&gt;The browser uses &lt;strong&gt;math.js&lt;/strong&gt; for symbolic differentiation and numerical evaluation, &lt;strong&gt;Three.js&lt;/strong&gt; for the scene, and &lt;strong&gt;GSAP&lt;/strong&gt; for interface animation. Particle paths use a fourth-order Runge–Kutta step.&lt;/p&gt;

&lt;p&gt;The expression evaluator permits a limited mathematical syntax and known symbols. It does not execute arbitrary generated JavaScript.&lt;/p&gt;

&lt;p&gt;For Stokes comparisons, the application independently samples the boundary line integral and the curl flux through a parameterized surface. The boundary is a circle in &lt;code&gt;z=0&lt;/code&gt;; the surface lift bends its interior while keeping that boundary fixed.&lt;/p&gt;

&lt;p&gt;At the default resolution, the line integral uses 128 angular samples and the surface integral uses 32 radial by 128 angular samples.&lt;/p&gt;

&lt;p&gt;The scene is an aid to inspection. Particle density is not presented as a measurement of divergence. Numerical agreement is not presented as a proof.&lt;/p&gt;

&lt;h3&gt;
  
  
  Automate the repetition
&lt;/h3&gt;

&lt;p&gt;For the vortex &lt;code&gt;F=(-y,x,0)&lt;/code&gt;, the curl is &lt;code&gt;(0,0,2)&lt;/code&gt;. Around a counterclockwise circle of radius R, circulation is &lt;code&gt;2πR²&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;FieldTrip produces this comparison:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Radius&lt;/th&gt;
&lt;th&gt;Boundary circulation&lt;/th&gt;
&lt;th&gt;Sampled curl flux&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;0.5&lt;/td&gt;
&lt;td&gt;1.5708&lt;/td&gt;
&lt;td&gt;1.5708&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;1.0&lt;/td&gt;
&lt;td&gt;6.2832&lt;/td&gt;
&lt;td&gt;6.2832&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;1.5&lt;/td&gt;
&lt;td&gt;14.1372&lt;/td&gt;
&lt;td&gt;14.1372&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;2.0&lt;/td&gt;
&lt;td&gt;25.1327&lt;/td&gt;
&lt;td&gt;25.1327&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;Doubling the radius quadruples the result. Reversing the boundary orientation and surface normal changes both signs. Lifting the interior of the spanning surface preserves the boundary and, for this smooth example, preserves the matching flux.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F28ez6syfyqpaqd9j6t8e.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F28ez6syfyqpaqd9j6t8e.png" alt="Automatically calculated radius comparisons with replay controls." width="799" height="364"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;One request becomes four computed configurations. Clicking a row brings that configuration back into the scene.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;That is the laziness I wanted: less parameter bookkeeping, more time noticing what changes.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Example That Makes Me Stop Scrolling
&lt;/h2&gt;

&lt;p&gt;The excluded-axis field is&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;F = (-y/(x²+y²), x/(x²+y²), 0).
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Away from the z-axis, its curl is zero. Yet a positively oriented circle around that axis has circulation &lt;code&gt;2π&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fikmqjdzztey4okr2b2xs.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fikmqjdzztey4okr2b2xs.png" alt="The excluded-axis field, with its missing axis and domain warning visible." width="799" height="398"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;This does not contradict Stokes' theorem. The spanning disk crosses the axis where the field is undefined, so the theorem's smoothness requirements are not satisfied.&lt;/p&gt;

&lt;p&gt;The numerical sampler can miss that axis and return a curl-flux value of zero. A finite set of successful evaluations cannot establish that the entire surface is admissible.&lt;/p&gt;

&lt;p&gt;FieldTrip therefore marks the known example &lt;strong&gt;ASSUMPTIONS FAIL&lt;/strong&gt;. The exported &lt;code&gt;invalid&lt;/code&gt; flag reports sampled evaluation failures; it is not a certificate that the domain is valid.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fngv1ohjbdkfjgh63323n.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fngv1ohjbdkfjgh63323n.png" alt="Nonzero circulation, sampled zero curl flux, and the assumptions-fail warning." width="800" height="300"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;This is a classic counterexample to an unchecked inference, not a new mathematical discovery.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;That distinction became a design rule: show the values and the conditions under which they mean something.&lt;/p&gt;

&lt;h2&gt;
  
  
  I Tested the Planner, Including the Uncomfortable Results
&lt;/h2&gt;

&lt;p&gt;I created a frozen set of &lt;strong&gt;24 questions&lt;/strong&gt;: 15 supported requests and 9 outside the lab's scope. I ran the same bounded-planner prompt and dataset against two local models.&lt;/p&gt;

&lt;p&gt;A fully correct plan requires both the expected field ID and comparison ID.&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Measure&lt;/th&gt;
&lt;th&gt;Gemma 3 1B&lt;/th&gt;
&lt;th&gt;Gemma 4 E2B&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Correct field, all 24 questions&lt;/td&gt;
&lt;td&gt;9/24&lt;/td&gt;
&lt;td&gt;16/24&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Correct field and comparison, all 24&lt;/td&gt;
&lt;td&gt;5/24&lt;/td&gt;
&lt;td&gt;14/24&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Correct field on supported requests&lt;/td&gt;
&lt;td&gt;9/15&lt;/td&gt;
&lt;td&gt;15/15&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Correct field and comparison on supported requests&lt;/td&gt;
&lt;td&gt;5/15&lt;/td&gt;
&lt;td&gt;13/15&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Correct rejection of unsupported requests&lt;/td&gt;
&lt;td&gt;0/9&lt;/td&gt;
&lt;td&gt;1/9&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;&lt;strong&gt;Gemma 4 selected every supported field correctly in this small test. It was poor at rejecting unsupported requests.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;It also made two comparison mistakes: adding a radius comparison where none was requested, and missing an orientation comparison.&lt;/p&gt;

&lt;p&gt;Those failures explain two features in the app: the review step and a conservative code-based scope gate. Known requests such as “solve the heat equation” are stopped before inference. Ambiguous requests such as “change the surface” ask for a field or behavior instead of silently replacing the current experiment.&lt;/p&gt;

&lt;p&gt;The scope gate was added &lt;strong&gt;after&lt;/strong&gt; these measurements. Its behavior is not an improvement in the model's rejection score. Its phrase rules are incomplete and can overblock wording; they are a boundary for this small lab, not a general intent detector.&lt;/p&gt;

&lt;p&gt;Earlier formula-translation tests also informed the redesign. On a separate 30-question development set, the small model-only approach passed 2/30. A hybrid route passed 27/30, but all 27 successes came from the direct parser. Calling that “90% AI accuracy” would misrepresent what happened.&lt;/p&gt;

&lt;p&gt;These are author-created development tests, not an independent benchmark or an end-to-end accuracy claim. The repository includes &lt;a href="https://github.com/abhitorch81/r9/tree/main/evaluation" rel="noopener noreferrer"&gt;questions, runners, raw responses, timings, and results&lt;/a&gt;. The bounded runs record matching dataset and prompt hashes so the comparison can be inspected.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why Open Innovation Matters
&lt;/h2&gt;

&lt;p&gt;I built and tested the local version on a Windows laptop with an Intel i7-8565U, 16 GB RAM, and integrated graphics.&lt;/p&gt;

&lt;p&gt;With the model and app dependencies downloaded, the local workflow can run without an internet connection. I tested an offline investigation and exported its results. Prompts sent through the local Ollama configuration stay on the laptop; a cloud model key is not required.&lt;/p&gt;

&lt;p&gt;Local inference still consumes hardware resources and electricity. The benefit is &lt;strong&gt;no per-request cloud inference fee&lt;/strong&gt;, rather than a claim that computation is free.&lt;/p&gt;

&lt;p&gt;Gemma's open weights let me compare models on my own task and keep the inference under my control. The source lets someone change the planner, inspect the formulas, replace a model adapter, or challenge a calculation without waiting for a service provider to expose those decisions.&lt;/p&gt;

&lt;p&gt;The original application code is MIT-licensed. Gemma's weights and the third-party libraries retain their own licenses and terms.&lt;/p&gt;

&lt;p&gt;Open innovation mattered most when the first approach failed: I could inspect the raw outputs, narrow the model's responsibility, and keep the useful parts of the workflow.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Lazy Payoff—and the Limits
&lt;/h2&gt;

&lt;p&gt;FieldTrip saves the repetitive setup of a supported investigation: selecting an example, creating its scene, calculating operators, running comparisons, and collecting configurations for replay.&lt;/p&gt;

&lt;p&gt;I have not measured learning gains or a manual-versus-automated time saving, so I am not claiming either.&lt;/p&gt;

&lt;p&gt;It does not solve arbitrary vector calculus problems, PDEs, quantum equations, or arbitrary surface geometries. It does not prove theorems. Custom non-polynomial domains still need review. Local model requests can take several seconds, and a valid plan can still be the wrong interpretation.&lt;/p&gt;

&lt;p&gt;The useful outcome is smaller and concrete: &lt;strong&gt;ask for an experiment, review the setup, and spend your attention exploring it.&lt;/strong&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Run It Yourself
&lt;/h2&gt;

&lt;p&gt;For the public curated lab, &lt;a href="https://fieldtrip-lab.onrender.com" rel="noopener noreferrer"&gt;open FieldTrip on Render&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;For local AI, install Ollama and Python, download the &lt;a href="https://github.com/abhitorch81/r9" rel="noopener noreferrer"&gt;repository&lt;/a&gt;, then pull the model:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;ollama pull gemma4:e2b
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Run &lt;code&gt;start-windows.cmd&lt;/code&gt;, open &lt;code&gt;http://localhost:8765&lt;/code&gt;, and select &lt;code&gt;gemma4:e2b&lt;/code&gt; in the Gemma settings. Use the local &lt;code&gt;/api/generate&lt;/code&gt; proxy described in the README. A successful settings save alone does not demonstrate inference; submit a supported question and inspect the plan review.&lt;/p&gt;

&lt;p&gt;Try this first:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Show a rotating field and compare loop sizes.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Then try the loophole. The missing axis is the part worth slowing down for.&lt;/p&gt;

&lt;h2&gt;
  
  
  Prize Categories
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Best Use of Gemma:&lt;/strong&gt; local, structured experiment planning with measured model comparisons and a review step.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Best Use of Render:&lt;/strong&gt; hosting the public interactive lab so visitors can explore curated experiments without installing the local AI stack.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Built for &lt;strong&gt;Slow and Steady&lt;/strong&gt;: let the lab handle the repetition, and keep the curiosity for yourself.&lt;/p&gt;

</description>
      <category>devchallenge</category>
      <category>hf26challenge</category>
    </item>
    <item>
      <title>Overturn: the denials small clinics stopped fighting</title>
      <dc:creator>Abhishek Nandy</dc:creator>
      <pubDate>Sun, 18 Oct 2026 14:38:46 +0000</pubDate>
      <link>https://dev.to/abhilegend/overturn-the-denials-small-clinics-stopped-fighting-5ano</link>
      <guid>https://dev.to/abhilegend/overturn-the-denials-small-clinics-stopped-fighting-5ano</guid>
      <description>&lt;p&gt;&lt;em&gt;This is a submission for the &lt;a href="https://dev.to/challenges/hacktoberfest-week2-2026-10-12"&gt;Hacktoberfest Open-Source AI Challenge Week 2: Side Hustle&lt;/a&gt;&lt;/em&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  What I Built
&lt;/h2&gt;

&lt;p&gt;Every small clinic has a drawer of denied claims it has quietly given up on. Each one is real money, often a few thousand dollars. Each one also takes a billing person about twenty minutes to read the denial, decode the code, check the chart, and write an appeal. At a few hundred denials a month, most of them get written off, because the work costs more than the expected return.&lt;/p&gt;

&lt;p&gt;Overturn changes that arithmetic. It is an appeal agent that reads a denial notice, decodes it, estimates the chance of winning, compares three possible moves, drafts the letter, and checks the draft against the notice. The billing person sees a ranked queue, so the valuable denials come first.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Who would pay for it:&lt;/strong&gt; a clinic, or a billing service that works denials for several clinics. The question the app answers is "which of these denials is worth a staff member's time," and that question has a dollar answer.&lt;/p&gt;

&lt;h2&gt;
  
  
  Demo
&lt;/h2&gt;

&lt;p&gt;Two recordings, one from the laptop and one from the live Render deployment:&lt;/p&gt;

&lt;p&gt;  &lt;iframe src="https://www.youtube.com/embed/pG0gvUQZM5k" width="710" height="399"&gt;
  &lt;/iframe&gt;
&lt;/p&gt;

&lt;p&gt;  &lt;iframe src="https://www.youtube.com/embed/F03QBMt9ziA" width="710" height="399"&gt;
  &lt;/iframe&gt;
&lt;/p&gt;

&lt;p&gt;The laptop run uses a 7B model and the local retrieval index. The Render run uses a 3B model on a CPU instance. The differences matter, and I explain them below.&lt;/p&gt;

&lt;h2&gt;
  
  
  Code
&lt;/h2&gt;

&lt;p&gt;The code is on the &lt;code&gt;master&lt;/code&gt; branch: &lt;a href="https://github.com/abhitorch81/week2hoct/tree/master" rel="noopener noreferrer"&gt;https://github.com/abhitorch81/week2hoct/tree/master&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The repo contains the Python app, the playbook of the twelve most common denial codes, the test suite, the Mastra agent, the Tinker training pipeline, and the Render blueprint.&lt;/p&gt;

&lt;h2&gt;
  
  
  How I Built It
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Live on Render
&lt;/h3&gt;

&lt;p&gt;The web app runs on Render as a web service, with a separate worker service that runs the Temporal workflows. The screenshots below come from the live deployment.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The deploy pipeline.&lt;/strong&gt; The service is connected to the GitHub repo on the &lt;code&gt;master&lt;/code&gt; branch and deploys automatically on every push. The deploy history shows six deploys, the latest of which was an automatic deploy that took about 47 seconds. Two earlier deploys switched the default model to &lt;code&gt;qwen2.5:3b-instruct&lt;/code&gt;, one through the blueprint for the 4 GB instance and one by hand. Rollback is one click on any earlier deploy.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fvz86mhl39lxn5ect5f52.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fvz86mhl39lxn5ect5f52.png" alt="The Render deploy history for the web service, with auto-deploy from master" width="800" height="450"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The app in production.&lt;/strong&gt; The header tells you the state of the system at a glance. The model is &lt;code&gt;qwen2.5:3b-instruct&lt;/code&gt; running on Ollama, marked REMOTE because it runs on a private service inside Render's network. The odds model is scikit-learn with an AUC of 0.77. Retrieval is marked &lt;code&gt;memory&lt;/code&gt; with 18 wins, not pgvector, because the Render deployment has no database URL configured and falls back to an in-memory index with the same scoring. The runner is Temporal. The external-call counter reads 0.&lt;/p&gt;

&lt;p&gt;The page also shows the workflow the app is running. Each card is one step of the pipeline, with its own timing. Step 7, drafting the appeal, took about 103 seconds, and every other step took under 20 milliseconds. That single number explains most of the design: the model is the only slow part, so the model is the only part that needs careful budgeting.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/article_images%2F09_render_app_trace.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/article_images%2F09_render_app_trace.png" alt="The Render app running an appeal as a Temporal workflow, with per-step timings and the agent trace" width="800" height="400"&gt;&lt;/a&gt;&lt;br&gt;
The Render deploy history for the web service, with auto-deploy from master&lt;/p&gt;

&lt;p&gt;The Trace and cost panel gives the same story in numbers. End-to-end time was 103,671 ms, with 1,587 LLM tokens. The cost line shows $0.00 for local compute and an illustrative $0.0079 at a $5 per million token closed API price. The "0 / 0" for external and total model calls needs explaining, and I come back to it in the honest-limits section.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The Temporal worker.&lt;/strong&gt; Each run is a Temporal workflow, and each pipeline step is an activity. The worker process registers on the &lt;code&gt;overturn&lt;/code&gt; task queue, and the screenshot shows the worker's health: a recent heartbeat, the Python SDK version, 474 workflow tasks processed, and a workflow cache hit rate of 100 percent. A cache hit means the worker did not have to replay the workflow history to continue it, which is what keeps long runs cheap.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fv7urmdx5pc2fggxukyf5.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fv7urmdx5pc2fggxukyf5.png" alt="The Temporal Cloud worker page for the overturn task queue" width="800" height="450"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The event history.&lt;/strong&gt; This is the screenshot I find most useful for explaining durability. The history lists every scheduled and completed activity. You can see the &lt;code&gt;run_step&lt;/code&gt; activity scheduled, started, and completed, followed by a workflow task that records the result. If the worker died between two of these events, Temporal would replay the history on a new worker and continue from the last completed step. The completed step's result is stored in the history, so the model does not run again for finished work.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fevqc4l4blf3j7nitkyyc.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fevqc4l4blf3j7nitkyyc.png" alt="The Temporal event history for appeal-6f2215de, showing scheduled, started, and completed activities" width="800" height="450"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  How the Decision Works
&lt;/h3&gt;

&lt;p&gt;The most useful thing Overturn does is refuse to write letters for claims that are not worth fighting. The math is simple enough to check by hand.&lt;/p&gt;

&lt;p&gt;For each candidate move, the app computes an expected value:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;expected value = win probability × denied amount − staff cost&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;For CLM-26009, a BCBS claim denied with CO-50 ("not medically necessary"), the denied amount is $6,286.64 and the win probability for an appeal is about 61.25 percent. The expected value is 0.6125 × $6,286.64 − $85 ≈ $3,765, which matches the $3,765 shown in the queue and the agent's answer. The app compares that with a resubmission, which has its own recovery odds and a $22 staff cost, and with a write-off, which has zero recovery and zero cost. The highest value wins.&lt;/p&gt;

&lt;p&gt;This is a deliberately simple model. It treats the win probability as the main unknown, and it uses staff time as the only cost. A real practice would add appeal deadlines, the payer's history, and the value of the staff's time elsewhere. Those are inputs the clinic can change, which is why they are environment variables rather than constants.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fyeddzkxv1h9gtsyn273h.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fyeddzkxv1h9gtsyn273h.png" alt="The three moves compared by expected value" width="799" height="297"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  What the Model Does, and What It Does Not
&lt;/h3&gt;

&lt;p&gt;The model writes one thing: the argument section of the appeal. Everything else in the letter, including the letterhead, the claim block, the specific request, and the list of enclosures, is built from the notice's facts by code. That split is the core safety idea. A model can invent a sentence, but it cannot invent a claim number if the claim number never passes through the model.&lt;/p&gt;

&lt;p&gt;The fact-checker enforces this. It compares every claim ID, CPT code, denial code, and amount in the draft against the source notice. A failed draft gets one corrective retry. If the second attempt also fails, the app falls back to a template letter and shows you the rejected draft, so you can see what went wrong.&lt;/p&gt;

&lt;p&gt;I tested the checker with a fake model that invents codes on purpose. It rejected every bad draft. That test is &lt;code&gt;test_hallucinated_llm_draft_is_rejected&lt;/code&gt; in the repo.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fwvuqt9alb5c8stq8lepj.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fwvuqt9alb5c8stq8lepj.png" alt="The pipeline from notice to letter, with the fact-check retry loop" width="800" height="1633"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Retrieval: Learning From Past Wins
&lt;/h3&gt;

&lt;p&gt;Overturn searches past wins for similar denials. The search has two parts. Keyword search (BM25) finds exact matches on codes and payer names, which vector search often misses. Vector search finds paraphrases of similar arguments, which keyword search misses. The two result lists are merged with reciprocal rank fusion, and matches that share the denial code and payer get a boost.&lt;/p&gt;

&lt;p&gt;On the laptop, the index lives in Postgres with pgvector on Tiger Cloud, and it currently holds 19 documents. On Render, the same scoring runs over an in-memory index with 18 wins. The retrieval code path is the same, but the storage differs. I want to be clear about that, because a reader who sees "pgvector" in the laptop header and "memory" on Render should not assume they are the same deployment.&lt;/p&gt;

&lt;h3&gt;
  
  
  Durability: Why the Workflow Is Built This Way
&lt;/h3&gt;

&lt;p&gt;A denial pipeline runs for minutes, calls a model that is occasionally slow, and writes to a database. Any of those can fail partway through. A plain function would lose its progress. Overturn runs the same eight steps in two ways: a local durable runner that checkpoints each step, and Temporal, which records each step in an event history on the server.&lt;/p&gt;

&lt;p&gt;The benefit is concrete. In the Render run, the drafting step took 103 seconds. If the worker had restarted during that step, Temporal would have retried the activity with backoff, and finished steps would not have run again. The local runner does the same thing with a checkpoint file. I have tested the local runner and the Temporal activity logic. The kill-and-resume demo on Temporal is the one piece I have not yet recorded from start to finish.&lt;/p&gt;

&lt;h3&gt;
  
  
  Fine-Tuning on Tinker
&lt;/h3&gt;

&lt;p&gt;The task is narrow, which makes it a good candidate for a small fine-tuned model. The pipeline builds 300 training pairs. Each pair is the exact prompt the app sends the model, plus a target argument: the playbook argument for that denial code, followed by two sentences from a past winning appeal for the same code. A pair is kept only if its assembled letter passes the fact-checker and the argument stays under 170 words.&lt;/p&gt;

&lt;p&gt;Training uses a LoRA adapter on Qwen3.5-4B, rank 16, with 3 epochs in batches of 8. That is 114 optimizer steps at a learning rate of 1e-4. The loss is computed only on the completion tokens, so the model learns to write the argument and does not learn to reproduce the prompt. The training run finished and printed a &lt;code&gt;tinker://&lt;/code&gt; sampler path, which I set as &lt;code&gt;TINKER_MODEL_PATH&lt;/code&gt;. The Model arena then lists the base model and the tuned model next to the local model.&lt;/p&gt;

&lt;p&gt;What the tuning can teach is limited. The targets are synthetic, so the tune mostly teaches format and house style: short paragraphs, no letterhead, and placeholders where facts are missing. It cannot discover new legal arguments. A real clinic would replace the targets with the arguments of appeals that actually won, and the app's "Mark as won" button saves exactly that text.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fujui9anxok8lmuqerlnz.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fujui9anxok8lmuqerlnz.png" alt="The fine-tuning loop and the agent" width="800" height="262"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Model Comparison in the Arena
&lt;/h3&gt;

&lt;p&gt;The Model arena runs the same synthetic notices through a local baseline and through hosted open-weight models on Backboard. It ranks each model by first-draft pass rate: how often a draft passes the fact-checker with no retry. That is a narrow measure, but it is the one that matters most for this app, because a draft that needs a retry costs time, and a draft that fails costs the clinic a letter.&lt;/p&gt;

&lt;p&gt;Small samples on synthetic data are noisy, and the arena says so on screen. Treat it as a smoke test, not a benchmark.&lt;/p&gt;

&lt;h3&gt;
  
  
  An Agent on Top of the API
&lt;/h3&gt;

&lt;p&gt;Overturn also has a conversational front end, built with Mastra. The Denial Desk agent uses an open model with memory and two tools: one lists the queue, and one analyzes a claim. It reaches Overturn over HTTP and never sees the raw notice.&lt;/p&gt;

&lt;p&gt;In one run, I asked the agent what to work on first. It called the queue tool and returned the top claim, CLM-26009. Then I asked it to analyze that claim. It called the analysis tool, which returned an overturn probability of 0.612, an expected value of $3,765.26, and appeal as the best move. The agent's answer matched the app's dashboard.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F41kvsgilr0ixj212xmbw.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F41kvsgilr0ixj212xmbw.png" alt="Mastra Studio: the agent calls the queue and analysis tools" width="800" height="450"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;tching the app's numbers](&lt;a href="https://dev-to-uploads.s3.us-east-2.amazonaws.com/uploads/articles/rp6pg1wgstp37yq7d39c.png" rel="noopener noreferrer"&gt;https://dev-to-uploads.s3.us-east-2.amazonaws.com/uploads/articles/rp6pg1wgstp37yq7d39c.png&lt;/a&gt;)&lt;/p&gt;

&lt;p&gt;The agent runs against the local app on port 8000. Pointing it at the Render URL is a one-line change, and I have not yet recorded that version.&lt;/p&gt;

&lt;h3&gt;
  
  
  Lessons From Putting This on Render
&lt;/h3&gt;

&lt;p&gt;Three things took longer than I expected, and they might save you time.&lt;/p&gt;

&lt;p&gt;First, the default branch matters. My GitHub repository's default branch was an initial commit with only a license, and the code lived on &lt;code&gt;master&lt;/code&gt;. Render deployed from &lt;code&gt;master&lt;/code&gt; correctly, but the repository page showed no README, which made the project look empty to anyone who visited. Setting the default branch fixed that in one step.&lt;/p&gt;

&lt;p&gt;Second, deploy the model choice, not just the code. The Render instance is too small for the 7B model, and the switch to 3B was made for the 4 GB instance. The header's model name is the first thing to check after a deploy.&lt;/p&gt;

&lt;p&gt;Third, the observability tools tell you different things. The Render event log shows whether the service is up. The Temporal event history shows whether each step ran. The app's trace shows where the time went. None of them alone tells you whether the answer is right, which is why the fact-checker exists.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why Does Open Innovation Matter?
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Why Open Models Matter Here
&lt;/h3&gt;

&lt;p&gt;Privacy is the first reason. A denial notice carries a patient name, a member ID, and a diagnosis. A local model needs no business associate agreement with an API vendor, and there is nothing to leak. That claim holds only if the data-location map above holds, which is why I keep returning to it.&lt;/p&gt;

&lt;p&gt;Cost is the second. Per-token pricing on every notice, retry, and redraft erodes the margin on a claim worth a few hundred dollars. A local model is a fixed cost, and that changes which denials are worth working. [PLACEHOLDER: tokens and latency per draft, measured from the trace]&lt;/p&gt;

&lt;p&gt;Control is the third. Open weights let a clinic swap the model with one environment variable, fine-tune on its own winning appeals, and compare models on its own data. A closed API would turn each of those into a vendor conversation.&lt;/p&gt;

&lt;h3&gt;
  
  
  Where the Data Goes
&lt;/h3&gt;

&lt;p&gt;This section is the one I would most like readers to check, so here is the full map.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fsmp5a18xrw18osdwf50q.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fsmp5a18xrw18osdwf50q.png" alt="Which paths stay on the machine and which leave it" width="799" height="297"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;On the laptop, the real notice goes to the local model on Ollama and the local Temporal dev server. The retrieval index is on Tiger Cloud, and I still need to confirm exactly what it stores. If it holds notice text, patient data leaves the machine, and the privacy claim in this article would be wrong for that path. I am checking that before I publish.&lt;/p&gt;

&lt;p&gt;On Render, the web service and the worker talk to a private Ollama service inside Render's network. Render's private networking counts as local in my counter, by design, which is why the header reads 0 external calls. That is a definition I chose, and a reader may disagree with it. For a deployment that is exposed to the public internet, the honest label is "private cloud," not "local."&lt;/p&gt;

&lt;p&gt;The Model arena and the Tinker sampler are cloud paths. The arena accepts only synthetic notices, and the app warns before it runs. The header counter does not yet include these calls, so it does not prove the claim for every path. I am fixing that before I say it.&lt;/p&gt;

&lt;h3&gt;
  
  
  Honest Limits
&lt;/h3&gt;

&lt;p&gt;The data is synthetic. The odds model is only as good as a clinic's history, and a new clinic has little. Drafts are drafts, not legal advice. Appeal windows and criteria vary by payer and plan, so the notice has the final word.&lt;/p&gt;

&lt;p&gt;The Render deployment is not the same system as the laptop. It uses a smaller model, a different retrieval backend, and a 103-second drafting step on CPU. A demo that shows both should say so.&lt;/p&gt;

&lt;p&gt;The external-call counter is the weakest part of the privacy story. It counts model calls, it treats Render's private network as local, and it does not yet cover the Tinker and Backboard paths. Fixing that is the first item on my list before I make the claim without qualification.&lt;/p&gt;

&lt;p&gt;The Temporal kill-and-resume demo has not been recorded end to end, and the Render deployment has not been load-tested. I have marked these rather than let the demo suggest more was tested than was.&lt;/p&gt;

&lt;h3&gt;
  
  
  What Next
&lt;/h3&gt;

&lt;p&gt;Real denials from a real clinic, under a proper data agreement, is the test that matters. I would also like the data-location check to be automatic, so the privacy claim is enforced in code instead of described in prose.&lt;/p&gt;

&lt;p&gt;If you work in revenue cycle, billing, or health tech, the code runs on a laptop with Ollama, and the Render deployment is live. I would like to hear where the playbook is wrong, which denial codes show up most for you, and where the drafts fall short.&lt;/p&gt;

&lt;p&gt;Repository: &lt;a href="https://github.com/abhitorch81/week2hoct/tree/master" rel="noopener noreferrer"&gt;https://github.com/abhitorch81/week2hoct/tree/master&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  My Agent Session
&lt;/h2&gt;

&lt;h2&gt;
  
  
  My First User
&lt;/h2&gt;

&lt;p&gt;The idea came from my friend Manoj Pareek, who works in this business. I gave him the app, and he used it. He was happy with it, and as a thank-you he bought me a KFC treat. That was my first earning from Overturn. It was a gift, not a license fee, so I am not counting it as revenue. It is the first real user signal I have, and I am treating it that way.&lt;/p&gt;

&lt;h2&gt;
  
  
  Prize Categories
&lt;/h2&gt;

&lt;p&gt;I am entering these categories, because the project uses each technology in a working code path:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Render&lt;/strong&gt; (Featured): the web service, the worker, and the private Ollama service run on Render from the &lt;code&gt;master&lt;/code&gt; branch.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Tinker&lt;/strong&gt; (Featured): the LoRA fine-tuning pipeline, trained and wired into the arena.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Backboard&lt;/strong&gt; (Partner): the hosted-model arena.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Temporal&lt;/strong&gt; (Partner): the durable workflow runner, on Temporal Cloud and locally.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Tiger Data&lt;/strong&gt; (Partner): pgvector retrieval on the laptop. The Render deployment does not use it yet.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Mastra&lt;/strong&gt; (Partner): the Denial Desk agent, which calls the Overturn API through two tools.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;I did not enter TabPFN, because the odds model falls back to scikit-learn in my current runs. I did not enter Entire, because I have not enabled it on a real session. Sentry is in the code but off.&lt;/p&gt;

</description>
      <category>devchallenge</category>
      <category>hf26challenge</category>
      <category>ai</category>
      <category>python</category>
    </item>
    <item>
      <title>"The Gene Went Up. Could AI Tell What We Actually Know?"</title>
      <dc:creator>Abhishek Nandy</dc:creator>
      <pubDate>Sat, 10 Oct 2026 20:18:19 +0000</pubDate>
      <link>https://dev.to/abhilegend/the-gene-went-up-could-ai-tell-what-we-actually-know-4ljj</link>
      <guid>https://dev.to/abhilegend/the-gene-went-up-could-ai-tell-what-we-actually-know-4ljj</guid>
      <description>&lt;p&gt;&lt;em&gt;This is a submission for the &lt;a href="https://dev.to/challenges/kaggle-2026-09-23"&gt;Kaggle Benchmarking Challenge&lt;/a&gt;.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;A gene can increase in one cell type and decrease in another. Ask whether its average expression increased, and the answer depends on which mixture of cells you mean.&lt;/p&gt;

&lt;p&gt;There is a subtler question: &lt;strong&gt;can we know that expression increased without knowing its exact percentage increase?&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Yes. And a model should be able to say both things at once.&lt;/p&gt;

&lt;p&gt;I built &lt;strong&gt;CellCaution&lt;/strong&gt; to test that boundary. In a small, controlled study, Gemma answered every primary case correctly. Verification instructions gave GPT-5.4 mini more correct verdicts but fewer correct complete answers. Claude's strict score looked disastrous until I separated response-format compliance from the mathematical content.&lt;/p&gt;

&lt;p&gt;Those findings changed how I would evaluate an AI assistant for scientific analysis: the verdict, the number, and the output contract each need their own evidence.&lt;/p&gt;

&lt;h2&gt;
  
  
  What I Benchmarked
&lt;/h2&gt;

&lt;p&gt;My interest comes from working on AI for single-cell RNA sequencing. Before asking a model to interpret a complicated biological study, I wanted to know whether it could handle a small descriptive comparison whose answer I could calculate exactly.&lt;/p&gt;

&lt;p&gt;Consider these synthetic expression measurements:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Cell type&lt;/th&gt;
&lt;th&gt;Control mean&lt;/th&gt;
&lt;th&gt;Treated mean&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;A&lt;/td&gt;
&lt;td&gt;17&lt;/td&gt;
&lt;td&gt;22&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;B&lt;/td&gt;
&lt;td&gt;53&lt;/td&gt;
&lt;td&gt;50&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;Type A increased by 5 units. Type B decreased by 3. We standardize both groups to the &lt;strong&gt;same reference composition&lt;/strong&gt;, so the comparison uses the same weights on both sides.&lt;/p&gt;

&lt;p&gt;At 80% A and 20% B, the control mean is 24.2 and the treated mean is 27.6. The increase is approximately &lt;strong&gt;14.05%&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;Now change only what we know about the reference population:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Information supplied&lt;/th&gt;
&lt;th&gt;Is a strict increase supported?&lt;/th&gt;
&lt;th&gt;Unique percentage&lt;/th&gt;
&lt;th&gt;Possible percentage range&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Exactly 80% A&lt;/td&gt;
&lt;td&gt;Yes&lt;/td&gt;
&lt;td&gt;14.05%&lt;/td&gt;
&lt;td&gt;14.05% to 14.05%&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;A is between 65% and 85%&lt;/td&gt;
&lt;td&gt;Yes&lt;/td&gt;
&lt;td&gt;&lt;code&gt;null&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;7.43% to 16.96%&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Any composition is allowed&lt;/td&gt;
&lt;td&gt;Insufficient information&lt;/td&gt;
&lt;td&gt;&lt;code&gt;null&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;−5.66% to 29.41%&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;In the middle row, every allowed reference gives an increase. But the constraints do not identify one percentage. Returning &lt;code&gt;null&lt;/code&gt; is the mathematically correct answer to the magnitude question, even though the direction is known.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Figure 1. These are final v2 measurements, not an example drawn from the earlier pilot.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fzl033695f84czznn9bds.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fzl033695f84czznn9bds.png" alt=" " width="800" height="483"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;That is the specific behavior I wanted to measure: &lt;strong&gt;preserving what is known while refusing to manufacture what is not.&lt;/strong&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  A benchmark with exact answer keys
&lt;/h3&gt;

&lt;p&gt;The primary dataset contains &lt;strong&gt;24 cases&lt;/strong&gt;: eight templates, each presented under fixed, constrained, and unrestricted reference compositions. The templates cover two or three cell types and four expression patterns: mixed changes, additive increases, proportional changes, and no change.&lt;/p&gt;

&lt;p&gt;Each response must provide four JSON fields:&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;"verdict"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"supported"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"standardized_change_percent"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;null&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"percentage_range"&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="mf"&gt;7.4324324324&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mf"&gt;16.9642857143&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"explanation"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Every allowed reference gives an increase, but the percentage varies."&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 verdict is &lt;code&gt;supported&lt;/code&gt; when the treated mean is strictly higher at every allowed reference, &lt;code&gt;unsupported&lt;/code&gt; when it is higher at none, and &lt;code&gt;insufficient_information&lt;/code&gt; when the answer depends on the reference.&lt;/p&gt;

&lt;p&gt;The &lt;strong&gt;primary score requires both the correct verdict and the correct percentage or &lt;code&gt;null&lt;/code&gt;&lt;/strong&gt;. Range accuracy is measured separately. Numerical answers use a tolerance of 0.05 percentage points. The explanation is retained for inspection, but an LLM judge does not score its prose.&lt;/p&gt;

&lt;p&gt;The answer keys use rational arithmetic. Allowed populations are every convex mixture of the supplied reference vertices. Differences are linear in the weights. With positive control means, the percentage ratio at any mixture is a weighted average of the vertex ratios, with weights proportional to each vertex's control mean. Its extrema therefore occur at vertices. This gives exact bounds without relying on a sampled grid or another model's judgment.&lt;/p&gt;

&lt;h2&gt;
  
  
  Models Tested
&lt;/h2&gt;

&lt;p&gt;I used three models available through the Kaggle Benchmarks registry:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Model&lt;/th&gt;
&lt;th&gt;Requested registry ID&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Gemma 4 31B&lt;/td&gt;
&lt;td&gt;&lt;code&gt;google/gemma-4-31b&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;GPT-5.4 mini&lt;/td&gt;
&lt;td&gt;&lt;code&gt;openai/gpt-5.4-mini-2026-03-17&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Claude Sonnet 4.6&lt;/td&gt;
&lt;td&gt;&lt;code&gt;anthropic/claude-sonnet-4-6@default&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;This was a manageable lineup spanning three model families, including Gemma and two hosted assistants. It is a comparison of these requested models on this task, rather than a claim about their providers as a whole.&lt;/p&gt;

&lt;p&gt;For each model I recorded 24 direct responses, 24 responses with explicit verification instructions, and eight reordered controls: &lt;strong&gt;168 included case evaluations&lt;/strong&gt;. The verification prompt asks the model to calculate weighted means at each vertex and check signs, extrema, and uniqueness before returning its answer.&lt;/p&gt;

&lt;p&gt;I used SDK defaults rather than a shared, explicitly controlled temperature or reasoning budget. The exported IDs identify the requested models; they do not independently verify the exact backend revision served.&lt;/p&gt;

&lt;h2&gt;
  
  
  Findings
&lt;/h2&gt;

&lt;h3&gt;
  
  
  1. A score must say what it counts
&lt;/h3&gt;

&lt;p&gt;Here are the direct-prompt results under the frozen output contract. Invalid outputs count as incorrect in these totals.&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Model&lt;/th&gt;
&lt;th&gt;Valid output / 24&lt;/th&gt;
&lt;th&gt;Verdict correct / 24&lt;/th&gt;
&lt;th&gt;Percentage or null correct / 24&lt;/th&gt;
&lt;th&gt;Range correct / 24&lt;/th&gt;
&lt;th&gt;Primary correct / 24&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Gemma 4 31B&lt;/td&gt;
&lt;td&gt;24&lt;/td&gt;
&lt;td&gt;24&lt;/td&gt;
&lt;td&gt;24&lt;/td&gt;
&lt;td&gt;24&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;24&lt;/strong&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;GPT-5.4 mini&lt;/td&gt;
&lt;td&gt;24&lt;/td&gt;
&lt;td&gt;18&lt;/td&gt;
&lt;td&gt;16&lt;/td&gt;
&lt;td&gt;14&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;12&lt;/strong&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Claude Sonnet 4.6&lt;/td&gt;
&lt;td&gt;1&lt;/td&gt;
&lt;td&gt;1&lt;/td&gt;
&lt;td&gt;1&lt;/td&gt;
&lt;td&gt;1&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;1&lt;/strong&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;Gemma was consistently correct on this small dataset. That is a useful result, but 24 related synthetic cases do not establish broad scientific reliability.&lt;/p&gt;

&lt;p&gt;Claude's result needs a different explanation. In 23 of its 24 direct responses, the frozen parser rejected the response format. Extra text around a JSON block violates the requested contract. A whole JSON object or a single enclosing fence is accepted; surrounding commentary is not.&lt;/p&gt;

&lt;p&gt;I kept that strict score and added a &lt;strong&gt;post-hoc diagnostic&lt;/strong&gt;, applied uniformly across models: for an invalid response containing exactly one fenced JSON block, extract that block and run the unchanged grader. No values are repaired, and no additional model calls are made.&lt;/p&gt;

&lt;p&gt;Under this diagnostic, Claude's direct primary result becomes &lt;strong&gt;23/24&lt;/strong&gt;. Its remaining error is mathematical: in the fixed-composition example, it returned about 22.40% instead of 14.05%, consistent with averaging cell-type percentage changes rather than comparing the weighted means.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Figure 2. The extraction diagnostic does not replace the frozen strict score.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F0kvhb7bjbk9ozva0mncg.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F0kvhb7bjbk9ozva0mncg.png" alt=" " width="800" height="452"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The practical lesson is to report both layers. A downstream program needs a usable response. A scientific evaluation also needs to distinguish an unusable response from incorrect mathematical content.&lt;/p&gt;

&lt;h3&gt;
  
  
  2. Verification helped one component and hurt another
&lt;/h3&gt;

&lt;p&gt;The verification instructions produced this primary-score comparison:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Model&lt;/th&gt;
&lt;th&gt;Direct / 24&lt;/th&gt;
&lt;th&gt;Verification / 24&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Gemma 4 31B&lt;/td&gt;
&lt;td&gt;24&lt;/td&gt;
&lt;td&gt;24&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;GPT-5.4 mini&lt;/td&gt;
&lt;td&gt;12&lt;/td&gt;
&lt;td&gt;9&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Claude Sonnet 4.6&lt;/td&gt;
&lt;td&gt;1&lt;/td&gt;
&lt;td&gt;0&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;For GPT, verdict accuracy increased from &lt;strong&gt;18 to 21&lt;/strong&gt;, while percentage-or-null accuracy fell from &lt;strong&gt;16 to 10&lt;/strong&gt;. The combined primary score fell from &lt;strong&gt;12 to 9&lt;/strong&gt;. Range accuracy remained 14/24.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Figure 3. One recorded generation per case and prompt arm; this comparison needs replication.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fnqjrgikbh88vjxzywr2g.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fnqjrgikbh88vjxzywr2g.png" alt=" " width="800" height="468"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The instruction to verify an answer is not itself evidence of improvement. Here it helped the qualitative decision while the numerical contract became less reliable. A verdict-only benchmark would have missed that tradeoff.&lt;/p&gt;

&lt;p&gt;Claude's verification responses were all invalid under the strict contract; all 24 passed the separately reported extraction diagnostic. Again, the strict score alone cannot explain the failure mechanism.&lt;/p&gt;

&lt;h3&gt;
  
  
  3. Reordering the evidence exposed another instability
&lt;/h3&gt;

&lt;p&gt;For eight constrained cases, I reversed the cell-type rows and every corresponding weight. The mathematical problem and gold answer stay the same.&lt;/p&gt;

&lt;p&gt;Gemma preserved its verdict, percentage/null, and range across all &lt;strong&gt;8/8&lt;/strong&gt; pairs. GPT preserved all three across &lt;strong&gt;3/8&lt;/strong&gt; pairs. Claude also matched across 8/8 under the extraction diagnostic; its reordered strict outputs were invalid, so strict agreement is not assessable.&lt;/p&gt;

&lt;p&gt;This is evidence of observed answer instability under equivalent presentations. With one generation per presentation, I cannot separate an ordering effect from ordinary generation variability. The next experiment should repeat both orders with controlled settings.&lt;/p&gt;

&lt;h3&gt;
  
  
  What I had to fix in the benchmark itself
&lt;/h3&gt;

&lt;p&gt;The first control exports failed the audit: they contained primary-case IDs and verification prompts rather than the intended reordered controls. The execution path had reused results across task contexts.&lt;/p&gt;

&lt;p&gt;I excluded those exports and reran the controls under a separate task identity. The included recovery files contain the intended eight control IDs and exact prompts for each model. I then checked all 168 included records against the fixtures and recomputed their strict grades from the raw responses.&lt;/p&gt;

&lt;p&gt;That repair matters. An evaluator that checks only whether a run finished can quietly score the wrong experiment.&lt;/p&gt;

&lt;h3&gt;
  
  
  What changed my thinking
&lt;/h3&gt;

&lt;p&gt;I started by testing whether models could get a biological comparison right. I ended up caring just as much about whether they could represent the boundary of the available evidence.&lt;/p&gt;

&lt;p&gt;For an analysis assistant, I would now check three things independently: whether its conclusion follows from the constraints, whether its number is actually identifiable, and whether software can consume its response. A confident explanation cannot substitute for any of those checks.&lt;/p&gt;

&lt;p&gt;This study uses synthetic descriptive measurements. It does not test raw sequencing analysis, biological replication, causal treatment effects, significance testing, or clinical decisions. The 24 cases share eight templates, and the verification comparison is exploratory. I would next add repeated runs, more unseen template families, and a calculator-assisted arm to distinguish arithmetic errors from failures to reason about missing information.&lt;/p&gt;

&lt;h2&gt;
  
  
  My Benchmark
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;&lt;a href="https://www.kaggle.com/benchmarks/abhicloudstalk/cellcaution-reasoning-about-cell-composition" rel="noopener noreferrer"&gt;Explore CellCaution on Kaggle&lt;/a&gt;&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;&lt;a href="https://www.kaggle.com/benchmarks/tasks/abhicloudstalk/cellcaution-script-v2" rel="noopener noreferrer"&gt;Published CellCaution task&lt;/a&gt;&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;The public leaderboard currently contains Gemma's publication-validation run. The tables in this article describe the separately audited three-model study; they should not be read as three imported leaderboard runs.&lt;/p&gt;

&lt;p&gt;The implementation builds on the &lt;a href="https://github.com/Kaggle/kaggle-benchmarks/blob/ci/cookbook.md" rel="noopener noreferrer"&gt;Kaggle Benchmarks Python library and cookbook&lt;/a&gt;. The synthetic fixtures, reference-constraint design, rational answer keys, and component-level evaluation were developed for CellCaution. AI assistance was used for implementation, auditing, visualizations, and editing; I reviewed the measurements and claims against the exported responses.&lt;/p&gt;

&lt;p&gt;The question I want a scientific assistant to preserve is simple: &lt;strong&gt;what can these measurements establish—and what would it be inventing?&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;&lt;a href="https://www.kaggle.com/datasets/abhicloudstalk/cellcaution-v2-audited-study-evidence/" rel="noopener noreferrer"&gt;Inspect the audited study evidence&lt;/a&gt;&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;The dataset contains exact prompts, raw model responses, answer keys, recorded scores, and the frozen protocol for all 168 included evaluations. It includes the recovered reordered controls and excludes the invalid original control exports.&lt;/p&gt;

&lt;h3&gt;
  
  
  One failure, two different problems
&lt;/h3&gt;

&lt;p&gt;In direct case &lt;strong&gt;F01&lt;/strong&gt;, Claude Sonnet 4.6 saw control means of 17 and 53, treated means of 22 and 50, and a fixed reference of 80% type A and 20% type B.&lt;/p&gt;

&lt;p&gt;Its returned JSON reported:&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;"verdict"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"supported"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"standardized_change_percent"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mf"&gt;22.397&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"percentage_range"&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="mf"&gt;22.397&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mf"&gt;22.397&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;&lt;em&gt;Excerpt: the explanation field and surrounding prose are omitted here; the evidence dataset retains the full response.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;The verdict was correct. The number was not. The control weighted mean is &lt;strong&gt;24.2&lt;/strong&gt; and the treated weighted mean is &lt;strong&gt;27.6&lt;/strong&gt;, so the requested increase is:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;100 × (27.6 − 24.2) / 24.2 = 14.0496%.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;The response instead averaged the cell-type percentage changes:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;0.8 × [100 × (22 − 17) / 17] + 0.2 × [100 × (50 − 53) / 53] ≈ 22.3973%.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;These formulas answer different questions because the cell types have different control baselines. Averaging their percentage changes is not the percentage change of their weighted mean.&lt;/p&gt;

&lt;p&gt;This response also included prose outside its JSON block, so it failed the frozen output contract. Extracting the block resolves that formatting issue for the separate diagnostic, but leaves the mathematical error intact. A correct verdict and a detailed explanation did not guarantee a correct magnitude.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;&lt;a href="https://www.kaggle.com/code/abhicloudstalk/cellcaution-v2-reproduce-the-audited-study" rel="noopener noreferrer"&gt;Reproduce the audited study&lt;/a&gt;&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;This notebook verifies the frozen protocol and exact prompts, recomputes all 168 included evaluations from raw responses, and reproduces the score tables and charts without additional model calls.&lt;/p&gt;

&lt;h3&gt;
  
  
  Did the verification result survive repetition?
&lt;/h3&gt;

&lt;p&gt;After the original study, I added two complete GPT-5.4 mini repeats using the same 24 cases, frozen prompts, scorer and requested model ID. Each repeat included direct and verification arms, adding &lt;strong&gt;96 case evaluations&lt;/strong&gt;. This was an exploratory extension planned after seeing the original findings.&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Run&lt;/th&gt;
&lt;th&gt;Direct primary / 24&lt;/th&gt;
&lt;th&gt;Verification primary / 24&lt;/th&gt;
&lt;th&gt;Verdict: direct → verification&lt;/th&gt;
&lt;th&gt;Percentage/null: direct → verification&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Original&lt;/td&gt;
&lt;td&gt;12&lt;/td&gt;
&lt;td&gt;9&lt;/td&gt;
&lt;td&gt;18 → 21&lt;/td&gt;
&lt;td&gt;16 → 10&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Repeat 1&lt;/td&gt;
&lt;td&gt;12&lt;/td&gt;
&lt;td&gt;11&lt;/td&gt;
&lt;td&gt;19 → 20&lt;/td&gt;
&lt;td&gt;14 → 12&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Repeat 2&lt;/td&gt;
&lt;td&gt;14&lt;/td&gt;
&lt;td&gt;13&lt;/td&gt;
&lt;td&gt;20 → 19&lt;/td&gt;
&lt;td&gt;15 → 16&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;All 96 new outputs passed the format contract, and their scores were recomputed from the raw responses against the frozen grader.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Verification produced a lower combined primary score in all three recorded runs.&lt;/strong&gt; However, the component-level pattern varied. In the second repeat, percentage/null accuracy improved while verdict accuracy declined—the reverse of the original tradeoff.&lt;/p&gt;

&lt;p&gt;That changed my interpretation. The evidence supports a narrower observation: this verification checklist did not improve the complete answer score in these three runs. It does not establish that verification generally harms numerical reasoning.&lt;/p&gt;

&lt;p&gt;The original study contained 168 evaluations; this extension brings the total to &lt;strong&gt;264&lt;/strong&gt;. The repeats reuse the same 24 cases from eight templates, so they measure observed response stability rather than generalization to new problems. SDK defaults were retained; temperature and reasoning budgets were not explicitly controlled.&lt;/p&gt;

&lt;p&gt;The frozen replication plan, all four response exports and the summary are included in &lt;a href="https://www.kaggle.com/datasets/abhicloudstalk/cellcaution-v2-audited-study-evidence/" rel="noopener noreferrer"&gt;Version 2 of the audited evidence dataset&lt;/a&gt;.&lt;/p&gt;

</description>
      <category>devchallenge</category>
      <category>kagglechallenge</category>
      <category>ai</category>
      <category>machinelearning</category>
    </item>
  </channel>
</rss>
