<?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: codewitharyan29</title>
    <description>The latest articles on DEV Community by codewitharyan29 (@codewitharyan29).</description>
    <link>https://dev.to/codewitharyan29</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%2F4065878%2F04861b36-4758-40cc-88c6-b96cee0bd97f.png</url>
      <title>DEV Community: codewitharyan29</title>
      <link>https://dev.to/codewitharyan29</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/codewitharyan29"/>
    <language>en</language>
    <item>
      <title>Same score, 23 places apart: the normalization maths behind a hackathon judging platform</title>
      <dc:creator>codewitharyan29</dc:creator>
      <pubDate>Fri, 02 Oct 2026 07:11:31 +0000</pubDate>
      <link>https://dev.to/codewitharyan29/same-score-23-places-apart-the-normalization-maths-behind-a-hackathon-judging-platform-2mji</link>
      <guid>https://dev.to/codewitharyan29/same-score-23-places-apart-the-normalization-maths-behind-a-hackathon-judging-platform-2mji</guid>
      <description>&lt;p&gt;Two projects in the official DOGFOOD data set both had a raw average score of &lt;strong&gt;3.44&lt;/strong&gt;, from three judges each.&lt;/p&gt;

&lt;p&gt;After I normalized the scores, one finished &lt;strong&gt;#37&lt;/strong&gt;. The other finished &lt;strong&gt;#14&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;Same number. 23 places apart. This is the story of why, and of the other technical problems I hit while building &lt;strong&gt;Verdict&lt;/strong&gt;, a platform that runs a hackathon end to end: events, teams, deadline-enforced submissions, rubric judging, public voting, certificates, webhooks and an embeddable gallery. It was built for the DOGFOOD hackathon by &lt;a class="mentioned-user" href="https://dev.to/partnerships_raptors"&gt;@partnerships_raptors&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Stack: FastAPI, SQLAlchemy 2.0 async, PostgreSQL, Redis, Next.js. &lt;br&gt;
Repo: &lt;a href="https://github.com/Lakshya-Varshney/Verdict" rel="noopener noreferrer"&gt;https://github.com/Lakshya-Varshney/Verdict&lt;/a&gt;&lt;br&gt;
Demo_Video: &lt;a href="https://youtu.be/bmdsrYy_dy4" rel="noopener noreferrer"&gt;https://youtu.be/bmdsrYy_dy4&lt;/a&gt;&lt;/p&gt;


&lt;h2&gt;
  
  
  1. The problem: a raw average cannot tell who was asked
&lt;/h2&gt;

&lt;p&gt;The obvious way to rank projects is to average judge scores. It silently assumes every judge uses the scale the same way.&lt;/p&gt;

&lt;p&gt;They do not. On the official data (40 projects, 30 judges, 126 review rows), judges with at least 5 reviews had personal averages from &lt;strong&gt;3.11&lt;/strong&gt; to &lt;strong&gt;4.22&lt;/strong&gt;, with the same spread (standard deviation 0.81 for both). That is a full point of pure leniency.&lt;/p&gt;

