<?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: realist</title>
    <description>The latest articles on DEV Community by realist (@realist).</description>
    <link>https://dev.to/realist</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%2F4071167%2F7d5a5be3-cd0c-4565-bf42-f38e84604160.png</url>
      <title>DEV Community: realist</title>
      <link>https://dev.to/realist</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/realist"/>
    <language>en</language>
    <item>
      <title>Three Judges Scored the Same Project Twice. They Disagreed With Themselves by 1.11 Points.</title>
      <dc:creator>realist</dc:creator>
      <pubDate>Wed, 07 Oct 2026 17:01:19 +0000</pubDate>
      <link>https://dev.to/realist/three-judges-scored-the-same-project-twice-they-disagreed-with-themselves-by-111-points-4cdi</link>
      <guid>https://dev.to/realist/three-judges-scored-the-same-project-twice-they-disagreed-with-themselves-by-111-points-4cdi</guid>
      <description>&lt;p&gt;&lt;em&gt;Why Evenhand ranks 40 hackathon projects in 2 groups, and shows its receipts.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;A hackathon ranking is forty claims that each project beat the one below it. We wanted to know how many of those claims the scores could actually support.&lt;/p&gt;

&lt;p&gt;The organisers' sample data for DOGFOOD 2026 hides one project twice. Dry Harbour is filed as &lt;code&gt;prj_07&lt;/code&gt; and again as &lt;code&gt;prj_41&lt;/code&gt;: one team, one project, submitted twice. It is a trap. A judging portal is supposed to notice and not count the project twice.&lt;/p&gt;

&lt;p&gt;Three of the sample data's invented judges scored both copies. Each score is the mean of three criteria on a 1–5 scale.&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Judge&lt;/th&gt;
&lt;th&gt;First copy (&lt;code&gt;prj_07&lt;/code&gt;)&lt;/th&gt;
&lt;th&gt;Second copy (&lt;code&gt;prj_41&lt;/code&gt;)&lt;/th&gt;
&lt;th&gt;Gap&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;jdg_19&lt;/td&gt;
&lt;td&gt;2.33&lt;/td&gt;
&lt;td&gt;3.67&lt;/td&gt;
&lt;td&gt;+1.33&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;jdg_21&lt;/td&gt;
&lt;td&gt;3.67&lt;/td&gt;
&lt;td&gt;4.67&lt;/td&gt;
&lt;td&gt;+1.00&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;jdg_26&lt;/td&gt;
&lt;td&gt;4.67&lt;/td&gt;
&lt;td&gt;3.67&lt;/td&gt;
&lt;td&gt;−1.00&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;Same project, same judge: 1.11 points apart on average. Our own model of judging noise says two honest looks at the same work should differ by about 0.68. Two judges went up and one went down, so this is not the second copy simply looking better.&lt;/p&gt;

&lt;p&gt;Evenhand, the portal we built, was supposed to turn 121 of these scores into a ranking of 40 projects. It returned two groups.&lt;/p&gt;

&lt;p&gt;Three made-up judges are not a study, and we will be careful about that below. This is the write-up of what that number did to everything else we built, what broke on the way, and the one decision we would take back.&lt;/p&gt;

&lt;h2&gt;
  
  
  What Evenhand is, and who built it
&lt;/h2&gt;

&lt;p&gt;DOGFOOD 2026's premise: every team builds the same hackathon platform in 72 hours, and the winner is forked to run future Hackathon Raptors events. You build the thing that will judge you. The organisers supplied a spec, an acceptance checker (&lt;code&gt;run.py&lt;/code&gt;, seven probes) and sample data: 41 projects, 30 judges, 8 tracks and 126 reviews, with traps planted in it.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://github.com/Len3hq/evenhand" rel="noopener noreferrer"&gt;Evenhand&lt;/a&gt; is our entry: a public gallery, a judge console where judges cannot see each other's scores, and rankings corrected for harsh and generous judges. One command brings it up, seeded, with the network off:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight console"&gt;&lt;code&gt;&lt;span class="gp"&gt;$&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;docker compose up
&lt;span class="c"&gt;...
&lt;/span&gt;&lt;span class="go"&gt;seeded. test logins (DEMO_MODE=true: demo only, never use in production):
  organizer    Authorization: Bearer dev-organizer-7f2a    (organizer@evenhand.local)
  judge_a      Authorization: Bearer dev-judge-a-91bc      (jdg_24 Diego Herrera)
  judge_b      Authorization: Bearer dev-judge-b-44de      (jdg_26 Jonas Vogel)
  participant  Authorization: Bearer dev-participant-2e88  (priya1@example.org)
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Under it: a NestJS API that owns every rule, a Next.js web app that is only one of its clients, Postgres, and a judging engine in plain TypeScript with no I/O, so the code that decides who wins can be tested on its own.&lt;/p&gt;

&lt;p&gt;Each decision is a dated record in &lt;a href="https://github.com/Len3hq/evenhand/tree/main/docs/decisions" rel="noopener noreferrer"&gt;&lt;code&gt;docs/decisions&lt;/code&gt;&lt;/a&gt;, 26 of them, written when the call was made rather than afterwards. The bugs below were found by the sample data or by running it offline, not by reading code.&lt;/p&gt;

&lt;h2&gt;
  
  
  A trap used as an instrument
&lt;/h2&gt;

