<?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: Devanshu Biswas</title>
    <description>The latest articles on DEV Community by Devanshu Biswas (@dev48v).</description>
    <link>https://dev.to/dev48v</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%2F3929385%2F75a3696c-143d-4252-ba59-6ed4083ca827.jpg</url>
      <title>DEV Community: Devanshu Biswas</title>
      <link>https://dev.to/dev48v</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/dev48v"/>
    <language>en</language>
    <item>
      <title>I Built a Triage Tool Whose Only Promise Is That Every Item Lands on Exactly One Shelf</title>
      <dc:creator>Devanshu Biswas</dc:creator>
      <pubDate>Mon, 12 Oct 2026 11:22:21 +0000</pubDate>
      <link>https://dev.to/dev48v/i-built-a-triage-tool-whose-only-promise-is-that-every-item-lands-on-exactly-one-shelf-gjl</link>
      <guid>https://dev.to/dev48v/i-built-a-triage-tool-whose-only-promise-is-that-every-item-lands-on-exactly-one-shelf-gjl</guid>
      <description>&lt;p&gt;You have a folder. Screenshots, half-written notes, saved links, a photo of a whiteboard. Somewhere in there are the things that belong to projects you are working on, and the things somebody is waiting on you for.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;shelf&lt;/strong&gt; reads the folder, pulls text out of whatever it can, scores that text against a project list &lt;em&gt;you&lt;/em&gt; wrote, and writes a board. It runs on your machine, needs no key, and installs nothing.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;📦 &lt;strong&gt;Repo (MIT):&lt;/strong&gt; &lt;a href="https://github.com/dev48v/shelf" rel="noopener noreferrer"&gt;https://github.com/dev48v/shelf&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;👉 &lt;strong&gt;Walkthrough, runs in your browser:&lt;/strong&gt; &lt;a href="https://dev48.in/agentlab/vol5-01-shelf.html" rel="noopener noreferrer"&gt;https://dev48.in/agentlab/vol5-01-shelf.html&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;python shelf.py build &lt;span class="nt"&gt;--pile&lt;/span&gt; demo/pile &lt;span class="nt"&gt;--projects&lt;/span&gt; demo/projects.md
6 items -&amp;gt; board.md
note: tesseract not found&lt;span class="p"&gt;;&lt;/span&gt; images used sidecars or filenames
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight markdown"&gt;&lt;code&gt;&lt;span class="gu"&gt;## Replies you owe  (1)&lt;/span&gt;
| item | read via | why |
|---|---|---|
| &lt;span class="sb"&gt;`email-accountant.txt`&lt;/span&gt; | file contents | the text reads as someone waiting on a reply |

&lt;span class="gu"&gt;## Kitchen rebuild  (2)&lt;/span&gt;
| &lt;span class="sb"&gt;`note-quote.txt`&lt;/span&gt; | file contents | matched 83% of the terms for Kitchen rebuild |
| &lt;span class="sb"&gt;`whiteboard.png`&lt;/span&gt; | sidecar .txt  | matched 50% of the terms for Kitchen rebuild |

&lt;span class="gu"&gt;## The partition&lt;/span&gt;
1 Replies you owe + 2 Kitchen rebuild + 1 Cycling trip + 2 Unfiled = 6 of 6
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  The one rule
&lt;/h2&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Every item lands on exactly one shelf, and the board says why.&lt;/strong&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Not &lt;em&gt;mostly&lt;/em&gt; one. A triage tool that can silently drop an item, or quietly file it twice, is worse than no triage tool — because you stop checking it, and then it is just another pile.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;check_partition&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;items&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;projects&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="n"&gt;valid&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="n"&gt;p&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;name&lt;/span&gt; &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;p&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;projects&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="n"&gt;UNFILED&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;OWED&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;item&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;items&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;item&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;shelf&lt;/span&gt; &lt;span class="ow"&gt;not&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;valid&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
            &lt;span class="k"&gt;raise&lt;/span&gt; &lt;span class="nc"&gt;AssertionError&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;item&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;name&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s"&gt; landed on unknown shelf &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;item&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;shelf&lt;/span&gt;&lt;span class="si"&gt;!r}&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="ow"&gt;not&lt;/span&gt; &lt;span class="n"&gt;item&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;reason&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
            &lt;span class="k"&gt;raise&lt;/span&gt; &lt;span class="nc"&gt;AssertionError&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;item&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;name&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s"&gt; was filed with no reason given&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="n"&gt;shelved&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{}&lt;/span&gt;
    &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;item&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;items&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="n"&gt;shelved&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;setdefault&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;item&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;shelf&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;[]).&lt;/span&gt;&lt;span class="nf"&gt;append&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;item&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;name&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="nf"&gt;sum&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;len&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;v&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;v&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;shelved&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;values&lt;/span&gt;&lt;span class="p"&gt;())&lt;/span&gt; &lt;span class="o"&gt;!=&lt;/span&gt; &lt;span class="nf"&gt;len&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;items&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
        &lt;span class="k"&gt;raise&lt;/span&gt; &lt;span class="nc"&gt;AssertionError&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;the partition does not account for every item&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;It runs on every build and the run &lt;strong&gt;fails&lt;/strong&gt; if it does not hold. The board prints the sum at the bottom so you can see that it did.&lt;/p&gt;

&lt;h2&gt;
  
  
  🔴 The bug that invariant exists to catch
&lt;/h2&gt;

&lt;p&gt;A &lt;code&gt;.txt&lt;/code&gt; sitting next to an image of the same stem is that image's &lt;strong&gt;caption&lt;/strong&gt; — evidence about the image, not an item of its own. Count it as an item and the board claims 7 where the folder holds 6.&lt;/p&gt;

&lt;p&gt;It is a one-line mistake, it produces a board that looks entirely reasonable, and the only thing standing between you and it is a count that has to add up.&lt;/p&gt;

&lt;h2&gt;
  
  
  Term overlap, not similarity
&lt;/h2&gt;

&lt;p&gt;OCR output is garbled. Half a word is missing, &lt;code&gt;0&lt;/code&gt; is &lt;code&gt;O&lt;/code&gt;, a line is nonsense. Counting how many of &lt;strong&gt;your&lt;/strong&gt; terms survive is robust to exactly that damage. An edit distance or an embedding needs clean text to behave, and clean text is the one thing a pile of screenshots does not have.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;w1dget !!! spr0cket ... widget ~~~ sprocket   →  still scores 100%
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Inject noise every eleventh character of a real note and it still files correctly.&lt;/p&gt;

&lt;h2&gt;
  
  
  The categories are yours
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight markdown"&gt;&lt;code&gt;&lt;span class="gh"&gt;# Kitchen rebuild&lt;/span&gt;
quote, builder, worktop, plumber, tiles, splashback
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Headings are shelves, the lines under them are terms. Nothing invents a category or infers one from the pile. &lt;strong&gt;A classifier that writes its own labels is answering a question you did not ask.&lt;/strong&gt; Multi-word terms must appear in order — &lt;code&gt;self assessment&lt;/code&gt; matches "my self assessment form" and not "assessment of self".&lt;/p&gt;

&lt;h2&gt;
  
  
  Provenance, on every row
&lt;/h2&gt;

&lt;p&gt;A score built on a filename is worth far less than one built on contents, and the board says which: &lt;em&gt;file contents&lt;/em&gt;, &lt;em&gt;sidecar .txt&lt;/em&gt;, &lt;em&gt;OCR&lt;/em&gt;, or &lt;em&gt;filename only&lt;/em&gt;.&lt;/p&gt;

&lt;p&gt;🔴 And a filename alone never counts as somebody waiting on you. &lt;code&gt;can-you-reply-to-this.png&lt;/code&gt; is a note to self, not a person.&lt;/p&gt;

&lt;h2&gt;
  
  
  🔴 How the walkthrough page is checked
&lt;/h2&gt;

&lt;p&gt;The page ports the rules to JavaScript so you can run them live. A port that drifts from its original is the specific failure no JavaScript-only test can see — so the verifier &lt;strong&gt;runs the Python&lt;/strong&gt; and demands the two agree item for item, shelf for shelf, reason for reason and provenance for provenance.&lt;/p&gt;

&lt;p&gt;That check found its own bug: &lt;code&gt;execFileSync&lt;/code&gt; returns stdout only, unittest writes its summary to &lt;strong&gt;stderr&lt;/strong&gt;, and "the suite ran" was passing on the CLI's print output. &lt;code&gt;spawnSync&lt;/code&gt; reading both streams fixed it.&lt;/p&gt;

&lt;p&gt;Python 3.10+, standard library only. &lt;strong&gt;51 tests&lt;/strong&gt;, 124 cross-language verifier asserts, 29 in-page checks, 0 failures.&lt;/p&gt;

</description>
      <category>python</category>
      <category>productivity</category>
      <category>opensource</category>
      <category>programming</category>
    </item>
    <item>
      <title>Cost Per Correct Answer Is Worse Than Useless, and It Will Tell You to Use the Cheap Model</title>
      <dc:creator>Devanshu Biswas</dc:creator>
      <pubDate>Mon, 12 Oct 2026 11:21:40 +0000</pubDate>
      <link>https://dev.to/dev48v/cost-per-correct-answer-is-worse-than-useless-and-it-will-tell-you-to-use-the-cheap-model-1bia</link>
      <guid>https://dev.to/dev48v/cost-per-correct-answer-is-worse-than-useless-and-it-will-tell-you-to-use-the-cheap-model-1bia</guid>
      <description>&lt;p&gt;A small model costs a fraction of a large one and gets most easy requests right. A router that could tell easy from hard would cut the bill dramatically. Every production LLM system reaches this point, and the argument that follows is always accuracy versus cost — which is the wrong frame, because you can buy any accuracy you like by spending more.&lt;/p&gt;

&lt;p&gt;👉 &lt;strong&gt;Live, runs in your browser:&lt;/strong&gt; &lt;a href="https://dev48.in/ai/days/day81-cost-routing.html" rel="noopener noreferrer"&gt;https://dev48.in/ai/days/day81-cost-routing.html&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  The metric everyone proposes
&lt;/h2&gt;

&lt;p&gt;"Use &lt;strong&gt;cost per correct answer&lt;/strong&gt;." Here it is, on 2,000 requests, small model at 1 unit and big at 20:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;strategy&lt;/th&gt;
&lt;th&gt;accuracy&lt;/th&gt;
&lt;th&gt;cost / request&lt;/th&gt;
&lt;th&gt;&lt;strong&gt;cost / correct&lt;/strong&gt;&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;always small&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;74.5%&lt;/td&gt;
&lt;td&gt;1.00&lt;/td&gt;
&lt;td&gt;
&lt;strong&gt;1.34&lt;/strong&gt; ← the winner&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;route on difficulty&lt;/td&gt;
&lt;td&gt;95.8%&lt;/td&gt;
&lt;td&gt;6.42&lt;/td&gt;
&lt;td&gt;6.70&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;oracle&lt;/td&gt;
&lt;td&gt;95.8%&lt;/td&gt;
&lt;td&gt;5.85&lt;/td&gt;
&lt;td&gt;6.11&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;always big&lt;/td&gt;
&lt;td&gt;95.8%&lt;/td&gt;
&lt;td&gt;20.00&lt;/td&gt;
&lt;td&gt;20.88&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;🔴 &lt;strong&gt;It picks the small model outright.&lt;/strong&gt; A 74%-accurate model at 1 unit beats a 96%-accurate one at 20 on that metric, every time, and it always will. My first draft of this page asserted the opposite and the in-page check disproved it.&lt;/p&gt;

&lt;p&gt;Cost per correct answer, on its own, hands the prize to the cheapest model that is vaguely adequate.&lt;/p&gt;