&lt;p&gt;Now look at the two projects:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Project&lt;/th&gt;
&lt;th&gt;Raw mean (rank)&lt;/th&gt;
&lt;th&gt;Its reviewers' own average&lt;/th&gt;
&lt;th&gt;Normalized rank&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Flat Meadow&lt;/td&gt;
&lt;td&gt;3.44 (#24)&lt;/td&gt;
&lt;td&gt;
&lt;strong&gt;3.97&lt;/strong&gt; (generous panel: 4.22, 3.61, 4.08)&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;#37&lt;/strong&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Glass Signal&lt;/td&gt;
&lt;td&gt;3.44 (#26)&lt;/td&gt;
&lt;td&gt;3.48 (close to the pooled mean 3.57)&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;#14&lt;/strong&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;Flat Meadow got a below-par verdict from each of its reviewers. Glass Signal got a normal one from each of its reviewers. The raw average sees two identical numbers.&lt;/p&gt;
&lt;h2&gt;
  
  
  2. The fix: per-judge z-scores
&lt;/h2&gt;

&lt;p&gt;For each judge, compute their mean and standard deviation over everything they scored, then convert each score into "how far above or below this judge's own usual":&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;z(judge, project) = (score - judge_mean) / judge_stddev
normalized(project) = mean of z over the judges who reviewed it
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Why z-scores and not min-max or rank averaging? They correct both leniency (the mean) and spread (the standard deviation), and they stay well-defined when projects have different numbers of reviews. Two reviews on one project and five on another is fine, because each project is just the mean over whoever reviewed it.&lt;/p&gt;

&lt;p&gt;Result on the real data: &lt;strong&gt;38 of 40 projects change rank&lt;/strong&gt;, Spearman correlation 0.864 against the raw ranking. I also recomputed everything independently with plain &lt;code&gt;statistics&lt;/code&gt; and no application code, and the two agree to floating-point precision.&lt;/p&gt;

&lt;h2&gt;
  
  
  3. The edge case that decides whether the maths is correct
&lt;/h2&gt;

&lt;p&gt;A z-score divides by the judge's standard deviation. What if a judge gave 4/4/4 on every project? Or reviewed only one project? Then the standard deviation is zero and there is nothing to divide by.&lt;/p&gt;

&lt;p&gt;The tempting shortcut is to use that judge's raw score. I rejected it, because it mixes two scales. Raw scores sit around 3.0. Z-scores sit around 0. If you average them together, every project a flat judge touched jumps by roughly that judge's raw score, and the judge with no opinion ends up as the loudest voice in the ranking.&lt;/p&gt;

&lt;p&gt;The design that works: standardize the flat judge's raw score against the &lt;strong&gt;pooled&lt;/strong&gt; mean and standard deviation of every score in the event.&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;if&lt;/span&gt; &lt;span class="ow"&gt;not&lt;/span&gt; &lt;span class="n"&gt;flat&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="n"&gt;z&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;score&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="n"&gt;mu&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;/&lt;/span&gt; &lt;span class="n"&gt;sigma&lt;/span&gt;                 &lt;span class="c1"&gt;# judge's own scale
&lt;/span&gt;&lt;span class="k"&gt;elif&lt;/span&gt; &lt;span class="n"&gt;pooled_sd&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="mf"&gt;1e-12&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="n"&gt;z&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;score&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="n"&gt;pooled_mu&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;/&lt;/span&gt; &lt;span class="n"&gt;pooled_sd&lt;/span&gt;      &lt;span class="c1"&gt;# same scale as everyone else
&lt;/span&gt;&lt;span class="k"&gt;else&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="n"&gt;z&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mf"&gt;0.0&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The flat judge still contributes a real signal (higher raw score means higher z), it lands on the same scale as everyone else, and it is never silently trusted: they appear in a &lt;code&gt;zero_variance_judges&lt;/code&gt; list and in the audit log. The results payload also separates "identical scores everywhere" from "only one review", so the UI never mislabels a judge who simply had too little data. There is a regression test for exactly this: &lt;code&gt;test_zero_variance_judge_does_not_swamp_z_scores&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;On the official data, four judges take this path.&lt;/p&gt;

&lt;h2&gt;
  
  
  4. Real data fights back: the duplicate project row
&lt;/h2&gt;

&lt;p&gt;The official &lt;code&gt;fixtures.json&lt;/code&gt; has 41 project rows for 40 teams. &lt;code&gt;prj_41&lt;/code&gt; repeats &lt;code&gt;prj_07&lt;/code&gt;: same team, same title ("Dry Harbour"), same repo, different id. Deduplicating by id would never catch it, so I merge by team plus title.&lt;/p&gt;

&lt;p&gt;The interesting part is the consequence. Three judges had scored both ids, so their criterion rows collided after the merge: 9 rows, taking 378 score rows down to 369. One of those judges had scored the original at 2.33 and the duplicate at 3.67. Under the documented rule (the later review wins), all three of her remaining projects score 3.67, and she correctly lands in the zero-variance path from section 3.&lt;/p&gt;

&lt;p&gt;Data cleaning is not neutral. It changes the maths downstream. So I wrote the rule down (later review wins, averaging is the alternative) and surfaced every flagged judge in the results payload.&lt;/p&gt;

&lt;h2&gt;
  
  
  5. Role isolation, tested by attacking the running system
&lt;/h2&gt;

&lt;p&gt;Reading the code tells you what you intended. Probing the live API tells you what you built. I ran adversarial probes against the running system and found 12 real problems, each now covered by a regression test. The ones that taught me the most:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;An organizer could grant themselves &lt;code&gt;admin&lt;/code&gt;.&lt;/strong&gt; Every permission check accepts &lt;code&gt;admin&lt;/code&gt; from any event, so one self-grant meant site-wide control. Only an admin can grant admin now.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Any organizer could read every other event's audit log.&lt;/strong&gt; 98 of the first 100 rows returned belonged to other events. Audit access is scoped to the caller's own events.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Judges could change scores after results were published.&lt;/strong&gt; The judging window was enforced in the UI, not the API. A UI button is not an access control.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;The vote count was hidden in one field and leaked in another.&lt;/strong&gt; While voting was open &lt;code&gt;count&lt;/code&gt; was null, but &lt;code&gt;vote_count&lt;/code&gt; still carried the real number.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;alice+1@gmail.com&lt;/code&gt; counted as a new voter.&lt;/strong&gt; Email voting now uses a canonical mailbox: lowercase, drop the &lt;code&gt;+tag&lt;/code&gt;, ignore Gmail dots.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Roles are read from the database on every request and scoped per event, so cross-event isolation is structural rather than a convention.&lt;/p&gt;

&lt;h2&gt;
  
  
  6. A 13 second gallery, and &lt;code&gt;lazy="raise"&lt;/code&gt;
&lt;/h2&gt;

&lt;p&gt;With the real 40-project data the gallery took &lt;strong&gt;13 s&lt;/strong&gt; and &lt;code&gt;/roles&lt;/code&gt; took &lt;strong&gt;18 s&lt;/strong&gt;, because SQLAlchemy was eager-loading every relationship.&lt;/p&gt;

&lt;p&gt;I set &lt;code&gt;lazy="raise"&lt;/code&gt; on every relationship. Any accidental lazy load now throws instead of quietly running extra queries, so every query has to be written explicitly. It forces more typing and gives predictable performance. &lt;code&gt;test_real_scale_endpoints_stay_fast&lt;/code&gt; keeps it that way.&lt;/p&gt;

&lt;h2&gt;
  
  
  7. An audit log you can verify, not just trust
&lt;/h2&gt;

&lt;p&gt;An append-only audit log is normally append-only by convention. Anyone with database access can still edit history.&lt;/p&gt;

&lt;p&gt;In Verdict, every audit row contains the hash of the previous row. &lt;code&gt;GET /admin/audit/verify&lt;/code&gt; and an offline script both detect tampering. To keep the chain linear, appends take a Postgres advisory lock (&lt;code&gt;pg_advisory_xact_lock&lt;/code&gt;), so two concurrent transactions can never read the same tip hash and fork the history.&lt;/p&gt;

&lt;p&gt;The same principle runs through the certificates: Ed25519 signatures instead of HMAC, so anyone can verify a certificate offline with only a public key, with no shared secret. Webhooks use a transactional outbox, so events cannot be lost or invented, and no message broker is needed to run offline.&lt;/p&gt;

&lt;h2&gt;
  
  
  8. The scope decision: normalized absolute scores
&lt;/h2&gt;

&lt;p&gt;Judging can be built two ways: judges score each project against a rubric, or judges compare two projects at a time (pairwise, Bradley-Terry style). I shipped normalized absolute scoring.&lt;/p&gt;

&lt;p&gt;It works directly with the weighted rubric, the result is recomputed from raw scores on demand (never trusted from storage), and I could check it independently against the real data. That made the correctness work possible: the normalization proof, the role-isolation tests and the verifiable audit trail.&lt;/p&gt;

&lt;h2&gt;
  
  
  What I would carry into the next build
&lt;/h2&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Test the maths on messy real data, not a clean example.&lt;/strong&gt; My clean example proved the idea. The duplicate row and the flat judges decided the actual design.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Attack the running system early.&lt;/strong&gt; Role-isolation bugs hide in code that reads correctly.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Make failures loud.&lt;/strong&gt; &lt;code&gt;lazy="raise"&lt;/code&gt; and server-side checks turn "slow and silently wrong" into "fails immediately".&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;The full normalization proof, the threat model and all the tests are in the repo: &lt;a href="https://github.com/Lakshya-Varshney/Verdict" rel="noopener noreferrer"&gt;https://github.com/Lakshya-Varshney/Verdict&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Built for DOGFOOD. Tagging &lt;a class="mentioned-user" href="https://dev.to/partnerships_raptors"&gt;@partnerships_raptors&lt;/a&gt; for the Write Up Quest.&lt;/em&gt;&lt;/p&gt;

</description>
      <category>hackathon</category>
      <category>python</category>
      <category>fastapi</category>
      <category>security</category>
    </item>
    <item>
      <title>Killed My Own Process 300 Times to Prove ChronoVault Doesn't Lie About Durability</title>
      <dc:creator>codewitharyan29</dc:creator>
      <pubDate>Thu, 03 Sep 2026 15:27:27 +0000</pubDate>
      <link>https://dev.to/codewitharyan29/chronovault-writes-9-13x-slower-than-diskcache-heres-why-i-shipped-it-anyway-3gb1</link>
      <guid>https://dev.to/codewitharyan29/chronovault-writes-9-13x-slower-than-diskcache-heres-why-i-shipped-it-anyway-3gb1</guid>
      <description>&lt;p&gt;&lt;strong&gt;TL;DR:&lt;/strong&gt; I built &lt;strong&gt;ChronoVault v2&lt;/strong&gt; for Hackathon Raptors' Zero Dependency Hackathon (Track D — Data &amp;amp; Storage) — a content-addressable snapshot and recovery engine, zero runtime dependencies, backed by a hand-rolled pack-file format. A comment saying "this is atomic" is worth nothing to a judge, so instead of asserting crash-safety, I hard-killed my own writer process mid-write, over and over, and made the vault prove it survived. This is that story, plus a benchmark number I didn't want to publish and three Windows bugs that only showed up on real hardware.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;279 tests. 20 CLI commands. 0 runtime dependencies. &lt;code&gt;judge_mode.py&lt;/code&gt; VERIFIED on Python 3.11 and 3.14.&lt;/strong&gt;&lt;/p&gt;




&lt;h2&gt;
  
  
  Proving crash-safety instead of asserting it
&lt;/h2&gt;

&lt;p&gt;Anyone can write "atomic writes, crash-safe" in a README. It costs nothing and proves nothing. So &lt;code&gt;recover-check&lt;/code&gt; is tested against a real &lt;code&gt;SIGKILL&lt;/code&gt;, not a simulated one: a writer subprocess runs a snapshot, gets hard-killed at a randomized point mid-write, and the vault is reopened cold — no manual repair step allowed.&lt;/p&gt;

&lt;p&gt;The test checks three things, not one:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Every snapshot acknowledged &lt;em&gt;before&lt;/em&gt; the kill is intact.&lt;/li&gt;
&lt;li&gt;The in-flight snapshot at the moment of the kill is either cleanly absent or a discarded partial write — never garbage returned from a read.&lt;/li&gt;
&lt;li&gt;The vault is immediately usable afterward, with no repair command required.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;This is also where the pack-quarantine logic earned its place instead of being a "nice to have." Early in testing, a kill landing mid-pack-write left a truncated pack that the recovery scan initially tried to read as valid — and returned a wrong length from. Quarantine — isolate anything that fails its checksum before it ever reaches the index — closed that hole. The fix wasn't "trust the file less," it was "never let an unverified pack answer a read at all."&lt;/p&gt;

&lt;p&gt;Run it enough times and the interesting failures stop being about the crash itself and start being about the code paths a crash exposes that a clean shutdown never does. That's the actual value of hard-killing your own process 300 times instead of once: the first ten runs prove the happy path works, and the next 290 are what find the pack-quarantine gap.&lt;/p&gt;




&lt;h2&gt;
  
  
  Architecture at a glance
&lt;/h2&gt;



&lt;pre data-lang="mermaid"&gt;&lt;code&gt;flowchart TD
    subgraph L1["CLI"]
        CLI["cli.py — 20 subcommands"]
    end

    subgraph L2["Core Engine"]
        Snap["snapshot.py&amp;lt;br/&amp;gt;atomic rename + fsync"]
        CAS["Content-Addressed Store&amp;lt;br/&amp;gt;hashlib SHA-256, pack files"]
        Idx["path_history.py&amp;lt;br/&amp;gt;rename-aware lineage"]
        Rec["recover.py&amp;lt;br/&amp;gt;recover-check, SIGKILL-tested replay"]
    end

    subgraph L3["Persistence"]
        Pack[("Pack files on disk")]
        Quar["Pack quarantine&amp;lt;br/&amp;gt;corrupt/truncated packs isolated"]
    end

    CLI --&amp;gt; Snap
    CLI --&amp;gt; CAS
    CLI --&amp;gt; Idx
    CLI --&amp;gt; Rec
    Snap --&amp;gt; CAS
    Snap --&amp;gt; Idx
    CAS --&amp;gt; Pack
    Idx --&amp;gt; Pack
    Rec -.-&amp;gt;|verified via SIGKILL injection| Snap
    Rec --&amp;gt; Pack
    Pack --&amp;gt; Quar

    style L3 fill:#2d2d2d,stroke:#f5a623,stroke-width:2px,color:#fff
    style Rec fill:#2d2d2d,stroke:#f5a623,stroke-width:2px,color:#fff&lt;/code&gt;&lt;/pre&gt;



&lt;p&gt;The shape that matters is where recovery sits: it reads pack files directly, never through the CLI or the snapshot writer, the same way a &lt;code&gt;verify&lt;/code&gt;-style tool deliberately bypasses the normal open path. That's deliberate — if the write path itself is what crashed, recovery can't depend on any code the crash might have left half-updated.&lt;/p&gt;




&lt;h2&gt;
  
  
  The number I didn't want to publish
&lt;/h2&gt;

&lt;p&gt;Every other number in this post makes ChronoVault look good. This one doesn't.&lt;/p&gt;

&lt;p&gt;I benchmarked &lt;code&gt;vault snapshot&lt;/code&gt; against &lt;code&gt;diskcache&lt;/code&gt; — the closest real-world comparison for local content-addressed storage:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Metric&lt;/th&gt;
&lt;th&gt;ChronoVault&lt;/th&gt;
&lt;th&gt;diskcache&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Write throughput&lt;/td&gt;
&lt;td&gt;baseline&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;9–13x faster&lt;/strong&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;On-disk size, repeated / near-duplicate snapshots&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;5–47x smaller&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;baseline&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;Why it loses on writes: every &lt;code&gt;vault snapshot&lt;/code&gt; call pays for a full content hash of each file plus a pack-index lookup before a single byte is committed, because deduplication has to happen &lt;em&gt;before&lt;/em&gt; the write, not after. &lt;code&gt;diskcache&lt;/code&gt; skips that entirely. That's the whole trade in one sentence: I pay a hashing-and-lookup tax on every write so a hundred near-identical snapshots don't cost a hundred times the disk.&lt;/p&gt;

&lt;p&gt;If your workload is write-latency-bound, &lt;code&gt;diskcache&lt;/code&gt; is the right tool and I'm not pretending otherwise. ChronoVault wins when the workload is storage-bound — long retention windows, many near-duplicate snapshots, disk-constrained environments. A real loser column next to a real winner column, not a benchmark that only shows its best number.&lt;/p&gt;




&lt;h2&gt;
  
  
  Where the standard library actually made me suffer
&lt;/h2&gt;

&lt;p&gt;Three places, all invisible until I tested on real Windows hardware instead of assuming POSIX behavior generalizes.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;&lt;code&gt;multiprocessing&lt;/code&gt;'s spawn context raised a &lt;code&gt;KeyboardInterrupt&lt;/code&gt; that wasn't one.&lt;/strong&gt; Worker processes died mid-operation with nobody near Ctrl+C. Windows' spawn-based multiprocessing ties process teardown to &lt;code&gt;_winapi.WaitForSingleObject&lt;/code&gt;, which can surface as a spurious interrupt with no signal ever sent — a path fork-based Unix multiprocessing never touches. I misdiagnosed this &lt;strong&gt;three separate times&lt;/strong&gt; as my own bug before proving otherwise, using a detached &lt;code&gt;Start-Job&lt;/code&gt; test that ran the worker fully isolated from the parent's console.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;&lt;code&gt;WinError 32&lt;/code&gt; — Windows refusing to delete a file it considers still open.&lt;/strong&gt; POSIX lets you &lt;code&gt;unlink()&lt;/code&gt; an open file; the inode survives until the last handle closes. Windows refuses the delete outright. Handling it meant retrying past the handle-release window instead of importing &lt;code&gt;psutil&lt;/code&gt; to hide it.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;A join-before-drain ordering bug deadlocked the IPC queue.&lt;/strong&gt; &lt;code&gt;worker.join()&lt;/code&gt; was called before the result queue was fully drained. If the OS-level pipe buffer filled before the parent drained it, the child blocked on a full pipe and the parent blocked on &lt;code&gt;join()&lt;/code&gt; — a deadlock that only appears under load. Fixed by draining before joining.&lt;/p&gt;

&lt;p&gt;None of these are exotic. They're the specific price of touching &lt;code&gt;multiprocessing&lt;/code&gt; and cross-process queues without a battle-tested wrapper library between you and the OS.&lt;/p&gt;




&lt;h2&gt;
  
  
  The edge case that ate an afternoon
&lt;/h2&gt;

&lt;p&gt;Rename-aware file lineage in the path-history index. The question sounds simple — "was this file renamed, or deleted and recreated?" — until content, path, and inode can each change independently, sometimes in the same tick, with zero heuristics borrowed from someone else's diffing library to lean on.&lt;/p&gt;

&lt;p&gt;The ambiguous case that actually happened: a file renamed &lt;em&gt;and&lt;/em&gt; edited in the same snapshot cycle. "Renamed A→B, then edited" and "A deleted, unrelated B created" are both defensible reads of the same delta, and guessing wrong either invents a false history or silently drops a real one.&lt;/p&gt;

&lt;p&gt;I didn't solve it by getting cleverer. I wrote down, explicitly, which cases the lineage tracker resolves correctly, and which ones it honestly doesn't try to disambiguate. A documented limitation beats a confident guess that's wrong on the exact case a judge tests.&lt;/p&gt;




&lt;h2&gt;
  
  
  What I'd do differently
&lt;/h2&gt;

&lt;p&gt;The Windows CI matrix went in &lt;em&gt;after&lt;/em&gt; most of the Windows-specific bugs had already been found manually, one laptop test at a time. Every area I later ran through real Windows hardware for the first time produced at least one genuine finding. If Ubuntu + Windows CI had existed from the start, at least two of those bugs would have surfaced in an automated run instead of on my own machine, days closer to the deadline than I'd have liked.&lt;/p&gt;




&lt;h2&gt;
  
  
  Where it landed
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;279 tests&lt;/strong&gt; passing on Python 3.11 and 3.14, &lt;code&gt;judge_mode.py&lt;/code&gt; VERIFIED&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;20 CLI commands&lt;/strong&gt;, zero third-party runtime dependencies&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Crash-safety proven, not asserted&lt;/strong&gt; — &lt;code&gt;recover-check&lt;/code&gt; verified against real &lt;code&gt;SIGKILL&lt;/code&gt; injection, with pack quarantine closing the gap testing found&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;CI green&lt;/strong&gt; across Python 3.10–3.14 × Ubuntu + Windows&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;STDLIB.md&lt;/code&gt;&lt;/strong&gt; documents 14 real substitutions, each cross-referenced to the file that uses it&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;All four bonus categories claimed (+16)&lt;/strong&gt; — Single File, Reproducible Build, Package Killer re-earned via the diskcache numbers above, STDLIB Log
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;python judge_mode.py
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;runs the full verification — test suite, single-file hash check, isolated-mode dependency audit, and the content-addressing proof's 8 invariants — as one command, so a judge doesn't have to trust this post; they can re-derive it.&lt;/p&gt;




&lt;h2&gt;
  
  
  Closing thought
&lt;/h2&gt;

&lt;p&gt;Anyone with an AI coding agent can produce a CLI that hashes files and calls it content-addressed storage. The hard part was building enough ways to catch myself being wrong — a crash-safety claim verified with a real &lt;code&gt;SIGKILL&lt;/code&gt; instead of a comment asserting atomicity, a benchmark table honest enough to put a 9–13x loss next to a 5–47x win, and a &lt;code&gt;judge_mode.py&lt;/code&gt; that re-derives every claim in this post instead of asking a judge to trust it. The vault is the artifact. The willingness to kill it 300 times and publish what survived is the actual submission.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Repo:&lt;/strong&gt; &lt;a href="https://github.com/codewitharyan29/ChronoVault-v2" rel="noopener noreferrer"&gt;github.com/codewitharyan29/ChronoVault-v2&lt;/a&gt;&lt;br&gt;
&lt;strong&gt;Track:&lt;/strong&gt; D — Data &amp;amp; Storage&lt;/p&gt;

&lt;p&gt;Built for Zero Dependency 2026, run by &lt;a href="https://dev.to/raptorsdev"&gt;@partnerships_raptors&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  ZeroDependencyHack #Python #OpenSource #DeveloperTools #CLI
&lt;/h2&gt;

&lt;h2&gt;
  
  
  PythonStandardLibrary #BuildInPublic
&lt;/h2&gt;

</description>
      <category>python</category>
      <category>opensource</category>
      <category>hackhathonraptors</category>
      <category>buildinpublic</category>
    </item>
    <item>
      <title>Porting natsort to Rust taught me that "it compiles" is the easy 5%</title>
      <dc:creator>codewitharyan29</dc:creator>
      <pubDate>Thu, 06 Aug 2026 14:44:40 +0000</pubDate>
      <link>https://dev.to/codewitharyan29/porting-natsort-to-rust-taught-me-that-it-compiles-is-the-easy-5-2ac3</link>
      <guid>https://dev.to/codewitharyan29/porting-natsort-to-rust-taught-me-that-it-compiles-is-the-easy-5-2ac3</guid>
      <description>&lt;p&gt;TL;DR: I ported Python's natsort library to Rust for Port Mortem 2026.&lt;/p&gt;

&lt;p&gt;Writing the sorting algorithm turned out to be the easy part.&lt;/p&gt;

&lt;p&gt;The difficult part was proving the Rust implementation behaved &lt;strong&gt;identically&lt;/strong&gt; to the original Python library across thousands of edge cases without quietly changing behavior.&lt;/p&gt;

&lt;p&gt;That meant treating verification as the real project.&lt;/p&gt;




&lt;p&gt;&lt;strong&gt;Why I chose natsort&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Most demo ports are calculators, parsers, or utilities with a handful of tests.&lt;/p&gt;

&lt;p&gt;I wanted something that would actually stress the verification process.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;natsort&lt;/code&gt; sorts strings the way humans expect:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;file2
file10
file20
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;instead of lexicographic order.&lt;/p&gt;

&lt;p&gt;That sounds simple until you discover it supports:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;seven algorithm variants&lt;/li&gt;
&lt;li&gt;signed and floating-point numbers&lt;/li&gt;
&lt;li&gt;scientific notation&lt;/li&gt;
&lt;li&gt;full Unicode numeric characters&lt;/li&gt;
&lt;li&gt;locale-aware behavior&lt;/li&gt;
&lt;li&gt;Roman numerals&lt;/li&gt;
&lt;li&gt;fractions&lt;/li&gt;
&lt;li&gt;a real upstream test suite covering years of edge cases&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;A port that looks correct on five examples proves almost nothing.&lt;/p&gt;

&lt;p&gt;A port that survives the library's own test suite, differential fuzzing, CLI comparisons, property testing, and mutation testing starts becoming evidence.&lt;/p&gt;




&lt;h1&gt;
  
  
  The architecture
&lt;/h1&gt;

&lt;p&gt;The verification pipeline ended up looking like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;             Python Test Suite
                    │
                    ▼
          Thin Python Adapter
                    │
                    ▼
             Compiled Rust Binary
                    │
                    ▼
          Rust natsort Implementation
                    │
                    ▼
 Compare:
 • stdout
 • stderr
 • exit codes
 • ordering
 • fuzz results
 • properties
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The adapter intentionally contains almost no logic.&lt;/p&gt;

&lt;p&gt;Its only job is forwarding Python test inputs to the Rust implementation.&lt;/p&gt;

&lt;p&gt;That design choice turned out to matter more than I expected.&lt;/p&gt;




&lt;h1&gt;
  
  
  What broke (and why those bugs mattered)
&lt;/h1&gt;

&lt;h2&gt;
  
  
  1. Differential fuzzing found a bug in the original library
&lt;/h2&gt;

&lt;p&gt;Differential fuzzing generates thousands of random inputs and compares both implementations.&lt;/p&gt;

&lt;p&gt;Most mismatches were my mistakes.&lt;/p&gt;

&lt;p&gt;One wasn't.&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="o"&gt;&amp;gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class="nf"&gt;natsorted&lt;/span&gt;&lt;span class="p"&gt;([&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;1e400&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;1e500&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt; &lt;span class="n"&gt;alg&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;ns&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;REAL&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;1e400&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;1e500&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;

&lt;span class="o"&gt;&amp;gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class="nf"&gt;natsorted&lt;/span&gt;&lt;span class="p"&gt;([&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;1e500&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;1e400&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt; &lt;span class="n"&gt;alg&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;ns&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;REAL&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;1e500&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;1e400&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;Both calls succeed.&lt;/p&gt;

&lt;p&gt;Both disagree.&lt;/p&gt;

&lt;p&gt;The reason is surprisingly simple.&lt;/p&gt;

&lt;p&gt;Both values overflow to floating-point infinity.&lt;/p&gt;

&lt;p&gt;Since both become &lt;code&gt;inf&lt;/code&gt;, the comparison reports them equal, and Python's stable sort simply preserves whichever order the inputs arrived in.&lt;/p&gt;

&lt;p&gt;Rust's &lt;code&gt;f64&lt;/code&gt; behaves the same way.&lt;/p&gt;

&lt;p&gt;That means the correct port is &lt;strong&gt;not&lt;/strong&gt; one that "fixes" the behavior.&lt;/p&gt;

&lt;p&gt;The correct port reproduces it.&lt;/p&gt;

&lt;p&gt;I reported the issue upstream:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;natsort#192&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Finding a real bug in a mature library simply by holding another implementation beside it was one of the most satisfying moments of the project.&lt;/p&gt;




&lt;h2&gt;
  
  
  2. My adapter was lying to me
&lt;/h2&gt;

&lt;p&gt;I assumed the adapter was a thin wrapper.&lt;/p&gt;

&lt;p&gt;It wasn't.&lt;/p&gt;

&lt;p&gt;One function calculated the correct Rust-backed answer...&lt;/p&gt;

&lt;p&gt;...and then ignored it.&lt;/p&gt;

&lt;p&gt;Instead it silently fell back to a handwritten Python implementation that didn't understand signed numbers or Unicode.&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="nf"&gt;index_natsorted&lt;/span&gt;&lt;span class="p"&gt;([&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;-5&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;-1&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;3&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt; &lt;span class="n"&gt;alg&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;ns&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;REAL&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="n"&gt;returned&lt;/span&gt;&lt;span class="p"&gt;:&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="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="n"&gt;correct&lt;/span&gt;&lt;span class="p"&gt;:&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="mi"&gt;1&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;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Differential testing doesn't automatically catch bugs in the adapter.&lt;/p&gt;

&lt;p&gt;You have to verify the adapter itself.&lt;/p&gt;

&lt;p&gt;That lesson probably saved me more time than any optimization.&lt;/p&gt;




&lt;h2&gt;
  
  
  3. Windows disagreed with Linux
&lt;/h2&gt;

&lt;p&gt;Everything looked perfect.&lt;/p&gt;

&lt;p&gt;Until I ran it on Windows.&lt;/p&gt;

&lt;p&gt;Five bugs immediately appeared.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;executable needed &lt;code&gt;.exe&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;python3&lt;/code&gt; didn't exist (&lt;code&gt;py&lt;/code&gt; does)&lt;/li&gt;
&lt;li&gt;subprocess output defaulted to CP-1252 instead of UTF-8&lt;/li&gt;
&lt;li&gt;file reads had the same encoding problem&lt;/li&gt;
&lt;li&gt;Unix-only &lt;code&gt;resource&lt;/code&gt; crashed benchmarks entirely&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;None of those appeared on Linux.&lt;/p&gt;

&lt;p&gt;None appeared in my development environment.&lt;/p&gt;

&lt;p&gt;Every one appeared on a real Windows machine.&lt;/p&gt;

&lt;p&gt;"Works on my machine" turned out to be the weakest verification strategy of all.&lt;/p&gt;




&lt;h2&gt;
  
  
  4. GitHub Actions lied with a green checkmark
&lt;/h2&gt;

&lt;p&gt;The deployment workflow looked successful.&lt;/p&gt;

&lt;p&gt;The live website returned 404.&lt;/p&gt;

&lt;p&gt;The culprit?&lt;/p&gt;

&lt;p&gt;A multi-line shell block:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;curl ... | sh
build
deploy
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The installation silently failed.&lt;/p&gt;

&lt;p&gt;The following commands still exited successfully.&lt;/p&gt;

&lt;p&gt;GitHub reported the entire step as green.&lt;/p&gt;

&lt;p&gt;The fix wasn't clever.&lt;/p&gt;

&lt;p&gt;It was boring.&lt;/p&gt;

&lt;p&gt;I switched to:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;cargo &lt;span class="nb"&gt;install &lt;/span&gt;wasm-pack
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;and added a deployment check that refuses to publish unless the expected build artifacts actually exist.&lt;/p&gt;

&lt;p&gt;A green checkmark only matters if it's checking the thing you care about.&lt;/p&gt;




&lt;h1&gt;
  
  
  How I proved behavioral equivalence
&lt;/h1&gt;

&lt;p&gt;The implementation wasn't the deliverable.&lt;/p&gt;

&lt;p&gt;Evidence was.&lt;/p&gt;

&lt;p&gt;I ended up with six independent layers of verification.&lt;/p&gt;

&lt;h3&gt;
  
  
  1. Upstream tests
&lt;/h3&gt;

&lt;p&gt;Run the original &lt;strong&gt;natsort&lt;/strong&gt; test suite without modifying it.&lt;/p&gt;

&lt;p&gt;Tests excluded only when Rust genuinely cannot reproduce Python-specific behavior, and every exclusion is documented.&lt;/p&gt;

&lt;h3&gt;
  
  
  2. Differential fuzzing
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;27,000&lt;/strong&gt; seeded comparisons across all algorithm variants.&lt;/p&gt;

&lt;p&gt;Same inputs.&lt;/p&gt;

&lt;p&gt;Same outputs.&lt;/p&gt;

&lt;p&gt;Reproducible failures.&lt;/p&gt;

&lt;h3&gt;
  
  
  3. CLI differential testing
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;2,800&lt;/strong&gt; CLI invocations comparing:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;stdout&lt;/li&gt;
&lt;li&gt;stderr&lt;/li&gt;
&lt;li&gt;exit codes&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Matching output alone isn't enough.&lt;/p&gt;

&lt;p&gt;Programs communicate failure through exit codes too.&lt;/p&gt;

&lt;h3&gt;
  
  
  4. Property testing
&lt;/h3&gt;

&lt;p&gt;Randomized verification of:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;idempotence&lt;/li&gt;
&lt;li&gt;antisymmetry&lt;/li&gt;
&lt;li&gt;transitivity&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;These find classes of bugs that handwritten examples never will.&lt;/p&gt;

&lt;h3&gt;
  
  
  5. Mutation testing
&lt;/h3&gt;

&lt;p&gt;I deliberately broke my implementation.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;reversed comparisons&lt;/li&gt;
&lt;li&gt;removed signs&lt;/li&gt;
&lt;li&gt;altered parsing logic&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;If the verification pipeline couldn't detect injected bugs, it wasn't trustworthy.&lt;/p&gt;

&lt;p&gt;Every injected mutation was caught.&lt;/p&gt;

&lt;h3&gt;
  
  
  6. Honest benchmarking
&lt;/h3&gt;

&lt;p&gt;Instead of publishing a single "10× faster" headline, I reported:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;mean&lt;/li&gt;
&lt;li&gt;median&lt;/li&gt;
&lt;li&gt;p95&lt;/li&gt;
&lt;li&gt;p99&lt;/li&gt;
&lt;li&gt;peak memory&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The real result was &lt;strong&gt;6–8× faster&lt;/strong&gt;, depending on workload.&lt;/p&gt;

&lt;p&gt;Honest numbers build more confidence than inflated ones.&lt;/p&gt;




&lt;h1&gt;
  
  
  The decision I'd take back
&lt;/h1&gt;

&lt;p&gt;I spent a huge amount of time chasing perfect Unicode parity.&lt;/p&gt;

&lt;p&gt;It was technically rewarding.&lt;/p&gt;

&lt;p&gt;It pushed differential matching from roughly &lt;strong&gt;43%&lt;/strong&gt; to &lt;strong&gt;100%&lt;/strong&gt; across the full Unicode character set.&lt;/p&gt;

&lt;p&gt;I'm proud of that work.&lt;/p&gt;

&lt;p&gt;But I crossed the point of diminishing returns.&lt;/p&gt;

&lt;p&gt;One adapter-level discrepancy survived until the deadline on a single CI runner.&lt;/p&gt;

&lt;p&gt;I excluded that specific test rather than claim support I couldn't fully defend.&lt;/p&gt;

&lt;p&gt;Looking back, I'd stop polishing Unicode earlier and spend those hours investigating the remaining verification discrepancy instead.&lt;/p&gt;

&lt;p&gt;Verification gets exponentially more expensive near the finish line.&lt;/p&gt;

&lt;p&gt;Knowing when to stop is part of engineering.&lt;/p&gt;




&lt;h1&gt;
  
  
  Final numbers
&lt;/h1&gt;

&lt;p&gt;By the end of the project:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Original upstream tests running against Rust&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;27,000&lt;/strong&gt; differential fuzz comparisons&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;2,800&lt;/strong&gt; CLI differential runs&lt;/li&gt;
&lt;li&gt;Property testing&lt;/li&gt;
&lt;li&gt;Mutation testing&lt;/li&gt;
&lt;li&gt;Honest benchmark suite&lt;/li&gt;
&lt;li&gt;6–8× speedup&lt;/li&gt;
&lt;li&gt;One upstream bug reported&lt;/li&gt;
&lt;li&gt;Cross-platform verification on Linux and Windows&lt;/li&gt;
&lt;/ul&gt;




&lt;h1&gt;
  
  
  Reproduce everything
&lt;/h1&gt;

&lt;p&gt;Everything needed to verify the project is public.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Demo&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://youtu.be/jVz_2AP4U4w" rel="noopener noreferrer"&gt;https://youtu.be/jVz_2AP4U4w&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Live WASM demo + verification dashboard&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://codewitharyan29.github.io/Port-Mortem/" rel="noopener noreferrer"&gt;https://codewitharyan29.github.io/Port-Mortem/&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Repository&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://github.com/codewitharyan29/Port-Mortem" rel="noopener noreferrer"&gt;https://github.com/codewitharyan29/Port-Mortem&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Upstream issue&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://github.com/SethMMorton/natsort/issues/192" rel="noopener noreferrer"&gt;https://github.com/SethMMorton/natsort/issues/192&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;**Hackathon rapters : &lt;a href="https://www.raptors.dev/project/code-resurrection-2026-port-mortem" rel="noopener noreferrer"&gt;https://www.raptors.dev/project/code-resurrection-2026-port-mortem&lt;/a&gt;&lt;/p&gt;




&lt;h1&gt;
  
  
  Closing thoughts
&lt;/h1&gt;

&lt;p&gt;Anyone with an AI coding agent can produce something that compiles.&lt;/p&gt;

&lt;p&gt;Compilation is the easy part.&lt;/p&gt;

&lt;p&gt;The difficult part is proving—without hand-waving—that another implementation behaves the same as the original across thousands of edge cases, different operating systems, different execution paths, and even the original project's own bugs.&lt;/p&gt;

&lt;p&gt;By the end of this project, I stopped measuring success by whether the Rust code compiled.&lt;/p&gt;

&lt;p&gt;I measured success by how much evidence I had that it behaved like Python.&lt;/p&gt;

&lt;h2&gt;
  
  
  Built for Port Mortem / Code Resurrection by Hackathon Raptors
&lt;/h2&gt;

&lt;p&gt;&lt;em&gt;#PortMortem2026 #HackathonRaptors #Rust #Python #OpenSource #SystemsProgramming #Testing #Verification #Fuzzing&lt;/em&gt;&lt;br&gt;
**&lt;/p&gt;

</description>
      <category>algorithms</category>
      <category>python</category>
      <category>rust</category>
    </item>
  </channel>
</rss>