&lt;p&gt;You normally cannot measure how much a judge disagrees with themselves, because nobody judges the same thing twice. The duplicate made that happen by accident. In statistics this is called a test–retest, and the trap gave us three pairs of one for free.&lt;/p&gt;

&lt;p&gt;The 0.68 comes from the model. After accounting for which project was judged and how generous each judge is, the leftover noise on a single review is about 0.60 points. Two independent draws of that noise differ by 2σ/√π ≈ 0.68 on average. The three judges differed by 1.11. They disagree with themselves more than the model expects them to disagree with anyone.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;A duplicate entry is a free test–retest. Measure it before you trust a ranking built on the same judges.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Two caveats belong here, not in a footnote.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;These judges are invented.&lt;/strong&gt; The organisers generated them. The 1.11 tells you how the sample data was made, not how people judge. We make no claim about human nature.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Three pairs is three pairs.&lt;/strong&gt; We show all three gaps above and print no p-value, because an interval on n = 3 would be decoration.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;This is not a DOGFOOD problem.&lt;/strong&gt; Here each project has two to five reviews, so a one-point wobble per judge is enough to swap neighbours anywhere in the ranking. And no event needs a lucky duplicate to measure that wobble. Show two or three judges one calibration entry twice, under two names, and you learn how far a judge moves on identical work before you publish anything that depends on it.&lt;/p&gt;

&lt;p&gt;The claim we keep is narrow: on this data, there is not enough signal to put 40 projects in order, and the software should notice that by itself instead of a human spotting it by hand. Evenhand does. Every number in this section comes from one command, which regenerates our &lt;a href="https://github.com/Len3hq/evenhand/blob/main/docs/proof/normalization.md" rel="noopener noreferrer"&gt;Normalization Proof&lt;/a&gt; from the sample data:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;git clone https://github.com/Len3hq/evenhand &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="nb"&gt;cd &lt;/span&gt;evenhand
npm &lt;span class="nb"&gt;install
&lt;/span&gt;npm run proof &lt;span class="nt"&gt;-w&lt;/span&gt; @evenhand/judging-engine   &lt;span class="c"&gt;# writes docs/proof/normalization.md&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Section 7 of that file holds the three pairs and section 4 the two groups.&lt;/p&gt;

&lt;h2&gt;
  
  
  What broke, in the order it hurt
&lt;/h2&gt;

&lt;p&gt;Four things that looked right until the sample data, or a start with the network off, showed otherwise.&lt;/p&gt;

&lt;h3&gt;
  
  
  The textbook fix deletes four judges
&lt;/h3&gt;

&lt;p&gt;The usual correction for harsh and generous judges is the per-judge z-score:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// the textbook correction (we did not ship it)&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;z&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;score&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="nx"&gt;judgeMean&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;/&lt;/span&gt; &lt;span class="nx"&gt;judgeSd&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;On this data &lt;code&gt;judgeSd&lt;/code&gt; is zero four times. jdg_07 gave 4/4/4 to every project. jdg_12 and jdg_23 have one review each. jdg_19's three reviews happen to share an average. Divide by zero and those four judges quietly leave the ranking. The formula also assumes every judge saw projects of average quality, which is false when judges are matched to tracks and some tracks are stronger.&lt;/p&gt;

&lt;p&gt;So we estimate everything at once. Each score is the overall average, plus how good the project is, plus how generous the judge is, plus noise:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;score(project p, judge j) = μ + q_p + b_j + ε
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Every project quality and every judge's generosity is pulled towards zero unless the data argues otherwise. A judge with one review barely moves. A flat judge's generosity is absorbed without dividing by anything. A project with two reviews is pulled harder towards the middle and shown with a wider error bar.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;For the maths people.&lt;/strong&gt; This is ridge regression, the approach of Roos, Rothe and Scheuermann (AAAI 2011), solved exactly with a hand-written Cholesky factorisation in the pure engine. The two penalties are chosen by exact leave-one-out error from the hat matrix, e_i / (1 − h_ii), which a test checks against real refits. We planned a grid of 0.25 to 8 and had to widen it to 0.125 to 64, because the choice hit both edges. Rows are summed in a fixed order, so the result is identical to the bit whatever order reviews arrive in.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;&lt;strong&gt;Before you trust a correction, count whom it quietly drops.&lt;/strong&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Three different marks, one average
&lt;/h3&gt;

&lt;p&gt;The progress dashboard warns organisers about a judge who gives every project the same marks. Our first version compared each judge's weighted scores. The sample data showed why that is wrong: it flagged jdg_19 as well, whose marks were 3/5/3, 3/4/4 and 5/4/2. All three average 3.67 by chance. That judge told the projects apart.&lt;/p&gt;

&lt;p&gt;A test built around jdg_07 alone would have passed; it took the full sample data to show the extra name. The fix compares marks criterion by criterion, and the case that fooled us is now a test:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="nf"&gt;it&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;does not flag different marks that happen to average the same (fixture jdg_19)&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="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nf"&gt;expect&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="nf"&gt;isFlat&lt;/span&gt;&lt;span class="p"&gt;([&lt;/span&gt;
      &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;functionality&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;quality&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;5&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;innovation&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;3&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt;
      &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;functionality&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;quality&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;4&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;innovation&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;4&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt;
      &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;functionality&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;5&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;quality&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;4&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;innovation&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt;
    &lt;span class="p"&gt;]),&lt;/span&gt;
  &lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;toBe&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="p"&gt;});&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Only jdg_07 is flagged now. &lt;strong&gt;Test a warning against the case it must not fire on, not only the case it must.&lt;/strong&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  The merge that would have deleted a judge