&lt;h2&gt;
  
  
  The question that does have an answer
&lt;/h2&gt;

&lt;p&gt;Fix a &lt;strong&gt;quality floor&lt;/strong&gt; first. Given the accuracy you have promised, what is the cheapest way to reach it?&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;the promise&lt;/th&gt;
&lt;th&gt;cheapest that keeps it&lt;/th&gt;
&lt;th&gt;spend vs always-big&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;70%&lt;/td&gt;
&lt;td&gt;always small&lt;/td&gt;
&lt;td&gt;5%&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;90%&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;cascade with a verifier&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;28%&lt;/strong&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;95%&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;route on difficulty&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;32%&lt;/strong&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;At a 90% promise the small model is &lt;strong&gt;not on the menu&lt;/strong&gt; — it is not cheap, it is ineligible. And routing then matches always-big's accuracy &lt;strong&gt;exactly&lt;/strong&gt; for under a third of the spend. That saving is the entire value of routing, and it only becomes visible once the quality bar is written down.&lt;/p&gt;

&lt;h2&gt;
  
  
  🔴 Routing on the small model's confidence
&lt;/h2&gt;

&lt;p&gt;The obvious design: ask the small model, escalate when it says it is unsure. Cheap, needs no training, and it fails exactly where it matters.&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;the small model says&lt;/th&gt;
&lt;th&gt;requests&lt;/th&gt;
&lt;th&gt;actually right&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;95% confident&lt;/td&gt;
&lt;td&gt;843&lt;/td&gt;
&lt;td&gt;100%&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;90% confident&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;406&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;0%&lt;/strong&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;60% confident&lt;/td&gt;
&lt;td&gt;303&lt;/td&gt;
&lt;td&gt;61%&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;55% confident&lt;/td&gt;
&lt;td&gt;448&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;There is a band where it is &lt;strong&gt;90% confident and 0% correct&lt;/strong&gt;. Those are precisely the requests escalation exists for, and confidence is blind to them by construction. Confidence measures fluency, not correctness.&lt;/p&gt;

&lt;h2&gt;
  
  
  The router is not free
&lt;/h2&gt;

&lt;p&gt;Most comparisons quietly price the router at zero. It is a model call too.&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;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;req&lt;/span&gt; &lt;span class="k"&gt;of&lt;/span&gt; &lt;span class="nx"&gt;reqs&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;r&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;strategy&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;req&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;opts&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="nx"&gt;cost&lt;/span&gt; &lt;span class="o"&gt;+=&lt;/span&gt; &lt;span class="nx"&gt;r&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;cost&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nx"&gt;routerCost&lt;/span&gt; &lt;span class="o"&gt;+=&lt;/span&gt; &lt;span class="nx"&gt;routerPrice&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;     &lt;span class="c1"&gt;// on EVERY request, whichever way it decides&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;🔴 In my first version that price was read from the options and &lt;strong&gt;never billed&lt;/strong&gt; — no strategy returned a &lt;code&gt;router&lt;/code&gt; field, so the column stayed at zero and the overhead table was meaningless. Difference two runs and a charge that never lands cannot hide.&lt;/p&gt;

&lt;h2&gt;
  
  
  The ceiling
&lt;/h2&gt;

&lt;p&gt;An oracle router knows in advance which requests the small model will get right and sends only the rest. No real router beats it, and the gap between yours and the oracle is the only honest measure of how much routing skill you have left to gain.&lt;/p&gt;

&lt;p&gt;Here the oracle matches always-big's accuracy for &lt;strong&gt;29%&lt;/strong&gt; of the spend. Routing on difficulty reaches &lt;strong&gt;32%&lt;/strong&gt;. There is not much left on the table.&lt;/p&gt;

&lt;h2&gt;
  
  
  Everything is reproducible
&lt;/h2&gt;

&lt;p&gt;No API, no sampling. Difficulty is u² for u uniform, so P(difficulty &amp;lt; d) = √d and every number is re-derivable in closed form — which is how the verifier checks the simulation rather than trusting it.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;184 verifier asserts, 34 in-page checks, 0 failures.&lt;/strong&gt;&lt;/p&gt;

</description>
      <category>ai</category>
      <category>llm</category>
      <category>architecture</category>
      <category>programming</category>
    </item>
    <item>
      <title>The RoPE Decay Curve Everyone Plots Is Noise, and Taking the Absolute Value Does Not Fix It</title>
      <dc:creator>Devanshu Biswas</dc:creator>
      <pubDate>Mon, 12 Oct 2026 11:20:59 +0000</pubDate>
      <link>https://dev.to/dev48v/the-rope-decay-curve-everyone-plots-is-noise-and-taking-the-absolute-value-does-not-fix-it-1ean</link>
      <guid>https://dev.to/dev48v/the-rope-decay-curve-everyone-plots-is-noise-and-taking-the-absolute-value-does-not-fix-it-1ean</guid>
      <description>&lt;p&gt;Rotary position embeddings do something that sounds impossible. They inject each token's &lt;strong&gt;absolute&lt;/strong&gt; position into the query and key — and the attention score between any two tokens depends only on the &lt;strong&gt;distance&lt;/strong&gt; between them. Move a whole sentence ten tokens later and every score inside it is unchanged.&lt;/p&gt;

&lt;p&gt;👉 &lt;strong&gt;Live, runs in your browser:&lt;/strong&gt; &lt;a href="https://dev48.in/dl/day81-rotary-embeddings.html" rel="noopener noreferrer"&gt;https://dev48.in/dl/day81-rotary-embeddings.html&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  The identity, in one line
&lt;/h2&gt;

&lt;p&gt;Write a 2-D pair of the query at position &lt;em&gt;m&lt;/em&gt; as a complex number &lt;em&gt;q&lt;/em&gt;, and the key at &lt;em&gt;n&lt;/em&gt; as &lt;em&gt;k&lt;/em&gt;. RoPE multiplies them by e^(imθ) and e^(inθ):&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Re[ (q·e^(imθ)) · conj(k·e^(inθ)) ] = Re[ q·conj(k)·e^(i(m−n)θ) ]
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The absolute positions appear only as their difference. Verified over 42 combinations of offset and shift — including a shift of &lt;strong&gt;4000&lt;/strong&gt; — worst deviation 1e−15. Additive sinusoidal embeddings, by contrast, give a different score for the same offset at different absolute positions.&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="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;rope&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;vec&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;pos&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;base&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;10000&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;th&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;thetas&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;vec&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;length&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;base&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;out&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;Array&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;vec&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;length&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;let&lt;/span&gt; &lt;span class="nx"&gt;i&lt;/span&gt; &lt;span class="o"&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;i&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="nx"&gt;vec&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;length&lt;/span&gt; &lt;span class="o"&gt;/&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="nx"&gt;i&lt;/span&gt;&lt;span class="o"&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;a&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;vec&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="nx"&gt;i&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt; &lt;span class="nx"&gt;b&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;vec&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="nx"&gt;i&lt;/span&gt;&lt;span class="o"&gt;+&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt; &lt;span class="nx"&gt;ang&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;pos&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="nx"&gt;th&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;i&lt;/span&gt;&lt;span class="p"&gt;];&lt;/span&gt;
    &lt;span class="nx"&gt;out&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="nx"&gt;i&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;   &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;a&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;cos&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;ang&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="nx"&gt;b&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;sin&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;ang&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="nx"&gt;out&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="nx"&gt;i&lt;/span&gt;&lt;span class="o"&gt;+&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;a&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;sin&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;ang&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="nx"&gt;b&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;cos&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;ang&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;out&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  🔴 Now the part people get wrong
&lt;/h2&gt;

&lt;p&gt;"Attention under RoPE decays with distance." Plot it carelessly and you get a flat line of random scatter, because &lt;strong&gt;two independent random vectors have an expected dot product of zero at every distance&lt;/strong&gt;. The curve you drew was sampling noise.&lt;/p&gt;

&lt;p&gt;The usual next move is to average the &lt;em&gt;magnitude&lt;/em&gt; instead. &lt;strong&gt;That does not rescue it either:&lt;/strong&gt;&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;offset&lt;/th&gt;
&lt;th&gt;q against &lt;strong&gt;itself&lt;/strong&gt;
&lt;/th&gt;
&lt;th&gt;mean raw score, 600 pairs&lt;/th&gt;
&lt;th&gt;mean |score|, same pairs&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;0&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;10.60&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;−0.07&lt;/td&gt;
&lt;td&gt;3.17&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;16&lt;/td&gt;
&lt;td&gt;8.05&lt;/td&gt;
&lt;td&gt;−0.37&lt;/td&gt;
&lt;td&gt;3.12&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;256&lt;/td&gt;
&lt;td&gt;4.95&lt;/td&gt;
&lt;td&gt;−0.28&lt;/td&gt;
&lt;td&gt;3.04&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;1024&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;0.69&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;−0.38&lt;/td&gt;
&lt;td&gt;3.02&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;Three columns, same vectors, same offsets. The mean raw score never leaves ±0.38 against a typical magnitude of 3.2. The mean |score| moves &lt;strong&gt;4% across the whole range&lt;/strong&gt;. Only the &lt;strong&gt;self&lt;/strong&gt; column collapses — and that is the decay people are describing: the pairs start perfectly in phase and dephase at different rates until their cosines cancel.&lt;/p&gt;

&lt;p&gt;It is dephasing, not attenuation, and it is a statement about a &lt;strong&gt;fixed&lt;/strong&gt; vector.&lt;/p&gt;

&lt;h2&gt;
  
  
  🔴 The bug that produced the first, wrong measurement
&lt;/h2&gt;

&lt;p&gt;Each "random" vector got its own Park-Miller generator seeded by a nearby integer. &lt;strong&gt;Nearby LCG seeds give correlated streams.&lt;/strong&gt; The supposedly independent mean wandered to ±2.5 — an order of magnitude past its standard error — and looked like structure.&lt;/p&gt;

&lt;p&gt;One generator for the whole study, and it drops to ±0.38. If your "independent samples" come from &lt;code&gt;rng(seed + i)&lt;/code&gt;, they are not independent.&lt;/p&gt;

&lt;h2&gt;
  
  
  🔴 NTK scaling cannot touch the fastest pair
&lt;/h2&gt;

&lt;p&gt;Train to 512, ask about 4096. It is tempting to blame the fast pairs, but they were already wrapping constantly inside the training window — another twenty turns is nothing new.&lt;/p&gt;

&lt;p&gt;The pair that leaves familiar ground is the &lt;strong&gt;slowest&lt;/strong&gt; one. Its period is longer than the whole context, so across training it swept only &lt;strong&gt;2.6% of a turn&lt;/strong&gt;; push the position eightfold and it swings into angles that never once appeared.&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;strategy&lt;/th&gt;
&lt;th&gt;slowest pair's angle&lt;/th&gt;
&lt;th&gt;&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;trained, at 512&lt;/td&gt;
&lt;td&gt;0.162 rad&lt;/td&gt;
&lt;td&gt;what the model saw&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;untouched, at 4096&lt;/td&gt;
&lt;td&gt;1.295 rad&lt;/td&gt;
&lt;td&gt;8× past anything in training&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;position interpolation&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;0.162 rad&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;back exactly where it trained&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;NTK-aware base&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;0.162 rad&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;back exactly where it trained&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;Both repairs land it in the same place. They differ at the &lt;strong&gt;fast&lt;/strong&gt; end: θ₀ = base⁰ = 1 whatever you do to the base, so &lt;strong&gt;NTK cannot slow the fastest pair at all&lt;/strong&gt;, while interpolation slows every pair equally and costs resolution between adjacent tokens.&lt;/p&gt;

&lt;h2&gt;
  
  
  How it is checked
&lt;/h2&gt;

&lt;p&gt;The verifier multiplies &lt;strong&gt;complex numbers&lt;/strong&gt; rather than using the real 2×2 form, extracts the rotation as a &lt;strong&gt;matrix&lt;/strong&gt; and tests RᵀR = I with det = +1, and re-measures the decay with a Marsaglia polar sampler so a bug in the page's generator cannot reproduce itself in the check.&lt;/p&gt;

&lt;p&gt;It caught a sign error in my closed form — &lt;code&gt;+&lt;/code&gt; where the derivation gives &lt;code&gt;−&lt;/code&gt; on the sin term.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;226 verifier asserts, 28 in-page checks, 0 failures.&lt;/strong&gt;&lt;/p&gt;

</description>
      <category>machinelearning</category>
      <category>ai</category>
      <category>deeplearning</category>
      <category>python</category>
    </item>
    <item>
      <title>Leave-One-Out Attributed 20 for a Prediction of 10, Then Gave Two Real Features Exactly Zero</title>
      <dc:creator>Devanshu Biswas</dc:creator>
      <pubDate>Mon, 12 Oct 2026 11:20:16 +0000</pubDate>
      <link>https://dev.to/dev48v/leave-one-out-attributed-20-for-a-prediction-of-10-then-gave-two-real-features-exactly-zero-2l48</link>
      <guid>https://dev.to/dev48v/leave-one-out-attributed-20-for-a-prediction-of-10-then-gave-two-real-features-exactly-zero-2l48</guid>
      <description>&lt;p&gt;You have a model and one prediction. The question everyone asks is: how much of this number is down to each input? Shapley values answer it by asking a more careful question — &lt;strong&gt;how much does this feature add, on average, over every possible order in which the features could have arrived?&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;👉 &lt;strong&gt;Live, runs in your browser:&lt;/strong&gt; &lt;a href="https://dev48.in/ml/day81-shapley-values.html" rel="noopener noreferrer"&gt;https://dev48.in/ml/day81-shapley-values.html&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  The obvious alternative, and both ways it breaks
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Leave-one-out&lt;/strong&gt;: drop a feature, see how much the prediction moves, call that its importance. One line of code.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Case 1 — interaction.&lt;/strong&gt; A model that fires only when &lt;em&gt;both&lt;/em&gt; features are present, worth 10.&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;feature&lt;/th&gt;
&lt;th&gt;Shapley&lt;/th&gt;
&lt;th&gt;leave-one-out&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;has_card&lt;/td&gt;
&lt;td&gt;5&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;10&lt;/strong&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;is_verified&lt;/td&gt;
&lt;td&gt;5&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;10&lt;/strong&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;total&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;
&lt;strong&gt;10&lt;/strong&gt; ✓&lt;/td&gt;
&lt;td&gt;
&lt;strong&gt;20&lt;/strong&gt; ✗&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;Drop either one and the prediction falls to the floor, so leave-one-out scores them both as fully responsible. It attributes &lt;strong&gt;twice the prediction&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Case 2 — redundancy.&lt;/strong&gt; Two features carrying the same information.&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;feature&lt;/th&gt;
&lt;th&gt;Shapley&lt;/th&gt;
&lt;th&gt;leave-one-out&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;signal&lt;/td&gt;
&lt;td&gt;4&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;0&lt;/strong&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;signal_copy&lt;/td&gt;
&lt;td&gt;4&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;0&lt;/strong&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;Drop either copy and nothing moves, so it scores both at &lt;strong&gt;exactly zero&lt;/strong&gt;. A feature-importance plot built this way shows real signal as irrelevant, and somebody deletes it.&lt;/p&gt;

&lt;p&gt;🔴 It errs in &lt;strong&gt;both directions&lt;/strong&gt; — over-attributing interaction, under-attributing redundancy — and it agrees with Shapley &lt;strong&gt;exactly&lt;/strong&gt; on additive models. Which is precisely why an additive example proves nothing about an attribution method.&lt;/p&gt;

&lt;h2&gt;
  
  
  Three derivations that agree to the last bit
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;phi_i = sum over S not containing i of
          |S|! (n-|S|-1)! / n!  *  ( v(S + i) - v(S) )
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;That factorial weight is not decoration. It is exactly the fraction of the n! orderings in which S arrives before i and everything else after — a probability distribution over the position at which feature i arrives, summing to 1.&lt;/p&gt;

&lt;p&gt;The page computes the same values a second way, as an average over every permutation. The verifier computes them a &lt;strong&gt;third&lt;/strong&gt; way that touches neither factorials nor permutations: &lt;strong&gt;Möbius inversion&lt;/strong&gt; of the set function into Harsanyi dividends, then split each dividend equally among the coalition that earned it.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;m(S)   = sum over T subset of S of (-1)^(|S|-|T|) v(T)
phi_i  = sum over S containing i of m(S) / |S|
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;For the AND model the whole dividend sits on the pair — &lt;code&gt;m({a,b}) = 10&lt;/code&gt;, &lt;code&gt;m({a}) = m({b}) = 0&lt;/code&gt; — which &lt;em&gt;is&lt;/em&gt; the statement that neither feature does anything alone, in one line.&lt;/p&gt;

&lt;h2&gt;
  
  
  All four axioms, checked
&lt;/h2&gt;

&lt;p&gt;Shapley values are not a heuristic that happens to work. They are the &lt;strong&gt;unique&lt;/strong&gt; attribution satisfying four requirements at once:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Efficiency&lt;/strong&gt; — the values sum to the prediction minus the baseline. This is the one leave-one-out cannot keep.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Dummy&lt;/strong&gt; — a feature that never changes anything scores exactly 0.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Symmetry&lt;/strong&gt; — interchangeable features get identical values.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Linearity&lt;/strong&gt; — attributing a sum of models is the sum of the attributions.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Checked on every model, with symmetry re-derived by swapping columns of v rather than trusting the page's detector.&lt;/p&gt;

&lt;h2&gt;
  
  
  The sampled estimator keeps one property for free
&lt;/h2&gt;

&lt;p&gt;Exact Shapley costs 2ⁿ model evaluations. Past about twenty features that is hopeless, so practice samples permutations.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Efficiency stays exact at every sample count — even at 1.&lt;/strong&gt; Each permutation's marginal contributions telescope to v(full) − v(empty) whatever order you drew. What sampling costs you is per-feature accuracy, and that is the number you should report.&lt;/p&gt;

&lt;h2&gt;
  
  
  And attribution has a sign
&lt;/h2&gt;

&lt;p&gt;A feature that lowers the prediction gets a negative value: +6 for the helpful one, &lt;strong&gt;−4&lt;/strong&gt; for the harmful one. A method that reports magnitudes only cannot say that.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;470 verifier asserts, 23 in-page checks, 0 failures.&lt;/strong&gt;&lt;/p&gt;

</description>
      <category>machinelearning</category>
      <category>datascience</category>
      <category>python</category>
      <category>programming</category>
    </item>
    <item>
      <title>Myers Diff Does 1.6% of the Work on Real Files and 105% on Random Ones</title>
      <dc:creator>Devanshu Biswas</dc:creator>
      <pubDate>Mon, 12 Oct 2026 11:19:36 +0000</pubDate>
      <link>https://dev.to/dev48v/myers-diff-does-16-of-the-work-on-real-files-and-105-on-random-ones-3123</link>
      <guid>https://dev.to/dev48v/myers-diff-does-16-of-the-work-on-real-files-and-105-on-random-ones-3123</guid>
      <description>&lt;p&gt;Every diff you have read is the answer to one question: what is the &lt;strong&gt;shortest sequence of deletions and insertions&lt;/strong&gt; that turns the old file into the new one? Myers' 1986 algorithm answers it by treating the two files as the axes of a grid and finding the cheapest path across — where moving diagonally, on a line both files share, is free.&lt;/p&gt;

&lt;p&gt;👉 &lt;strong&gt;Live, runs in your browser:&lt;/strong&gt; &lt;a href="https://dev48.in/solve/day81-myers-diff.html" rel="noopener noreferrer"&gt;https://dev48.in/solve/day81-myers-diff.html&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  The search
&lt;/h2&gt;

&lt;p&gt;Rather than exploring the grid cell by cell, Myers asks: &lt;em&gt;how far can I get using exactly D edits?&lt;/em&gt; For each D it keeps only the &lt;strong&gt;furthest-reaching&lt;/strong&gt; point on each diagonal — one number per diagonal, not a whole row — and stops the first time one reaches the corner. Because D only increases, the first arrival is minimal by construction.&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;for &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;D&lt;/span&gt; &lt;span class="o"&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;D&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;=&lt;/span&gt; &lt;span class="nx"&gt;MAX&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="nx"&gt;D&lt;/span&gt;&lt;span class="o"&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;trace&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="nx"&gt;V&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;slice&lt;/span&gt;&lt;span class="p"&gt;());&lt;/span&gt;                   &lt;span class="c1"&gt;// snapshot, for backtracking later&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;let&lt;/span&gt; &lt;span class="nx"&gt;k&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="nx"&gt;D&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="nx"&gt;k&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;=&lt;/span&gt; &lt;span class="nx"&gt;D&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="nx"&gt;k&lt;/span&gt; &lt;span class="o"&gt;+=&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&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;x&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;k&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="nx"&gt;D&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;k&lt;/span&gt; &lt;span class="o"&gt;!==&lt;/span&gt; &lt;span class="nx"&gt;D&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="nx"&gt;V&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;k&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="o"&gt;+&lt;/span&gt;&lt;span class="nx"&gt;MAX&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="nx"&gt;V&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;k&lt;/span&gt;&lt;span class="o"&gt;+&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="o"&gt;+&lt;/span&gt;&lt;span class="nx"&gt;MAX&lt;/span&gt;&lt;span class="p"&gt;]))&lt;/span&gt;
      &lt;span class="p"&gt;?&lt;/span&gt; &lt;span class="nx"&gt;V&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;k&lt;/span&gt;&lt;span class="o"&gt;+&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="o"&gt;+&lt;/span&gt;&lt;span class="nx"&gt;MAX&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;                         &lt;span class="c1"&gt;// moved down (insertion)&lt;/span&gt;
      &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;V&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;k&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="o"&gt;+&lt;/span&gt;&lt;span class="nx"&gt;MAX&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;                    &lt;span class="c1"&gt;// moved right (deletion)&lt;/span&gt;
    &lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;y&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;x&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="nx"&gt;k&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="k"&gt;while &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;x&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="nx"&gt;N&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="nx"&gt;y&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="nx"&gt;M&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="nx"&gt;a&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;x&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="nx"&gt;b&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;y&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;x&lt;/span&gt;&lt;span class="o"&gt;++&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="nx"&gt;y&lt;/span&gt;&lt;span class="o"&gt;++&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;   &lt;span class="c1"&gt;// free diagonal&lt;/span&gt;
    &lt;span class="nx"&gt;V&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;k&lt;/span&gt;&lt;span class="o"&gt;+&lt;/span&gt;&lt;span class="nx"&gt;MAX&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;x&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="nx"&gt;x&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;=&lt;/span&gt; &lt;span class="nx"&gt;N&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="nx"&gt;y&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;=&lt;/span&gt; &lt;span class="nx"&gt;M&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;distance&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;D&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;script&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nf"&gt;backtrack&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;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  🔴 The speed claim is half a truth
&lt;/h2&gt;

&lt;p&gt;Everyone quotes O((N+M)D) against O(NM) and stops. Measured over 800 generated pairs:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;corpus&lt;/th&gt;
&lt;th&gt;worst D&lt;/th&gt;
&lt;th&gt;Myers work vs an O(NM) table&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;a 40-line file with 4 edits&lt;/td&gt;
&lt;td&gt;8&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;1.6%&lt;/strong&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;random strings, 4-symbol alphabet&lt;/td&gt;
&lt;td&gt;14&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;105%&lt;/strong&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;On random strings D grows with the input, O((N+M)D) degenerates, and Myers does &lt;strong&gt;more work than the table it replaced&lt;/strong&gt;. My first draft of this page quoted only the flattering corpus; the in-page check caught it.&lt;/p&gt;