&lt;/h3&gt;

&lt;p&gt;The obvious rule for Dry Harbour is "keep the newer copy, drop the older one and its reviews". Dropping &lt;code&gt;prj_07&lt;/code&gt;'s reviews deletes jdg_01's only score in the whole file. "The later score wins" cannot be computed either, because the sample scores carry no timestamps.&lt;/p&gt;

&lt;p&gt;So confirming a duplicate moves reviews that exist only on the older copy across to the newer one. Where a judge reviewed both, the newer review counts and the older one is set aside, never deleted. &lt;code&gt;prj_41&lt;/code&gt; ends with 6 counted reviews, and undoing the decision restores every row; a test compares them with a snapshot taken before.&lt;/p&gt;

&lt;p&gt;The schema had its own version of this fight. "One live submission per team" is a database index, and "both copies stay visible until an organiser decides" breaks it on import. The answer was one more column:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="k"&gt;CREATE&lt;/span&gt; &lt;span class="k"&gt;UNIQUE&lt;/span&gt; &lt;span class="k"&gt;INDEX&lt;/span&gt; &lt;span class="nv"&gt;"submissions_one_live_per_team"&lt;/span&gt;
  &lt;span class="k"&gt;ON&lt;/span&gt; &lt;span class="nv"&gt;"submissions"&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;"event_id"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;"team_id"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="k"&gt;WHERE&lt;/span&gt; &lt;span class="nv"&gt;"superseded_by_id"&lt;/span&gt; &lt;span class="k"&gt;IS&lt;/span&gt; &lt;span class="k"&gt;NULL&lt;/span&gt; &lt;span class="k"&gt;AND&lt;/span&gt; &lt;span class="k"&gt;NOT&lt;/span&gt; &lt;span class="nv"&gt;"duplicate_hold"&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;Before a cleanup rule deletes anything, count whose only record it deletes.&lt;/strong&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  The server that wanted the internet
&lt;/h3&gt;

&lt;p&gt;The rule was that &lt;code&gt;docker compose up&lt;/code&gt; must work with no network. Prisma picks its database engine binary by the OpenSSL version it detects &lt;em&gt;at install time&lt;/em&gt;. When a build stage saw a different OpenSSL from the runtime image, the container tried to download a matching engine on start, which fails with no network.&lt;/p&gt;

&lt;p&gt;A developer's machine always has a network, so this bug cannot appear there. It only appears on the machine the rule is about. Every stage now installs the same OpenSSL, and the build refuses to finish rather than letting the start fail later:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight docker"&gt;&lt;code&gt;&lt;span class="c"&gt;# Fail the build (not the offline start) if the schema engine for this OpenSSL is missing.&lt;/span&gt;
&lt;span class="k"&gt;RUN &lt;/span&gt;&lt;span class="nb"&gt;ls &lt;/span&gt;node_modules/@prisma/engines/schema-engine-&lt;span class="k"&gt;*&lt;/span&gt;openssl-3.0.x &lt;span class="o"&gt;&amp;gt;&lt;/span&gt;/dev/null &lt;span class="se"&gt;\
&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt; &lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"Prisma schema engine for OpenSSL 3 missing: runtime would try to download it"&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&amp;amp;2&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="nb"&gt;exit &lt;/span&gt;1&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="o"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;If a step needs the network, make the build fail, not the start.&lt;/strong&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  The number we did not want to publish
&lt;/h2&gt;

&lt;p&gt;Here is the number that makes our model look bad. Hide one review, predict it from all the others, and repeat for every review. Guessing "the average of all the other scores" gives a mean squared error of 0.3996. The full model, which knows the project and the judge, gives 0.3961. That is &lt;strong&gt;0.9% better than guessing the average&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;On this data, knowing which project a score belongs to barely helps you guess the score. The penalties the data picks, λ_b = 32 and λ_q = 16, mean "shrink hard": every project is pulled most of the way back to the middle. Halving or doubling them barely moves the order (Spearman ρ 0.999, the same top five), so no amount of tuning was going to make the ranking decisive.&lt;/p&gt;

&lt;p&gt;We could have left the number in a notebook and printed 40 confident ranks. Instead the results page prints it, as the signal level, alongside the ranking it undermines, and asks readers to read tie groups rather than single ranks when it is low.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Print how much better your model does than the plain average, next to the ranking it produces.&lt;/strong&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  The part we are proudest of is a refusal
&lt;/h2&gt;

&lt;p&gt;Evenhand will not tell you who came 17th.&lt;/p&gt;

&lt;p&gt;A ranking run still produces an ordered list. Each project gets its raw mean, its corrected score with an error bar, a rank, and a one-line reason. It also gets a &lt;strong&gt;tie group&lt;/strong&gt;: projects within one pooled standard deviation of the group's leader are marked as equal, and the page says to read them that way. On the sample data that gives 16 projects in the first group and 24 in the second. The corrected scores of all 40 fit between 3.46 and 3.72, with an error bar of about ±0.15 on each. Printing them as 1 to 40 would be claiming precision the data does not have.&lt;/p&gt;

&lt;p&gt;A reason reads like this, so an organiser can see why a project moved:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;+0.12: its judges were on average 0.12 less generous than the rest; pulled towards the mean (2 reviews)
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



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