&lt;p&gt;The speed is not a property of the algorithm. It is a property of the algorithm &lt;strong&gt;meeting the inputs it was designed for&lt;/strong&gt; — and files people actually diff are nearly identical, which is why it wins in practice.&lt;/p&gt;

&lt;h2&gt;
  
  
  The only honest test of a diff
&lt;/h2&gt;

&lt;p&gt;Not "does it look right". &lt;strong&gt;Does the script rebuild the target?&lt;/strong&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="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;apply&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;a&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;script&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;out&lt;/span&gt; &lt;span class="o"&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;i&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;0&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;e&lt;/span&gt; &lt;span class="k"&gt;of&lt;/span&gt; &lt;span class="nx"&gt;script&lt;/span&gt;&lt;span class="p"&gt;)&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="nx"&gt;e&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;op&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;keep&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;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;a&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;i&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;!==&lt;/span&gt; &lt;span class="nx"&gt;e&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;line&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;keep does not match&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt; &lt;span class="nx"&gt;out&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="nx"&gt;a&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;i&lt;/span&gt;&lt;span class="o"&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;else&lt;/span&gt; &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;e&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;op&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;del&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;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;a&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;i&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;!==&lt;/span&gt; &lt;span class="nx"&gt;e&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;line&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;delete does not match&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt; &lt;span class="nx"&gt;i&lt;/span&gt;&lt;span class="o"&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;else&lt;/span&gt; &lt;span class="nx"&gt;out&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="nx"&gt;e&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;line&lt;/span&gt;&lt;span class="p"&gt;);&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="nx"&gt;i&lt;/span&gt; &lt;span class="o"&gt;!==&lt;/span&gt; &lt;span class="nx"&gt;a&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;length&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;script consumed &lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="nx"&gt;i&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt; of &lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="nx"&gt;a&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;length&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;out&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;800 of 800&lt;/strong&gt; generated pairs rebuilt exactly, with &lt;strong&gt;D = N + M − 2L&lt;/strong&gt; satisfied every time against an independently computed longest common subsequence.&lt;/p&gt;

&lt;h2&gt;
  
  
  🔴 The bug in the V array
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;V&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;Array&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="nx"&gt;MAX&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;fill&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="c1"&gt;// wrong&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;V&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;Array&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="nx"&gt;MAX&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;fill&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="c1"&gt;// right&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The search reads &lt;code&gt;V[k-1]&lt;/code&gt; and &lt;code&gt;V[k+1]&lt;/code&gt;, so both ends need a slot of headroom. With &lt;strong&gt;two empty inputs&lt;/strong&gt; MAX is 0, the array is one slot, the read runs off the end, returns &lt;code&gt;undefined&lt;/code&gt;, and the loop never terminates. Every non-degenerate case worked perfectly.&lt;/p&gt;

&lt;h2&gt;
  
  
  Shortest is not readable
&lt;/h2&gt;

&lt;p&gt;Several edit scripts can be equally short and they are not equally good to read:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;change&lt;/th&gt;
&lt;th&gt;D&lt;/th&gt;
&lt;th&gt;hunks&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;two lines changed, adjacent&lt;/td&gt;
&lt;td&gt;4&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;1&lt;/strong&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;two lines changed, far apart&lt;/td&gt;
&lt;td&gt;4&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;2&lt;/strong&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;Same minimal distance, one tidy block versus two separate places to look. &lt;strong&gt;Minimising D says nothing whatever about this&lt;/strong&gt; — which is why real diff tools bolt heuristics on top rather than trusting minimality alone.&lt;/p&gt;

&lt;h2&gt;
  
  
  How it is checked
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;188 asserts&lt;/strong&gt; by three routes the page does not use: &lt;strong&gt;breadth-first search&lt;/strong&gt; over the edit graph by layers, a &lt;strong&gt;full-table LCS&lt;/strong&gt; with the subsequence reconstructed and verified as a subsequence of both inputs, and an exhaustive proof that &lt;strong&gt;no script shorter than D exists&lt;/strong&gt; for small inputs.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;188 verifier asserts, 27 in-page checks, 0 failures.&lt;/strong&gt;&lt;/p&gt;

</description>
      <category>algorithms</category>
      <category>git</category>
      <category>javascript</category>
      <category>programming</category>
    </item>
    <item>
      <title>Five Stages of Self-Critique Took 5/17 to 15/17. Removing One Gate Threw All of It Away.</title>
      <dc:creator>Devanshu Biswas</dc:creator>
      <pubDate>Mon, 12 Oct 2026 11:18:55 +0000</pubDate>
      <link>https://dev.to/dev48v/five-stages-of-self-critique-took-517-to-1517-removing-one-gate-threw-all-of-it-away-2e53</link>
      <guid>https://dev.to/dev48v/five-stages-of-self-critique-took-517-to-1517-removing-one-gate-threw-all-of-it-away-2e53</guid>
      <description>&lt;p&gt;Metacognitive Prompting borrows the shape of human self-monitoring. Instead of one shot at an answer, the model walks five stages: &lt;strong&gt;understand&lt;/strong&gt; the question, form a &lt;strong&gt;preliminary&lt;/strong&gt; answer, &lt;strong&gt;critique&lt;/strong&gt; it, &lt;strong&gt;decide&lt;/strong&gt;, and report &lt;strong&gt;confidence&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;The claim is that the critique catches what the first pass got wrong. To measure that rather than assert it, everything here runs against a deterministic model with &lt;strong&gt;known, deliberate failure modes&lt;/strong&gt; against tasks whose answers are known. No API, no sampling. Re-run it and the numbers repeat.&lt;/p&gt;

&lt;p&gt;👉 &lt;strong&gt;Live, runs in your browser:&lt;/strong&gt; &lt;a href="https://dev48.in/prompt/day81-metacognitive-prompting.html" rel="noopener noreferrer"&gt;https://dev48.in/prompt/day81-metacognitive-prompting.html&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  The gate is the whole design
&lt;/h2&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;pipeline&lt;/th&gt;
&lt;th&gt;correct&lt;/th&gt;
&lt;th&gt;rescued a wrong answer&lt;/th&gt;
&lt;th&gt;ruined a right one&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;single pass (baseline)&lt;/td&gt;
&lt;td&gt;5 / 17&lt;/td&gt;
&lt;td&gt;—&lt;/td&gt;
&lt;td&gt;—&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;metacognitive, gated&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;15 / 17&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;10&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;0&lt;/strong&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;metacognitive, ungated&lt;/td&gt;
&lt;td&gt;5 / 17&lt;/td&gt;
&lt;td&gt;5&lt;/td&gt;
&lt;td&gt;5&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;Stage 3 must be allowed to &lt;strong&gt;find nothing&lt;/strong&gt;, and stage 4 must act only on a named defect:&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="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;decide&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;prelim&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;crit&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;gated&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="o"&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;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;gated&lt;/span&gt;&lt;span class="p"&gt;)&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;crit&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;defect&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;answer&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;prelim&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;revised&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;false&lt;/span&gt; &lt;span class="p"&gt;};&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;answer&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;crit&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;suggests&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;revised&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt; &lt;span class="p"&gt;};&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="c1"&gt;// the control: always "improve" something, even with nothing named&lt;/span&gt;
  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;answer&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;crit&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;suggests&lt;/span&gt; &lt;span class="o"&gt;??&lt;/span&gt; &lt;span class="nf"&gt;perturb&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;prelim&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;revised&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt; &lt;span class="p"&gt;};&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;🔴 &lt;strong&gt;Ungated revision throws the entire gain away.&lt;/strong&gt; It lands back at 5/17 — level with running no stages at all — and it &lt;em&gt;undoes the five misreads stage 1 had just fixed&lt;/em&gt;. Ask a model to find fault and it will always find fault.&lt;/p&gt;

&lt;h2&gt;
  
  
  Which stage earns which fix — by ablation, not by claim
&lt;/h2&gt;

&lt;p&gt;Run the pipeline with stage 1 removed, with stage 3 removed, and with both, then attribute each recovered task to the stage whose removal loses it.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Remove &lt;strong&gt;comprehension&lt;/strong&gt; → only the &lt;strong&gt;misreads&lt;/strong&gt; are lost.&lt;/li&gt;
&lt;li&gt;Remove &lt;strong&gt;critique&lt;/strong&gt; → only the &lt;strong&gt;rule slips&lt;/strong&gt; are.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;No task needs both.&lt;/strong&gt; The two gains sum to the total with nothing unattributed.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;🔴 That attribution did not hold at first. My critique stage had a branch that caught misreads, so stage 1 was recovering nothing and the ablation measured 0. The branch was wrong and had to go: &lt;strong&gt;an answer carries no evidence that a different question was answered.&lt;/strong&gt; Only the restatement from stage 1 holds that evidence. Giving the critic a misread branch makes stage 1 decorative — and is a lie about what a critic can see.&lt;/p&gt;

&lt;h2&gt;
  
  
  Stage 5 is worse than useless
&lt;/h2&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;reported confidence&lt;/th&gt;
&lt;th&gt;tasks&lt;/th&gt;
&lt;th&gt;actually correct&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;95%&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;2&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;0%&lt;/strong&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;90%&lt;/td&gt;
&lt;td&gt;10&lt;/td&gt;
&lt;td&gt;100%&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;70%&lt;/td&gt;
&lt;td&gt;5&lt;/td&gt;
&lt;td&gt;100%&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;The model is &lt;strong&gt;surest exactly where it is least entitled to be&lt;/strong&gt;. The 95% bucket is the underdetermined questions — the ones with no answer — because there is nothing there to hesitate about. The most confident bucket is the least accurate one.&lt;/p&gt;

&lt;p&gt;Adding a confidence stage is cheap and plausible. It is also, on this task set, actively misleading. If you ship one, measure its calibration or do not report it.&lt;/p&gt;

&lt;h2&gt;
  
  
  🔴 And the mistake that nearly hid all of this
&lt;/h2&gt;

&lt;p&gt;My first task set was &lt;strong&gt;100% traps&lt;/strong&gt;. The single-pass baseline scored 0/12, so nothing could possibly be worse than it, and the harm of ungated revision was invisible — it rescued 5 and ruined 5 of nothing.&lt;/p&gt;

&lt;p&gt;Clean tasks had to be added before the damage was measurable at all. &lt;strong&gt;A benchmark made only of failures cannot measure harm&lt;/strong&gt;, because there is nothing left to break.&lt;/p&gt;

&lt;h2&gt;
  
  
  The honest limit
&lt;/h2&gt;

&lt;p&gt;Overreach — confidently answering a question that has no answer — survives all five stages. Nothing in the pipeline looks for an underdetermined question, so nothing catches it.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;120 verifier asserts, 34 in-page checks, 0 failures.&lt;/strong&gt;&lt;/p&gt;

</description>
      <category>ai</category>
      <category>llm</category>
      <category>prompteng</category>
      <category>programming</category>
    </item>
    <item>
      <title>The Dynamic Island Is a Priority Queue, and Its Corner Radius Is a Constraint</title>
      <dc:creator>Devanshu Biswas</dc:creator>
      <pubDate>Mon, 12 Oct 2026 11:18:14 +0000</pubDate>
      <link>https://dev.to/dev48v/the-dynamic-island-is-a-priority-queue-and-its-corner-radius-is-a-constraint-23gl</link>
      <guid>https://dev.to/dev48v/the-dynamic-island-is-a-priority-queue-and-its-corner-radius-is-a-constraint-23gl</guid>
      <description>&lt;p&gt;Apple's Dynamic Island is one black rounded rectangle that changes size. What makes it feel alive is not the animation. It is that &lt;strong&gt;two things can want it at once&lt;/strong&gt;, and the rules for who gets it are fixed, total and deterministic.&lt;/p&gt;

&lt;p&gt;👉 &lt;strong&gt;Live, runs in your browser:&lt;/strong&gt; &lt;a href="https://dev48.in/design/day81-dynamic-island.html" rel="noopener noreferrer"&gt;https://dev48.in/design/day81-dynamic-island.html&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Not a stack
&lt;/h2&gt;

&lt;p&gt;The tempting model is a stack: whatever started last is on top. It is wrong in a way users notice immediately — start a timer during a phone call and the call disappears.&lt;/p&gt;

&lt;p&gt;The real model is a &lt;strong&gt;priority queue keyed by the kind of activity&lt;/strong&gt;, with start time used only to break ties between two activities of equal rank.&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="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;compareActivities&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;a&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;b&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;ra&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;KINDS&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;a&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;kind&lt;/span&gt;&lt;span class="p"&gt;].&lt;/span&gt;&lt;span class="nx"&gt;rank&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;rb&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;KINDS&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;b&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;kind&lt;/span&gt;&lt;span class="p"&gt;].&lt;/span&gt;&lt;span class="nx"&gt;rank&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="nx"&gt;ra&lt;/span&gt; &lt;span class="o"&gt;!==&lt;/span&gt; &lt;span class="nx"&gt;rb&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;ra&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="nx"&gt;rb&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;                  &lt;span class="c1"&gt;// call beats timer beats music&lt;/span&gt;
  &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;a&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;startedAt&lt;/span&gt; &lt;span class="o"&gt;!==&lt;/span&gt; &lt;span class="nx"&gt;b&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;startedAt&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;a&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;startedAt&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="nx"&gt;b&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;startedAt&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;a&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;id&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="nx"&gt;b&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;id&lt;/span&gt; &lt;span class="p"&gt;?&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;a&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;id&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;b&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;id&lt;/span&gt; &lt;span class="p"&gt;?&lt;/span&gt; &lt;span class="mi"&gt;1&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="c1"&gt;// total, never 0 for distinct ids&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Checked over &lt;strong&gt;all 31 non-empty subsets&lt;/strong&gt; of a five-activity pool, in &lt;strong&gt;every permutation&lt;/strong&gt; of each: one order-independent owner every time. Both models agree while only one activity runs, which is exactly why the stack version ships and then breaks in the field.&lt;/p&gt;

&lt;h2&gt;
  
  
  The corner radius is not a style
&lt;/h2&gt;

&lt;p&gt;Pick a fixed radius and animate the box from the small pill to the tall expanded card, and the corners visibly change character. The shape stops reading as the same object and starts reading as a box that grew.&lt;/p&gt;

&lt;p&gt;The radius has to be a &lt;strong&gt;function of the height&lt;/strong&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="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;radiusFor&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;h&lt;/span&gt;&lt;span class="p"&gt;)&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="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;h&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="mi"&gt;0&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;height must be positive&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="k"&gt;return&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;min&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;SCREEN_R&lt;/span&gt;&lt;span class="p"&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;max&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;MIN_R&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;h&lt;/span&gt; &lt;span class="o"&gt;/&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;A single fixed 22 is wrong by up to &lt;strong&gt;10 points&lt;/strong&gt; across the four presentations — enough that the small pill looks over-rounded and the sheet looks square.&lt;/p&gt;

&lt;h2&gt;
  
  
  🔴 Two bugs the verifier found
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;A stateless resolver teleports.&lt;/strong&gt; Given the current state it happily names a presentation several hops away. Ending the last activity while expanded resolves straight to &lt;code&gt;minimal&lt;/code&gt; — and &lt;code&gt;expanded → minimal&lt;/code&gt; is not a transition the machine allows. An exhaustive replay of &lt;strong&gt;every 4-event sequence&lt;/strong&gt; found &lt;strong&gt;36&lt;/strong&gt; such jumps.&lt;/p&gt;

&lt;p&gt;The fix is not to widen the transition table. It is to make the machine &lt;strong&gt;route&lt;/strong&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="nf"&gt;transitionPath&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;expanded&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;minimal&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;  &lt;span class="c1"&gt;// =&amp;gt; ["expanded", "compact", "minimal"]&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;An interrupted expansion came back by itself.&lt;/strong&gt; Expand the music, take a call, end the call — and the music silently reopened in its expanded state. The user expanded it, something took the island, and that expansion is gone for good:&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="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;owner&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;ownerOf&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;s&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;activities&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="nx"&gt;s&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;expandedBy&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="nx"&gt;owner&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="nx"&gt;owner&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;id&lt;/span&gt; &lt;span class="o"&gt;!==&lt;/span&gt; &lt;span class="nx"&gt;s&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;expandedBy&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt; &lt;span class="nx"&gt;s&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;expandedBy&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;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  The spring has to land exactly
&lt;/h2&gt;

&lt;p&gt;A spring that merely approaches its target is a bug. The island has to arrive at a whole number of points and &lt;strong&gt;stop&lt;/strong&gt;, or the next transition starts from a position that is off by a fraction and the error compounds. Critically damped, then snap once distance and velocity are both under a threshold.&lt;/p&gt;

&lt;p&gt;The verifier checks this against the &lt;strong&gt;closed form&lt;/strong&gt; of a critically damped oscillator —&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;x(t) = target + (A + Bt)·e^(−ωt),  ω = √k&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;— because "it never overshoots" is a property of the solution, and no amount of stepping establishes it.&lt;/p&gt;

&lt;h2&gt;
  
  
  One scalar, not two
&lt;/h2&gt;

&lt;p&gt;Animate width and height as separate springs and the island passes through shapes that never existed as a presentation. One scalar drives both, so every frame lies on the straight line between two real presentations. Worst deviation: &lt;strong&gt;0&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;🔴 Two independently tuned springs wander up to &lt;strong&gt;15 points&lt;/strong&gt; off that line — &lt;strong&gt;and still arrive at the right size&lt;/strong&gt;. Which is precisely why a screenshot test of the end state misses this bug entirely.&lt;/p&gt;

&lt;h2&gt;
  
  
  🔴 And one claim of mine that was wrong
&lt;/h2&gt;

&lt;p&gt;I asserted in the verifier that at least one transition pair must be one-way. It failed. The size ladder is &lt;strong&gt;symmetric&lt;/strong&gt; by design: every legal step is legal in reverse. That is a design choice worth pinning down, not an oversight.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;210 verifier asserts, 35 in-page checks, 0 failures.&lt;/strong&gt;&lt;/p&gt;

</description>
      <category>webdev</category>
      <category>javascript</category>
      <category>ux</category>
      <category>programming</category>
    </item>
    <item>
      <title>34 Assignments Satisfy Every Clue and Every Corner. Only 22 Are Solutions.</title>
      <dc:creator>Devanshu Biswas</dc:creator>
      <pubDate>Mon, 12 Oct 2026 11:17:33 +0000</pubDate>
      <link>https://dev.to/dev48v/34-assignments-satisfy-every-clue-and-every-corner-only-22-are-solutions-28fg</link>
      <guid>https://dev.to/dev48v/34-assignments-satisfy-every-clue-and-every-corner-only-22-are-solutions-28fg</guid>
      <description>&lt;p&gt;Slitherlink gives you a grid. Some cells carry a number from 0 to 3. You draw segments along the grid lines, and when you are done they must form &lt;strong&gt;one single closed loop&lt;/strong&gt; — no branches, no loose ends, and no second loop sitting somewhere else on the board. A cell marked 3 has exactly three of its four sides on that loop.&lt;/p&gt;

&lt;p&gt;👉 &lt;strong&gt;Live, runs in your browser:&lt;/strong&gt; &lt;a href="https://dev48.in/game/day81-slitherlink.html" rel="noopener noreferrer"&gt;https://dev48.in/game/day81-slitherlink.html&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  The two rules everyone implements
&lt;/h2&gt;

&lt;p&gt;They are both easy and both local.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Clues.&lt;/strong&gt; Count the drawn sides of each numbered cell. It must equal the number.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Degrees.&lt;/strong&gt; Every lattice vertex must have &lt;strong&gt;0&lt;/strong&gt; segments touching it, or exactly &lt;strong&gt;2&lt;/strong&gt; — one in, one out. Anything else is a loose end or a junction.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Write a solver that enforces exactly those and it looks finished. It is not.&lt;/p&gt;

&lt;h2&gt;
  
  
  What the count says
&lt;/h2&gt;

&lt;p&gt;Here is every edge assignment on one 3×3 board that satisfies &lt;strong&gt;every clue&lt;/strong&gt; and leaves &lt;strong&gt;every vertex at degree 0 or 2&lt;/strong&gt;, grouped by how many loops it actually drew:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;what the segments drew&lt;/th&gt;
&lt;th&gt;assignments&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;one loop — a real solution&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;22&lt;/strong&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;2 separate loops&lt;/td&gt;
&lt;td&gt;7&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;3 separate loops&lt;/td&gt;
&lt;td&gt;4&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;4 separate loops&lt;/td&gt;
&lt;td&gt;1&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;&lt;strong&gt;34 pass both local tests. 22 are solutions.&lt;/strong&gt; A solver that stops there accepts twelve boards it must refuse, on this grid alone.&lt;/p&gt;

&lt;p&gt;The reason is structural, not a corner case. A graph in which every vertex has degree 0 or 2 is a &lt;strong&gt;disjoint union of cycles&lt;/strong&gt;. It may be one cycle, or two, or five. Slitherlink demands exactly one, and that condition is &lt;strong&gt;global&lt;/strong&gt; — no amount of looking at one cell or one corner can see it.&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="c1"&gt;// necessary, and nowhere near sufficient&lt;/span&gt;
&lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;degreesLegal&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;clues&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;on&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;v&lt;/span&gt; &lt;span class="k"&gt;of&lt;/span&gt; &lt;span class="nx"&gt;vertices&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;d&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;incidentEdges&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;v&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;filter&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;e&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;on&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;e&lt;/span&gt;&lt;span class="p"&gt;]).&lt;/span&gt;&lt;span class="nx"&gt;length&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="nx"&gt;d&lt;/span&gt; &lt;span class="o"&gt;!==&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="nx"&gt;d&lt;/span&gt; &lt;span class="o"&gt;!==&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="c1"&gt;// the part that is not local: walk the on-edges and count components&lt;/span&gt;
&lt;span class="nf"&gt;loopCount&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;clues&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;on&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Making the search finish
&lt;/h2&gt;

&lt;p&gt;A 3×3 board has 24 candidate segments, so brute force is 2²⁴ — about 16.7 million. Two cheap prunes collapse it.&lt;/p&gt;

&lt;p&gt;Assign edges in index order. &lt;strong&gt;The moment the last edge of a clued cell is decided, that clue is final&lt;/strong&gt; — check it now rather than at the leaf. Same for a vertex: once every edge touching it has a value, its degree is final and must be 0 or 2. Both tests fire deep in the tree.&lt;/p&gt;

&lt;p&gt;The pruned search touches &lt;strong&gt;0.1752%&lt;/strong&gt; of the brute-force tree. Without the prunes the 3×3 boards do not finish.&lt;/p&gt;

&lt;h2&gt;
  
  
  Every 2×2 board there is
&lt;/h2&gt;