&lt;p&gt;Each hollow dot is a project's raw average; the filled dot and bar are its corrected score ± 1 SD. Most of the raw spread is noise the model cannot tie to the project, so it pulls every score towards the middle. Judges' generosity moves no score by more than 0.1 points.&lt;/p&gt;

&lt;p&gt;The refusal has teeth in three places:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Every run is a receipt.&lt;/strong&gt; It stores a SHA-256 hash of its inputs and of its result. Results stay hidden from everyone until an organiser publishes a run.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;A stale run cannot be published.&lt;/strong&gt; If a review, a weight or a duplicate decision changes after the run, the input hash no longer matches and publishing is refused until someone runs it again.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;The proof cannot drift from the code.&lt;/strong&gt; The Normalization Proof is generated by code, and a test regenerates it and fails if the committed copy differs by a single byte. The numbers in this post are the numbers the code computes today.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The obvious question is how anyone awards first, second and third from two groups. The list is still ordered, so the order is there for anyone who wants it. But the decision lives in the top group: 16 projects the data cannot separate. Two honest ways through are prizes per track, which compare judges who saw similar work, or a final human panel on the top group. A project with a single final review is listed and never ranked at all.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;A third decimal place is not a tiebreak.&lt;/strong&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Two other refusals
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;The server refuses before it looks.&lt;/strong&gt; When judge B asks for judge A's scores, the API refuses before asking the database whether judge A exists. A real judge and a made-up one get byte-identical 403s, so nobody can probe for valid ids. A test tries every role against every operation on every change: 448 expected status codes.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="nf"&gt;it&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;refuses judge_b before looking anything up: same answer for a real and a fake judge&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;async &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;real&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;t&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;http&lt;/span&gt;&lt;span class="p"&gt;().&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;/api/judges/jdg_24/scores&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;set&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;headers&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;judge_b&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;fake&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;t&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;http&lt;/span&gt;&lt;span class="p"&gt;().&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;/api/judges/jdg_nope/scores&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;set&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;headers&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;judge_b&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="nf"&gt;expect&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;real&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;status&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;toBe&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;403&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="nf"&gt;expect&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;fake&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;body&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;toEqual&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;real&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;body&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;Decide permission from who is asking, before you look up what they asked for.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The stack refuses the internet.&lt;/strong&gt; The database and API sit on a Docker network with no route out, so "no external calls at runtime" holds even if someone adds code that tries. &lt;code&gt;npm run drill&lt;/code&gt; proves it: it clones a fresh copy, builds it, cuts the network, checks that a request to the internet now fails from both containers, and then runs the organisers' checker (7/7) and 46 browser steps inside the sealed network.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Prove an offline rule by cutting the network, not by reading the code.&lt;/strong&gt;&lt;/p&gt;

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

&lt;p&gt;A post about refusing to overclaim should list its own gaps. These are in the README too, written so a reviewer does not have to find them.&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Limit&lt;/th&gt;
&lt;th&gt;What it means&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Our best accuracy numbers are on data we generated&lt;/td&gt;
&lt;td&gt;On 20 seeded synthetic events the model recovers the true order better than raw means or z-scores (Spearman ρ 0.92 against 0.90 and 0.74). That proves the code finds a planted truth. It does not prove real judges behave like our generator.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;A judge's &lt;em&gt;scale&lt;/em&gt; is not modelled&lt;/td&gt;
&lt;td&gt;One judge who uses 1–5 and another who only uses 3–4 look the same to the model. With 1 to 11 reviews per judge there is too little data to estimate it.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;The audit log stops at the database superuser&lt;/td&gt;
&lt;td&gt;Triggers refuse &lt;code&gt;UPDATE&lt;/code&gt;, &lt;code&gt;DELETE&lt;/code&gt; and &lt;code&gt;TRUNCATE&lt;/code&gt; on the log, but a superuser can switch triggers off. Hash-chaining is not built.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Per-IP rate limits can be dodged&lt;/td&gt;
&lt;td&gt;Behind the bundled proxy a client can rotate &lt;code&gt;X-Forwarded-For&lt;/code&gt;. A reverse proxy in front has to overwrite it.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;No email and no TLS in the box&lt;/td&gt;
&lt;td&gt;Invite links are shared by hand; put a TLS-terminating proxy in front for anything beyond a laptop.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Not built&lt;/td&gt;
&lt;td&gt;Image uploads and community voting.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Receipts are hashes, not signatures&lt;/td&gt;
&lt;td&gt;The SHA-256 hashes prove a published result matches its inputs. They do not prove who published it.&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;h2&gt;
  
  
  The numbers