&lt;p&gt;Five possibilities per cell — blank, 0, 1, 2, 3 — over four cells. 625 boards:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;&lt;/th&gt;
&lt;th&gt;boards&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;no solution at all&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;523&lt;/strong&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;exactly one — a real puzzle&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;61&lt;/strong&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;more than one — ambiguous&lt;/td&gt;
&lt;td&gt;41&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;Most boards are not puzzles. That is worth knowing before you write a generator.&lt;/p&gt;

&lt;h2&gt;
  
  
  🔴 And a 4 is legal
&lt;/h2&gt;

&lt;p&gt;I asserted in the self-check that a 4 is impossible, and the check failed. It is satisfiable: the loop is that cell's own perimeter, four segments, every corner at degree 2. Standard puzzles omit 4 &lt;strong&gt;because it solves itself&lt;/strong&gt;, not because it is illegal.&lt;/p&gt;

&lt;p&gt;A lone 3 on a 1×1 board genuinely is impossible — three sides is an open path.&lt;/p&gt;

&lt;h2&gt;
  
  
  How it is checked
&lt;/h2&gt;

&lt;p&gt;The page runs 24 checks in your browser. A separate verifier runs &lt;strong&gt;210 asserts&lt;/strong&gt; by a route the page does not use: it &lt;strong&gt;enumerates every simple cycle&lt;/strong&gt; in the lattice graph by depth-first search, so "it is one closed loop" is true by construction and never tested for. A solution is then just a cycle whose edges happen to satisfy the clues.&lt;/p&gt;

&lt;p&gt;That verifier found a real gap in my own reasoning. I first checked single-loop-ness as &lt;em&gt;connected, with edges equal to vertices&lt;/em&gt;. That also admits a &lt;strong&gt;lollipop&lt;/strong&gt; — one cycle with a tree hanging off it. The degrees have to be checked too.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;210 verifier asserts, 24 in-page checks, 0 failures.&lt;/strong&gt; One file, no build step, no dependencies.&lt;/p&gt;

</description>
      <category>algorithms</category>
      <category>javascript</category>
      <category>programming</category>
      <category>puzzle</category>
    </item>
    <item>
      <title>Your Withdrawal Rate Moves Time-to-FI by 31 Months. Your Balance, the Number in the Biggest Font, Moves It 3.</title>
      <dc:creator>Devanshu Biswas</dc:creator>
      <pubDate>Thu, 17 Sep 2026 12:35:10 +0000</pubDate>
      <link>https://dev.to/dev48v/your-withdrawal-rate-moves-time-to-fi-by-31-months-your-balance-the-number-in-the-biggest-font-23d7</link>
      <guid>https://dev.to/dev48v/your-withdrawal-rate-moves-time-to-fi-by-31-months-your-balance-the-number-in-the-biggest-font-23d7</guid>
      <description>&lt;p&gt;A freedom calculator answers three questions — how long the money lasts, what number ends the job, how far away that is — and they are &lt;strong&gt;one recurrence asked from two sides&lt;/strong&gt;. Build it exactly, measure it honestly, and the finding is not that the arithmetic is wrong. It is that the answer is mostly a statement about assumptions, and the ones that move it most are the ones the genre does not show you.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Weekend Builds Vol 4 · #05 · the finale — Vol 4 is now complete at 5 of 5.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;👉 &lt;strong&gt;Live, runs in your browser:&lt;/strong&gt; &lt;a href="https://dev48v.infy.uk/agentlab/vol4-05-freedom-calculator.html" rel="noopener noreferrer"&gt;https://dev48v.infy.uk/agentlab/vol4-05-freedom-calculator.html&lt;/a&gt;&lt;br&gt;
👉 &lt;strong&gt;PUBLIC, MIT, &lt;code&gt;dependencies = []&lt;/code&gt;:&lt;/strong&gt; &lt;a href="https://github.com/dev48v/freedom-calculator" rel="noopener noreferrer"&gt;https://github.com/dev48v/freedom-calculator&lt;/a&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;python &lt;span class="nt"&gt;-m&lt;/span&gt; freedom_calculator                &lt;span class="c"&gt;# runway, the FI number, time to FI&lt;/span&gt;
python &lt;span class="nt"&gt;-m&lt;/span&gt; freedom_calculator &lt;span class="nt"&gt;--sensitivity&lt;/span&gt;  &lt;span class="c"&gt;# which input the answer rests on&lt;/span&gt;
python &lt;span class="nt"&gt;-m&lt;/span&gt; freedom_calculator &lt;span class="nt"&gt;--assumptions&lt;/span&gt;  &lt;span class="c"&gt;# the model, and what it costs&lt;/span&gt;
python &lt;span class="nt"&gt;-m&lt;/span&gt; freedom_calculator &lt;span class="nt"&gt;--float&lt;/span&gt;        &lt;span class="c"&gt;# why money is not a float here&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Declared inputs, derived signs, measured spans
&lt;/h2&gt;

&lt;p&gt;You declare five numbers. The scenario on the page: 42,000 balance, 3,200 a month out, 5,400 a month in, 5% real return, 4% withdrawal rate. From those: an FI number of &lt;strong&gt;960,000&lt;/strong&gt; (25× annual spend) and &lt;strong&gt;231 months&lt;/strong&gt; to reach it.&lt;/p&gt;

&lt;p&gt;Every direction below is &lt;strong&gt;derived on paper&lt;/strong&gt; from the recurrence, then &lt;strong&gt;measured&lt;/strong&gt; by bumping the input and watching the answer. The two are kept apart so they can be checked against each other, and the suite asserts they agree for every input at six bump sizes — plus a non-vacuity test, because a calculator that ignored its inputs entirely would pass a sign check perfectly.&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;±10% on…&lt;/th&gt;
&lt;th&gt;months, down / base / up&lt;/th&gt;
&lt;th&gt;span&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;what you spend a month&lt;/td&gt;
&lt;td&gt;198 / 231 / 269&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;71&lt;/strong&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;what you earn a month&lt;/td&gt;
&lt;td&gt;271 / 231 / 202&lt;/td&gt;
&lt;td&gt;69&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;
&lt;strong&gt;withdrawal rate&lt;/strong&gt; (usually hidden)&lt;/td&gt;
&lt;td&gt;248 / 231 / 217&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;31&lt;/strong&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;real return you assume&lt;/td&gt;
&lt;td&gt;241 / 231 / 222&lt;/td&gt;
&lt;td&gt;19&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;balance you already have&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;233 / 231 / 230&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;3&lt;/strong&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;The row to sit with is the last one. The genre is arranged around a number worth about a tenth of what the hidden constant is worth.&lt;/p&gt;

&lt;p&gt;There is also a row that must be exactly zero: &lt;strong&gt;runway does not depend on the withdrawal rate at all.&lt;/strong&gt; It is a drawdown question. The derived sign is 0 and the measured direction must be 0 too — not small, not usually, &lt;em&gt;exactly&lt;/em&gt;, at every bump size on every scenario. That identity is what stops the two halves of the calculator leaking into each other.&lt;/p&gt;

&lt;h2&gt;
  
  
  The argument people actually have, priced
&lt;/h2&gt;

&lt;p&gt;The 4% rule is a rule about a number under argument — 3% for the cautious, 5% for the cheerful:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;withdrawal rate&lt;/th&gt;
&lt;th&gt;FI number&lt;/th&gt;
&lt;th&gt;months&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;3%&lt;/td&gt;
&lt;td&gt;1,280,000&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;278&lt;/strong&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;4%&lt;/td&gt;
&lt;td&gt;960,000&lt;/td&gt;
&lt;td&gt;231&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;5%&lt;/td&gt;
&lt;td&gt;768,000&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;198&lt;/strong&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;&lt;strong&gt;512,000&lt;/strong&gt; of spread — &lt;strong&gt;53.3%&lt;/strong&gt; of the headline number — and &lt;strong&gt;80 months&lt;/strong&gt; of your life, from one constant most calculators will not let you type.&lt;/p&gt;

&lt;p&gt;Forgetting to subtract inflation is worth &lt;strong&gt;43 months&lt;/strong&gt;: a nominal 8% instead of a real 5% brings the date from 231 months forward to 188, on which date you actually hold ~717,562 against a target of 960,000.&lt;/p&gt;

&lt;h2&gt;
  
  
  Money is an exact rational because the output is an integer
&lt;/h2&gt;

&lt;p&gt;Not fastidiousness. The output is a &lt;strong&gt;month count that turns on a comparison&lt;/strong&gt;, and a comparison decided by the last bit of a binary approximation is a month count decided by the last bit of a binary approximation.&lt;/p&gt;

&lt;p&gt;Over 120,000 balances that are exactly &lt;em&gt;n&lt;/em&gt; months of spend, &lt;code&gt;floor(balance / spend)&lt;/code&gt; in doubles answers &lt;em&gt;n&lt;/em&gt;−1 in &lt;strong&gt;12,736&lt;/strong&gt; of them — &lt;strong&gt;10.61%&lt;/strong&gt;. Exact rationals: &lt;strong&gt;0&lt;/strong&gt;. Whole months, not cents.&lt;/p&gt;

&lt;p&gt;The zero-net-burn case is worse. Summed as floats, the same basket of line items lands on a net burn of &lt;code&gt;2.27e-13&lt;/code&gt; instead of zero, and a calculator that divides reports 43,980,465,111,040,000 months with a straight face. This package reaches the unbounded answer with a comparison and &lt;strong&gt;no division at all&lt;/strong&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="c1"&gt;// runway and time-to-FI are the SAME recurrence, asked from two sides&lt;/span&gt;
&lt;span class="c1"&gt;//     b[m+1] = b[m] * (1 + r) + flow          flow = income - spend&lt;/span&gt;
&lt;span class="c1"&gt;// the zero-net-burn identity, decided BEFORE any division ever happens&lt;/span&gt;
&lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;cmp&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;add&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;mul&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;balance&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;r&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="nx"&gt;flow&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="nx"&gt;ZERO&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;=&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;status&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;unbounded&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt; &lt;span class="p"&gt;};&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;A test walks the syntax tree of &lt;code&gt;runway()&lt;/code&gt; and asserts there is no division operator in it, with docstrings stripped first so it cannot pass on the paragraph explaining that the division is not there.&lt;/p&gt;

&lt;h2&gt;
  
  
  Same returns, reversed, a 17× different answer
&lt;/h2&gt;

&lt;p&gt;The model has no sequence-of-returns risk in it. That is a limitation, and here is what it costs — with no RNG anywhere. One declared list of 30 yearly real returns, mean &lt;strong&gt;exactly&lt;/strong&gt; the same 5% the smooth model uses, applied forward, then reversed. Start at the FI number, draw 4%:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;order&lt;/th&gt;
&lt;th&gt;ending balance&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;forward (crash at the front)&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;~127,381&lt;/strong&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;reversed&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;~2,163,974&lt;/strong&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;the smooth model's answer&lt;/td&gt;
&lt;td&gt;~1,625,807&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;&lt;strong&gt;17.0× from order alone.&lt;/strong&gt; Same numbers, same mean, same multiset, same product — and the control proves the product is identical: withdraw nothing and the two orders end on the &lt;strong&gt;identical cent&lt;/strong&gt;, 4,176,967.52, because multiplication commutes. The entire gap is &lt;em&gt;order&lt;/em&gt;, and the smooth model's answer sits between the two giving no hint which one you got.&lt;/p&gt;

&lt;p&gt;The sequence was built to put the crash at the front. Stated, not hidden — the size of the crash is not the point. The point is that reversing a list changes the answer &lt;em&gt;at all&lt;/em&gt;, which the smooth model says is impossible.&lt;/p&gt;

&lt;h2&gt;
  
  
  What this is not
&lt;/h2&gt;