&lt;/h2&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Number&lt;/th&gt;
&lt;th&gt;What it counts&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;1.11&lt;/td&gt;
&lt;td&gt;average self-disagreement of three sample judges on one project, 1–5 scale&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;0.68&lt;/td&gt;
&lt;td&gt;the gap the model's noise predicts for the same comparison&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;0.9%&lt;/td&gt;
&lt;td&gt;how much better the model predicts a hidden review than the plain average&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;2&lt;/td&gt;
&lt;td&gt;tie groups for 40 projects&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;121&lt;/td&gt;
&lt;td&gt;reviews in the ranking (126 in the file; the older duplicate's 5 held out)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;448&lt;/td&gt;
&lt;td&gt;expected status codes in the role-isolation test&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;7 / 7&lt;/td&gt;
&lt;td&gt;organisers' checker probes passing, also with the network cut&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;46 / 46&lt;/td&gt;
&lt;td&gt;browser steps passing, with no Content-Security-Policy violation&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;107 + 665&lt;/td&gt;
&lt;td&gt;unit and end-to-end tests&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;26&lt;/td&gt;
&lt;td&gt;decision records, written as the decisions were made&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;43&lt;/td&gt;
&lt;td&gt;hours from first commit to last&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;h2&gt;
  
  
  The decision we would take back
&lt;/h2&gt;

&lt;p&gt;The hours we spent making the model exact: the hand-written Cholesky solve, exact leave-one-out, the widened grid. It is good engineering, and on this data it changed nothing a reader can see. The signal is 0.9%, and the order barely moves whatever the penalties are.&lt;/p&gt;

&lt;p&gt;The same hours could have hash-chained the audit log, so that every entry carries a hash of the one before it and a rewritten history no longer lines up. Today, someone with superuser access to the database can switch the triggers off and rewrite the trail without leaving a mark. Our pitch is that rankings come with receipts. A portal built on that pitch should have spent its last hours making the receipts hold all the way down.&lt;/p&gt;

&lt;h2&gt;
  
  
  What we would tell someone building one of these
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Find the test–retest before you rank.&lt;/strong&gt; A duplicate, a resubmission, a calibration entry: anywhere a judge saw the same work twice tells you how much of a score is noise.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Print the boring baseline.&lt;/strong&gt; Report how much better your model predicts a hidden score than the plain average does. If it is 1%, print groups, not ranks.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Count whom a correction drops.&lt;/strong&gt; Z-scores, minimum-review rules and duplicate merges all remove people quietly. Count them by name.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Refuse before you look up.&lt;/strong&gt; Decide permission from the caller, then test that a real and a fake id get the same answer.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Generate the proof from the code.&lt;/strong&gt; Our proof document is rebuilt by a test, and the build fails if one byte drifts.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Write down what you did not build&lt;/strong&gt;, before a reviewer finds it.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Try it
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;git clone https://github.com/Len3hq/evenhand &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="nb"&gt;cd &lt;/span&gt;evenhand
docker compose up                                    &lt;span class="c"&gt;# seeded portal on http://localhost:8080&lt;/span&gt;
curl &lt;span class="nt"&gt;-H&lt;/span&gt; &lt;span class="s1"&gt;'Authorization: Bearer dev-judge-b-44de'&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  localhost:8080/api/judges/jdg_24/scores            &lt;span class="c"&gt;# 403: not your scores&lt;/span&gt;
npm &lt;span class="nb"&gt;install&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; npm run proof &lt;span class="nt"&gt;-w&lt;/span&gt; @evenhand/judging-engine   &lt;span class="c"&gt;# regenerates the Normalization Proof&lt;/span&gt;
npm run drill                                        &lt;span class="c"&gt;# fresh clone, network cut, checker and browser steps&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The first build needs the internet, for about 3 to 4 minutes. After that, nothing does.&lt;/p&gt;

&lt;h2&gt;
  
  
  Where the hours went
&lt;/h2&gt;

&lt;p&gt;We expected the hard part to be the maths. It was not. Writing a ridge regression and a Cholesky solver is the fast part now; ours was built with Claude Code. What took the hours was deciding what the output is allowed to claim: that two scores 0.03 apart are not a ranking, that a flat judge is not a broken one, and that a duplicate is a measurement before it is a cleanup job.&lt;/p&gt;

&lt;p&gt;The organisers planted Dry Harbour to see whether a portal would count one project twice. It turned out to be the only place in the data where a judge could be measured against themselves, and that measurement set the shape of everything else we built.&lt;/p&gt;

&lt;p&gt;If you take one thing from this: before you print a rank, find out how far one judge moves on the same work. On our data it was more than a point.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Repository:&lt;/strong&gt; &lt;a href="https://github.com/Len3hq/evenhand" rel="noopener noreferrer"&gt;github.com/Len3hq/evenhand&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;The proof, regenerated from the sample data:&lt;/strong&gt; &lt;a href="https://github.com/Len3hq/evenhand/blob/main/docs/proof/normalization.md" rel="noopener noreferrer"&gt;docs/proof/normalization.md&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Scoring, normalisation and threat model:&lt;/strong&gt; &lt;a href="https://github.com/Len3hq/evenhand/blob/main/JUDGING.md" rel="noopener noreferrer"&gt;JUDGING.md&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Every decision, dated:&lt;/strong&gt; &lt;a href="https://github.com/Len3hq/evenhand/tree/main/docs/decisions" rel="noopener noreferrer"&gt;docs/decisions&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Built for &lt;a href="https://dogfoodhack.com" rel="noopener noreferrer"&gt;DOGFOOD 2026&lt;/a&gt; by Hackathon Raptors. #hackathonraptors #dogfoodhack&lt;/p&gt;

</description>
      <category>hackathonraptors</category>
      <category>dogfoodhack</category>
      <category>typescript</category>
      <category>testing</category>
    </item>
    <item>
      <title>Porting qs to Go: an honest account</title>
      <dc:creator>realist</dc:creator>
      <pubDate>Mon, 10 Aug 2026 19:00:35 +0000</pubDate>
      <link>https://dev.to/realist/porting-qs-to-go-an-honest-account-5e4l</link>
      <guid>https://dev.to/realist/porting-qs-to-go-an-honest-account-5e4l</guid>
      <description>&lt;p&gt;I spent the weekend rewriting &lt;code&gt;qs&lt;/code&gt;, the query-string parser bundled inside Express, used across a huge chunk of the Node ecosystem to Go language. This is what actually happened: what I picked, where my first version was wrong, how I checked that the rewrite behaves the same as the original.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why &lt;code&gt;qs&lt;/code&gt;, why Go
&lt;/h2&gt;

&lt;p&gt;I wanted a library that mattered and a library I could actually finish. &lt;code&gt;qs&lt;/code&gt; fit both: 8.9k stars on github, a real dependency in a lot of production code, and about 1,000 lines of core logic. To port properly in a weekend instead of half-porting something bigger.&lt;/p&gt;

&lt;p&gt;Go was the target for a specific reason, not just because "Go is fast." &lt;code&gt;qs&lt;/code&gt; today only runs inside a Node process ; npm install, node_modules, a JS runtime spinning up to split a query string. A Go port compiles to a single static binary with no runtime and, if I did it right, no external dependencies at all. That last part turned out to matter more than I expected, because it ruled out the easy way to handle one particular edge case (more on that below).&lt;/p&gt;

&lt;p&gt;The hackathon's rule made the whole thing harder in a useful way: the port has to pass the &lt;em&gt;original&lt;/em&gt; test suite, completely unmodified, not a rewritten version of it that matches my own understanding of the spec. &lt;/p&gt;

&lt;h2&gt;
  
  
  Getting the original tests to run against Go at all
&lt;/h2&gt;

&lt;p&gt;JavaScript can't call a Go function directly, so before porting any logic I had to build a bridge between the two. The Go code compiles into a CLI that reads a JSON request off stdin and writes a JSON response to stdout:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="err"&gt;//&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;request&lt;/span&gt;&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="err"&gt;→&lt;/span&gt;&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="err"&gt;bin/qs&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;(stdin)&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nl"&gt;"method"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"parse"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"args"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;"a[b]=c"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{}]}&lt;/span&gt;&lt;span class="w"&gt;

&lt;/span&gt;&lt;span class="err"&gt;//&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;response&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;←&lt;/span&gt;&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="err"&gt;bin/qs&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;(stdout)&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nl"&gt;"result"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nl"&gt;"a"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nl"&gt;"b"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"c"&lt;/span&gt;&lt;span class="p"&gt;}}}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;To keep the test files themselves untouched, I intercepted Node's module loader so that when the original &lt;a href="https://github.com/ljharb/qs" rel="noopener noreferrer"&gt;&lt;code&gt;ljharb/qs&lt;/code&gt;&lt;/a&gt; tests call &lt;code&gt;require('../')&lt;/code&gt;, they get silently redirected to a small adapter that shells out to the Go binary instead of the real package. The test files never change,  &lt;code&gt;tests/original/&lt;/code&gt; matches the upstream commit (&lt;code&gt;3a890d4&lt;/code&gt;) byte for byte, verifiable with &lt;code&gt;git diff&lt;/code&gt;. &lt;/p&gt;

&lt;p&gt;This mattered because it's the part that's easiest to fudge under a deadline.&lt;/p&gt;

&lt;h2&gt;
  
  
  What was actually broken
&lt;/h2&gt;

&lt;p&gt;My first working version passed 166 of 241 parse tests. By the end it was 221 of 241 which brings it upto 390 of 410 across the full suite, 95% and almost every point between those two numbers came from a specific, traceable bug.&lt;/p&gt;

&lt;h3&gt;
  
  
  Scalar-into-array merges were structurally wrong.
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;input:     a=b  followed by  a[]=c

expected:  ["b", "c"]                      // qs wraps the scalar, then concatenates
got:       {"0": "b", "1": "c"}            // my merge built an index-keyed object instead
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;That looks similar in a JSON dump but is a different data structure. The fix was small once I found it. Detect "target is scalar, source is array" as its own case:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight go"&gt;&lt;code&gt;&lt;span class="c"&gt;// merge.go&lt;/span&gt;
&lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;isScalar&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;target&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="nb"&gt;append&lt;/span&gt;&lt;span class="p"&gt;([]&lt;/span&gt;&lt;span class="n"&gt;any&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="n"&gt;target&lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt; &lt;span class="n"&gt;source&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;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;But finding it meant realizing the merge code had no concept of what a value used to be before merging, only what it currently looked like.&lt;/p&gt;

&lt;h3&gt;
  
  
  a[0]=b and a[]=c weren't recognized as the same array.
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;input:     a[0]=b  followed by  a[]=c

expected:  {a: ['b', 'c']}
got:       {a: {0: ['c', 'b']}}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;I was parsing bracket-index notation and empty-bracket notation into two separate arrays, then trying to reconcile them during a merge step. By that point the information needed to merge them correctly was already gone. Nine tests failed on this shape before I traced it back to where the real fix belonged: the original resolves this ambiguity during key parsing, before any object gets constructed, not afterward.&lt;/p&gt;

&lt;h3&gt;
  
  
  &lt;code&gt;depth: false&lt;/code&gt; was silently ignored.
&lt;/h3&gt;

&lt;p&gt;Not a parsing bug but a bridge bug. &lt;code&gt;qs&lt;/code&gt; treats &lt;code&gt;depth: false&lt;/code&gt; as "no limit," but my CLI's option decoder only checked for a numeric depth field in the incoming JSON:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight go"&gt;&lt;code&gt;&lt;span class="c"&gt;// before — bool false falls through, depth stays at its default (5)&lt;/span&gt;
&lt;span class="k"&gt;switch&lt;/span&gt; &lt;span class="n"&gt;v&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="n"&gt;opts&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s"&gt;"depth"&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="k"&gt;type&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="k"&gt;case&lt;/span&gt; &lt;span class="kt"&gt;float64&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;
    &lt;span class="n"&gt;parsed&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Depth&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kt"&gt;int&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="p"&gt;}&lt;/span&gt;

&lt;span class="c"&gt;// after&lt;/span&gt;
&lt;span class="k"&gt;switch&lt;/span&gt; &lt;span class="n"&gt;v&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="n"&gt;opts&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s"&gt;"depth"&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="k"&gt;type&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="k"&gt;case&lt;/span&gt; &lt;span class="kt"&gt;float64&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;
    &lt;span class="n"&gt;parsed&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Depth&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kt"&gt;int&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;case&lt;/span&gt; &lt;span class="kt"&gt;bool&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="n"&gt;v&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="n"&gt;parsed&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Depth&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="m"&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;One added case fixed it. The lesson: when you port a library across a JSON-serialized boundary, the serialization layer accumulates its own bugs, separate from whatever you're porting.&lt;/p&gt;

&lt;p&gt;Some failures I left as documented limitations rather than forcing a fix.  Eight tests expect a JavaScript &lt;code&gt;TypeError&lt;/code&gt; for something like passing &lt;code&gt;null&lt;/code&gt; where a boolean belongs but &lt;code&gt;JSON.stringify&lt;/code&gt; collapses "explicitly null," "undefined," and "never set" into the same thing once it crosses into JSON, so by the time it reaches Go it's just &lt;code&gt;nil&lt;/code&gt;. Recovering that distinction needs type-checking on the JavaScript side of the bridge, before the JSON is built, a different piece of work than porting &lt;code&gt;qs&lt;/code&gt; itself. One test passes a circular reference into &lt;code&gt;parse()&lt;/code&gt;, which can't be fixed inside this architecture at all: &lt;code&gt;json.Marshal&lt;/code&gt; cannot serialize a cycle, in any language.&lt;/p&gt;

&lt;h2&gt;
  
  
  Checking equivalence beyond what the tests happened to cover
&lt;/h2&gt;

&lt;p&gt;A test suite only checks the inputs someone thought to write down. To go past that, I ran Go's native fuzzer against a roundtrip property that doesn't depend on any test file:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight go"&gt;&lt;code&gt;&lt;span class="c"&gt;// fuzz/harness_test.go&lt;/span&gt;
&lt;span class="k"&gt;func&lt;/span&gt; &lt;span class="n"&gt;FuzzRoundtrip&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;f&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;testing&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;F&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;f&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Fuzz&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;func&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;t&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;testing&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;T&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;raw&lt;/span&gt; &lt;span class="kt"&gt;string&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="n"&gt;a&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="n"&gt;qs&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Parse&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;raw&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;qs&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;DefaultOptions&lt;/span&gt;&lt;span class="p"&gt;())&lt;/span&gt;
        &lt;span class="n"&gt;b&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="n"&gt;qs&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Parse&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;qs&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Stringify&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;a&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;qs&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;DefaultOptions&lt;/span&gt;&lt;span class="p"&gt;()),&lt;/span&gt; &lt;span class="n"&gt;qs&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;DefaultOptions&lt;/span&gt;&lt;span class="p"&gt;())&lt;/span&gt;
        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="n"&gt;reflect&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;DeepEqual&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;a&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;b&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="n"&gt;t&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Fatalf&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"roundtrip mismatch for %q:&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s"&gt;  a=%#v&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s"&gt;  b=%#v"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;raw&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;a&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;b&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;p&gt;Across millions of randomly generated inputs; deep nesting, malformed percent-encoding, empty and boundary values. It caught a real divergence within the first run:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;input:      "%80"                      // a lone, invalid continuation byte