&lt;p&gt;Deterministic arithmetic on declared inputs. No RNG, no Monte Carlo, no historical data. Constant real returns, constant burn in today's money, &lt;strong&gt;no taxes&lt;/strong&gt;, no lumpy years, no property, no state pension. The monthly rate is the annual rate over twelve rather than its twelfth root — the usual convention, and slightly generous. Anything past a 100-year horizon is reported as &lt;em&gt;beyond the horizon&lt;/em&gt; rather than as a number, because a model that answers "3,847 months" is not being more precise, it is being less honest.&lt;/p&gt;

&lt;p&gt;The JavaScript on the page is exact rationals over BigInt, checked against the Python's &lt;code&gt;reference.json&lt;/code&gt; numerator for denominator, and against a third implementation in the verifier that simulates month by month instead of using the closed form.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;50 pytest · 239 verifier asserts · 44 in-page self-checks · 0 failures.&lt;/strong&gt; MIT, Python standard library only.&lt;/p&gt;

</description>
      <category>python</category>
      <category>opensource</category>
      <category>programming</category>
      <category>webdev</category>
    </item>
    <item>
      <title>180 RAG Citation Defects Only a Free id Lookup Catches, 60 Only the Grader Does: Neither Subsumes the Other</title>
      <dc:creator>Devanshu Biswas</dc:creator>
      <pubDate>Thu, 17 Sep 2026 12:34:28 +0000</pubDate>
      <link>https://dev.to/dev48v/180-rag-citation-defects-only-a-free-id-lookup-catches-60-only-the-grader-does-neither-subsumes-id4</link>
      <guid>https://dev.to/dev48v/180-rag-citation-defects-only-a-free-id-lookup-catches-60-only-the-grader-does-neither-subsumes-id4</guid>
      <description>&lt;p&gt;A RAG answer says &lt;code&gt;[c091]&lt;/code&gt; and everybody relaxes. There are two checks you can run on that bracket, and they catch different things.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Citation resolution&lt;/strong&gt; is pure code: is &lt;code&gt;c091&lt;/code&gt; actually one of the chunks we assembled for &lt;em&gt;this&lt;/em&gt; question? Zero model calls. &lt;strong&gt;Faithfulness&lt;/strong&gt; is model-scored: does the assembled context support the claim?&lt;/p&gt;

&lt;p&gt;👉 &lt;strong&gt;Live, runs in your browser:&lt;/strong&gt; &lt;a href="https://dev48v.infy.uk/ai/days/day80-citation-verification.html" rel="noopener noreferrer"&gt;https://dev48v.infy.uk/ai/days/day80-citation-verification.html&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  The 2×2, and why both off-diagonals matter
&lt;/h2&gt;

&lt;p&gt;420 seeded answers, 7 defect classes, 60 each:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;&lt;/th&gt;
&lt;th&gt;faithfulness says supported&lt;/th&gt;
&lt;th&gt;says unsupported&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;resolution passes&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;120&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;60&lt;/strong&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;resolution fails&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;180&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;60&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;If either off-diagonal cell were empty, one check would subsume the other and you could drop it. Both are populated &lt;strong&gt;by measurement&lt;/strong&gt;: 180 answers where the identifier is wrong and the prose is fine, 60 where the identifier is fine and the prose is invented.&lt;/p&gt;

&lt;p&gt;A pipeline running one check is not running a cheaper version of the other. It is running a different check whose blind spot is exactly what the other one is for.&lt;/p&gt;

&lt;h2&gt;
  
  
  The class people get wrong
&lt;/h2&gt;

&lt;p&gt;&lt;code&gt;stale_id&lt;/code&gt;: a &lt;strong&gt;real&lt;/strong&gt; store id that was &lt;strong&gt;not in this assembled context&lt;/strong&gt;. It looks real. It resolves perfectly against your vector store.&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;check&lt;/th&gt;
&lt;th&gt;catches&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;resolution against the &lt;strong&gt;assembled context&lt;/strong&gt;
&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;60 / 60&lt;/strong&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;resolution against the &lt;strong&gt;chunk store&lt;/strong&gt;
&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;0 / 60&lt;/strong&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;Same check, one wrong set. That is the entire difference between a working guard and a decorative one:&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="c1"&gt;// check 1 — resolution. No model. This is the entire thing.&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;inContext&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;Set&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="nx"&gt;assembled&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;          &lt;span class="c1"&gt;// ids WE assembled&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;resolves&lt;/span&gt;  &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;answer&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;cites&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;every&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;id&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;inContext&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;has&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;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;pass&lt;/span&gt;      &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;answer&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;cites&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;length&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="nx"&gt;resolves&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="c1"&gt;// the bug: checking the STORE instead of the assembled context&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;wrong&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;answer&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;cites&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;every&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;id&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;store&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;has&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;id&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt; &lt;span class="c1"&gt;// c091 is real...&lt;/span&gt;
                                                       &lt;span class="c1"&gt;// ...just not here&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Both blind spots, stated as numbers
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Resolution never reads the claim.&lt;/strong&gt; An unsupported sentence carrying an id that genuinely is in the context is a clean pass — &lt;strong&gt;0 of 60&lt;/strong&gt; caught.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;A context-scoped faithfulness grader is id-blind.&lt;/strong&gt; Claim in, assembled chunks in, supported-or-not out. An invented id, a stale one, or no id at all does not change its input by one token — &lt;strong&gt;0 of 180&lt;/strong&gt; citation defects caught. That is not inferred: the self-check replaces every citation with nonsense and measures the verdict move on &lt;strong&gt;0 of 420&lt;/strong&gt; answers.&lt;/p&gt;

&lt;p&gt;Neither number moves by tuning a threshold. Both are structural.&lt;/p&gt;

&lt;h2&gt;
  
  
  The residual neither one catches
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;60 answers cite a chunk that &lt;em&gt;is&lt;/em&gt; in the assembled context and does &lt;em&gt;not&lt;/em&gt; support the claim&lt;/strong&gt;, while some other assembled chunk does. Resolution passes — the id is in the context. Context-scoped faithfulness passes — the context supports the claim. Both answer their own question correctly; neither question is "does &lt;em&gt;this&lt;/em&gt; chunk support &lt;em&gt;this&lt;/em&gt; sentence".&lt;/p&gt;

&lt;p&gt;A citation-level grader does catch all 60, and costs one call &lt;strong&gt;per citation&lt;/strong&gt; rather than per answer — 180 here against 420, and far worse on answers citing five sources each. It also cannot run on a citation that did not resolve: you cannot hand a grader a chunk you could not find. Third layer, never a replacement.&lt;/p&gt;

&lt;h2&gt;
  
  
  What the free check is worth
&lt;/h2&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;&lt;/th&gt;
&lt;th&gt;defects caught (of 360)&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;faithfulness alone&lt;/td&gt;
&lt;td&gt;120&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;resolution alone&lt;/td&gt;
&lt;td&gt;240&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;union&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;300&lt;/strong&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;plus citation-level&lt;/td&gt;
&lt;td&gt;360&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;Zero false alarms from either check on the clean class. Resolution costs &lt;strong&gt;360 comparisons and 0 model calls&lt;/strong&gt; over the whole corpus; grading everything costs &lt;strong&gt;420&lt;/strong&gt; calls; running the free check first and grading only what survives costs &lt;strong&gt;180&lt;/strong&gt; — &lt;strong&gt;240 model calls removed with the union catch rate unchanged&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;One cost that is not money: everything short-circuiting skips sits in the cell where &lt;em&gt;the answer text was fine&lt;/em&gt;. For those the repair is re-citing, not re-answering, and an ordering that dumps them into one undifferentiated "failed" bucket sends correct answers back to be rewritten.&lt;/p&gt;

&lt;h2&gt;
  
  
  The limitation to read first
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;The entailment check here is a stand-in, not a model&lt;/strong&gt; — a stated token rule over 12 disjoint topic vocabularies, which separates supported from unsupported with no overlap at all. Real graders are wrong a great deal, and that error is part of why the free check is worth keeping. So read the faithfulness column as a &lt;em&gt;ceiling&lt;/em&gt; and the resolution column as exact, because that one is code. The corpus is synthetic, one claim per answer, one citation per claim, no multi-hop, no reranker, no abstention. And nothing here asks whether the &lt;em&gt;source&lt;/em&gt; is any good.&lt;/p&gt;

&lt;p&gt;38 in-page checks, 138 verifier asserts, 0 failures.&lt;/p&gt;

</description>
      <category>ai</category>
      <category>llm</category>
      <category>programming</category>
      <category>webdev</category>
    </item>
    <item>
      <title>Pack Four Docs Without the Block Mask and Doc 4 Spends 83.78% of Its Attention on Documents It Never Saw</title>
      <dc:creator>Devanshu Biswas</dc:creator>
      <pubDate>Thu, 17 Sep 2026 12:33:46 +0000</pubDate>
      <link>https://dev.to/dev48v/pack-four-docs-without-the-block-mask-and-doc-4-spends-8378-of-its-attention-on-documents-it-54hp</link>
      <guid>https://dev.to/dev48v/pack-four-docs-without-the-block-mask-and-doc-4-spends-8378-of-its-attention-on-documents-it-54hp</guid>
      <description>&lt;p&gt;Sequence packing concatenates short documents into one row so you stop paying for padding. On the length distribution here that lifts utilisation from 27.98% to 99.81%.&lt;/p&gt;

&lt;p&gt;But attention inside a row is &lt;strong&gt;global&lt;/strong&gt;. Without a block-diagonal mask, the fourth document in the row spends &lt;strong&gt;83.78%&lt;/strong&gt; of its layer-1 attention on documents it has never seen — worst single token &lt;strong&gt;94.90%&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;👉 &lt;strong&gt;Live, runs in your browser:&lt;/strong&gt; &lt;a href="https://dev48v.infy.uk/dl/day80-sequence-packing.html" rel="noopener noreferrer"&gt;https://dev48v.infy.uk/dl/day80-sequence-packing.html&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  The ground truth is declared; everything else is measured
&lt;/h2&gt;

&lt;p&gt;Every document is first pushed through the stack &lt;strong&gt;on its own&lt;/strong&gt;, ordinary causal mask, positions 0…L−1. That unpacked run is the ground truth, and every packed configuration is scored against it token by token, coordinate by coordinate — not against a loss curve, which is exactly the instrument that cannot see either bug.&lt;/p&gt;

&lt;p&gt;The only thing that differs between a correct packed row and a broken one:&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="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;allow&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;cfg&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;block&lt;/span&gt;
  &lt;span class="p"&gt;?&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;i&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;s&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;s&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;=&lt;/span&gt; &lt;span class="nx"&gt;i&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="nx"&gt;p&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;docIds&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;s&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="nx"&gt;p&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;docIds&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;i&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;   &lt;span class="c1"&gt;// block-diagonal ∩ causal&lt;/span&gt;
  &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;i&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;s&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;s&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;=&lt;/span&gt; &lt;span class="nx"&gt;i&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;                                 &lt;span class="c1"&gt;// causal only: the mask bug&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;posIds&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;cfg&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;reset&lt;/span&gt; &lt;span class="p"&gt;?&lt;/span&gt; &lt;span class="nx"&gt;p&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;local&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;p&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;tokens&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;_&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;i&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;i&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  The tolerance is a prediction, not a fudge
&lt;/h2&gt;

&lt;p&gt;With the mask &lt;strong&gt;and&lt;/strong&gt; per-document position reset, the packed row reproduces the unpacked run to &lt;strong&gt;2.22e-16&lt;/strong&gt; — across five independently seeded models, every token, every coordinate.&lt;/p&gt;

&lt;p&gt;It is not exactly zero for a stated reason. A batched attention kernel subtracts a row maximum before exponentiating, and takes that maximum over the &lt;em&gt;whole causal prefix of the row&lt;/em&gt;, not over the current document. Subtracting a constant leaves a softmax mathematically unchanged, so this is a reassociation that moves the last bit and nothing else. The engine reproduces that kernel behaviour rather than hiding it.&lt;/p&gt;