parse:      string containing raw byte 0x80        // Go strings can hold arbitrary bytes
stringify:  "%EF%BF%BD"                             // range over the string replaces
                                                      // the invalid byte with U+FFFD first
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;JavaScript never hits this because &lt;code&gt;decodeURIComponent&lt;/code&gt; throws on malformed input before a bad byte can exist as a JS string at all. I documented it rather than trying to patch around it: for binary-safe roundtrips, base64-encode first, the same advice the WHATWG URL spec gives for the same class of problem.&lt;/p&gt;

&lt;p&gt;cold start and memory numbers are real and measured:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight console"&gt;&lt;code&gt;&lt;span class="go"&gt;realist@realist-MacBook-Pro portmortem-qs % go test -bench=. -benchmem -benchtime=1s ./bench/
goos: darwin
goarch: arm64
pkg: github.com/Len3hq/qs-go/bench
cpu: Apple M1
BenchmarkParse-8                   47172             25417 ns/op           38255 B/op        453 allocs/op
BenchmarkParseSimple-8           1393298               845.4 ns/op          1664 B/op         19 allocs/op
BenchmarkParseNested-8            580536              1964 ns/op            2849 B/op         36 allocs/op
BenchmarkParseArray-8             394047              2965 ns/op            4705 B/op         66 allocs/op
BenchmarkStringify-8              112891             10538 ns/op            4729 B/op        223 allocs/op
BenchmarkStringifySimple-8       5312035               226.2 ns/op            72 B/op          6 allocs/op
BenchmarkStringifyNested-8       1427907               941.0 ns/op           248 B/op         16 allocs/op
BenchmarkStringifyArray-8         770910              1471 ns/op             568 B/op         31 allocs/op
PASS
ok      github.com/Len3hq/qs-go/bench   12.840s
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Where JS and Go just model things differently
&lt;/h2&gt;

&lt;p&gt;A few divergences weren't bugs at all, they're places where the two languages disagree about what's even representable and the honest move is documenting the gap.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;qs&lt;/code&gt; guards against keys named &lt;code&gt;__proto__&lt;/code&gt; because writing to it in JavaScript can reach up and mutate &lt;code&gt;Object.prototype&lt;/code&gt; for the whole program:&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;// why the guard exists in JS&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;obj&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{};&lt;/span&gt;
&lt;span class="nx"&gt;obj&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;__proto__&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;polluted&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="p"&gt;({}).&lt;/span&gt;&lt;span class="nx"&gt;polluted&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="c1"&gt;// true — every object in the program is now affected&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Go maps have no prototype chain, so the same key is just a string like any other:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight go"&gt;&lt;code&gt;&lt;span class="n"&gt;m&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="k"&gt;map&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="kt"&gt;string&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="n"&gt;any&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="s"&gt;"__proto__"&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="s"&gt;"x"&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="c"&gt;// m["__proto__"] is just a string key. Nothing else in the program is reachable from it.&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;I kept the &lt;code&gt;AllowPrototypes&lt;/code&gt; option for API compatibility, but it's a documented no-op, because the thing it protects against doesn't exist in Go.&lt;/p&gt;

&lt;p&gt;Sparse arrays are the same kind of gap:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;JS:  [, 'b', , 'd']          // holes — positions never assigned anything
Go:  make([]any, 4)          // [nil, nil, nil, nil] — no concept of "never assigned"
                              // vs. "explicitly set to null"
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;I used &lt;code&gt;nil&lt;/code&gt; for holes and a separate sentinel type for explicit nulls, which recovers the distinction in most cases, but the original test suite's equality check still treats a JS hole and a Go &lt;code&gt;nil&lt;/code&gt; as different things in a handful of deeply nested cases.&lt;/p&gt;