&lt;p&gt;The verifier then tests the explanation instead of trusting it: an independently written attention stack that subtracts &lt;strong&gt;no&lt;/strong&gt; maximum at all gives the identity at &lt;strong&gt;exactly 0&lt;/strong&gt;. So the residual is the shift, and nothing else. The asserted tolerance is 1e-12 — four orders above the residual, twelve below the smallest real bug.&lt;/p&gt;

&lt;h2&gt;
  
  
  Two independent bugs, and the arithmetic proves they are two
&lt;/h2&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;configuration&lt;/th&gt;
&lt;th&gt;max Δ vs unpacked&lt;/th&gt;
&lt;th&gt;worst leaked mass&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;block mask + position reset&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;2.22e-16&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;0&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;no mask, positions reset&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;0.3607&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;94.90%&lt;/strong&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;mask, no position reset&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;0.7607&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;0&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;neither (naive packing)&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;0.7773&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;94.90%&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;0.7773 is neither the sum (1.1214) nor the max (0.7607). They interact, so you cannot subtract one to estimate the other.&lt;/p&gt;

&lt;p&gt;Two controls separate them. &lt;strong&gt;Zero the position table&lt;/strong&gt; and the position bug has to vanish exactly — there is nothing left for a wrong id to index — and it drops to 1.11e-16 while the mask bug is untouched at 0.3498. And across a growing-neighbour sweep the two respond completely differently to the same change: the &lt;strong&gt;mask&lt;/strong&gt; error correlates with leaked mass at &lt;strong&gt;r = 0.9814&lt;/strong&gt;, the &lt;strong&gt;position&lt;/strong&gt; error at &lt;strong&gt;r = 0.1384&lt;/strong&gt;, because a position offset is already wrong once the neighbour has a single token.&lt;/p&gt;

&lt;h2&gt;
  
  
  Nothing throws, and here is why
&lt;/h2&gt;

&lt;p&gt;Every attention row still sums to 1 to &lt;strong&gt;4.44e-16 in all five configurations&lt;/strong&gt;. A masked softmax renormalises over whatever it is left with, so the shape is right, the loss falls, and the model just learns something slightly wrong.&lt;/p&gt;

&lt;p&gt;Both bugs also leave the &lt;strong&gt;first&lt;/strong&gt; document in the row bit-identical — exactly 0, not nearly zero. Causal attention cannot look forward, and its positions start at 0 whether or not anyone resets them. Spot-check a packed batch on the first sequence and both bugs are invisible.&lt;/p&gt;

&lt;h2&gt;
  
  
  Three honest subtractions
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Damage is not monotone in neighbour length.&lt;/strong&gt; The foreign mass is strictly increasing by construction, from 0.2261 to 0.8772 mean. The error is L×gap and the second factor moves with the content of whatever tokens get appended — there is a visible &lt;strong&gt;dip at n = 4&lt;/strong&gt;. "More neighbour, more damage" is a trend here, not a law.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Length-sorted batching already gets most of the win.&lt;/strong&gt; Bucketed batches reach &lt;strong&gt;99.06%&lt;/strong&gt; utilisation against packing's 99.81%. The 3.57× is measured against arrival-order batching, which is the baseline packing is sold against but not the only alternative.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Packing does not make attention cheaper unless the mask does.&lt;/strong&gt; Attention cost is quadratic in the row, so a packed row computed &lt;strong&gt;densely&lt;/strong&gt; costs &lt;strong&gt;1.027×&lt;/strong&gt; the padded baseline — slightly &lt;em&gt;more&lt;/em&gt;. It is the block-diagonal structure, exploited by a kernel that skips the off-diagonal blocks, that brings it to &lt;strong&gt;0.1625×&lt;/strong&gt;. The mask is not only what makes packing correct; it is what makes it fast.&lt;/p&gt;

&lt;p&gt;The models are &lt;strong&gt;not trained&lt;/strong&gt;, positions are a learned absolute table rather than RoPE, one head, two layers, float64 throughout. So which bug is &lt;em&gt;larger&lt;/em&gt; is a property of this toy's scales and is not a general claim. The claim is that both are non-zero, independent, and invisible to the loss.&lt;/p&gt;

&lt;p&gt;38 in-page checks, 126 verifier asserts, 0 failures.&lt;/p&gt;

</description>
      <category>deeplearning</category>
      <category>machinelearning</category>
      <category>python</category>
      <category>ai</category>
    </item>
    <item>
      <title>Model A Beats B on Every Slice and Loses 57.60% to 79.06%: 9,510 Reversals, 0 on a Shared Mixture</title>
      <dc:creator>Devanshu Biswas</dc:creator>
      <pubDate>Thu, 17 Sep 2026 12:33:03 +0000</pubDate>
      <link>https://dev.to/dev48v/model-a-beats-b-on-every-slice-and-loses-5760-to-7906-9510-reversals-0-on-a-shared-mixture-2187</link>
      <guid>https://dev.to/dev48v/model-a-beats-b-on-every-slice-and-loses-5760-to-7906-9510-reversals-0-on-a-shared-mixture-2187</guid>
      <description>&lt;p&gt;Two models, one benchmark, three slices. Model A is ahead on &lt;strong&gt;all three&lt;/strong&gt; — 96.0 / 60.0 / 50.0 against 88.0 / 50.0 / 40.0.&lt;/p&gt;

&lt;p&gt;Pooled, A scores &lt;strong&gt;57.60%&lt;/strong&gt; and B scores &lt;strong&gt;79.06%&lt;/strong&gt;. A 21.5-point defeat assembled entirely out of victories.&lt;/p&gt;

&lt;p&gt;👉 &lt;strong&gt;Live, runs in your browser:&lt;/strong&gt; &lt;a href="https://dev48v.infy.uk/ml/day80-simpsons-paradox.html" rel="noopener noreferrer"&gt;https://dev48v.infy.uk/ml/day80-simpsons-paradox.html&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Declared, not sampled — and no winner decided by a float
&lt;/h2&gt;

&lt;p&gt;Every count on the page is a declared integer. Every accuracy is an exact rational, and every comparison is settled by BigInt cross-multiplication, so a reversal is &lt;em&gt;proved&lt;/em&gt; rather than observed through rounding:&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="c1"&gt;// every winner is decided by cross-multiplication, never by a float&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;rcmp&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;a&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;b&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;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;l&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;a&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;n&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="nx"&gt;b&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;d&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;r&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;b&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;n&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="nx"&gt;a&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;d&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
                         &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;l&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="nx"&gt;r&lt;/span&gt; &lt;span class="p"&gt;?&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;l&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;r&lt;/span&gt; &lt;span class="p"&gt;?&lt;/span&gt; &lt;span class="mi"&gt;1&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="p"&gt;};&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;There is no p-value anywhere, because nothing is estimated. What &lt;em&gt;is&lt;/em&gt; measured is an exhaustive sweep.&lt;/p&gt;

&lt;h2&gt;
  
  
  The identity the whole thing rests on
&lt;/h2&gt;

&lt;p&gt;Pooled (micro) accuracy is not a different kind of quantity from the per-slice rates. It &lt;strong&gt;is&lt;/strong&gt; their mean, weighted by slice size:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;micro(M) = Σ c_g / Σ n_g = Σ (n_g/N) · (c_g/n_g)
macro(M) = (1/G)  ·  Σ (c_g/n_g)
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The two metrics differ in exactly one place — the weights — and the entire paradox lives there. A weighted mean with &lt;strong&gt;fixed&lt;/strong&gt; non-negative weights is monotone in its inputs, so per-slice dominance would be inherited by the pooled number. The reversal &lt;em&gt;requires&lt;/em&gt; the two models to carry different weights.&lt;/p&gt;

&lt;p&gt;Which is why macro and micro disagree &lt;strong&gt;in sign in 9,510 of 9,510&lt;/strong&gt; reversals found. Not usually. All of them.&lt;/p&gt;

&lt;h2&gt;
  
  
  The smallest reversal in existence is nine observations
&lt;/h2&gt;

&lt;p&gt;Sweeping every two-group table with per-cell n from 1 to 8 — &lt;strong&gt;3,748,096 tables&lt;/strong&gt;, of which &lt;strong&gt;774,400&lt;/strong&gt; put A strictly ahead in both groups — gives &lt;strong&gt;9,510&lt;/strong&gt; reversals. The minimum total sample across all of them is &lt;strong&gt;9&lt;/strong&gt;, reached by exactly 4 tables:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;group    model A        model B
g1       1/1 = 100%     2/3 = 66.7%
g2       1/4 =  25%     0/1 =  0%
pooled   2/5 =  40%     2/4 = 50%
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Four cells and nine data points are enough to rank two models backwards. There is nothing subtle or large-sample about it.&lt;/p&gt;

&lt;p&gt;At the other end: the largest pooled reversal found is &lt;strong&gt;5/9 against 2/9&lt;/strong&gt; — 55.6 points — and the largest per-group lead that still loses is &lt;strong&gt;3/8&lt;/strong&gt;, so A is ahead by at least 37.5 points in &lt;em&gt;every&lt;/em&gt; group and loses anyway. There is no margin of per-group superiority large enough to make a pooled scalar safe to read.&lt;/p&gt;

&lt;h2&gt;
  
  
  The negative result is the one worth keeping
&lt;/h2&gt;

&lt;p&gt;Re-run the same sweep with the two models forced onto the same slice mixture:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;constraint&lt;/th&gt;
&lt;th&gt;comparisons&lt;/th&gt;
&lt;th&gt;reversals&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;the full sweep&lt;/td&gt;
&lt;td&gt;774,400&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;9,510&lt;/strong&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;same n in every group&lt;/td&gt;
&lt;td&gt;14,400&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;0&lt;/strong&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;same slice &lt;strong&gt;proportions&lt;/strong&gt;, totals free&lt;/td&gt;
&lt;td&gt;32,544&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;0&lt;/strong&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;same &lt;strong&gt;total&lt;/strong&gt; n only, mixtures free&lt;/td&gt;
&lt;td&gt;72,816&lt;/td&gt;
&lt;td&gt;
&lt;strong&gt;502&lt;/strong&gt; (smallest at 10 obs)&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;Zero is not "rare". With the weights shared, pooled accuracy is a weighted mean using the &lt;em&gt;same&lt;/em&gt; weights for both models, and monotonicity does the rest. Matching only the proportions is already enough. &lt;strong&gt;Matching only the sample size is not.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Every zero is printed next to the number of comparisons behind it, so an impossibility is distinguishable from an empty search — and the degenerate control (one group, where a reversal would be a contradiction) duly reports 0 over 1,936 comparisons.&lt;/p&gt;

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

&lt;p&gt;A third slice does &lt;strong&gt;not&lt;/strong&gt; make it cheaper: each extra group adds two more cells that must each be strictly won, and the smallest three-group reversal costs &lt;strong&gt;13&lt;/strong&gt; observations against nine. The sweep covers two groups at per-cell n ≤ 8, so the minimum is exact for two groups and a claim about nothing else. And the page never says which number is &lt;em&gt;right&lt;/em&gt; — macro and micro answer different questions, and choosing needs a deployment mixture a benchmark does not carry.&lt;/p&gt;

&lt;p&gt;The narrow claim is the defensible one: a pooled scalar published without the slice sizes that produced it is not a ranking anybody can check.&lt;/p&gt;

&lt;p&gt;27 in-page checks, 134 verifier asserts, 0 failures.&lt;/p&gt;

</description>
      <category>machinelearning</category>
      <category>datascience</category>
      <category>python</category>
      <category>programming</category>
    </item>
  </channel>
</rss>