&lt;p&gt;The ISO-8859-1 charset support was a smaller version of the same tradeoff, but a deliberate one. &lt;code&gt;qs&lt;/code&gt; leans on a quirk of the browser's old &lt;code&gt;unescape()&lt;/code&gt; function for this charset. Go's standard library has no equivalent, and the easy fix &lt;code&gt;go get golang.org/x/text&lt;/code&gt;  was one command away. I didn't take it, because a single external dependency for one option would have undercut the whole pitch of a zero-dependency static binary. I hand-wrote the byte-to-rune mapping table instead:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight go"&gt;&lt;code&gt;&lt;span class="c"&gt;// Latin-1's first 256 codepoints map directly onto Unicode's first 256.&lt;/span&gt;
&lt;span class="k"&gt;func&lt;/span&gt; &lt;span class="n"&gt;latin1ToRune&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;b&lt;/span&gt; &lt;span class="kt"&gt;byte&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="kt"&gt;rune&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="kt"&gt;rune&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;b&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;Slower to build, and the right call for what the port was supposed to be.&lt;/p&gt;

&lt;h2&gt;
  
  
  What's still unfinished
&lt;/h2&gt;

&lt;p&gt;My CLI currently uses Go's &lt;code&gt;panic()&lt;/code&gt; to reject invalid option types:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight go"&gt;&lt;code&gt;&lt;span class="c"&gt;// current — kills the binary, JS side gets silence instead of a TypeError&lt;/span&gt;
&lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;_&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;ok&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="n"&gt;opts&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s"&gt;"decodeDotInKeys"&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="kt"&gt;bool&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt; &lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="n"&gt;ok&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nb"&gt;panic&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"decodeDotInKeys must be a boolean"&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;That single shortcut accounts for eight of my remaining twenty test failures and isn't a hard fix, it's the one I deprioritized in favor of the merge-logic bugs that felt more urgent at the time. It needs to become a returned error that the adapter turns back into a real &lt;code&gt;TypeError&lt;/code&gt;, not a crash.&lt;/p&gt;

&lt;p&gt;What's shipped now passes 390 of 410 tests from the completely unmodified original suite (95% pass) and every remaining failure traces to something specific: a handful to a real limitation of talking to Go over JSON, the rest to documented differences between how JavaScript and Go represent objects and arrays. Not logic I got wrong and didn't notice, logic I can point to and explain.&lt;/p&gt;




&lt;p&gt;Repo: &lt;a href="https://github.com/Len3hq/qs-go" rel="noopener noreferrer"&gt;github.com/Len3hq/qs-go&lt;/a&gt; — built for Port Mortem 2026, Track F (JavaScript → Go), hosted by &lt;a href="https://x.com/raptors_hack" rel="noopener noreferrer"&gt;HackathonRaptors&lt;/a&gt;.&lt;/p&gt;

</description>
      <category>opensource</category>
      <category>javascript</category>
      <category>go</category>
      <category>programming</category>
    </item>
  </channel>
</rss>
